daiso 1.2.7 → 1.2.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 +17 -17
- package/dist/api/actionsProxy.js +0 -10
- package/dist/api/cgvHandlers.js +4 -3
- package/dist/api/configStatus.js +7 -2
- package/dist/api/gs25Handlers.js +4 -3
- package/dist/api/handlers.js +11 -7
- package/dist/api/healthCheckDefinitions.js +0 -9
- package/dist/api/healthChecks.js +0 -1
- package/dist/api/lottecinemaHandlers.js +4 -3
- package/dist/api/lottemartHandlers.js +5 -142
- package/dist/api/megaboxHandlers.js +4 -3
- package/dist/api/opinetHandlers.js +2 -1
- package/dist/api/routes/cgvRoutes.js +3 -3
- package/dist/api/routes/cuRoutes.js +2 -2
- package/dist/api/routes/gs25Routes.js +3 -3
- package/dist/api/routes/lottecinemaRoutes.js +3 -3
- package/dist/api/routes/lottemartRoutes.js +4 -16
- package/dist/api/routes/megaboxRoutes.js +3 -3
- package/dist/api/routes/opinetRoutes.js +4 -4
- package/dist/cli.js +5 -5
- package/dist/cliHelp.js +0 -3
- package/dist/cliHelpDefinitions.js +2 -19
- package/dist/index.js +10 -11
- package/dist/middleware/dailyRateLimit.js +0 -1
- package/dist/pages/openapiFullSpec.js +0 -2
- package/dist/pages/openapiSpecActionParameters.js +4 -4
- package/dist/pages/openapiSpecComponents.js +0 -2
- package/dist/pages/openapiSpecPathsMegabox.js +1 -1
- package/dist/pages/promptReferenceText.js +0 -5
- package/dist/pages/promptRetailCinemaText.js +0 -26
- package/dist/services/cgv/index.js +3 -6
- package/dist/services/cgv/location.js +38 -125
- package/dist/services/cgv/tools/findTheaters.js +5 -4
- package/dist/services/cgv/tools/getTimetable.js +5 -4
- package/dist/services/cgv/tools/searchMovies.js +5 -4
- package/dist/services/cu/client.js +3 -35
- package/dist/services/cu/index.js +1 -0
- package/dist/services/cu/tools/checkInventory.js +3 -1
- package/dist/services/gs25/client.js +3 -33
- package/dist/services/gs25/index.js +2 -2
- package/dist/services/gs25/tools/checkInventory.js +3 -1
- package/dist/services/gs25/tools/findNearbyStores.js +3 -1
- package/dist/services/lottecinema/index.js +3 -6
- package/dist/services/lottecinema/location.js +13 -100
- package/dist/services/lottecinema/tools/findNearbyTheaters.js +7 -6
- package/dist/services/lottecinema/tools/getRemainingSeats.js +5 -4
- package/dist/services/lottecinema/tools/listNowShowing.js +5 -4
- package/dist/services/lottemart/client.js +5 -32
- package/dist/services/lottemart/geocode.js +3 -46
- package/dist/services/megabox/index.js +9 -8
- package/dist/services/megabox/location.js +19 -84
- package/dist/services/megabox/tools/findNearbyTheaters.js +7 -7
- package/dist/services/megabox/tools/getRemainingSeats.js +5 -5
- package/dist/services/megabox/tools/listNowShowing.js +5 -5
- package/dist/services/oliveyoung/tools/checkInventory.js +5 -5
- package/dist/services/oliveyoung/tools/findNearbyStores.js +3 -3
- package/dist/services/oliveyoung/tools/searchProducts.js +3 -3
- package/dist/services/oliveyoung/transport.js +49 -33
- package/dist/services/opinet/client.js +1 -1
- package/dist/services/opinet/index.js +1 -1
- package/dist/services/opinet/location.js +5 -56
- package/dist/services/opinet/tools.js +2 -2
- package/dist/utils/geocode.js +155 -0
- package/dist/utils/http.js +2 -2
- package/dist/utils/relayQuota.js +4 -2
- package/openapi.json +4 -183
- package/openapi.yaml +4 -131
- package/package.json +1 -2
- package/dist/pages/openapiSpecComponentsLotteMart.js +0 -85
- package/dist/pages/openapiSpecPathsLotteMart.js +0 -157
|
@@ -7,16 +7,16 @@ export function registerMegaboxRoutes(app) {
|
|
|
7
7
|
app.get('/api/megabox/theaters', async (c) => withEdgeCache(c.req.url, {
|
|
8
8
|
ttlSeconds: 60 * 60 * 24,
|
|
9
9
|
staleWhileRevalidateSeconds: 60 * 5,
|
|
10
|
-
keyPrefix: 'megabox-theaters-
|
|
10
|
+
keyPrefix: 'megabox-theaters-v2',
|
|
11
11
|
}, () => handleMegaboxFindNearbyTheaters(c)));
|
|
12
12
|
app.get('/api/megabox/movies', async (c) => withEdgeCache(c.req.url, {
|
|
13
13
|
ttlSeconds: 60 * 10,
|
|
14
14
|
staleWhileRevalidateSeconds: 60,
|
|
15
|
-
keyPrefix: 'megabox-movies-
|
|
15
|
+
keyPrefix: 'megabox-movies-v2',
|
|
16
16
|
}, () => handleMegaboxListNowShowing(c)));
|
|
17
17
|
app.get('/api/megabox/seats', async (c) => withEdgeCache(c.req.url, {
|
|
18
18
|
ttlSeconds: 60 * 3,
|
|
19
19
|
staleWhileRevalidateSeconds: 30,
|
|
20
|
-
keyPrefix: 'megabox-seats-
|
|
20
|
+
keyPrefix: 'megabox-seats-v2',
|
|
21
21
|
}, () => handleMegaboxGetRemainingSeats(c)));
|
|
22
22
|
}
|
|
@@ -4,21 +4,21 @@ export function registerOpinetRoutes(app) {
|
|
|
4
4
|
app.get('/api/opinet/average', async (c) => withEdgeCache(c.req.url, {
|
|
5
5
|
ttlSeconds: 60 * 60,
|
|
6
6
|
staleWhileRevalidateSeconds: 60 * 30,
|
|
7
|
-
keyPrefix: 'opinet-average-
|
|
7
|
+
keyPrefix: 'opinet-average-v2',
|
|
8
8
|
}, () => handleOpinetAveragePrices(c)));
|
|
9
9
|
app.get('/api/opinet/lowest', async (c) => withEdgeCache(c.req.url, {
|
|
10
10
|
ttlSeconds: 60 * 30,
|
|
11
11
|
staleWhileRevalidateSeconds: 60 * 10,
|
|
12
|
-
keyPrefix: 'opinet-lowest-
|
|
12
|
+
keyPrefix: 'opinet-lowest-v2',
|
|
13
13
|
}, () => handleOpinetLowestStations(c)));
|
|
14
14
|
app.get('/api/opinet/stations/around', async (c) => withEdgeCache(c.req.url, {
|
|
15
15
|
ttlSeconds: 60 * 20,
|
|
16
16
|
staleWhileRevalidateSeconds: 60 * 5,
|
|
17
|
-
keyPrefix: 'opinet-around-
|
|
17
|
+
keyPrefix: 'opinet-around-v2',
|
|
18
18
|
}, () => handleOpinetStationsAround(c)));
|
|
19
19
|
app.get('/api/opinet/station', async (c) => withEdgeCache(c.req.url, {
|
|
20
20
|
ttlSeconds: 60 * 60,
|
|
21
21
|
staleWhileRevalidateSeconds: 60 * 10,
|
|
22
|
-
keyPrefix: 'opinet-station-
|
|
22
|
+
keyPrefix: 'opinet-station-v2',
|
|
23
23
|
}, () => handleOpinetStationDetail(c)));
|
|
24
24
|
}
|
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,
|
|
15
|
+
import { handleCuStores, handleCuInventory, handleEmart24Stores, handleEmart24Products, handleEmart24Inventory, handleCompareProducts, handlePlaces, handleGs25Stores, handleGs25Products, handleGs25Inventory, handleSevenElevenProducts, handleSevenElevenStores, handleSevenElevenInventory, handleSevenElevenPopwords, handleSevenElevenCatalog, } from './cli/commands/convenience.js';
|
|
16
16
|
import { handleCgvTheaters, handleCgvMovies, handleCgvTimetable, handleLottecinemaTheaters, handleLottecinemaMovies, handleLottecinemaSeats, handleMegaboxTheaters, handleMegaboxMovies, handleMegaboxSeats, } from './cli/commands/cinema.js';
|
|
17
17
|
function rejectTopLevelUnknownOptions(options, command, deps) {
|
|
18
18
|
const parsed = parseCliArgs(options);
|
|
@@ -101,6 +101,10 @@ export async function runCli(argv, deps) {
|
|
|
101
101
|
resolvedDeps.writeOut(`claude ${cliArgs.join(' ')}`);
|
|
102
102
|
return 0;
|
|
103
103
|
}
|
|
104
|
+
if (command === 'lottemart-stores' || command === 'lottemart-products') {
|
|
105
|
+
resolvedDeps.writeErr('롯데마트 서비스 지원이 종료되었습니다.');
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
104
108
|
if (command === 'get')
|
|
105
109
|
return await handleGet(options, resolvedDeps);
|
|
106
110
|
if (command === 'products')
|
|
@@ -145,10 +149,6 @@ export async function runCli(argv, deps) {
|
|
|
145
149
|
return await handleEmart24Products(options, resolvedDeps);
|
|
146
150
|
if (command === 'emart24-inventory')
|
|
147
151
|
return await handleEmart24Inventory(options, resolvedDeps);
|
|
148
|
-
if (command === 'lottemart-stores')
|
|
149
|
-
return await handleLotteMartStores(options, resolvedDeps);
|
|
150
|
-
if (command === 'lottemart-products')
|
|
151
|
-
return await handleLotteMartProducts(options, resolvedDeps);
|
|
152
152
|
if (command === 'gs25-stores')
|
|
153
153
|
return await handleGs25Stores(options, resolvedDeps);
|
|
154
154
|
if (command === 'gs25-products')
|
package/dist/cliHelp.js
CHANGED
|
@@ -33,8 +33,6 @@ export function printHelp(writeOut) {
|
|
|
33
33
|
writeOut(' npx daiso cgv-theaters 강남 --limit 5');
|
|
34
34
|
writeOut(' npx daiso cgv-timetable --playDate 20260307 --theaterCode 0056');
|
|
35
35
|
writeOut(' npx daiso lottecinema-theaters --keyword "안산 중앙역" --limit 5');
|
|
36
|
-
writeOut(' npx daiso lottemart-stores 잠실 --area 서울 --limit 10');
|
|
37
|
-
writeOut(' npx daiso lottemart-products 콜라 --storeName 강변점 --area 서울');
|
|
38
36
|
writeOut(' npx daiso emart24-products 두바이 --pageSize 20');
|
|
39
37
|
writeOut(' npx daiso gs25-products 오감자');
|
|
40
38
|
writeOut(' npx daiso gs25-inventory 오감자 --storeKeyword 강남');
|
|
@@ -48,7 +46,6 @@ export function printHelp(writeOut) {
|
|
|
48
46
|
writeOut(' 제품명을 먼저 검색해 productId를 확인: npx daiso products 수납박스');
|
|
49
47
|
writeOut(' productId로 재고 확인: npx daiso inventory <productId> --keyword 강남역');
|
|
50
48
|
writeOut(' 매장을 먼저 검색해 storeName 또는 storeCode를 확인: npx daiso stores 강남역');
|
|
51
|
-
writeOut(' 롯데마트 매장 확인: npx daiso lottemart-stores 잠실 --area 서울');
|
|
52
49
|
writeOut('');
|
|
53
50
|
writeOut('상세 도움말:');
|
|
54
51
|
writeOut(' npx daiso help <command>');
|
|
@@ -26,8 +26,6 @@ export const COMMAND_LIST = [
|
|
|
26
26
|
'megabox-theaters',
|
|
27
27
|
'megabox-movies',
|
|
28
28
|
'megabox-seats',
|
|
29
|
-
'lottemart-stores',
|
|
30
|
-
'lottemart-products',
|
|
31
29
|
'emart24-stores',
|
|
32
30
|
'emart24-products',
|
|
33
31
|
'emart24-inventory',
|
|
@@ -262,23 +260,8 @@ export const COMMAND_DETAIL = {
|
|
|
262
260
|
'예시: daiso megabox-seats 강남 --playDate 20260310 --movieId 23816',
|
|
263
261
|
'예시: daiso megabox-seats --playDate 20260310 --keyword "코엑스" --movieId 23816',
|
|
264
262
|
],
|
|
265
|
-
'lottemart-stores': [
|
|
266
|
-
|
|
267
|
-
'설명: 롯데마트 계열 매장 검색 API를 호출합니다.',
|
|
268
|
-
'사용법: daiso lottemart-stores [keyword] [--area 값] [--brandVariant 값] [--lat 값] [--lng 값] [--limit N] [--json]',
|
|
269
|
-
'옵션: --keyword, --area, --brandVariant, --lat, --lng, --limit, --json',
|
|
270
|
-
'예시: daiso lottemart-stores 잠실 --area 서울',
|
|
271
|
-
'예시: daiso lottemart-stores --area 경기 --brandVariant lottemart --limit 10',
|
|
272
|
-
],
|
|
273
|
-
'lottemart-products': [
|
|
274
|
-
'명령: lottemart-products',
|
|
275
|
-
'설명: 롯데마트 매장 기준 상품 검색 API를 호출합니다.',
|
|
276
|
-
'사용법: daiso lottemart-products <keyword> [--storeCode 값 | --storeName 값] [--area 값] [--pageLimit N] [--json]',
|
|
277
|
-
'필수: <keyword>, --storeCode 또는 --storeName',
|
|
278
|
-
'옵션: --storeCode, --storeName, --area, --pageLimit, --json',
|
|
279
|
-
'예시: daiso lottemart-products 콜라 --storeName 강변점 --area 서울',
|
|
280
|
-
'예시: daiso lottemart-products 우유 --storeCode 2301 --pageLimit 2',
|
|
281
|
-
],
|
|
263
|
+
'lottemart-stores': ['롯데마트 서비스 지원이 종료되었습니다.'],
|
|
264
|
+
'lottemart-products': ['롯데마트 서비스 지원이 종료되었습니다.'],
|
|
282
265
|
'emart24-stores': [
|
|
283
266
|
'명령: emart24-stores',
|
|
284
267
|
'설명: 이마트24 매장 검색 API를 호출합니다.',
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { handleLotteMartFindStores } from './api/lottemartHandlers.js';
|
|
2
|
+
import { geocodeBindings } from './utils/geocode.js';
|
|
1
3
|
/**
|
|
2
4
|
* 다중 서비스 MCP 서버
|
|
3
5
|
*
|
|
@@ -17,7 +19,6 @@ import { createDtryxService } from './services/dtryx/index.js';
|
|
|
17
19
|
import { createMegaboxService } from './services/megabox/index.js';
|
|
18
20
|
import { createCgvService } from './services/cgv/index.js';
|
|
19
21
|
import { createLotteCinemaService } from './services/lottecinema/index.js';
|
|
20
|
-
import { createLotteMartService } from './services/lottemart/index.js';
|
|
21
22
|
import { createCuService } from './services/cu/index.js';
|
|
22
23
|
import { createEmart24Service } from './services/emart24/index.js';
|
|
23
24
|
import { createGs25Service } from './services/gs25/index.js';
|
|
@@ -50,7 +51,7 @@ const createRegistry = (bindings) => {
|
|
|
50
51
|
registry.registerAll([
|
|
51
52
|
createDaisoService,
|
|
52
53
|
() => createGs25Service({
|
|
53
|
-
|
|
54
|
+
...geocodeBindings(bindings),
|
|
54
55
|
zyteApiKey: bindings?.ZYTE_API_KEY,
|
|
55
56
|
apiKey: bindings?.GS25_API_KEY,
|
|
56
57
|
}),
|
|
@@ -66,25 +67,21 @@ const createRegistry = (bindings) => {
|
|
|
66
67
|
}),
|
|
67
68
|
() => createOpinetService({
|
|
68
69
|
apiKey: bindings?.OPINET_API_KEY,
|
|
69
|
-
|
|
70
|
+
...geocodeBindings(bindings),
|
|
70
71
|
}),
|
|
71
72
|
() => createCuService({
|
|
72
73
|
zyteApiKey: bindings?.ZYTE_API_KEY,
|
|
73
|
-
|
|
74
|
+
...geocodeBindings(bindings),
|
|
74
75
|
}),
|
|
75
76
|
createEmart24Service,
|
|
76
|
-
() =>
|
|
77
|
-
googleMapsApiKey: bindings?.GOOGLE_MAPS_API_KEY,
|
|
78
|
-
zyteApiKey: bindings?.ZYTE_API_KEY,
|
|
79
|
-
}),
|
|
80
|
-
createMegaboxService,
|
|
77
|
+
() => createMegaboxService(geocodeBindings(bindings)),
|
|
81
78
|
() => createDtryxService(dtryxTransportFromBindings(bindings)),
|
|
82
79
|
() => createLotteCinemaService({
|
|
83
|
-
|
|
80
|
+
...geocodeBindings(bindings),
|
|
84
81
|
}),
|
|
85
82
|
() => createCgvService({
|
|
86
83
|
zyteApiKey: bindings?.ZYTE_API_KEY,
|
|
87
|
-
|
|
84
|
+
...geocodeBindings(bindings),
|
|
88
85
|
}),
|
|
89
86
|
() => createOliveyoungService({
|
|
90
87
|
relayUrl: bindings?.OY_RELAY_URL,
|
|
@@ -303,6 +300,8 @@ app.get('/privacy', (c) => {
|
|
|
303
300
|
});
|
|
304
301
|
// GET API 엔드포인트 (MCP 미지원 에이전트용)
|
|
305
302
|
app.get('/api/actions/query', async (c) => {
|
|
303
|
+
if (['lottemartFindStores', 'lottemartSearchProducts'].includes(c.req.query('action') || ''))
|
|
304
|
+
return handleLotteMartFindStores(c);
|
|
306
305
|
try {
|
|
307
306
|
const targetUrl = buildActionQueryTargetUrl(c.req.url);
|
|
308
307
|
const headers = new Headers(c.req.raw.headers);
|
|
@@ -9,7 +9,6 @@ const PROTECTED_SERVICES = [
|
|
|
9
9
|
{ prefix: '/api/cgv/', service: 'cgv' },
|
|
10
10
|
{ prefix: '/api/cu/', service: 'cu' },
|
|
11
11
|
{ prefix: '/api/gs25/', service: 'gs25' },
|
|
12
|
-
{ prefix: '/api/lottemart/', service: 'lottemart' },
|
|
13
12
|
];
|
|
14
13
|
const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
15
14
|
// Cloudflare가 교차 존 Worker subrequest에 사용하는 공통 client IP
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
import { OPENAPI_PATHS_DAISO_OLIVEYOUNG } from './openapiSpecPathsDaisoOliveyoung.js';
|
|
5
5
|
import { OPENAPI_PATHS_CU } from './openapiSpecPathsCu.js';
|
|
6
6
|
import { OPENAPI_PATHS_EMART24 } from './openapiSpecPathsEmart24.js';
|
|
7
|
-
import { OPENAPI_PATHS_LOTTEMART } from './openapiSpecPathsLotteMart.js';
|
|
8
7
|
import { OPENAPI_PATHS_MEGABOX } from './openapiSpecPathsMegabox.js';
|
|
9
8
|
import { OPENAPI_PATHS_CGV } from './openapiSpecPathsCgv.js';
|
|
10
9
|
import { OPENAPI_PATHS_LOTTECINEMA } from './openapiSpecPathsLottecinema.js';
|
|
@@ -54,7 +53,6 @@ export function generateFullOpenApiSpec(baseUrl) {
|
|
|
54
53
|
...OPENAPI_PATHS_DAISO_OLIVEYOUNG,
|
|
55
54
|
...OPENAPI_PATHS_CU,
|
|
56
55
|
...OPENAPI_PATHS_EMART24,
|
|
57
|
-
...OPENAPI_PATHS_LOTTEMART,
|
|
58
56
|
...OPENAPI_PATHS_GS25,
|
|
59
57
|
...OPENAPI_PATHS_SEVENELEVEN,
|
|
60
58
|
...OPENAPI_PATHS_COMPARE,
|
|
@@ -192,7 +192,7 @@ export const ACTION_QUERY_PARAMETERS = [
|
|
|
192
192
|
name: 'area',
|
|
193
193
|
in: 'query',
|
|
194
194
|
required: false,
|
|
195
|
-
description: '
|
|
195
|
+
description: '지역',
|
|
196
196
|
schema: { type: 'string' },
|
|
197
197
|
},
|
|
198
198
|
{
|
|
@@ -220,7 +220,7 @@ export const ACTION_QUERY_PARAMETERS = [
|
|
|
220
220
|
name: 'brandVariant',
|
|
221
221
|
in: 'query',
|
|
222
222
|
required: false,
|
|
223
|
-
description: '
|
|
223
|
+
description: '브랜드 변형',
|
|
224
224
|
schema: { type: 'string' },
|
|
225
225
|
},
|
|
226
226
|
{
|
|
@@ -255,7 +255,7 @@ export const ACTION_QUERY_PARAMETERS = [
|
|
|
255
255
|
name: 'pageLimit',
|
|
256
256
|
in: 'query',
|
|
257
257
|
required: false,
|
|
258
|
-
description: '
|
|
258
|
+
description: '추가 페이지 수',
|
|
259
259
|
schema: { type: 'integer' },
|
|
260
260
|
},
|
|
261
261
|
{
|
|
@@ -304,7 +304,7 @@ export const ACTION_QUERY_PARAMETERS = [
|
|
|
304
304
|
name: 'storeName',
|
|
305
305
|
in: 'query',
|
|
306
306
|
required: false,
|
|
307
|
-
description: '
|
|
307
|
+
description: '매장명',
|
|
308
308
|
schema: { type: 'string' },
|
|
309
309
|
},
|
|
310
310
|
{
|
|
@@ -7,7 +7,6 @@ import { OPENAPI_DAISO_OLIVEYOUNG_COMPONENT_SCHEMAS } from './openapiSpecCompone
|
|
|
7
7
|
import { OPENAPI_EMART24_COMPONENT_SCHEMAS } from './openapiSpecComponentsEmart24.js';
|
|
8
8
|
import { OPENAPI_GS25_COMPONENT_SCHEMAS } from './openapiSpecComponentsGs25.js';
|
|
9
9
|
import { OPENAPI_LOTTECINEMA_COMPONENT_SCHEMAS } from './openapiSpecComponentsLottecinema.js';
|
|
10
|
-
import { OPENAPI_LOTTEMART_COMPONENT_SCHEMAS } from './openapiSpecComponentsLotteMart.js';
|
|
11
10
|
import { OPENAPI_MEGABOX_COMPONENT_SCHEMAS } from './openapiSpecComponentsMegabox.js';
|
|
12
11
|
import { OPENAPI_SEVENELEVEN_COMPONENT_SCHEMAS } from './openapiSpecComponentsSeveneleven.js';
|
|
13
12
|
export const OPENAPI_COMPONENTS = {
|
|
@@ -16,7 +15,6 @@ export const OPENAPI_COMPONENTS = {
|
|
|
16
15
|
...OPENAPI_MEGABOX_COMPONENT_SCHEMAS,
|
|
17
16
|
...OPENAPI_CU_COMPONENT_SCHEMAS,
|
|
18
17
|
...OPENAPI_EMART24_COMPONENT_SCHEMAS,
|
|
19
|
-
...OPENAPI_LOTTEMART_COMPONENT_SCHEMAS,
|
|
20
18
|
...OPENAPI_GS25_COMPONENT_SCHEMAS,
|
|
21
19
|
...OPENAPI_SEVENELEVEN_COMPONENT_SCHEMAS,
|
|
22
20
|
...OPENAPI_LOTTECINEMA_COMPONENT_SCHEMAS,
|
|
@@ -6,7 +6,7 @@ export const OPENAPI_PATHS_MEGABOX = {
|
|
|
6
6
|
get: {
|
|
7
7
|
operationId: 'megaboxFindNearbyTheaters',
|
|
8
8
|
summary: '메가박스 주변 지점 조회',
|
|
9
|
-
description: '좌표 또는 위치 키워드 기준으로 메가박스 지점을 거리순으로 조회합니다. lat/lng가 없으면 keyword를
|
|
9
|
+
description: '좌표 또는 위치 키워드 기준으로 메가박스 지점을 거리순으로 조회합니다. lat/lng가 없으면 keyword를 카카오 무료 위치 검색(장소명은 네이버 지역 검색으로 보완)로 보강할 수 있습니다.',
|
|
10
10
|
parameters: [
|
|
11
11
|
{
|
|
12
12
|
name: 'keyword',
|
|
@@ -54,8 +54,6 @@ export function buildPromptReferenceText(baseUrl) {
|
|
|
54
54
|
| GS25_STORE_SEARCH_FAILED | GS25 매장 조회 실패 |
|
|
55
55
|
| GS25_PRODUCT_SEARCH_FAILED | GS25 상품 조회 실패 |
|
|
56
56
|
| GS25_INVENTORY_CHECK_FAILED | GS25 재고 조회 실패 |
|
|
57
|
-
| LOTTEMART_STORE_SEARCH_FAILED | 롯데마트 매장 조회 실패 |
|
|
58
|
-
| LOTTEMART_PRODUCT_SEARCH_FAILED | 롯데마트 상품 조회 실패 |
|
|
59
57
|
| SEVENELEVEN_INVENTORY_CHECK_FAILED | 세븐일레븐 재고 조회 실패 |
|
|
60
58
|
| PLACES_SEARCH_FAILED | 주변 장소 검색 실패 |
|
|
61
59
|
| DEVELOPER_REQUEST_SUBMIT_FAILED | 개발자 요청 저장 실패 |
|
|
@@ -107,7 +105,6 @@ export function buildPromptReferenceText(baseUrl) {
|
|
|
107
105
|
- 너무 넓은 축약어로만 매칭된 경우에는 확정 상품처럼 단정하지 말고, 추정 매칭임을 짧게 밝힌 뒤 재고를 안내합니다.
|
|
108
106
|
- 위치/브랜드/상품명은 한 인자에 섞지 말고 가능한 한 분리해서 도구에 전달합니다.
|
|
109
107
|
8. **위치 기반 재고**: lat, lng 파라미터로 가까운 매장 우선 조회
|
|
110
|
-
9. **롯데마트 상품 조회**: /api/lottemart/products는 keyword와 함께 storeCode 또는 storeName이 필요합니다.
|
|
111
108
|
10. **세븐일레븐 재고 조회**: /api/seveneleven/inventory에 keyword + storeKeyword를 함께 주면 매장별 수량을 바로 확인할 수 있습니다.
|
|
112
109
|
11. **이마트24 재고 조회**: /api/emart24/inventory는 pluCd + storeKeyword 조합도 지원하므로, 상품 선택 뒤 매장 코드를 다시 모으지 않아도 됩니다.
|
|
113
110
|
12. **올리브영 재고 해석**: inventory.products[].storeInventory.stores[]가 있으면 그 매장별 stockLabel과 remainQuantity를 우선 사용하고, inStock는 그 주변 매장 기준 결과로 해석합니다.
|
|
@@ -128,8 +125,6 @@ MCP 연결 정보: ${baseUrl}/mcp
|
|
|
128
125
|
- daiso_check_inventory: 재고 확인
|
|
129
126
|
- daiso_get_price_info: 가격 정보 조회
|
|
130
127
|
- daiso_get_display_location: 진열 위치 조회
|
|
131
|
-
- lottemart_find_nearby_stores: 롯데마트 주변 매장 탐색
|
|
132
|
-
- lottemart_search_products: 롯데마트 상품 검색
|
|
133
128
|
- gs25_find_nearby_stores: GS25 주변 매장 탐색
|
|
134
129
|
- gs25_search_products: GS25 상품 검색
|
|
135
130
|
- gs25_check_inventory: GS25 재고 조회
|
|
@@ -48,32 +48,6 @@ export function buildPromptRetailCinemaText(baseUrl) {
|
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
51
|
-
### 6-2. 롯데마트 매장/상품 조회
|
|
52
|
-
|
|
53
|
-
**설명**: 롯데마트 계열 매장 검색과 특정 매장 기준 상품 가격/재고 조회를 제공합니다.
|
|
54
|
-
|
|
55
|
-
**URL**:
|
|
56
|
-
- ${baseUrl}/api/lottemart/stores?keyword={키워드}
|
|
57
|
-
- ${baseUrl}/api/lottemart/products?keyword={검색어}&storeName={매장명}
|
|
58
|
-
|
|
59
|
-
**선택 파라미터**:
|
|
60
|
-
- area: 지역 (예: 서울, 경기, 제주)
|
|
61
|
-
- brandVariant: lottemart, toysrus, max, bottlebunker, mealguru, grandgrocery
|
|
62
|
-
- lat: 위도 (선택)
|
|
63
|
-
- lng: 경도 (선택)
|
|
64
|
-
- limit: 최대 결과 수 (기본값: 20)
|
|
65
|
-
- storeCode 또는 storeName: 상품 검색 대상 매장
|
|
66
|
-
- pageLimit: 추가 조회할 최대 페이지 수 (기본값: 3)
|
|
67
|
-
- source: 상품 검색 경로 (auto, legacy, zetta). 빠른 검색이나 헬스체크에는 zetta 권장
|
|
68
|
-
|
|
69
|
-
**예시**:
|
|
70
|
-
- ${baseUrl}/api/lottemart/stores?keyword=잠실&area=서울&limit=10
|
|
71
|
-
- ${baseUrl}/api/lottemart/stores?area=경기&brandVariant=lottemart&limit=10
|
|
72
|
-
- ${baseUrl}/api/lottemart/products?keyword=콜라&storeName=강변점&area=서울
|
|
73
|
-
- ${baseUrl}/api/lottemart/products?keyword=우유&storeCode=2301&pageLimit=2&source=zetta
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
51
|
### 6-3. GS25 매장/상품/재고 조회
|
|
78
52
|
|
|
79
53
|
**설명**: GS25 매장 탐색, 상품 키워드 검색, 재고 조회를 제공합니다.
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CGV 서비스 프로바이더
|
|
3
|
-
*/
|
|
4
1
|
import { createFindTheatersTool } from './tools/findTheaters.js';
|
|
5
2
|
import { createSearchMoviesTool } from './tools/searchMovies.js';
|
|
6
3
|
import { createGetTimetableTool } from './tools/getTimetable.js';
|
|
@@ -18,9 +15,9 @@ class CgvService {
|
|
|
18
15
|
metadata = CGV_METADATA;
|
|
19
16
|
getTools() {
|
|
20
17
|
return [
|
|
21
|
-
createFindTheatersTool(this.options.zyteApiKey, this.options.googleMapsApiKey),
|
|
22
|
-
createSearchMoviesTool(this.options.zyteApiKey, this.options.googleMapsApiKey),
|
|
23
|
-
createGetTimetableTool(this.options.zyteApiKey, this.options.googleMapsApiKey),
|
|
18
|
+
createFindTheatersTool(this.options.zyteApiKey, this.options.googleMapsApiKey, this.options),
|
|
19
|
+
createSearchMoviesTool(this.options.zyteApiKey, this.options.googleMapsApiKey, this.options),
|
|
20
|
+
createGetTimetableTool(this.options.zyteApiKey, this.options.googleMapsApiKey, this.options),
|
|
24
21
|
];
|
|
25
22
|
}
|
|
26
23
|
}
|
|
@@ -1,32 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* CGV 위치 해석 및 근처 극장 조회 보조 모듈
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
4
|
+
import { geocodeLocation, readGeocodeEnvironment, searchKakaoPlaces, validKoreanCoordinates, } from '../../utils/geocode.js';
|
|
5
5
|
import { fetchCgvTheaters } from './client.js';
|
|
6
6
|
const DEFAULT_TIMEOUT_MS = 15000;
|
|
7
|
-
const USER_GEOCODE_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
8
|
-
const THEATER_GEOCODE_CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
9
|
-
const MAX_CANDIDATE_THEATERS = 12;
|
|
10
|
-
const LOCATION_STOPWORDS = new Set([
|
|
11
|
-
'cgv',
|
|
12
|
-
'극장',
|
|
13
|
-
'영화',
|
|
14
|
-
'주변',
|
|
15
|
-
'근처',
|
|
16
|
-
'오늘',
|
|
17
|
-
'상영',
|
|
18
|
-
'시간표',
|
|
19
|
-
'좌석',
|
|
20
|
-
'잔여',
|
|
21
|
-
'찾아',
|
|
22
|
-
'주세요',
|
|
23
|
-
'주세요요',
|
|
24
|
-
'역',
|
|
25
|
-
]);
|
|
26
7
|
const BRAND_PATTERN = /\b(?:cgv|씨지브이)\b/giu;
|
|
27
8
|
const TRAILING_INTENT_PATTERN = /\s+(?:극장|영화관|영화|상영작|시간표|좌석|잔여좌석|남은좌석|목록|찾고|찾아|찾아서|알려|보여|추천|조회|확인|해주세요|해줘).*/u;
|
|
28
|
-
const userGeocodeCache = new Map();
|
|
29
|
-
const theaterGeocodeCache = new Map();
|
|
30
9
|
function calculateDistanceKm(lat1, lng1, lat2, lng2) {
|
|
31
10
|
const toRad = (deg) => (deg * Math.PI) / 180;
|
|
32
11
|
const dLat = toRad(lat2 - lat1);
|
|
@@ -60,61 +39,12 @@ function sanitizeLocationKeyword(keyword) {
|
|
|
60
39
|
}
|
|
61
40
|
return sanitized || trimmed;
|
|
62
41
|
}
|
|
63
|
-
function extractLocationTokens(...values) {
|
|
64
|
-
const tokens = new Set();
|
|
65
|
-
for (const value of values) {
|
|
66
|
-
const matches = (value || '').match(/[0-9A-Za-z가-힣]+/g) || [];
|
|
67
|
-
for (const match of matches) {
|
|
68
|
-
const normalized = normalizeText(match);
|
|
69
|
-
if (normalized.length < 2 || LOCATION_STOPWORDS.has(normalized)) {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
tokens.add(normalized);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return [...tokens];
|
|
76
|
-
}
|
|
77
|
-
function scoreTheaterName(theaterName, tokens) {
|
|
78
|
-
const normalized = normalizeText(theaterName);
|
|
79
|
-
return tokens.reduce((score, token) => (normalized.includes(token) ? score + 1 : score), 0);
|
|
80
|
-
}
|
|
81
|
-
async function geocodeAddress(query, options, cacheKey, ttlMs, cache) {
|
|
82
|
-
const apiKey = (options.googleMapsApiKey || '').trim();
|
|
83
|
-
const trimmedQuery = query.trim();
|
|
84
|
-
if (apiKey.length === 0 || trimmedQuery.length === 0) {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
const cached = cache.get(cacheKey);
|
|
88
|
-
if (cached && cached.expiresAt > Date.now()) {
|
|
89
|
-
return cached.value;
|
|
90
|
-
}
|
|
91
|
-
const endpoint = new URL('https://maps.googleapis.com/maps/api/geocode/json');
|
|
92
|
-
endpoint.searchParams.set('address', trimmedQuery);
|
|
93
|
-
endpoint.searchParams.set('key', apiKey);
|
|
94
|
-
const body = await fetchJson(endpoint.toString(), {
|
|
95
|
-
method: 'GET',
|
|
96
|
-
timeout: options.timeout || DEFAULT_TIMEOUT_MS,
|
|
97
|
-
headers: { Accept: 'application/json' },
|
|
98
|
-
});
|
|
99
|
-
const firstResult = body.status === 'OK' ? body.results?.[0] : null;
|
|
100
|
-
const latitude = firstResult?.geometry?.location?.lat;
|
|
101
|
-
const longitude = firstResult?.geometry?.location?.lng;
|
|
102
|
-
const value = typeof latitude === 'number' && typeof longitude === 'number'
|
|
103
|
-
? {
|
|
104
|
-
latitude,
|
|
105
|
-
longitude,
|
|
106
|
-
formattedAddress: firstResult?.formatted_address || null,
|
|
107
|
-
}
|
|
108
|
-
: null;
|
|
109
|
-
cache.set(cacheKey, {
|
|
110
|
-
expiresAt: Date.now() + ttlMs,
|
|
111
|
-
value,
|
|
112
|
-
});
|
|
113
|
-
return value;
|
|
114
|
-
}
|
|
115
42
|
export async function resolveCgvLocation(params, options = {}) {
|
|
116
43
|
const keyword = sanitizeLocationKeyword((params.keyword || '').trim());
|
|
117
44
|
const hasCoordinates = isValidCoordinate(params.latitude) && isValidCoordinate(params.longitude);
|
|
45
|
+
if (hasCoordinates &&
|
|
46
|
+
!validKoreanCoordinates(params.latitude, params.longitude))
|
|
47
|
+
throw new Error('유효한 위도/경도를 입력해주세요.');
|
|
118
48
|
if (hasCoordinates) {
|
|
119
49
|
return {
|
|
120
50
|
keyword: keyword.length > 0 ? keyword : null,
|
|
@@ -133,7 +63,7 @@ export async function resolveCgvLocation(params, options = {}) {
|
|
|
133
63
|
formattedAddress: null,
|
|
134
64
|
};
|
|
135
65
|
}
|
|
136
|
-
const geocoded = await
|
|
66
|
+
const geocoded = await geocodeLocation(keyword, options);
|
|
137
67
|
return {
|
|
138
68
|
keyword,
|
|
139
69
|
latitude: geocoded?.latitude ?? null,
|
|
@@ -142,28 +72,6 @@ export async function resolveCgvLocation(params, options = {}) {
|
|
|
142
72
|
formattedAddress: geocoded?.formattedAddress ?? null,
|
|
143
73
|
};
|
|
144
74
|
}
|
|
145
|
-
async function geocodeCgvTheater(theater, options) {
|
|
146
|
-
const cleanName = theater.theaterName.replace(/^CGV\s*/i, '').trim();
|
|
147
|
-
const geocoded = await geocodeAddress(`대한민국 CGV ${cleanName}`, options, `theater:${theater.theaterCode}:${cleanName}`, THEATER_GEOCODE_CACHE_TTL_MS, theaterGeocodeCache);
|
|
148
|
-
return geocoded
|
|
149
|
-
? {
|
|
150
|
-
latitude: geocoded.latitude,
|
|
151
|
-
longitude: geocoded.longitude,
|
|
152
|
-
address: geocoded.formattedAddress,
|
|
153
|
-
}
|
|
154
|
-
: null;
|
|
155
|
-
}
|
|
156
|
-
function pickCandidateTheaters(theaters, tokens) {
|
|
157
|
-
if (tokens.length === 0) {
|
|
158
|
-
return theaters.slice(0, MAX_CANDIDATE_THEATERS);
|
|
159
|
-
}
|
|
160
|
-
const scored = theaters
|
|
161
|
-
.map((theater) => ({ theater, score: scoreTheaterName(theater.theaterName, tokens) }))
|
|
162
|
-
.filter((item) => item.score > 0)
|
|
163
|
-
.sort((left, right) => right.score - left.score || left.theater.theaterName.localeCompare(right.theater.theaterName))
|
|
164
|
-
.map((item) => item.theater);
|
|
165
|
-
return scored.slice(0, MAX_CANDIDATE_THEATERS);
|
|
166
|
-
}
|
|
167
75
|
export async function fetchCgvNearbyTheaters(params, options = {}) {
|
|
168
76
|
const limit = Math.max(1, params.limit || 10);
|
|
169
77
|
const timeout = params.timeout || DEFAULT_TIMEOUT_MS;
|
|
@@ -174,34 +82,40 @@ export async function fetchCgvNearbyTheaters(params, options = {}) {
|
|
|
174
82
|
zyteApiKey: options.zyteApiKey,
|
|
175
83
|
});
|
|
176
84
|
const location = await resolveCgvLocation(params, options);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
};
|
|
85
|
+
if (location.keyword && location.latitude === null)
|
|
86
|
+
throw new Error(`위치를 좌표로 변환하지 못했습니다: ${location.keyword}`);
|
|
87
|
+
const geocodeOptions = { ...readGeocodeEnvironment(), ...options };
|
|
88
|
+
let resolvedCandidates;
|
|
89
|
+
if (location.latitude !== null && location.longitude !== null) {
|
|
90
|
+
// 주변 장소를 공식 CGV 목록에 연결하여 전국 앞부분만 검색하는 오류를 막습니다.
|
|
91
|
+
const places = await searchKakaoPlaces('CGV', geocodeOptions, {
|
|
92
|
+
latitude: location.latitude,
|
|
93
|
+
longitude: location.longitude,
|
|
94
|
+
});
|
|
95
|
+
resolvedCandidates = places.flatMap((place) => {
|
|
96
|
+
const cleanName = normalizeText((place.place_name || '').replace(/^CGV\s*/iu, ''));
|
|
97
|
+
const theater = theaters.find((item) => normalizeText(item.theaterName.replace(/^CGV\s*/iu, '')) === cleanName);
|
|
98
|
+
const latitude = Number(place.y);
|
|
99
|
+
const longitude = Number(place.x);
|
|
100
|
+
if (!theater || !validKoreanCoordinates(latitude, longitude))
|
|
101
|
+
return [];
|
|
102
|
+
return [
|
|
103
|
+
{
|
|
104
|
+
...theater,
|
|
105
|
+
latitude,
|
|
106
|
+
longitude,
|
|
107
|
+
address: place.road_address_name || place.address_name || null,
|
|
108
|
+
distanceKm: Number(calculateDistanceKm(location.latitude, location.longitude, latitude, longitude).toFixed(2)),
|
|
109
|
+
},
|
|
110
|
+
];
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
resolvedCandidates = [];
|
|
187
115
|
}
|
|
188
|
-
const candidates = pickCandidateTheaters(theaters, tokens);
|
|
189
|
-
const resolvedCandidates = await Promise.all(candidates.map(async (theater) => {
|
|
190
|
-
const geocoded = await geocodeCgvTheater(theater, options);
|
|
191
|
-
const distanceKm = geocoded && location.latitude !== null && location.longitude !== null
|
|
192
|
-
? Number(calculateDistanceKm(location.latitude, location.longitude, geocoded.latitude, geocoded.longitude).toFixed(2))
|
|
193
|
-
: null;
|
|
194
|
-
return {
|
|
195
|
-
...theater,
|
|
196
|
-
latitude: geocoded?.latitude ?? null,
|
|
197
|
-
longitude: geocoded?.longitude ?? null,
|
|
198
|
-
distanceKm,
|
|
199
|
-
address: geocoded?.address ?? null,
|
|
200
|
-
};
|
|
201
|
-
}));
|
|
202
116
|
const sorted = resolvedCandidates.sort((left, right) => {
|
|
203
|
-
const leftDistance = left.distanceKm
|
|
204
|
-
const rightDistance = right.distanceKm
|
|
117
|
+
const leftDistance = left.distanceKm;
|
|
118
|
+
const rightDistance = right.distanceKm;
|
|
205
119
|
return leftDistance - rightDistance || left.theaterName.localeCompare(right.theaterName);
|
|
206
120
|
});
|
|
207
121
|
return {
|
|
@@ -230,6 +144,5 @@ export async function resolveCgvNearestTheater(params, options = {}) {
|
|
|
230
144
|
};
|
|
231
145
|
}
|
|
232
146
|
export function __testOnlyClearCgvLocationCaches() {
|
|
233
|
-
|
|
234
|
-
theaterGeocodeCache.clear();
|
|
147
|
+
// 캐시를 유지하지 않으므로 초기화할 상태가 없습니다.
|
|
235
148
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import * as z from 'zod';
|
|
5
5
|
import { fetchCgvTheaters, toYyyymmdd } from '../client.js';
|
|
6
6
|
import { fetchCgvNearbyTheaters } from '../location.js';
|
|
7
|
-
async function findTheaters(args, apiKey, googleMapsApiKey) {
|
|
7
|
+
async function findTheaters(args, apiKey, googleMapsApiKey, geocodeOptions = {}) {
|
|
8
8
|
const { playDate = toYyyymmdd(), regionCode, keyword, latitude, longitude, limit = 30, timeoutMs = 15000, } = args;
|
|
9
9
|
if (keyword || typeof latitude === 'number' || typeof longitude === 'number') {
|
|
10
10
|
const result = await fetchCgvNearbyTheaters({
|
|
@@ -18,7 +18,8 @@ async function findTheaters(args, apiKey, googleMapsApiKey) {
|
|
|
18
18
|
}, {
|
|
19
19
|
timeout: timeoutMs,
|
|
20
20
|
zyteApiKey: apiKey,
|
|
21
|
-
|
|
21
|
+
...geocodeOptions,
|
|
22
|
+
googleMapsApiKey,
|
|
22
23
|
});
|
|
23
24
|
return {
|
|
24
25
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
@@ -47,7 +48,7 @@ async function findTheaters(args, apiKey, googleMapsApiKey) {
|
|
|
47
48
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
48
49
|
};
|
|
49
50
|
}
|
|
50
|
-
export function createFindTheatersTool(apiKey, googleMapsApiKey) {
|
|
51
|
+
export function createFindTheatersTool(apiKey, googleMapsApiKey, geocodeOptions = {}) {
|
|
51
52
|
return {
|
|
52
53
|
name: 'cgv_find_theaters',
|
|
53
54
|
metadata: {
|
|
@@ -63,6 +64,6 @@ export function createFindTheatersTool(apiKey, googleMapsApiKey) {
|
|
|
63
64
|
timeoutMs: z.number().optional().default(15000).describe('요청 제한 시간(ms, 기본값: 15000)'),
|
|
64
65
|
},
|
|
65
66
|
},
|
|
66
|
-
handler: ((args) => findTheaters(args, apiKey, googleMapsApiKey)),
|
|
67
|
+
handler: ((args) => findTheaters(args, apiKey, googleMapsApiKey, geocodeOptions)),
|
|
67
68
|
};
|
|
68
69
|
}
|
|
@@ -5,7 +5,7 @@ import * as z from 'zod';
|
|
|
5
5
|
import { fetchCgvTimetable, toYyyymmdd } from '../client.js';
|
|
6
6
|
import { resolveCgvNearestTheater } from '../location.js';
|
|
7
7
|
import { filterAndSortTimetable } from '../timetable.js';
|
|
8
|
-
async function getTimetable(args, apiKey, googleMapsApiKey) {
|
|
8
|
+
async function getTimetable(args, apiKey, googleMapsApiKey, geocodeOptions = {}) {
|
|
9
9
|
const { playDate = toYyyymmdd(), theaterCode, movieCode, keyword, latitude, longitude, limit = 50, timeoutMs = 15000, } = args;
|
|
10
10
|
let resolvedTheater = null;
|
|
11
11
|
let targetTheaterCode = theaterCode;
|
|
@@ -19,7 +19,8 @@ async function getTimetable(args, apiKey, googleMapsApiKey) {
|
|
|
19
19
|
}, {
|
|
20
20
|
timeout: timeoutMs,
|
|
21
21
|
zyteApiKey: apiKey,
|
|
22
|
-
|
|
22
|
+
...geocodeOptions,
|
|
23
|
+
googleMapsApiKey,
|
|
23
24
|
});
|
|
24
25
|
resolvedTheater = resolved.theater;
|
|
25
26
|
targetTheaterCode = resolved.theater?.theaterCode;
|
|
@@ -53,7 +54,7 @@ async function getTimetable(args, apiKey, googleMapsApiKey) {
|
|
|
53
54
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
|
-
export function createGetTimetableTool(apiKey, googleMapsApiKey) {
|
|
57
|
+
export function createGetTimetableTool(apiKey, googleMapsApiKey, geocodeOptions = {}) {
|
|
57
58
|
return {
|
|
58
59
|
name: 'cgv_get_timetable',
|
|
59
60
|
metadata: {
|
|
@@ -70,6 +71,6 @@ export function createGetTimetableTool(apiKey, googleMapsApiKey) {
|
|
|
70
71
|
timeoutMs: z.number().optional().default(15000).describe('요청 제한 시간(ms, 기본값: 15000)'),
|
|
71
72
|
},
|
|
72
73
|
},
|
|
73
|
-
handler: ((args) => getTimetable(args, apiKey, googleMapsApiKey)),
|
|
74
|
+
handler: ((args) => getTimetable(args, apiKey, googleMapsApiKey, geocodeOptions)),
|
|
74
75
|
};
|
|
75
76
|
}
|