daiso 1.0.5 → 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 (64) hide show
  1. package/README.md +101 -7
  2. package/dist/api/actionsProxy.js +111 -21
  3. package/dist/api/configStatus.js +23 -0
  4. package/dist/api/gs25Handlers.js +68 -22
  5. package/dist/api/handlers.js +32 -3
  6. package/dist/api/healthChecks.js +233 -20
  7. package/dist/api/lottemartHandlers.js +17 -1
  8. package/dist/api/placesHandlers.js +44 -0
  9. package/dist/api/response.js +2 -0
  10. package/dist/api/routes/healthRoutes.js +20 -3
  11. package/dist/api/routes/placesRoutes.js +9 -0
  12. package/dist/bin.js +0 -0
  13. package/dist/cli/args.js +21 -0
  14. package/dist/cli/commandOptions.js +96 -0
  15. package/dist/cli/commands/convenience.js +73 -1
  16. package/dist/cli/commands/daiso.js +18 -2
  17. package/dist/cli/http.js +58 -0
  18. package/dist/cli/interactiveItemSearch.js +4 -2
  19. package/dist/cli/interactivePrompt.js +3 -2
  20. package/dist/cli.js +25 -1
  21. package/dist/cliHelp.js +20 -6
  22. package/dist/cliInteractive.js +75 -6
  23. package/dist/cliPicker.js +1 -0
  24. package/dist/cliRenderer.js +93 -3
  25. package/dist/core/errors.js +66 -0
  26. package/dist/core/registry.js +246 -3
  27. package/dist/index.js +19 -4
  28. package/dist/pages/openapiFullSpec.js +3 -0
  29. package/dist/pages/openapiSpecActions.js +308 -42
  30. package/dist/pages/openapiSpecPathsCu.js +7 -0
  31. package/dist/pages/openapiSpecPathsLotteMart.js +14 -0
  32. package/dist/pages/openapiSpecPathsOliveyoung.js +35 -0
  33. package/dist/pages/openapiSpecPathsPlaces.js +85 -0
  34. package/dist/pages/promptReferenceText.js +16 -7
  35. package/dist/pages/promptRetailCinemaText.js +24 -2
  36. package/dist/services/daiso/client.js +8 -0
  37. package/dist/services/daiso/index.js +2 -0
  38. package/dist/services/daiso/tools/checkInventory.js +4 -2
  39. package/dist/services/daiso/tools/findInventoryByName.js +222 -0
  40. package/dist/services/daiso/tools/getDisplayLocation.js +2 -2
  41. package/dist/services/emart24/client.js +9 -0
  42. package/dist/services/emart24/tools/searchProducts.js +27 -17
  43. package/dist/services/gs25/api.js +3 -0
  44. package/dist/services/gs25/client.js +216 -30
  45. package/dist/services/gs25/index.js +11 -3
  46. package/dist/services/gs25/tools/checkInventory.js +39 -12
  47. package/dist/services/gs25/tools/findNearbyStores.js +19 -5
  48. package/dist/services/gs25/tools/searchProducts.js +39 -25
  49. package/dist/services/lottemart/client.js +10 -1
  50. package/dist/services/lottemart/tools/searchProducts.js +8 -1
  51. package/dist/services/lottemart/zetta.js +1 -1
  52. package/dist/services/oliveyoung/client.js +206 -86
  53. package/dist/services/oliveyoung/tools/checkInventory.js +7 -2
  54. package/dist/services/places/client.js +116 -0
  55. package/dist/services/places/index.js +23 -0
  56. package/dist/services/places/tools/searchNearby.js +56 -0
  57. package/dist/services/places/types.js +1 -0
  58. package/dist/services/seveneleven/client.js +7 -0
  59. package/dist/services/seveneleven/tools/searchProducts.js +29 -18
  60. package/dist/utils/http.js +82 -10
  61. package/dist/utils/zyte.js +74 -26
  62. package/openapi.json +22 -3
  63. package/openapi.yaml +16 -3
  64. package/package.json +28 -9
@@ -14,48 +14,314 @@ const ACTION_QUERY_PARAMETERS = [
14
14
  enum: ACTION_QUERY_ACTIONS,
15
15
  },
16
16
  },
17
- { name: 'q', in: 'query', required: false, description: '다이소 제품 검색어', schema: { type: 'string' } },
18
- { name: 'query', in: 'query', required: false, description: '세븐일레븐 상품 검색어', schema: { type: 'string' } },
19
- { name: 'itemCode', in: 'query', required: false, description: 'GS25 상품 코드', schema: { type: 'string' } },
20
- { name: 'productId', in: 'query', required: false, description: '제품 ID 또는 상세 조회용 path 파라미터', schema: { type: 'string' } },
21
- { name: 'keyword', in: 'query', required: false, description: '공통 키워드 파라미터', schema: { type: 'string' } },
22
- { name: 'sido', in: 'query', required: false, description: '다이소 시/도', schema: { type: 'string' } },
23
- { name: 'gugun', in: 'query', required: false, description: '다이소 구/군', schema: { type: 'string' } },
24
- { name: 'dong', in: 'query', required: false, description: '다이소 동', schema: { type: 'string' } },
25
- { name: 'area', in: 'query', required: false, description: '롯데마트 지역', schema: { type: 'string' } },
26
- { name: 'area1', in: 'query', required: false, description: '이마트24 1차 지역', schema: { type: 'string' } },
27
- { name: 'area2', in: 'query', required: false, description: '이마트24 2차 지역', schema: { type: 'string' } },
28
- { name: 'areaCode', in: 'query', required: false, description: '메가박스 지역 코드', schema: { type: 'string' } },
29
- { name: 'brandVariant', in: 'query', required: false, description: '롯데마트 브랜드 변형', schema: { type: 'string' } },
30
- { name: 'lat', in: 'query', required: false, description: '위도', schema: { type: 'number', format: 'float' } },
31
- { name: 'lng', in: 'query', required: false, description: '경도', schema: { type: 'number', format: 'float' } },
32
- { name: 'page', in: 'query', required: false, description: '페이지 번호', schema: { type: 'integer' } },
33
- { name: 'pageIdx', in: 'query', required: false, description: '올리브영 페이지 번호', schema: { type: 'integer' } },
34
- { name: 'pageLimit', in: 'query', required: false, description: '롯데마트 추가 페이지 수', schema: { type: 'integer' } },
35
- { name: 'pageSize', in: 'query', required: false, description: '페이지 크기', schema: { type: 'integer' } },
36
- { name: 'size', in: 'query', required: false, description: '페이지 크기 또는 검색 크기', schema: { type: 'integer' } },
37
- { name: 'offset', in: 'query', required: false, description: 'CU 검색 오프셋', schema: { type: 'integer' } },
38
- { name: 'limit', in: 'query', required: false, description: '최대 결과 수', schema: { type: 'integer' } },
39
- { name: 'storeLimit', in: 'query', required: false, description: '매장 결과 제한 수', schema: { type: 'integer' } },
40
- { name: 'storeCode', in: 'query', required: false, description: '매장 코드', schema: { type: 'string' } },
41
- { name: 'storeName', in: 'query', required: false, description: '매장명', schema: { type: 'string' } },
42
- { name: 'storeKeyword', in: 'query', required: false, description: '매장 키워드', schema: { type: 'string' } },
43
- { name: 'playDate', in: 'query', required: false, description: '상영일(YYYYMMDD)', schema: { type: 'string' } },
44
- { name: 'regionCode', in: 'query', required: false, description: 'CGV 지역 코드', schema: { type: 'string' } },
45
- { name: 'theaterCode', in: 'query', required: false, description: 'CGV 극장 코드', schema: { type: 'string' } },
46
- { name: 'theaterId', in: 'query', required: false, description: '메가박스/롯데시네마 지점 ID', schema: { type: 'string' } },
47
- { name: 'movieCode', in: 'query', required: false, description: 'CGV 영화 코드', schema: { type: 'string' } },
48
- { name: 'movieId', in: 'query', required: false, description: '메가박스/롯데시네마 영화 ID', schema: { type: 'string' } },
49
- { name: 'pluCd', in: 'query', required: false, description: '이마트24 상품 PLU 코드', schema: { type: 'string' } },
50
- { name: 'bizNoArr', in: 'query', required: false, description: '이마트24 사업자번호 목록', schema: { type: 'string' } },
51
- { name: 'label', in: 'query', required: false, description: '세븐일레븐 인기 검색어 라벨', schema: { type: 'string' } },
52
- { name: 'sort', in: 'query', required: false, description: '정렬 기준', schema: { type: 'string' } },
53
- { name: 'searchSort', in: 'query', required: false, description: 'CU 검색 정렬 기준', schema: { type: 'string' } },
54
- { name: 'service24h', in: 'query', required: false, description: '이마트24 24시간 필터', schema: { type: 'boolean' } },
55
- { name: 'includeSoldOut', in: 'query', required: false, description: '올리브영 품절 포함 여부', schema: { type: 'boolean' } },
56
- { name: 'includeIssues', in: 'query', required: false, description: '세븐일레븐 이달의 행사지 포함 여부', schema: { type: 'boolean' } },
57
- { name: 'includeExhibition', in: 'query', required: false, description: '세븐일레븐 전시상품 포함 여부', schema: { type: 'boolean' } },
58
- { name: 'timeoutMs', in: 'query', required: false, description: '세븐일레븐 inventory 타임아웃', schema: { type: 'integer' } },
17
+ {
18
+ name: 'q',
19
+ in: 'query',
20
+ required: false,
21
+ description: '다이소 제품 검색어',
22
+ schema: { type: 'string' },
23
+ },
24
+ {
25
+ name: 'query',
26
+ in: 'query',
27
+ required: false,
28
+ description: '세븐일레븐 상품 검색어',
29
+ schema: { type: 'string' },
30
+ },
31
+ {
32
+ name: 'itemCode',
33
+ in: 'query',
34
+ required: false,
35
+ description: 'GS25 상품 코드',
36
+ schema: { type: 'string' },
37
+ },
38
+ {
39
+ name: 'productId',
40
+ in: 'query',
41
+ required: false,
42
+ description: '제품 ID 또는 상세 조회용 path 파라미터. 상품명만 알면 먼저 daisoSearchProducts 또는 /api/daiso/products로 productId를 확인하세요.',
43
+ schema: { type: 'string' },
44
+ },
45
+ {
46
+ name: 'keyword',
47
+ in: 'query',
48
+ required: false,
49
+ description: '공통 키워드 파라미터',
50
+ schema: { type: 'string' },
51
+ },
52
+ {
53
+ name: 'location',
54
+ in: 'query',
55
+ required: false,
56
+ description: '주변 장소 검색 지역/역/주소 키워드',
57
+ schema: { type: 'string' },
58
+ },
59
+ {
60
+ name: 'category',
61
+ in: 'query',
62
+ required: false,
63
+ description: '주변 장소 카테고리(restaurant, cafe, food, dessert, all)',
64
+ schema: { type: 'string' },
65
+ },
66
+ {
67
+ name: 'sido',
68
+ in: 'query',
69
+ required: false,
70
+ description: '다이소 시/도',
71
+ schema: { type: 'string' },
72
+ },
73
+ {
74
+ name: 'gugun',
75
+ in: 'query',
76
+ required: false,
77
+ description: '다이소 구/군',
78
+ schema: { type: 'string' },
79
+ },
80
+ {
81
+ name: 'dong',
82
+ in: 'query',
83
+ required: false,
84
+ description: '다이소 동',
85
+ schema: { type: 'string' },
86
+ },
87
+ {
88
+ name: 'area',
89
+ in: 'query',
90
+ required: false,
91
+ description: '롯데마트 지역',
92
+ schema: { type: 'string' },
93
+ },
94
+ {
95
+ name: 'area1',
96
+ in: 'query',
97
+ required: false,
98
+ description: '이마트24 1차 지역',
99
+ schema: { type: 'string' },
100
+ },
101
+ {
102
+ name: 'area2',
103
+ in: 'query',
104
+ required: false,
105
+ description: '이마트24 2차 지역',
106
+ schema: { type: 'string' },
107
+ },
108
+ {
109
+ name: 'areaCode',
110
+ in: 'query',
111
+ required: false,
112
+ description: '메가박스 지역 코드',
113
+ schema: { type: 'string' },
114
+ },
115
+ {
116
+ name: 'brandVariant',
117
+ in: 'query',
118
+ required: false,
119
+ description: '롯데마트 브랜드 변형',
120
+ schema: { type: 'string' },
121
+ },
122
+ {
123
+ name: 'lat',
124
+ in: 'query',
125
+ required: false,
126
+ description: '위도',
127
+ schema: { type: 'number', format: 'float' },
128
+ },
129
+ {
130
+ name: 'lng',
131
+ in: 'query',
132
+ required: false,
133
+ description: '경도',
134
+ schema: { type: 'number', format: 'float' },
135
+ },
136
+ {
137
+ name: 'page',
138
+ in: 'query',
139
+ required: false,
140
+ description: '페이지 번호',
141
+ schema: { type: 'integer' },
142
+ },
143
+ {
144
+ name: 'pageIdx',
145
+ in: 'query',
146
+ required: false,
147
+ description: '올리브영 페이지 번호',
148
+ schema: { type: 'integer' },
149
+ },
150
+ {
151
+ name: 'pageLimit',
152
+ in: 'query',
153
+ required: false,
154
+ description: '롯데마트 추가 페이지 수',
155
+ schema: { type: 'integer' },
156
+ },
157
+ {
158
+ name: 'pageSize',
159
+ in: 'query',
160
+ required: false,
161
+ description: '페이지 크기',
162
+ schema: { type: 'integer' },
163
+ },
164
+ {
165
+ name: 'size',
166
+ in: 'query',
167
+ required: false,
168
+ description: '페이지 크기 또는 검색 크기',
169
+ schema: { type: 'integer' },
170
+ },
171
+ {
172
+ name: 'offset',
173
+ in: 'query',
174
+ required: false,
175
+ description: 'CU 검색 오프셋',
176
+ schema: { type: 'integer' },
177
+ },
178
+ {
179
+ name: 'limit',
180
+ in: 'query',
181
+ required: false,
182
+ description: '최대 결과 수',
183
+ schema: { type: 'integer' },
184
+ },
185
+ {
186
+ name: 'storeLimit',
187
+ in: 'query',
188
+ required: false,
189
+ description: '매장 결과 제한 수',
190
+ schema: { type: 'integer' },
191
+ },
192
+ {
193
+ name: 'storeCode',
194
+ in: 'query',
195
+ required: false,
196
+ description: '매장 코드. 다이소 진열 위치는 /api/daiso/inventory 응답의 storeInventory.stores[].storeCode에서 확인하세요.',
197
+ schema: { type: 'string' },
198
+ },
199
+ {
200
+ name: 'storeName',
201
+ in: 'query',
202
+ required: false,
203
+ description: '매장명. 롯데마트 상품 조회에서 storeCode가 없을 때 사용하며, 모르면 먼저 lottemartFindNearbyStores 또는 /api/lottemart/stores로 확인하세요.',
204
+ schema: { type: 'string' },
205
+ },
206
+ {
207
+ name: 'storeKeyword',
208
+ in: 'query',
209
+ required: false,
210
+ description: '매장 키워드',
211
+ schema: { type: 'string' },
212
+ },
213
+ {
214
+ name: 'playDate',
215
+ in: 'query',
216
+ required: false,
217
+ description: '상영일(YYYYMMDD)',
218
+ schema: { type: 'string' },
219
+ },
220
+ {
221
+ name: 'regionCode',
222
+ in: 'query',
223
+ required: false,
224
+ description: 'CGV 지역 코드',
225
+ schema: { type: 'string' },
226
+ },
227
+ {
228
+ name: 'theaterCode',
229
+ in: 'query',
230
+ required: false,
231
+ description: 'CGV 극장 코드',
232
+ schema: { type: 'string' },
233
+ },
234
+ {
235
+ name: 'theaterId',
236
+ in: 'query',
237
+ required: false,
238
+ description: '메가박스/롯데시네마 지점 ID',
239
+ schema: { type: 'string' },
240
+ },
241
+ {
242
+ name: 'movieCode',
243
+ in: 'query',
244
+ required: false,
245
+ description: 'CGV 영화 코드',
246
+ schema: { type: 'string' },
247
+ },
248
+ {
249
+ name: 'movieId',
250
+ in: 'query',
251
+ required: false,
252
+ description: '메가박스/롯데시네마 영화 ID',
253
+ schema: { type: 'string' },
254
+ },
255
+ {
256
+ name: 'pluCd',
257
+ in: 'query',
258
+ required: false,
259
+ description: '이마트24 상품 PLU 코드',
260
+ schema: { type: 'string' },
261
+ },
262
+ {
263
+ name: 'bizNoArr',
264
+ in: 'query',
265
+ required: false,
266
+ description: '이마트24 사업자번호 목록',
267
+ schema: { type: 'string' },
268
+ },
269
+ {
270
+ name: 'label',
271
+ in: 'query',
272
+ required: false,
273
+ description: '세븐일레븐 인기 검색어 라벨',
274
+ schema: { type: 'string' },
275
+ },
276
+ {
277
+ name: 'sort',
278
+ in: 'query',
279
+ required: false,
280
+ description: '정렬 기준',
281
+ schema: { type: 'string' },
282
+ },
283
+ {
284
+ name: 'searchSort',
285
+ in: 'query',
286
+ required: false,
287
+ description: 'CU 검색 정렬 기준',
288
+ schema: { type: 'string' },
289
+ },
290
+ {
291
+ name: 'service24h',
292
+ in: 'query',
293
+ required: false,
294
+ description: '이마트24 24시간 필터',
295
+ schema: { type: 'boolean' },
296
+ },
297
+ {
298
+ name: 'includeSoldOut',
299
+ in: 'query',
300
+ required: false,
301
+ description: '올리브영 품절 포함 여부',
302
+ schema: { type: 'boolean' },
303
+ },
304
+ {
305
+ name: 'includeIssues',
306
+ in: 'query',
307
+ required: false,
308
+ description: '세븐일레븐 이달의 행사지 포함 여부',
309
+ schema: { type: 'boolean' },
310
+ },
311
+ {
312
+ name: 'includeExhibition',
313
+ in: 'query',
314
+ required: false,
315
+ description: '세븐일레븐 전시상품 포함 여부',
316
+ schema: { type: 'boolean' },
317
+ },
318
+ {
319
+ name: 'timeoutMs',
320
+ in: 'query',
321
+ required: false,
322
+ description: '세븐일레븐 inventory 타임아웃',
323
+ schema: { type: 'integer' },
324
+ },
59
325
  ];
60
326
  export function generateOpenApiSpec(baseUrl) {
61
327
  return {
@@ -114,6 +114,13 @@ export const OPENAPI_PATHS_CU = {
114
114
  description: '재고 정렬 방식',
115
115
  schema: { type: 'string', default: 'recom' },
116
116
  },
117
+ {
118
+ name: 'storeCheck',
119
+ in: 'query',
120
+ required: false,
121
+ description: 'false이면 주변 매장 조회를 생략하고 재고 상품 검색만 수행',
122
+ schema: { type: 'boolean', default: true },
123
+ },
117
124
  ],
118
125
  responses: {
119
126
  '200': {
@@ -111,6 +111,20 @@ export const OPENAPI_PATHS_LOTTEMART = {
111
111
  description: '추가 조회할 최대 페이지 수',
112
112
  schema: { type: 'integer', default: 3, minimum: 1, maximum: 20 },
113
113
  },
114
+ {
115
+ name: 'source',
116
+ in: 'query',
117
+ required: false,
118
+ description: '상품 검색 경로',
119
+ schema: { type: 'string', enum: ['auto', 'legacy', 'zetta'], default: 'auto' },
120
+ },
121
+ {
122
+ name: 'timeoutMs',
123
+ in: 'query',
124
+ required: false,
125
+ description: '요청 제한 시간(ms)',
126
+ schema: { type: 'integer', default: 45000, minimum: 1 },
127
+ },
114
128
  ],
115
129
  responses: {
116
130
  '200': {
@@ -44,6 +44,13 @@ export const OPENAPI_PATHS_OLIVEYOUNG = {
44
44
  description: '품절 포함 여부 (true/false)',
45
45
  schema: { type: 'boolean', default: false },
46
46
  },
47
+ {
48
+ name: 'timeoutMs',
49
+ in: 'query',
50
+ required: false,
51
+ description: '요청 제한 시간(ms)',
52
+ schema: { type: 'integer', default: 15000, minimum: 1 },
53
+ },
47
54
  ],
48
55
  responses: {
49
56
  '200': {
@@ -115,6 +122,13 @@ export const OPENAPI_PATHS_OLIVEYOUNG = {
115
122
  description: '최대 결과 수',
116
123
  schema: { type: 'integer', default: 20, minimum: 1, maximum: 100 },
117
124
  },
125
+ {
126
+ name: 'timeoutMs',
127
+ in: 'query',
128
+ required: false,
129
+ description: '요청 제한 시간(ms)',
130
+ schema: { type: 'integer', default: 15000, minimum: 1 },
131
+ },
118
132
  ],
119
133
  responses: {
120
134
  '200': {
@@ -199,6 +213,27 @@ export const OPENAPI_PATHS_OLIVEYOUNG = {
199
213
  description: '품절 포함 여부 (true/false)',
200
214
  schema: { type: 'boolean', default: false },
201
215
  },
216
+ {
217
+ name: 'storeLimit',
218
+ in: 'query',
219
+ required: false,
220
+ description: '반환할 주변 매장 최대 수',
221
+ schema: { type: 'integer', default: 10, minimum: 1, maximum: 100 },
222
+ },
223
+ {
224
+ name: 'stockCheckLimit',
225
+ in: 'query',
226
+ required: false,
227
+ description: '주변 매장별 재고를 보강할 상품 수. 낮출수록 빠르게 응답합니다.',
228
+ schema: { type: 'integer', default: 5, minimum: 0, maximum: 20 },
229
+ },
230
+ {
231
+ name: 'timeoutMs',
232
+ in: 'query',
233
+ required: false,
234
+ description: '요청 제한 시간(ms)',
235
+ schema: { type: 'integer', default: 15000, minimum: 1 },
236
+ },
202
237
  ],
203
238
  responses: {
204
239
  '200': {
@@ -0,0 +1,85 @@
1
+ /**
2
+ * OpenAPI 경로 정의 (장소 검색)
3
+ */
4
+ export const OPENAPI_PATHS_PLACES = {
5
+ '/api/places/search': {
6
+ get: {
7
+ operationId: 'placesSearchNearby',
8
+ summary: '음식점/카페 등 주변 장소 검색',
9
+ description: '네이버 지역 검색으로 특정 지역의 음식점, 카페, 디저트 가게 등 주변 장소를 조회합니다. 좌표 반경 검색이 아니라 키워드 기반 검색입니다.',
10
+ parameters: [
11
+ {
12
+ name: 'location',
13
+ in: 'query',
14
+ required: false,
15
+ description: '지역/역/주소 키워드 (예: 강남역, 성수동)',
16
+ schema: { type: 'string' },
17
+ },
18
+ {
19
+ name: 'keyword',
20
+ in: 'query',
21
+ required: false,
22
+ description: '직접 검색어 (예: 라멘, 브런치, 조용한 카페)',
23
+ schema: { type: 'string' },
24
+ },
25
+ {
26
+ name: 'category',
27
+ in: 'query',
28
+ required: false,
29
+ description: '장소 카테고리',
30
+ schema: {
31
+ type: 'string',
32
+ enum: ['restaurant', 'cafe', 'food', 'dessert', 'all'],
33
+ default: 'all',
34
+ },
35
+ },
36
+ {
37
+ name: 'limit',
38
+ in: 'query',
39
+ required: false,
40
+ description: '최대 결과 수',
41
+ schema: { type: 'integer', default: 5, minimum: 1, maximum: 5 },
42
+ },
43
+ {
44
+ name: 'sort',
45
+ in: 'query',
46
+ required: false,
47
+ description: '정렬 방식',
48
+ schema: { type: 'string', enum: ['random', 'comment'], default: 'random' },
49
+ },
50
+ ],
51
+ responses: {
52
+ '200': {
53
+ description: '검색 성공',
54
+ content: {
55
+ 'application/json': {
56
+ schema: {
57
+ type: 'object',
58
+ properties: {
59
+ success: { type: 'boolean' },
60
+ data: {
61
+ type: 'object',
62
+ properties: {
63
+ provider: { type: 'string', example: 'naverLocal' },
64
+ searchMode: { type: 'string', example: 'keyword' },
65
+ query: { type: 'string', example: '강남역 카페' },
66
+ places: { type: 'array', items: { type: 'object' } },
67
+ },
68
+ },
69
+ },
70
+ },
71
+ },
72
+ },
73
+ },
74
+ '400': {
75
+ description: '잘못된 요청 (위치/검색어 누락)',
76
+ content: {
77
+ 'application/json': {
78
+ schema: { $ref: '#/components/schemas/ErrorResponse' },
79
+ },
80
+ },
81
+ },
82
+ },
83
+ },
84
+ },
85
+ };
@@ -57,6 +57,7 @@ export function buildPromptReferenceText(baseUrl) {
57
57
  | LOTTEMART_STORE_SEARCH_FAILED | 롯데마트 매장 조회 실패 |
58
58
  | LOTTEMART_PRODUCT_SEARCH_FAILED | 롯데마트 상품 조회 실패 |
59
59
  | SEVENELEVEN_INVENTORY_CHECK_FAILED | 세븐일레븐 재고 조회 실패 |
60
+ | PLACES_SEARCH_FAILED | 주변 장소 검색 실패 |
60
61
 
61
62
  ---
62
63
 
@@ -81,16 +82,23 @@ export function buildPromptReferenceText(baseUrl) {
81
82
  - 결과에서 원하는 제품의 id 확인
82
83
  - /api/daiso/inventory에 해당 id로 재고 조회
83
84
  - 진열 위치가 필요하면 재고 응답의 storeCode를 확인한 뒤 /api/daiso/display-location에 productId + storeCode로 조회
84
- 6. **상품명 단계 검색**:
85
+ - 다이소 재고 조회는 storeCode가 필요하지 않습니다.
86
+ 6. **최소 정보 요청 처리**:
87
+ - 사용자가 상품명만 주면 먼저 상품 검색 도구로 후보를 찾고, 응답의 productId/itemCode/pluCd를 다음 단계에 전달합니다.
88
+ - 사용자가 위치를 대강 말하면 keyword/storeKeyword에 그대로 넣어 먼저 조회하고, 결과가 없을 때만 공백 제거, 역/점 접미어 제거 같은 변형을 시도합니다.
89
+ - 도구가 productId, itemCode, storeCode 같은 내부 ID를 요구하면 사용자에게 바로 ID를 요구하지 말고, 검색/재고 응답에서 먼저 확인합니다.
90
+ - "근처", "주변"처럼 위치가 없는 표현만 있으면 기본 좌표 결과를 단정하지 말고 사용자의 지역/역명/좌표를 물어봅니다.
91
+ 7. **상품명 단계 검색**:
85
92
  - 재고 검색은 어떤 서비스이든 검색어 전체를 먼저 그대로 조회하고, 실패할 때만 공백/브랜드어 제거, 표기 변형, 더 짧은 핵심어 순으로 단계적으로 넓혀서 다시 시도합니다.
86
93
  - 너무 넓은 축약어로만 매칭된 경우에는 확정 상품처럼 단정하지 말고, 추정 매칭임을 짧게 밝힌 뒤 재고를 안내합니다.
87
94
  - 위치/브랜드/상품명은 한 인자에 섞지 말고 가능한 한 분리해서 도구에 전달합니다.
88
- 7. **위치 기반 재고**: lat, lng 파라미터로 가까운 매장 우선 조회
89
- 8. **롯데마트 상품 조회**: /api/lottemart/products는 keyword와 함께 storeCode 또는 storeName이 필요합니다.
90
- 9. **세븐일레븐 재고 조회**: /api/seveneleven/inventory에 keyword + storeKeyword를 함께 주면 매장별 수량을 바로 확인할 수 있습니다.
91
- 10. **이마트24 재고 조회**: /api/emart24/inventory는 pluCd + storeKeyword 조합도 지원하므로, 상품 선택 뒤 매장 코드를 다시 모으지 않아도 됩니다.
92
- 11. **올리브영 재고 해석**: inventory.products[].storeInventory.stores[]가 있으면 그 매장별 stockLabel과 remainQuantity를 우선 사용하고, inStock는 그 주변 매장 기준 결과로 해석합니다.
93
- 12. **올리브영 상품 이미지 표시**: /api/oliveyoung/products 또는 oliveyoung_search_products 결과에 imageUrl이 있으면 목록형 답변에서도 각 상품 이미지를 생략하지 말고 모두 렌더링합니다. 특히 \`?l=ko\` 같은 query string을 삭제하지 말고 전체 URL 그대로 마크다운 이미지 src에 넣습니다.
95
+ 8. **위치 기반 재고**: lat, lng 파라미터로 가까운 매장 우선 조회
96
+ 9. **롯데마트 상품 조회**: /api/lottemart/products는 keyword와 함께 storeCode 또는 storeName이 필요합니다.
97
+ 10. **세븐일레븐 재고 조회**: /api/seveneleven/inventory에 keyword + storeKeyword를 함께 주면 매장별 수량을 바로 확인할 수 있습니다.
98
+ 11. **이마트24 재고 조회**: /api/emart24/inventory는 pluCd + storeKeyword 조합도 지원하므로, 상품 선택 뒤 매장 코드를 다시 모으지 않아도 됩니다.
99
+ 12. **올리브영 재고 해석**: inventory.products[].storeInventory.stores[]가 있으면 그 매장별 stockLabel과 remainQuantity를 우선 사용하고, inStock는 그 주변 매장 기준 결과로 해석합니다.
100
+ 13. **올리브영 상품 이미지 표시**: /api/oliveyoung/products 또는 oliveyoung_search_products 결과에 imageUrl이 있으면 목록형 답변에서도 각 상품 이미지를 생략하지 말고 모두 렌더링합니다. 특히 \`?l=ko\` 같은 query string을 삭제하지 말고 전체 URL 그대로 마크다운 이미지 src에 넣습니다.
101
+ 14. **주변 음식점/카페 검색**: 음식점이나 카페만 물으면 \`places_search_nearby\` 또는 /api/places/search를 먼저 사용합니다. 이 기능은 네이버 지역 검색 기반 키워드 검색이므로 좌표 반경 검색처럼 단정하지 말고, \`searchMode: "keyword"\`임을 필요할 때 짧게 밝혀주세요.
94
102
 
95
103
  ---
96
104
 
@@ -115,6 +123,7 @@ MCP 연결 정보: ${baseUrl}/mcp
115
123
  - seveneleven_check_inventory: 세븐일레븐 재고 조회
116
124
  - seveneleven_get_search_popwords: 세븐일레븐 인기 검색어 조회
117
125
  - seveneleven_get_catalog_snapshot: 세븐일레븐 카탈로그 조회
126
+ - places_search_nearby: 음식점/카페 등 주변 장소 검색
118
127
  - oliveyoung_search_products: 올리브영 상품 검색
119
128
  - oliveyoung_find_nearby_stores: 올리브영 주변 매장 탐색
120
129
  - oliveyoung_check_inventory: 올리브영 재고 파악
@@ -2,7 +2,28 @@
2
2
  * 프롬프트 리테일/영화 API 섹션
3
3
  */
4
4
  export function buildPromptRetailCinemaText(baseUrl) {
5
- return `### 6-1. 롯데마트 매장/상품 조회
5
+ return `### 6-0. 주변 음식점/카페 검색
6
+
7
+ **설명**: 네이버 지역 검색으로 특정 지역의 음식점, 카페, 디저트 가게 등 주변 장소를 조회합니다. 좌표 반경 검색이 아니라 \`강남역 카페\` 같은 키워드 기반 검색입니다.
8
+
9
+ **URL**:
10
+ - ${baseUrl}/api/places/search?location={지역}&category={카테고리}
11
+ - ${baseUrl}/api/places/search?location={지역}&keyword={검색어}
12
+
13
+ **선택 파라미터**:
14
+ - location: 지역/역/주소 키워드 (예: 강남역, 성수동)
15
+ - category: restaurant, cafe, food, dessert, all
16
+ - keyword: 직접 검색어 (예: 라멘, 브런치, 조용한 카페)
17
+ - limit: 최대 결과 수 (기본값: 5, 네이버 지역 검색 최대 5)
18
+ - sort: random 또는 comment
19
+
20
+ **예시**:
21
+ - ${baseUrl}/api/places/search?location=강남역&category=cafe&limit=5
22
+ - ${baseUrl}/api/places/search?location=성수동&keyword=브런치&limit=5
23
+
24
+ ---
25
+
26
+ ### 6-1. 롯데마트 매장/상품 조회
6
27
 
7
28
  **설명**: 롯데마트 계열 매장 검색과 특정 매장 기준 상품 가격/재고 조회를 제공합니다.
8
29
 
@@ -18,12 +39,13 @@ export function buildPromptRetailCinemaText(baseUrl) {
18
39
  - limit: 최대 결과 수 (기본값: 20)
19
40
  - storeCode 또는 storeName: 상품 검색 대상 매장
20
41
  - pageLimit: 추가 조회할 최대 페이지 수 (기본값: 3)
42
+ - source: 상품 검색 경로 (auto, legacy, zetta). 빠른 검색이나 헬스체크에는 zetta 권장
21
43
 
22
44
  **예시**:
23
45
  - ${baseUrl}/api/lottemart/stores?keyword=잠실&area=서울&limit=10
24
46
  - ${baseUrl}/api/lottemart/stores?area=경기&brandVariant=lottemart&limit=10
25
47
  - ${baseUrl}/api/lottemart/products?keyword=콜라&storeName=강변점&area=서울
26
- - ${baseUrl}/api/lottemart/products?keyword=우유&storeCode=2301&pageLimit=2
48
+ - ${baseUrl}/api/lottemart/products?keyword=우유&storeCode=2301&pageLimit=2&source=zetta
27
49
 
28
50
  ---
29
51