daiso 1.0.4 → 1.0.5
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 +218 -540
- package/dist/api/actionsProxy.js +82 -0
- package/dist/api/cgvHandlers.js +103 -16
- package/dist/api/daisoHandlers.js +127 -0
- package/dist/api/emart24Handlers.js +176 -0
- package/dist/api/gs25Handlers.js +256 -0
- package/dist/api/handlers.js +184 -108
- package/dist/api/healthChecks.js +332 -0
- package/dist/api/lottecinemaHandlers.js +155 -0
- package/dist/api/lottemartHandlers.js +128 -0
- package/dist/api/megaboxHandlers.js +81 -50
- package/dist/api/routes/cuRoutes.js +17 -0
- package/dist/api/routes/daisoRoutes.js +6 -1
- package/dist/api/routes/emart24Routes.js +22 -0
- package/dist/api/routes/gs25Routes.js +22 -0
- package/dist/api/routes/healthRoutes.js +44 -0
- package/dist/api/routes/lottecinemaRoutes.js +22 -0
- package/dist/api/routes/lottemartRoutes.js +18 -0
- package/dist/api/routes/oliveyoungRoutes.js +6 -1
- package/dist/api/routes/sevenelevenRoutes.js +32 -0
- package/dist/api/sevenelevenHandlers.js +200 -0
- package/dist/cli/args.js +54 -0
- package/dist/cli/commands/convenience.js +241 -0
- package/dist/cli/commands/daiso.js +96 -0
- package/dist/cli/constants.js +5 -0
- package/dist/cli/deps.js +48 -0
- package/dist/cli/http.js +98 -0
- package/dist/cli/interactiveFetch.js +31 -0
- package/dist/cli/interactiveItemSearch.js +259 -0
- package/dist/cli/interactivePrompt.js +49 -0
- package/dist/cli/interactiveTypes.js +4 -0
- package/dist/cli/types.js +4 -0
- package/dist/cli.js +66 -197
- package/dist/cliHelp.js +206 -1
- package/dist/cliInteractive.js +174 -0
- package/dist/cliPicker.js +56 -0
- package/dist/cliRenderer.js +4 -0
- package/dist/index.js +147 -8
- package/dist/pages/openapi.js +2 -1
- package/dist/pages/openapiFullSpec.js +58 -0
- package/dist/pages/openapiResponse.js +28 -0
- package/dist/pages/openapiSpec.js +2 -41
- package/dist/pages/openapiSpecActions.js +109 -0
- package/dist/pages/openapiSpecComponents.js +30 -391
- package/dist/pages/openapiSpecComponentsCgv.js +24 -0
- package/dist/pages/openapiSpecComponentsCu.js +112 -0
- package/dist/pages/openapiSpecComponentsDaisoOliveyoung.js +342 -0
- package/dist/pages/openapiSpecComponentsEmart24.js +141 -0
- package/dist/pages/openapiSpecComponentsGs25.js +109 -0
- package/dist/pages/openapiSpecComponentsLotteMart.js +85 -0
- package/dist/pages/openapiSpecComponentsLottecinema.js +163 -0
- package/dist/pages/openapiSpecComponentsMegabox.js +144 -0
- package/dist/pages/openapiSpecComponentsSeveneleven.js +82 -0
- package/dist/pages/openapiSpecPathsCgv.js +66 -3
- package/dist/pages/openapiSpecPathsCu.js +138 -0
- package/dist/pages/openapiSpecPathsDaiso.js +302 -0
- package/dist/pages/openapiSpecPathsDaisoOliveyoung.js +4 -376
- package/dist/pages/openapiSpecPathsEmart24.js +223 -0
- package/dist/pages/openapiSpecPathsGs25.js +166 -0
- package/dist/pages/openapiSpecPathsLotteMart.js +135 -0
- package/dist/pages/openapiSpecPathsLottecinema.js +212 -0
- package/dist/pages/openapiSpecPathsMegabox.js +52 -3
- package/dist/pages/openapiSpecPathsOliveyoung.js +231 -0
- package/dist/pages/openapiSpecPathsSeveneleven.js +234 -0
- package/dist/pages/prompt.js +8 -402
- package/dist/pages/promptCoreText.js +278 -0
- package/dist/pages/promptReferenceText.js +131 -0
- package/dist/pages/promptRetailCinemaText.js +273 -0
- package/dist/services/cgv/client.js +6 -1
- package/dist/services/cgv/index.js +3 -3
- package/dist/services/cgv/location.js +235 -0
- package/dist/services/cgv/tools/findTheaters.js +30 -5
- package/dist/services/cgv/tools/getTimetable.js +42 -14
- package/dist/services/cgv/tools/searchMovies.js +40 -12
- package/dist/services/cgv/transport.js +0 -3
- package/dist/services/cu/api.js +11 -0
- package/dist/services/cu/client.js +297 -0
- package/dist/services/cu/index.js +21 -0
- package/dist/services/cu/tools/checkInventory.js +123 -0
- package/dist/services/cu/tools/findNearbyStores.js +49 -0
- package/dist/services/cu/types.js +4 -0
- package/dist/services/daiso/api.js +22 -2
- package/dist/services/daiso/client.js +59 -0
- package/dist/services/daiso/index.js +2 -0
- package/dist/services/daiso/product.js +17 -0
- package/dist/services/daiso/tools/checkInventory.js +76 -41
- package/dist/services/daiso/tools/findStores.js +17 -7
- package/dist/services/daiso/tools/getDisplayLocation.js +75 -0
- package/dist/services/emart24/api.js +11 -0
- package/dist/services/emart24/client.js +173 -0
- package/dist/services/emart24/index.js +22 -0
- package/dist/services/emart24/inventoryLookup.js +134 -0
- package/dist/services/emart24/tools/checkInventory.js +54 -0
- package/dist/services/emart24/tools/findNearbyStores.js +72 -0
- package/dist/services/emart24/tools/searchProducts.js +63 -0
- package/dist/services/emart24/types.js +4 -0
- package/dist/services/gs25/api.js +9 -0
- package/dist/services/gs25/client.js +198 -0
- package/dist/services/gs25/index.js +22 -0
- package/dist/services/gs25/storeUtils.js +168 -0
- package/dist/services/gs25/tools/checkInventory.js +163 -0
- package/dist/services/gs25/tools/findNearbyStores.js +103 -0
- package/dist/services/gs25/tools/searchProducts.js +50 -0
- package/dist/services/gs25/types.js +4 -0
- package/dist/services/lottecinema/api.js +12 -0
- package/dist/services/lottecinema/client.js +161 -0
- package/dist/services/lottecinema/index.js +30 -0
- package/dist/services/lottecinema/location.js +191 -0
- package/dist/services/lottecinema/tools/findNearbyTheaters.js +47 -0
- package/dist/services/lottecinema/tools/getRemainingSeats.js +85 -0
- package/dist/services/lottecinema/tools/listNowShowing.js +79 -0
- package/dist/services/lottecinema/types.js +4 -0
- package/dist/services/lottemart/api.js +31 -0
- package/dist/services/lottemart/client.js +245 -0
- package/dist/services/lottemart/clientTypes.js +4 -0
- package/dist/services/lottemart/config.js +4 -0
- package/dist/services/lottemart/debug.js +92 -0
- package/dist/services/lottemart/geocode.js +49 -0
- package/dist/services/lottemart/index.js +30 -0
- package/dist/services/lottemart/keyword.js +29 -0
- package/dist/services/lottemart/parser.js +185 -0
- package/dist/services/lottemart/session.js +331 -0
- package/dist/services/lottemart/storeSearch.js +52 -0
- package/dist/services/lottemart/tools/findNearbyStores.js +67 -0
- package/dist/services/lottemart/tools/searchProducts.js +62 -0
- package/dist/services/lottemart/types.js +4 -0
- package/dist/services/lottemart/zetta.js +105 -0
- package/dist/services/megabox/location.js +219 -0
- package/dist/services/megabox/tools/findNearbyTheaters.js +13 -44
- package/dist/services/megabox/tools/getRemainingSeats.js +31 -3
- package/dist/services/megabox/tools/listNowShowing.js +31 -3
- package/dist/services/oliveyoung/api.js +2 -0
- package/dist/services/oliveyoung/client.js +150 -9
- package/dist/services/oliveyoung/index.js +3 -1
- package/dist/services/oliveyoung/tools/checkInventory.js +15 -4
- package/dist/services/oliveyoung/tools/searchProducts.js +57 -0
- package/dist/services/seveneleven/api.js +15 -0
- package/dist/services/seveneleven/client.js +279 -0
- package/dist/services/seveneleven/index.js +30 -0
- package/dist/services/seveneleven/inventory.js +302 -0
- package/dist/services/seveneleven/productKeyword.js +141 -0
- package/dist/services/seveneleven/tools/checkInventory.js +82 -0
- package/dist/services/seveneleven/tools/getCatalogSnapshot.js +55 -0
- package/dist/services/seveneleven/tools/getSearchPopwords.js +41 -0
- package/dist/services/seveneleven/tools/searchProducts.js +56 -0
- package/dist/services/seveneleven/tools/searchStores.js +45 -0
- package/dist/services/seveneleven/types.js +4 -0
- package/dist/utils/cliInteractiveHelpers.js +63 -0
- package/dist/utils/daisoKeyword.js +30 -0
- package/dist/utils/http.js +16 -2
- package/openapi.json +1977 -734
- package/openapi.yaml +1430 -543
- package/package.json +28 -15
- package/dist/tools/checkInventory.js +0 -98
- package/dist/tools/findStores.js +0 -125
- package/dist/tools/getPriceInfo.js +0 -77
- package/dist/tools/searchProducts.js +0 -61
- package/dist/types/index.js +0 -4
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 편의점 및 기타 서비스 CLI 명령 핸들러
|
|
3
|
+
* (CU, 이마트24, 롯데마트, GS25, 세븐일레븐, 롯데시네마)
|
|
4
|
+
*/
|
|
5
|
+
import { printCommandHelp } from '../../cliHelp.js';
|
|
6
|
+
import { parseCliArgs, toUrl, applyOptionsToQuery, toQueryOptions } from '../args.js';
|
|
7
|
+
import { requestAndPrintResponse } from '../http.js';
|
|
8
|
+
export async function handleCuStores(options, deps) {
|
|
9
|
+
const parsed = parseCliArgs(options);
|
|
10
|
+
if (parsed.options.help === 'true') {
|
|
11
|
+
return printCommandHelp('cu-stores', deps.writeOut, deps.writeErr);
|
|
12
|
+
}
|
|
13
|
+
const keyword = parsed.positionals[0];
|
|
14
|
+
if (keyword) {
|
|
15
|
+
parsed.options.keyword = keyword;
|
|
16
|
+
}
|
|
17
|
+
const targetUrl = toUrl('/api/cu/stores');
|
|
18
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
19
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'cu-stores', parsed.options.json === 'true');
|
|
20
|
+
}
|
|
21
|
+
export async function handleCuInventory(options, deps) {
|
|
22
|
+
const parsed = parseCliArgs(options);
|
|
23
|
+
if (parsed.options.help === 'true') {
|
|
24
|
+
return printCommandHelp('cu-inventory', deps.writeOut, deps.writeErr);
|
|
25
|
+
}
|
|
26
|
+
const keyword = parsed.positionals[0];
|
|
27
|
+
if (!keyword) {
|
|
28
|
+
deps.writeErr('cu-inventory 명령은 검색어가 필요합니다. 예: daiso cu-inventory 과자');
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
const targetUrl = toUrl('/api/cu/inventory');
|
|
32
|
+
targetUrl.searchParams.set('keyword', keyword);
|
|
33
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
34
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'cu-inventory', parsed.options.json === 'true');
|
|
35
|
+
}
|
|
36
|
+
export async function handleEmart24Stores(options, deps) {
|
|
37
|
+
const parsed = parseCliArgs(options);
|
|
38
|
+
if (parsed.options.help === 'true') {
|
|
39
|
+
return printCommandHelp('emart24-stores', deps.writeOut, deps.writeErr);
|
|
40
|
+
}
|
|
41
|
+
const keyword = parsed.positionals[0];
|
|
42
|
+
if (keyword) {
|
|
43
|
+
parsed.options.keyword = keyword;
|
|
44
|
+
}
|
|
45
|
+
const targetUrl = toUrl('/api/emart24/stores');
|
|
46
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
47
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'emart24-stores', parsed.options.json === 'true');
|
|
48
|
+
}
|
|
49
|
+
export async function handleEmart24Products(options, deps) {
|
|
50
|
+
const parsed = parseCliArgs(options);
|
|
51
|
+
if (parsed.options.help === 'true') {
|
|
52
|
+
return printCommandHelp('emart24-products', deps.writeOut, deps.writeErr);
|
|
53
|
+
}
|
|
54
|
+
const keyword = parsed.positionals[0];
|
|
55
|
+
if (!keyword) {
|
|
56
|
+
deps.writeErr('emart24-products 명령은 검색어가 필요합니다. 예: daiso emart24-products 두바이');
|
|
57
|
+
return 1;
|
|
58
|
+
}
|
|
59
|
+
const targetUrl = toUrl('/api/emart24/products');
|
|
60
|
+
targetUrl.searchParams.set('keyword', keyword);
|
|
61
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
62
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'emart24-products', parsed.options.json === 'true');
|
|
63
|
+
}
|
|
64
|
+
export async function handleEmart24Inventory(options, deps) {
|
|
65
|
+
const parsed = parseCliArgs(options);
|
|
66
|
+
if (parsed.options.help === 'true') {
|
|
67
|
+
return printCommandHelp('emart24-inventory', deps.writeOut, deps.writeErr);
|
|
68
|
+
}
|
|
69
|
+
const pluCd = parsed.positionals[0];
|
|
70
|
+
const bizNoArr = parsed.options.bizNoArr;
|
|
71
|
+
if (!pluCd || !bizNoArr) {
|
|
72
|
+
deps.writeErr('emart24-inventory 명령은 pluCd와 --bizNoArr가 필요합니다. 예: daiso emart24-inventory 8800244010504 --bizNoArr 28339,05015');
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
const targetUrl = toUrl('/api/emart24/inventory');
|
|
76
|
+
targetUrl.searchParams.set('pluCd', pluCd);
|
|
77
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
78
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'emart24-inventory', parsed.options.json === 'true');
|
|
79
|
+
}
|
|
80
|
+
export async function handleLotteMartStores(options, deps) {
|
|
81
|
+
const parsed = parseCliArgs(options);
|
|
82
|
+
if (parsed.options.help === 'true') {
|
|
83
|
+
return printCommandHelp('lottemart-stores', deps.writeOut, deps.writeErr);
|
|
84
|
+
}
|
|
85
|
+
const keyword = parsed.positionals[0];
|
|
86
|
+
if (keyword) {
|
|
87
|
+
parsed.options.keyword = keyword;
|
|
88
|
+
}
|
|
89
|
+
const targetUrl = toUrl('/api/lottemart/stores');
|
|
90
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
91
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'lottemart-stores', parsed.options.json === 'true');
|
|
92
|
+
}
|
|
93
|
+
export async function handleLotteMartProducts(options, deps) {
|
|
94
|
+
const parsed = parseCliArgs(options);
|
|
95
|
+
if (parsed.options.help === 'true') {
|
|
96
|
+
return printCommandHelp('lottemart-products', deps.writeOut, deps.writeErr);
|
|
97
|
+
}
|
|
98
|
+
const keyword = parsed.positionals[0];
|
|
99
|
+
if (!keyword) {
|
|
100
|
+
deps.writeErr('lottemart-products 명령은 검색어가 필요합니다. 예: daiso lottemart-products 콜라 --storeName 강변점');
|
|
101
|
+
return 1;
|
|
102
|
+
}
|
|
103
|
+
if (!parsed.options.storeCode && !parsed.options.storeName) {
|
|
104
|
+
deps.writeErr('lottemart-products 명령은 --storeCode 또는 --storeName이 필요합니다. 예: daiso lottemart-products 콜라 --storeName 강변점');
|
|
105
|
+
return 1;
|
|
106
|
+
}
|
|
107
|
+
const targetUrl = toUrl('/api/lottemart/products');
|
|
108
|
+
targetUrl.searchParams.set('keyword', keyword);
|
|
109
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
110
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'lottemart-products', parsed.options.json === 'true');
|
|
111
|
+
}
|
|
112
|
+
export async function handleGs25Stores(options, deps) {
|
|
113
|
+
const parsed = parseCliArgs(options);
|
|
114
|
+
if (parsed.options.help === 'true') {
|
|
115
|
+
return printCommandHelp('gs25-stores', deps.writeOut, deps.writeErr);
|
|
116
|
+
}
|
|
117
|
+
const keyword = parsed.positionals[0];
|
|
118
|
+
if (keyword) {
|
|
119
|
+
parsed.options.keyword = keyword;
|
|
120
|
+
}
|
|
121
|
+
const targetUrl = toUrl('/api/gs25/stores');
|
|
122
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
123
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'gs25-stores', parsed.options.json === 'true');
|
|
124
|
+
}
|
|
125
|
+
export async function handleGs25Products(options, deps) {
|
|
126
|
+
const parsed = parseCliArgs(options);
|
|
127
|
+
if (parsed.options.help === 'true') {
|
|
128
|
+
return printCommandHelp('gs25-products', deps.writeOut, deps.writeErr);
|
|
129
|
+
}
|
|
130
|
+
const keyword = parsed.positionals[0];
|
|
131
|
+
if (!keyword) {
|
|
132
|
+
deps.writeErr('gs25-products 명령은 검색어가 필요합니다. 예: daiso gs25-products 오감자');
|
|
133
|
+
return 1;
|
|
134
|
+
}
|
|
135
|
+
const targetUrl = toUrl('/api/gs25/products');
|
|
136
|
+
targetUrl.searchParams.set('keyword', keyword);
|
|
137
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
138
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'gs25-products', parsed.options.json === 'true');
|
|
139
|
+
}
|
|
140
|
+
export async function handleGs25Inventory(options, deps) {
|
|
141
|
+
const parsed = parseCliArgs(options);
|
|
142
|
+
if (parsed.options.help === 'true') {
|
|
143
|
+
return printCommandHelp('gs25-inventory', deps.writeOut, deps.writeErr);
|
|
144
|
+
}
|
|
145
|
+
const keyword = parsed.positionals[0];
|
|
146
|
+
if (!keyword) {
|
|
147
|
+
deps.writeErr('gs25-inventory 명령은 검색어가 필요합니다. 예: daiso gs25-inventory 오감자');
|
|
148
|
+
return 1;
|
|
149
|
+
}
|
|
150
|
+
const targetUrl = toUrl('/api/gs25/inventory');
|
|
151
|
+
targetUrl.searchParams.set('keyword', keyword);
|
|
152
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
153
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'gs25-inventory', parsed.options.json === 'true');
|
|
154
|
+
}
|
|
155
|
+
export async function handleSevenElevenProducts(options, deps) {
|
|
156
|
+
const parsed = parseCliArgs(options);
|
|
157
|
+
if (parsed.options.help === 'true') {
|
|
158
|
+
return printCommandHelp('seveneleven-products', deps.writeOut, deps.writeErr);
|
|
159
|
+
}
|
|
160
|
+
const query = parsed.positionals[0];
|
|
161
|
+
if (!query) {
|
|
162
|
+
deps.writeErr('seveneleven-products 명령은 검색어가 필요합니다. 예: daiso seveneleven-products 삼각김밥');
|
|
163
|
+
return 1;
|
|
164
|
+
}
|
|
165
|
+
const targetUrl = toUrl('/api/seveneleven/products');
|
|
166
|
+
targetUrl.searchParams.set('query', query);
|
|
167
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
168
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'seveneleven-products', parsed.options.json === 'true');
|
|
169
|
+
}
|
|
170
|
+
export async function handleSevenElevenStores(options, deps) {
|
|
171
|
+
const parsed = parseCliArgs(options);
|
|
172
|
+
if (parsed.options.help === 'true') {
|
|
173
|
+
return printCommandHelp('seveneleven-stores', deps.writeOut, deps.writeErr);
|
|
174
|
+
}
|
|
175
|
+
const keyword = parsed.positionals[0];
|
|
176
|
+
if (!keyword) {
|
|
177
|
+
deps.writeErr('seveneleven-stores 명령은 검색어가 필요합니다. 예: daiso seveneleven-stores 안산 중앙역');
|
|
178
|
+
return 1;
|
|
179
|
+
}
|
|
180
|
+
const targetUrl = toUrl('/api/seveneleven/stores');
|
|
181
|
+
targetUrl.searchParams.set('keyword', keyword);
|
|
182
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
183
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'seveneleven-stores', parsed.options.json === 'true');
|
|
184
|
+
}
|
|
185
|
+
export async function handleSevenElevenPopwords(options, deps) {
|
|
186
|
+
const parsed = parseCliArgs(options);
|
|
187
|
+
if (parsed.options.help === 'true') {
|
|
188
|
+
return printCommandHelp('seveneleven-popwords', deps.writeOut, deps.writeErr);
|
|
189
|
+
}
|
|
190
|
+
const targetUrl = toUrl('/api/seveneleven/popwords');
|
|
191
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
192
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'seveneleven-popwords', parsed.options.json === 'true');
|
|
193
|
+
}
|
|
194
|
+
export async function handleSevenElevenCatalog(options, deps) {
|
|
195
|
+
const parsed = parseCliArgs(options);
|
|
196
|
+
if (parsed.options.help === 'true') {
|
|
197
|
+
return printCommandHelp('seveneleven-catalog', deps.writeOut, deps.writeErr);
|
|
198
|
+
}
|
|
199
|
+
const targetUrl = toUrl('/api/seveneleven/catalog');
|
|
200
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
201
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'seveneleven-catalog', parsed.options.json === 'true');
|
|
202
|
+
}
|
|
203
|
+
export async function handleLottecinemaTheaters(options, deps) {
|
|
204
|
+
const parsed = parseCliArgs(options);
|
|
205
|
+
if (parsed.options.help === 'true') {
|
|
206
|
+
return printCommandHelp('lottecinema-theaters', deps.writeOut, deps.writeErr);
|
|
207
|
+
}
|
|
208
|
+
const keyword = parsed.positionals[0];
|
|
209
|
+
if (keyword) {
|
|
210
|
+
parsed.options.keyword = keyword;
|
|
211
|
+
}
|
|
212
|
+
const targetUrl = toUrl('/api/lottecinema/theaters');
|
|
213
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
214
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'lottecinema-theaters', parsed.options.json === 'true');
|
|
215
|
+
}
|
|
216
|
+
export async function handleLottecinemaMovies(options, deps) {
|
|
217
|
+
const parsed = parseCliArgs(options);
|
|
218
|
+
if (parsed.options.help === 'true') {
|
|
219
|
+
return printCommandHelp('lottecinema-movies', deps.writeOut, deps.writeErr);
|
|
220
|
+
}
|
|
221
|
+
const keyword = parsed.positionals[0];
|
|
222
|
+
if (keyword) {
|
|
223
|
+
parsed.options.keyword = keyword;
|
|
224
|
+
}
|
|
225
|
+
const targetUrl = toUrl('/api/lottecinema/movies');
|
|
226
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
227
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'lottecinema-movies', parsed.options.json === 'true');
|
|
228
|
+
}
|
|
229
|
+
export async function handleLottecinemaSeats(options, deps) {
|
|
230
|
+
const parsed = parseCliArgs(options);
|
|
231
|
+
if (parsed.options.help === 'true') {
|
|
232
|
+
return printCommandHelp('lottecinema-seats', deps.writeOut, deps.writeErr);
|
|
233
|
+
}
|
|
234
|
+
const keyword = parsed.positionals[0];
|
|
235
|
+
if (keyword) {
|
|
236
|
+
parsed.options.keyword = keyword;
|
|
237
|
+
}
|
|
238
|
+
const targetUrl = toUrl('/api/lottecinema/seats');
|
|
239
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
240
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'lottecinema-seats', parsed.options.json === 'true');
|
|
241
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 다이소 관련 CLI 명령 핸들러
|
|
3
|
+
*/
|
|
4
|
+
import { printCommandHelp } from '../../cliHelp.js';
|
|
5
|
+
import { parseCliArgs, toUrl, applyOptionsToQuery, toQueryOptions } from '../args.js';
|
|
6
|
+
import { requestAndPrintResponse, requestAndPrintStoresWithKeywordFallback } from '../http.js';
|
|
7
|
+
export async function handleGet(options, deps) {
|
|
8
|
+
const parsed = parseCliArgs(options);
|
|
9
|
+
if (parsed.options.help === 'true') {
|
|
10
|
+
return printCommandHelp('get', deps.writeOut, deps.writeErr);
|
|
11
|
+
}
|
|
12
|
+
const targetPath = parsed.positionals[0];
|
|
13
|
+
if (!targetPath) {
|
|
14
|
+
deps.writeErr('get 명령은 경로가 필요합니다. 예: daiso get /api/daiso/products --q 수납박스');
|
|
15
|
+
return 1;
|
|
16
|
+
}
|
|
17
|
+
const targetUrl = toUrl(targetPath);
|
|
18
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
19
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'get', parsed.options.json === 'true');
|
|
20
|
+
}
|
|
21
|
+
export async function handleProducts(options, deps) {
|
|
22
|
+
const parsed = parseCliArgs(options);
|
|
23
|
+
if (parsed.options.help === 'true') {
|
|
24
|
+
return printCommandHelp('products', deps.writeOut, deps.writeErr);
|
|
25
|
+
}
|
|
26
|
+
const query = parsed.positionals[0];
|
|
27
|
+
if (!query) {
|
|
28
|
+
deps.writeErr('products 명령은 검색어가 필요합니다. 예: daiso products 수납박스');
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
const targetUrl = toUrl('/api/daiso/products');
|
|
32
|
+
targetUrl.searchParams.set('q', query);
|
|
33
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
34
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'products', parsed.options.json === 'true');
|
|
35
|
+
}
|
|
36
|
+
export async function handleProduct(options, deps) {
|
|
37
|
+
const parsed = parseCliArgs(options);
|
|
38
|
+
if (parsed.options.help === 'true') {
|
|
39
|
+
return printCommandHelp('product', deps.writeOut, deps.writeErr);
|
|
40
|
+
}
|
|
41
|
+
const productId = parsed.positionals[0];
|
|
42
|
+
if (!productId) {
|
|
43
|
+
deps.writeErr('product 명령은 제품 ID가 필요합니다. 예: daiso product 1034604');
|
|
44
|
+
return 1;
|
|
45
|
+
}
|
|
46
|
+
const targetUrl = toUrl(`/api/daiso/products/${productId}`);
|
|
47
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
48
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'product', parsed.options.json === 'true');
|
|
49
|
+
}
|
|
50
|
+
export async function handleStores(options, deps) {
|
|
51
|
+
const parsed = parseCliArgs(options);
|
|
52
|
+
if (parsed.options.help === 'true') {
|
|
53
|
+
return printCommandHelp('stores', deps.writeOut, deps.writeErr);
|
|
54
|
+
}
|
|
55
|
+
const keyword = parsed.positionals[0];
|
|
56
|
+
if (keyword) {
|
|
57
|
+
parsed.options.keyword = keyword;
|
|
58
|
+
}
|
|
59
|
+
if (!parsed.options.keyword && !parsed.options.sido) {
|
|
60
|
+
deps.writeErr('stores 명령은 keyword 또는 --sido가 필요합니다. 예: daiso stores 강남역 / daiso stores --sido 서울');
|
|
61
|
+
return 1;
|
|
62
|
+
}
|
|
63
|
+
return await requestAndPrintStoresWithKeywordFallback(deps.fetchImpl, deps.writeOut, deps.writeErr, toQueryOptions(parsed.options), parsed.options.json === 'true');
|
|
64
|
+
}
|
|
65
|
+
export async function handleInventory(options, deps) {
|
|
66
|
+
const parsed = parseCliArgs(options);
|
|
67
|
+
if (parsed.options.help === 'true') {
|
|
68
|
+
return printCommandHelp('inventory', deps.writeOut, deps.writeErr);
|
|
69
|
+
}
|
|
70
|
+
const productId = parsed.positionals[0];
|
|
71
|
+
if (!productId) {
|
|
72
|
+
deps.writeErr('inventory 명령은 제품 ID가 필요합니다. 예: daiso inventory 1034604 --keyword 강남역');
|
|
73
|
+
return 1;
|
|
74
|
+
}
|
|
75
|
+
const targetUrl = toUrl('/api/daiso/inventory');
|
|
76
|
+
targetUrl.searchParams.set('productId', productId);
|
|
77
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
78
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'inventory', parsed.options.json === 'true');
|
|
79
|
+
}
|
|
80
|
+
export async function handleDisplayLocation(options, deps) {
|
|
81
|
+
const parsed = parseCliArgs(options);
|
|
82
|
+
if (parsed.options.help === 'true') {
|
|
83
|
+
return printCommandHelp('display-location', deps.writeOut, deps.writeErr);
|
|
84
|
+
}
|
|
85
|
+
const productId = parsed.positionals[0];
|
|
86
|
+
const storeCode = parsed.positionals[1];
|
|
87
|
+
if (!productId || !storeCode) {
|
|
88
|
+
deps.writeErr('display-location 명령은 productId와 storeCode가 필요합니다. 예: daiso display-location 1034604 04515');
|
|
89
|
+
return 1;
|
|
90
|
+
}
|
|
91
|
+
const targetUrl = toUrl('/api/daiso/display-location');
|
|
92
|
+
targetUrl.searchParams.set('productId', productId);
|
|
93
|
+
targetUrl.searchParams.set('storeCode', storeCode);
|
|
94
|
+
applyOptionsToQuery(targetUrl, toQueryOptions(parsed.options));
|
|
95
|
+
return await requestAndPrintResponse(deps.fetchImpl, deps.writeOut, deps.writeErr, targetUrl, 'display-location', parsed.options.json === 'true');
|
|
96
|
+
}
|
package/dist/cli/deps.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI 기본 의존성 생성
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { spawn } from 'node:child_process';
|
|
8
|
+
import { runInteractiveCli } from '../cliInteractive.js';
|
|
9
|
+
function loadVersion() {
|
|
10
|
+
const cliPath = fileURLToPath(import.meta.url);
|
|
11
|
+
const packagePath = path.resolve(path.dirname(cliPath), '../../package.json');
|
|
12
|
+
if (!existsSync(packagePath)) {
|
|
13
|
+
return '0.0.0';
|
|
14
|
+
}
|
|
15
|
+
const raw = readFileSync(packagePath, 'utf8');
|
|
16
|
+
const parsed = JSON.parse(raw);
|
|
17
|
+
return parsed.version ?? '0.0.0';
|
|
18
|
+
}
|
|
19
|
+
async function execCommand(command, args) {
|
|
20
|
+
return await new Promise((resolve, reject) => {
|
|
21
|
+
const child = spawn(command, args, {
|
|
22
|
+
stdio: 'inherit',
|
|
23
|
+
shell: false,
|
|
24
|
+
});
|
|
25
|
+
child.on('error', (error) => {
|
|
26
|
+
reject(error);
|
|
27
|
+
});
|
|
28
|
+
child.on('close', (code) => {
|
|
29
|
+
resolve(code ?? 1);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function createDefaultDeps() {
|
|
34
|
+
return {
|
|
35
|
+
fetchImpl: fetch,
|
|
36
|
+
writeOut: (message) => {
|
|
37
|
+
process.stdout.write(`${message}\n`);
|
|
38
|
+
},
|
|
39
|
+
writeErr: (message) => {
|
|
40
|
+
process.stderr.write(`${message}\n`);
|
|
41
|
+
},
|
|
42
|
+
getVersion: loadVersion,
|
|
43
|
+
nowIso: () => new Date().toISOString(),
|
|
44
|
+
runCommand: execCommand,
|
|
45
|
+
isInteractiveTerminal: () => Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
46
|
+
runInteractive: runInteractiveCli,
|
|
47
|
+
};
|
|
48
|
+
}
|
package/dist/cli/http.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP 요청 및 응답 처리 유틸리티
|
|
3
|
+
*/
|
|
4
|
+
import { renderApiEnvelope } from '../cliRenderer.js';
|
|
5
|
+
import { buildDaisoStoreKeywordVariants } from '../utils/daisoKeyword.js';
|
|
6
|
+
import { applyOptionsToQuery, toUrl } from './args.js';
|
|
7
|
+
export async function requestAndPrintResponse(fetchImpl, writeOut, writeErr, url, command, asJson) {
|
|
8
|
+
try {
|
|
9
|
+
const response = await fetchImpl(url.toString());
|
|
10
|
+
if (!response.ok) {
|
|
11
|
+
const bodyText = await response.text();
|
|
12
|
+
writeErr(`요청 실패: HTTP ${response.status}`);
|
|
13
|
+
if (bodyText) {
|
|
14
|
+
writeErr(bodyText);
|
|
15
|
+
}
|
|
16
|
+
return 1;
|
|
17
|
+
}
|
|
18
|
+
const payload = (await response.json());
|
|
19
|
+
if (asJson) {
|
|
20
|
+
writeOut(JSON.stringify(payload, null, 2));
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
writeOut(renderApiEnvelope(command, url, payload));
|
|
24
|
+
}
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29
|
+
writeErr(`요청 중 오류 발생: ${message}`);
|
|
30
|
+
return 1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function toStoreCount(payload) {
|
|
34
|
+
if (typeof payload !== 'object' || payload === null) {
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
const record = payload;
|
|
38
|
+
if (record.success !== true || typeof record.data !== 'object' || record.data === null) {
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
const stores = record.data.stores;
|
|
42
|
+
return Array.isArray(stores) ? stores.length : 0;
|
|
43
|
+
}
|
|
44
|
+
export async function requestAndPrintStoresWithKeywordFallback(fetchImpl, writeOut, writeErr, options, asJson) {
|
|
45
|
+
const originalKeyword = options.keyword;
|
|
46
|
+
if (!originalKeyword) {
|
|
47
|
+
const url = toUrl('/api/daiso/stores');
|
|
48
|
+
applyOptionsToQuery(url, options);
|
|
49
|
+
return await requestAndPrintResponse(fetchImpl, writeOut, writeErr, url, 'stores', asJson);
|
|
50
|
+
}
|
|
51
|
+
const keywords = buildDaisoStoreKeywordVariants(originalKeyword);
|
|
52
|
+
const candidates = keywords.length > 0 ? keywords : [originalKeyword];
|
|
53
|
+
try {
|
|
54
|
+
let lastUrl = toUrl('/api/daiso/stores');
|
|
55
|
+
let lastPayload = null;
|
|
56
|
+
for (const keyword of candidates) {
|
|
57
|
+
const targetUrl = toUrl('/api/daiso/stores');
|
|
58
|
+
applyOptionsToQuery(targetUrl, { ...options, keyword });
|
|
59
|
+
lastUrl = targetUrl;
|
|
60
|
+
const response = await fetchImpl(targetUrl.toString());
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
const bodyText = await response.text();
|
|
63
|
+
writeErr(`요청 실패: HTTP ${response.status}`);
|
|
64
|
+
if (bodyText) {
|
|
65
|
+
writeErr(bodyText);
|
|
66
|
+
}
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
const payload = (await response.json());
|
|
70
|
+
lastPayload = payload;
|
|
71
|
+
if (toStoreCount(payload) > 0) {
|
|
72
|
+
if (keyword !== originalKeyword) {
|
|
73
|
+
writeOut(`입력 키워드 "${originalKeyword}" 대신 "${keyword}"로 매장을 찾았습니다.`);
|
|
74
|
+
}
|
|
75
|
+
if (asJson) {
|
|
76
|
+
writeOut(JSON.stringify(payload, null, 2));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
writeOut(renderApiEnvelope('stores', targetUrl, payload));
|
|
80
|
+
}
|
|
81
|
+
return 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (asJson) {
|
|
85
|
+
writeOut(JSON.stringify(lastPayload, null, 2));
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
writeOut(renderApiEnvelope('stores', lastUrl, lastPayload));
|
|
89
|
+
}
|
|
90
|
+
writeOut('힌트: "안산 중앙역" 대신 "안산중앙" 또는 "고잔"으로 검색해보세요.');
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
95
|
+
writeErr(`요청 중 오류 발생: ${message}`);
|
|
96
|
+
return 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 인터랙티브 CLI API 호출 헬퍼
|
|
3
|
+
*/
|
|
4
|
+
import { buildDaisoStoreKeywordVariants, parseStores } from '../utils/cliInteractiveHelpers.js';
|
|
5
|
+
const BASE_URL = 'https://mcp.aka.page';
|
|
6
|
+
export async function fetchEnvelope(fetchImpl, path, query = {}) {
|
|
7
|
+
const url = new URL(path, BASE_URL);
|
|
8
|
+
for (const [key, value] of Object.entries(query)) {
|
|
9
|
+
url.searchParams.set(key, value);
|
|
10
|
+
}
|
|
11
|
+
const response = await fetchImpl(url.toString());
|
|
12
|
+
if (!response.ok) {
|
|
13
|
+
throw new Error(`HTTP ${response.status} (${url.pathname})`);
|
|
14
|
+
}
|
|
15
|
+
return (await response.json());
|
|
16
|
+
}
|
|
17
|
+
export async function fetchStoresWithKeywordFallback(fetchImpl, service, keyword) {
|
|
18
|
+
const candidates = service === 'daiso' ? buildDaisoStoreKeywordVariants(keyword) : [keyword];
|
|
19
|
+
const keywords = candidates.length > 0 ? candidates : [keyword];
|
|
20
|
+
for (const currentKeyword of keywords) {
|
|
21
|
+
const payload = await fetchEnvelope(fetchImpl, `/api/${service}/stores`, {
|
|
22
|
+
keyword: currentKeyword,
|
|
23
|
+
limit: '10',
|
|
24
|
+
});
|
|
25
|
+
const stores = parseStores(payload);
|
|
26
|
+
if (stores.length > 0) {
|
|
27
|
+
return { stores, matchedKeyword: currentKeyword };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return { stores: [], matchedKeyword: keyword };
|
|
31
|
+
}
|