daiso 1.0.7 → 1.0.9
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.
- package/README.md +106 -7
- package/dist/api/actionsProxy.js +10 -0
- package/dist/api/compareHandlers.js +26 -0
- package/dist/api/configStatus.js +8 -0
- package/dist/api/feedbackHandlers.js +60 -0
- package/dist/api/healthCheckDefinitions.js +170 -0
- package/dist/api/healthCheckShape.js +106 -0
- package/dist/api/healthCheckTypes.js +4 -0
- package/dist/api/healthChecks.js +3 -271
- package/dist/api/opinetHandlers.js +91 -0
- package/dist/api/routes/compareRoutes.js +9 -0
- package/dist/api/routes/feedbackRoutes.js +5 -0
- package/dist/api/routes/healthRoutes.js +17 -1
- package/dist/api/routes/opinetRoutes.js +24 -0
- package/dist/bin.js +0 -0
- package/dist/cli/commandOptions.js +24 -2
- package/dist/cli/commands/apiCommand.js +24 -0
- package/dist/cli/commands/cinema.js +65 -0
- package/dist/cli/commands/convenience.js +75 -100
- package/dist/cli/http.js +1 -1
- package/dist/cli.js +12 -1
- package/dist/cliHelp.js +55 -8
- package/dist/cliRenderer.js +15 -0
- package/dist/core/outputSchema.js +141 -0
- package/dist/core/registry.js +1 -126
- package/dist/index.js +96 -28
- package/dist/pages/openapiFullSpec.js +8 -0
- package/dist/pages/openapiSpecActions.js +70 -0
- package/dist/pages/openapiSpecPathsCompare.js +59 -0
- package/dist/pages/openapiSpecPathsFeedback.js +95 -0
- package/dist/pages/openapiSpecPathsOpinet.js +81 -0
- package/dist/pages/promptReferenceText.js +18 -1
- package/dist/pages/promptRetailCinemaText.js +21 -4
- package/dist/services/compare/client.js +133 -0
- package/dist/services/compare/index.js +17 -0
- package/dist/services/compare/tools/compareProducts.js +45 -0
- package/dist/services/compare/types.js +1 -0
- package/dist/services/feedback/client.js +109 -0
- package/dist/services/feedback/index.js +22 -0
- package/dist/services/feedback/tools/submitDeveloperRequest.js +54 -0
- package/dist/services/feedback/types.js +3 -0
- package/dist/services/gs25/client.js +1 -84
- package/dist/services/gs25/productSearch.js +97 -0
- package/dist/services/oliveyoung/client.js +1 -55
- package/dist/services/oliveyoung/normalize.js +58 -0
- package/dist/services/opinet/client.js +231 -0
- package/dist/services/opinet/index.js +26 -0
- package/dist/services/opinet/location.js +119 -0
- package/dist/services/opinet/tools.js +80 -0
- package/dist/services/opinet/types.js +1 -0
- package/openapi.json +92 -0
- package/openapi.yaml +62 -0
- package/package.json +18 -17
|
@@ -6,6 +6,25 @@ 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
|
+
import { runApiCommand } from './apiCommand.js';
|
|
10
|
+
export async function handleCompareProducts(options, deps) {
|
|
11
|
+
const parsed = parseCliArgs(options);
|
|
12
|
+
if (parsed.options.help === 'true') {
|
|
13
|
+
return printCommandHelp('compare', deps.writeOut, deps.writeErr);
|
|
14
|
+
}
|
|
15
|
+
if (validateCommandOptions('compare', parsed.options, deps.writeErr)) {
|
|
16
|
+
return 1;
|
|
17
|
+
}
|
|
18
|
+
const keyword = parsed.positionals[0] || parsed.options.keyword || parsed.options.q;
|
|
19
|
+
if (!keyword) {
|
|
20
|
+
deps.writeErr('compare 명령은 검색어가 필요합니다. 예: daiso compare 콜라');
|
|
21
|
+
return 1;
|
|
22
|
+
}
|
|
23
|
+
const targetUrl = toUrl('/api/compare/products');
|
|
24
|
+
targetUrl.searchParams.set('keyword', keyword);
|
|
25
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
26
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'compare', parsed.options.json === 'true');
|
|
27
|
+
}
|
|
9
28
|
export async function handlePlaces(options, deps) {
|
|
10
29
|
const parsed = parseCliArgs(options);
|
|
11
30
|
if (parsed.options.help === 'true') {
|
|
@@ -204,110 +223,66 @@ export async function handleGs25Inventory(options, deps) {
|
|
|
204
223
|
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'gs25-inventory', parsed.options.json === 'true');
|
|
205
224
|
}
|
|
206
225
|
export async function handleSevenElevenProducts(options, deps) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
222
|
-
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'seveneleven-products', parsed.options.json === 'true');
|
|
226
|
+
return await runApiCommand({
|
|
227
|
+
command: 'seveneleven-products',
|
|
228
|
+
options,
|
|
229
|
+
deps,
|
|
230
|
+
path: '/api/seveneleven/products',
|
|
231
|
+
configure: (parsed) => {
|
|
232
|
+
const query = parsed.positionals[0];
|
|
233
|
+
if (!query) {
|
|
234
|
+
return 'seveneleven-products 명령은 검색어가 필요합니다. 예: daiso seveneleven-products 삼각김밥';
|
|
235
|
+
}
|
|
236
|
+
parsed.options.query = query;
|
|
237
|
+
return null;
|
|
238
|
+
},
|
|
239
|
+
});
|
|
223
240
|
}
|
|
224
241
|
export async function handleSevenElevenStores(options, deps) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
242
|
+
return await runApiCommand({
|
|
243
|
+
command: 'seveneleven-stores',
|
|
244
|
+
options,
|
|
245
|
+
deps,
|
|
246
|
+
path: '/api/seveneleven/stores',
|
|
247
|
+
configure: (parsed) => {
|
|
248
|
+
const keyword = parsed.positionals[0];
|
|
249
|
+
if (!keyword) {
|
|
250
|
+
return 'seveneleven-stores 명령은 검색어가 필요합니다. 예: daiso seveneleven-stores 안산 중앙역';
|
|
251
|
+
}
|
|
252
|
+
parsed.options.keyword = keyword;
|
|
253
|
+
return null;
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
export async function handleSevenElevenInventory(options, deps) {
|
|
258
|
+
return await runApiCommand({
|
|
259
|
+
command: 'seveneleven-inventory',
|
|
260
|
+
options,
|
|
261
|
+
deps,
|
|
262
|
+
path: '/api/seveneleven/inventory',
|
|
263
|
+
configure: (parsed) => {
|
|
264
|
+
const keyword = parsed.positionals[0] || parsed.options.keyword;
|
|
265
|
+
if (!keyword) {
|
|
266
|
+
return 'seveneleven-inventory 명령은 검색어가 필요합니다. 예: daiso seveneleven-inventory 핫식스 --storeKeyword "안산 중앙역"';
|
|
267
|
+
}
|
|
268
|
+
parsed.options.keyword = keyword;
|
|
269
|
+
return null;
|
|
270
|
+
},
|
|
271
|
+
});
|
|
241
272
|
}
|
|
242
273
|
export async function handleSevenElevenPopwords(options, deps) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
const targetUrl = toUrl('/api/seveneleven/popwords');
|
|
251
|
-
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
252
|
-
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'seveneleven-popwords', parsed.options.json === 'true');
|
|
274
|
+
return await runApiCommand({
|
|
275
|
+
command: 'seveneleven-popwords',
|
|
276
|
+
options,
|
|
277
|
+
deps,
|
|
278
|
+
path: '/api/seveneleven/popwords',
|
|
279
|
+
});
|
|
253
280
|
}
|
|
254
281
|
export async function handleSevenElevenCatalog(options, deps) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
const targetUrl = toUrl('/api/seveneleven/catalog');
|
|
263
|
-
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
264
|
-
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'seveneleven-catalog', parsed.options.json === 'true');
|
|
265
|
-
}
|
|
266
|
-
export async function handleLottecinemaTheaters(options, deps) {
|
|
267
|
-
const parsed = parseCliArgs(options);
|
|
268
|
-
if (parsed.options.help === 'true') {
|
|
269
|
-
return printCommandHelp('lottecinema-theaters', deps.writeOut, deps.writeErr);
|
|
270
|
-
}
|
|
271
|
-
const keyword = parsed.positionals[0];
|
|
272
|
-
if (keyword) {
|
|
273
|
-
parsed.options.keyword = keyword;
|
|
274
|
-
}
|
|
275
|
-
if (validateCommandOptions('lottecinema-theaters', parsed.options, deps.writeErr)) {
|
|
276
|
-
return 1;
|
|
277
|
-
}
|
|
278
|
-
const targetUrl = toUrl('/api/lottecinema/theaters');
|
|
279
|
-
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
280
|
-
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'lottecinema-theaters', parsed.options.json === 'true');
|
|
281
|
-
}
|
|
282
|
-
export async function handleLottecinemaMovies(options, deps) {
|
|
283
|
-
const parsed = parseCliArgs(options);
|
|
284
|
-
if (parsed.options.help === 'true') {
|
|
285
|
-
return printCommandHelp('lottecinema-movies', deps.writeOut, deps.writeErr);
|
|
286
|
-
}
|
|
287
|
-
const keyword = parsed.positionals[0];
|
|
288
|
-
if (keyword) {
|
|
289
|
-
parsed.options.keyword = keyword;
|
|
290
|
-
}
|
|
291
|
-
if (validateCommandOptions('lottecinema-movies', parsed.options, deps.writeErr)) {
|
|
292
|
-
return 1;
|
|
293
|
-
}
|
|
294
|
-
const targetUrl = toUrl('/api/lottecinema/movies');
|
|
295
|
-
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
296
|
-
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'lottecinema-movies', parsed.options.json === 'true');
|
|
297
|
-
}
|
|
298
|
-
export async function handleLottecinemaSeats(options, deps) {
|
|
299
|
-
const parsed = parseCliArgs(options);
|
|
300
|
-
if (parsed.options.help === 'true') {
|
|
301
|
-
return printCommandHelp('lottecinema-seats', deps.writeOut, deps.writeErr);
|
|
302
|
-
}
|
|
303
|
-
const keyword = parsed.positionals[0];
|
|
304
|
-
if (keyword) {
|
|
305
|
-
parsed.options.keyword = keyword;
|
|
306
|
-
}
|
|
307
|
-
if (validateCommandOptions('lottecinema-seats', parsed.options, deps.writeErr)) {
|
|
308
|
-
return 1;
|
|
309
|
-
}
|
|
310
|
-
const targetUrl = toUrl('/api/lottecinema/seats');
|
|
311
|
-
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
312
|
-
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'lottecinema-seats', parsed.options.json === 'true');
|
|
282
|
+
return await runApiCommand({
|
|
283
|
+
command: 'seveneleven-catalog',
|
|
284
|
+
options,
|
|
285
|
+
deps,
|
|
286
|
+
path: '/api/seveneleven/catalog',
|
|
287
|
+
});
|
|
313
288
|
}
|
package/dist/cli/http.js
CHANGED
|
@@ -54,7 +54,7 @@ function buildCliHint(command, url, bodyText) {
|
|
|
54
54
|
if (url.pathname === '/api/seveneleven/inventory') {
|
|
55
55
|
return [
|
|
56
56
|
'힌트: 세븐일레븐 재고는 매장 키워드가 필요합니다.',
|
|
57
|
-
'다음 명령 예시: daiso
|
|
57
|
+
'다음 명령 예시: daiso seveneleven-inventory <상품명> --storeKeyword 강남',
|
|
58
58
|
];
|
|
59
59
|
}
|
|
60
60
|
return [];
|
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,8 @@ 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, handlePlaces, handleGs25Stores, handleGs25Products, handleGs25Inventory, handleSevenElevenProducts, handleSevenElevenStores, handleSevenElevenPopwords, handleSevenElevenCatalog,
|
|
15
|
+
import { handleCuStores, handleCuInventory, handleEmart24Stores, handleEmart24Products, handleEmart24Inventory, handleLotteMartStores, handleLotteMartProducts, handleCompareProducts, handlePlaces, handleGs25Stores, handleGs25Products, handleGs25Inventory, handleSevenElevenProducts, handleSevenElevenStores, handleSevenElevenInventory, handleSevenElevenPopwords, handleSevenElevenCatalog, } from './cli/commands/convenience.js';
|
|
16
|
+
import { handleCgvTheaters, handleCgvMovies, handleCgvTimetable, handleLottecinemaTheaters, handleLottecinemaMovies, handleLottecinemaSeats, } from './cli/commands/cinema.js';
|
|
16
17
|
function rejectTopLevelUnknownOptions(options, command, deps) {
|
|
17
18
|
const parsed = parseCliArgs(options);
|
|
18
19
|
return validateCommandOptions(command, parsed.options, deps.writeErr);
|
|
@@ -112,12 +113,20 @@ export async function runCli(argv, deps) {
|
|
|
112
113
|
return await handleInventory(options, resolvedDeps);
|
|
113
114
|
if (command === 'display-location')
|
|
114
115
|
return await handleDisplayLocation(options, resolvedDeps);
|
|
116
|
+
if (command === 'compare')
|
|
117
|
+
return await handleCompareProducts(options, resolvedDeps);
|
|
115
118
|
if (command === 'places')
|
|
116
119
|
return await handlePlaces(options, resolvedDeps);
|
|
117
120
|
if (command === 'cu-stores')
|
|
118
121
|
return await handleCuStores(options, resolvedDeps);
|
|
119
122
|
if (command === 'cu-inventory')
|
|
120
123
|
return await handleCuInventory(options, resolvedDeps);
|
|
124
|
+
if (command === 'cgv-theaters')
|
|
125
|
+
return await handleCgvTheaters(options, resolvedDeps);
|
|
126
|
+
if (command === 'cgv-movies')
|
|
127
|
+
return await handleCgvMovies(options, resolvedDeps);
|
|
128
|
+
if (command === 'cgv-timetable')
|
|
129
|
+
return await handleCgvTimetable(options, resolvedDeps);
|
|
121
130
|
if (command === 'lottecinema-theaters')
|
|
122
131
|
return await handleLottecinemaTheaters(options, resolvedDeps);
|
|
123
132
|
if (command === 'lottecinema-movies')
|
|
@@ -144,6 +153,8 @@ export async function runCli(argv, deps) {
|
|
|
144
153
|
return await handleSevenElevenProducts(options, resolvedDeps);
|
|
145
154
|
if (command === 'seveneleven-stores')
|
|
146
155
|
return await handleSevenElevenStores(options, resolvedDeps);
|
|
156
|
+
if (command === 'seveneleven-inventory')
|
|
157
|
+
return await handleSevenElevenInventory(options, resolvedDeps);
|
|
147
158
|
if (command === 'seveneleven-popwords')
|
|
148
159
|
return await handleSevenElevenPopwords(options, resolvedDeps);
|
|
149
160
|
if (command === 'seveneleven-catalog')
|
package/dist/cliHelp.js
CHANGED
|
@@ -13,9 +13,13 @@ export const COMMAND_LIST = [
|
|
|
13
13
|
'stores',
|
|
14
14
|
'inventory',
|
|
15
15
|
'display-location',
|
|
16
|
+
'compare',
|
|
16
17
|
'places',
|
|
17
18
|
'cu-stores',
|
|
18
19
|
'cu-inventory',
|
|
20
|
+
'cgv-theaters',
|
|
21
|
+
'cgv-movies',
|
|
22
|
+
'cgv-timetable',
|
|
19
23
|
'lottecinema-theaters',
|
|
20
24
|
'lottecinema-movies',
|
|
21
25
|
'lottecinema-seats',
|
|
@@ -29,6 +33,7 @@ export const COMMAND_LIST = [
|
|
|
29
33
|
'gs25-inventory',
|
|
30
34
|
'seveneleven-products',
|
|
31
35
|
'seveneleven-stores',
|
|
36
|
+
'seveneleven-inventory',
|
|
32
37
|
'seveneleven-popwords',
|
|
33
38
|
'seveneleven-catalog',
|
|
34
39
|
];
|
|
@@ -44,9 +49,13 @@ const COMMAND_SUMMARY = {
|
|
|
44
49
|
stores: '다이소 매장 검색',
|
|
45
50
|
inventory: '다이소 재고 조회',
|
|
46
51
|
'display-location': '다이소 진열 위치 조회',
|
|
52
|
+
compare: '키 없는 통합 상품 가격 후보 비교',
|
|
47
53
|
places: '음식점/카페 등 주변 장소 검색',
|
|
48
54
|
'cu-stores': 'CU 매장 검색',
|
|
49
55
|
'cu-inventory': 'CU 재고 조회',
|
|
56
|
+
'cgv-theaters': 'CGV 극장 검색',
|
|
57
|
+
'cgv-movies': 'CGV 영화 검색',
|
|
58
|
+
'cgv-timetable': 'CGV 시간표 조회',
|
|
50
59
|
'lottecinema-theaters': '롯데시네마 주변 지점 조회',
|
|
51
60
|
'lottecinema-movies': '롯데시네마 영화/회차 조회',
|
|
52
61
|
'lottecinema-seats': '롯데시네마 잔여 좌석 조회',
|
|
@@ -60,6 +69,7 @@ const COMMAND_SUMMARY = {
|
|
|
60
69
|
'gs25-inventory': 'GS25 재고 조회',
|
|
61
70
|
'seveneleven-products': '세븐일레븐 상품 검색',
|
|
62
71
|
'seveneleven-stores': '세븐일레븐 매장 검색',
|
|
72
|
+
'seveneleven-inventory': '세븐일레븐 재고 조회',
|
|
63
73
|
'seveneleven-popwords': '세븐일레븐 인기 검색어 조회',
|
|
64
74
|
'seveneleven-catalog': '세븐일레븐 카탈로그 스냅샷 조회',
|
|
65
75
|
};
|
|
@@ -139,6 +149,15 @@ const COMMAND_DETAIL = {
|
|
|
139
149
|
'옵션: --json',
|
|
140
150
|
'예시: daiso display-location 1034604 04515',
|
|
141
151
|
],
|
|
152
|
+
compare: [
|
|
153
|
+
'명령: compare',
|
|
154
|
+
'설명: 새 외부 키 없이 다이소, GS25, 세븐일레븐, 이마트24 상품 검색을 묶어 가격 후보를 비교합니다.',
|
|
155
|
+
'사용법: daiso compare <keyword> [--services daiso,gs25,seveneleven,emart24] [--limit N] [--json]',
|
|
156
|
+
'필수: <keyword>',
|
|
157
|
+
'옵션: --services, --limit, --json',
|
|
158
|
+
'예시: daiso compare 콜라 --limit 3',
|
|
159
|
+
'예시: daiso compare 컵라면 --services seveneleven,emart24 --json',
|
|
160
|
+
],
|
|
142
161
|
places: [
|
|
143
162
|
'명령: places',
|
|
144
163
|
'설명: 네이버 지역 검색으로 음식점, 카페, 디저트 가게 등 주변 장소를 검색합니다.',
|
|
@@ -165,6 +184,30 @@ const COMMAND_DETAIL = {
|
|
|
165
184
|
'예시: daiso cu-inventory 과자',
|
|
166
185
|
'예시: daiso cu-inventory 컵라면 --storeKeyword 강남 --size 10',
|
|
167
186
|
],
|
|
187
|
+
'cgv-theaters': [
|
|
188
|
+
'명령: cgv-theaters',
|
|
189
|
+
'설명: CGV 극장 검색 API를 호출합니다.',
|
|
190
|
+
'사용법: daiso cgv-theaters [keyword] [--regionCode 값] [--playDate YYYYMMDD] [--lat 값] [--lng 값] [--limit N] [--json]',
|
|
191
|
+
'옵션: --keyword, --regionCode, --playDate, --lat, --lng, --limit, --json',
|
|
192
|
+
'예시: daiso cgv-theaters 강남 --limit 5',
|
|
193
|
+
'예시: daiso cgv-theaters --regionCode 01 --playDate 20260304',
|
|
194
|
+
],
|
|
195
|
+
'cgv-movies': [
|
|
196
|
+
'명령: cgv-movies',
|
|
197
|
+
'설명: CGV 영화 검색 API를 호출합니다.',
|
|
198
|
+
'사용법: daiso cgv-movies [keyword] [--playDate YYYYMMDD] [--theaterCode 값] [--movieCode 값] [--lat 값] [--lng 값] [--json]',
|
|
199
|
+
'옵션: --keyword, --playDate, --theaterCode, --movieCode, --lat, --lng, --json',
|
|
200
|
+
'예시: daiso cgv-movies 강남 --playDate 20260304',
|
|
201
|
+
'예시: daiso cgv-movies --playDate 20260304 --theaterCode 0056',
|
|
202
|
+
],
|
|
203
|
+
'cgv-timetable': [
|
|
204
|
+
'명령: cgv-timetable',
|
|
205
|
+
'설명: CGV 시간표 API를 호출합니다.',
|
|
206
|
+
'사용법: daiso cgv-timetable [keyword] [--playDate YYYYMMDD] [--theaterCode 값] [--movieCode 값] [--lat 값] [--lng 값] [--json]',
|
|
207
|
+
'옵션: --keyword, --playDate, --theaterCode, --movieCode, --lat, --lng, --json',
|
|
208
|
+
'예시: daiso cgv-timetable 강남 --playDate 20260304',
|
|
209
|
+
'예시: daiso cgv-timetable --playDate 20260304 --theaterCode 0056',
|
|
210
|
+
],
|
|
168
211
|
'lottecinema-theaters': [
|
|
169
212
|
'명령: lottecinema-theaters',
|
|
170
213
|
'설명: 롯데시네마 주변 지점 API를 호출합니다.',
|
|
@@ -271,6 +314,14 @@ const COMMAND_DETAIL = {
|
|
|
271
314
|
'옵션: --limit, --json',
|
|
272
315
|
'예시: daiso seveneleven-stores 안산 중앙역 --limit 10',
|
|
273
316
|
],
|
|
317
|
+
'seveneleven-inventory': [
|
|
318
|
+
'명령: seveneleven-inventory',
|
|
319
|
+
'설명: 세븐일레븐 재고 API를 호출합니다.',
|
|
320
|
+
'사용법: daiso seveneleven-inventory <keyword> [--storeKeyword 값] [--storeLimit N] [--page N] [--size N] [--json]',
|
|
321
|
+
'필수: <keyword>',
|
|
322
|
+
'옵션: --keyword, --storeKeyword, --storeLimit, --page, --size, --json',
|
|
323
|
+
'예시: daiso seveneleven-inventory 핫식스 --storeKeyword "안산 중앙역" --storeLimit 10',
|
|
324
|
+
],
|
|
274
325
|
'seveneleven-popwords': [
|
|
275
326
|
'명령: seveneleven-popwords',
|
|
276
327
|
'설명: 세븐일레븐 인기 검색어 API를 호출합니다.',
|
|
@@ -312,24 +363,20 @@ export function printHelp(writeOut) {
|
|
|
312
363
|
writeOut(' npx daiso inventory 1034604 --keyword 강남역');
|
|
313
364
|
writeOut(' npx daiso display-location 1034604 04515');
|
|
314
365
|
writeOut(' npx daiso places 강남역 --category cafe --limit 5');
|
|
315
|
-
writeOut(' npx daiso cu-stores 강남');
|
|
316
366
|
writeOut(' npx daiso cu-inventory 과자 --storeKeyword 강남');
|
|
367
|
+
writeOut(' npx daiso cgv-theaters 강남 --limit 5');
|
|
368
|
+
writeOut(' npx daiso cgv-timetable --playDate 20260307 --theaterCode 0056');
|
|
317
369
|
writeOut(' npx daiso lottecinema-theaters --keyword "안산 중앙역" --limit 5');
|
|
318
|
-
writeOut(' npx daiso lottecinema-movies --playDate 20260310 --keyword "안산 중앙역"');
|
|
319
|
-
writeOut(' npx daiso lottecinema-seats --playDate 20260310 --keyword "안산 중앙역" --movieId 23816');
|
|
320
370
|
writeOut(' npx daiso lottemart-stores 잠실 --area 서울 --limit 10');
|
|
321
371
|
writeOut(' npx daiso lottemart-products 콜라 --storeName 강변점 --area 서울');
|
|
322
|
-
writeOut(' npx daiso emart24-stores 강남 --limit 10');
|
|
323
372
|
writeOut(' npx daiso emart24-products 두바이 --pageSize 20');
|
|
324
|
-
writeOut(' npx daiso emart24-inventory 8800244010504 --bizNoArr 28339,05015');
|
|
325
|
-
writeOut(' npx daiso gs25-stores 강남 --limit 10');
|
|
326
373
|
writeOut(' npx daiso gs25-products 오감자');
|
|
327
374
|
writeOut(' npx daiso gs25-inventory 오감자 --storeKeyword 강남');
|
|
328
375
|
writeOut(' npx daiso seveneleven-products 삼각김밥 --size 20');
|
|
329
376
|
writeOut(' npx daiso seveneleven-stores 안산 중앙역 --limit 10');
|
|
377
|
+
writeOut(' npx daiso seveneleven-inventory 핫식스 --storeKeyword "안산 중앙역"');
|
|
330
378
|
writeOut(' npx daiso seveneleven-popwords --label home');
|
|
331
379
|
writeOut(' npx daiso seveneleven-catalog --limit 10');
|
|
332
|
-
writeOut(' npx daiso get /api/cgv/movies --playDate 20260307 --theaterCode 0056');
|
|
333
380
|
writeOut('');
|
|
334
381
|
writeOut('정보가 부족할 때:');
|
|
335
382
|
writeOut(' 제품명을 먼저 검색해 productId를 확인: npx daiso products 수납박스');
|
|
@@ -343,7 +390,7 @@ export function printHelp(writeOut) {
|
|
|
343
390
|
export function printCommandHelp(command, writeOut, writeErr) {
|
|
344
391
|
if (!Object.hasOwn(COMMAND_DETAIL, command)) {
|
|
345
392
|
writeErr(`도움말을 찾을 수 없는 명령어: ${command}`);
|
|
346
|
-
writeErr(
|
|
393
|
+
writeErr(`사용 가능한 명령어: ${COMMAND_LIST.join(', ')}`);
|
|
347
394
|
return 1;
|
|
348
395
|
}
|
|
349
396
|
const detail = COMMAND_DETAIL[command];
|
package/dist/cliRenderer.js
CHANGED
|
@@ -174,6 +174,18 @@ function formatInventory(data) {
|
|
|
174
174
|
}
|
|
175
175
|
return lines;
|
|
176
176
|
}
|
|
177
|
+
function formatCompare(data) {
|
|
178
|
+
const lines = [];
|
|
179
|
+
if (isRecord(data.bestPrice)) {
|
|
180
|
+
lines.push(`최저가 후보: ${toText(data.bestPrice.serviceName)} / ${toText(data.bestPrice.name)} / ${toText(data.bestPrice.price)}원`);
|
|
181
|
+
}
|
|
182
|
+
lines.push(...formatCollection('가격 후보', data.results));
|
|
183
|
+
lines.push(...formatCollection('오류', data.errors));
|
|
184
|
+
if (typeof data.note === 'string') {
|
|
185
|
+
lines.push(`참고: ${data.note}`);
|
|
186
|
+
}
|
|
187
|
+
return lines;
|
|
188
|
+
}
|
|
177
189
|
export function renderApiEnvelope(command, url, payload) {
|
|
178
190
|
if (!isRecord(payload)) {
|
|
179
191
|
return `요청 성공: ${url.pathname}\n${toText(payload)}\n\n원본 JSON은 --json 옵션으로 확인하세요.`;
|
|
@@ -199,6 +211,9 @@ export function renderApiEnvelope(command, url, payload) {
|
|
|
199
211
|
else if (command === 'display-location') {
|
|
200
212
|
lines.push(...formatDisplayLocation(data));
|
|
201
213
|
}
|
|
214
|
+
else if (command === 'compare') {
|
|
215
|
+
lines.push(...formatCompare(data));
|
|
216
|
+
}
|
|
202
217
|
else if (command === 'cu-inventory') {
|
|
203
218
|
lines.push(...formatCollection('매장 목록', isRecord(data.nearbyStores) ? data.nearbyStores.stores : undefined));
|
|
204
219
|
lines.push(...formatCollection('재고 항목', isRecord(data.inventory) ? data.inventory.items : undefined));
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP 도구 outputSchema 생성
|
|
3
|
+
*/
|
|
4
|
+
import * as z from 'zod';
|
|
5
|
+
const DEFAULT_OUTPUT_SCHEMA = z.object({}).loose().describe('도구 실행 결과(JSON 객체)');
|
|
6
|
+
function createFallbackOutputSchema(toolName) {
|
|
7
|
+
if (toolName.includes('compare')) {
|
|
8
|
+
return {
|
|
9
|
+
keyword: z.string().optional(),
|
|
10
|
+
services: z.array(z.string()).optional(),
|
|
11
|
+
serviceCount: z.number().optional(),
|
|
12
|
+
resultCount: z.number().optional(),
|
|
13
|
+
bestPrice: z.unknown().optional(),
|
|
14
|
+
results: z.array(z.unknown()).optional(),
|
|
15
|
+
errors: z.array(z.unknown()).optional(),
|
|
16
|
+
note: z.string().optional(),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (toolName.includes('search_products') ||
|
|
20
|
+
toolName.includes('searchProducts') ||
|
|
21
|
+
toolName.endsWith('_products')) {
|
|
22
|
+
return {
|
|
23
|
+
keyword: z.string().optional(),
|
|
24
|
+
query: z.string().optional(),
|
|
25
|
+
page: z.number().optional(),
|
|
26
|
+
pageSize: z.number().optional(),
|
|
27
|
+
totalCount: z.number().optional(),
|
|
28
|
+
count: z.number().optional(),
|
|
29
|
+
products: z.array(z.unknown()).optional(),
|
|
30
|
+
note: z.string().optional(),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (toolName.includes('find_stores') ||
|
|
34
|
+
toolName.includes('nearby_stores') ||
|
|
35
|
+
toolName.includes('search_stores')) {
|
|
36
|
+
return {
|
|
37
|
+
keyword: z.string().optional(),
|
|
38
|
+
searchParams: z.unknown().optional(),
|
|
39
|
+
location: z.unknown().optional(),
|
|
40
|
+
totalCount: z.number().optional(),
|
|
41
|
+
filteredCount: z.number().optional(),
|
|
42
|
+
count: z.number().optional(),
|
|
43
|
+
stores: z.array(z.unknown()).optional(),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (toolName.includes('places')) {
|
|
47
|
+
return {
|
|
48
|
+
provider: z.string().optional(),
|
|
49
|
+
searchMode: z.string().optional(),
|
|
50
|
+
query: z.string().optional(),
|
|
51
|
+
category: z.string().optional(),
|
|
52
|
+
location: z.string().optional(),
|
|
53
|
+
totalCount: z.number().optional(),
|
|
54
|
+
count: z.number().optional(),
|
|
55
|
+
places: z.array(z.unknown()).optional(),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (toolName.includes('inventory')) {
|
|
59
|
+
return {
|
|
60
|
+
productId: z.string().optional(),
|
|
61
|
+
keyword: z.string().optional(),
|
|
62
|
+
itemCode: z.string().nullable().optional(),
|
|
63
|
+
location: z.unknown().optional(),
|
|
64
|
+
product: z.unknown().optional(),
|
|
65
|
+
onlineStock: z.number().optional(),
|
|
66
|
+
inventory: z.unknown().optional(),
|
|
67
|
+
storeInventory: z.unknown().optional(),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (toolName.includes('display_location')) {
|
|
71
|
+
return {
|
|
72
|
+
productId: z.string().optional(),
|
|
73
|
+
storeCode: z.string().optional(),
|
|
74
|
+
displayLocation: z.unknown().optional(),
|
|
75
|
+
locations: z.array(z.unknown()).optional(),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (toolName.includes('price_info')) {
|
|
79
|
+
return {
|
|
80
|
+
productId: z.string().optional(),
|
|
81
|
+
productName: z.string().optional(),
|
|
82
|
+
product: z.unknown().optional(),
|
|
83
|
+
price: z.number().optional(),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (toolName.includes('theater')) {
|
|
87
|
+
return {
|
|
88
|
+
keyword: z.string().optional(),
|
|
89
|
+
location: z.unknown().optional(),
|
|
90
|
+
count: z.number().optional(),
|
|
91
|
+
theaters: z.array(z.unknown()).optional(),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (toolName.includes('movie') || toolName.includes('now_showing')) {
|
|
95
|
+
return {
|
|
96
|
+
keyword: z.string().optional(),
|
|
97
|
+
playDate: z.string().optional(),
|
|
98
|
+
theaterCode: z.string().optional(),
|
|
99
|
+
count: z.number().optional(),
|
|
100
|
+
movies: z.array(z.unknown()).optional(),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
if (toolName.includes('timetable') || toolName.includes('remaining_seats')) {
|
|
104
|
+
return {
|
|
105
|
+
playDate: z.string().optional(),
|
|
106
|
+
theaterCode: z.string().optional(),
|
|
107
|
+
movieCode: z.string().optional(),
|
|
108
|
+
count: z.number().optional(),
|
|
109
|
+
showtimes: z.array(z.unknown()).optional(),
|
|
110
|
+
seats: z.array(z.unknown()).optional(),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (toolName.includes('popwords')) {
|
|
114
|
+
return {
|
|
115
|
+
label: z.string().optional(),
|
|
116
|
+
keywords: z.array(z.string()).optional(),
|
|
117
|
+
count: z.number().optional(),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (toolName.includes('catalog')) {
|
|
121
|
+
return {
|
|
122
|
+
pages: z.array(z.unknown()).optional(),
|
|
123
|
+
issues: z.array(z.unknown()).optional(),
|
|
124
|
+
exhibitions: z.array(z.unknown()).optional(),
|
|
125
|
+
count: z.number().optional(),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return {};
|
|
129
|
+
}
|
|
130
|
+
export function createToolOutputSchema(toolName, outputSchema) {
|
|
131
|
+
const schema = outputSchema || createFallbackOutputSchema(toolName);
|
|
132
|
+
if (Object.keys(schema).length === 0) {
|
|
133
|
+
return DEFAULT_OUTPUT_SCHEMA;
|
|
134
|
+
}
|
|
135
|
+
return z
|
|
136
|
+
.object({
|
|
137
|
+
...schema,
|
|
138
|
+
standard: z.unknown().optional().describe('서비스 공통 상품/매장/영화관 정규화 결과'),
|
|
139
|
+
})
|
|
140
|
+
.loose();
|
|
141
|
+
}
|