daiso 1.0.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +88 -9
  2. package/dist/api/actionsProxy.js +3 -0
  3. package/dist/api/cgvHandlers.js +15 -3
  4. package/dist/api/configStatus.js +1 -1
  5. package/dist/api/dtryxHandlers.js +113 -0
  6. package/dist/api/emart24Handlers.js +6 -2
  7. package/dist/api/gs25Handlers.js +22 -19
  8. package/dist/api/handlers.js +3 -0
  9. package/dist/api/healthCheckDefinitions.js +56 -8
  10. package/dist/api/healthCheckShape.js +24 -8
  11. package/dist/api/healthChecks.js +48 -20
  12. package/dist/api/operationalAuth.js +32 -0
  13. package/dist/api/routes/dtryxRoutes.js +22 -0
  14. package/dist/api/routes/healthRoutes.js +19 -11
  15. package/dist/api/routes/rateLimitStatsRoutes.js +274 -0
  16. package/dist/api/routes/registerServiceRoutes.js +37 -0
  17. package/dist/api/sevenelevenHandlers.js +7 -3
  18. package/dist/cli/commandOptions.js +24 -0
  19. package/dist/cli/commands/cinema.js +27 -0
  20. package/dist/cli/commands/convenience.js +2 -2
  21. package/dist/cli.js +7 -1
  22. package/dist/cliHelp.js +3 -337
  23. package/dist/cliHelpDefinitions.js +369 -0
  24. package/dist/cliInteractive.js +8 -5
  25. package/dist/core/errors.js +17 -2
  26. package/dist/core/registry.js +13 -15
  27. package/dist/durableObjects/dailyRateLimiter.js +185 -0
  28. package/dist/durableObjects/rateLimitMetricsStore.js +144 -0
  29. package/dist/index.js +16 -30
  30. package/dist/middleware/dailyRateLimit.js +176 -0
  31. package/dist/pages/openapiFullSpec.js +2 -0
  32. package/dist/pages/openapiSpecActionParameters.js +429 -0
  33. package/dist/pages/openapiSpecActions.js +1 -391
  34. package/dist/pages/openapiSpecComponentsLottecinema.js +2 -2
  35. package/dist/pages/openapiSpecPathsDtryx.js +148 -0
  36. package/dist/pages/openapiSpecPathsLotteMart.js +10 -2
  37. package/dist/pages/promptRetailCinemaText.js +9 -1
  38. package/dist/services/cgv/client.js +7 -4
  39. package/dist/services/cgv/errors.js +12 -0
  40. package/dist/services/cgv/transport.js +19 -2
  41. package/dist/services/cu/client.js +42 -12
  42. package/dist/services/cu/index.js +13 -3
  43. package/dist/services/cu/tools/checkInventory.js +65 -45
  44. package/dist/services/cu/tools/findNearbyStores.js +4 -3
  45. package/dist/services/cu/upstreamError.js +15 -0
  46. package/dist/services/dtryx/api.js +25 -0
  47. package/dist/services/dtryx/client.js +133 -0
  48. package/dist/services/dtryx/index.js +24 -0
  49. package/dist/services/dtryx/location.js +216 -0
  50. package/dist/services/dtryx/tools/getRemainingSeats.js +117 -0
  51. package/dist/services/dtryx/tools/listCinemas.js +46 -0
  52. package/dist/services/dtryx/tools/listNowShowing.js +77 -0
  53. package/dist/services/dtryx/types.js +4 -0
  54. package/dist/services/dtryx/validation.js +21 -0
  55. package/dist/services/emart24/tools/searchProducts.js +37 -19
  56. package/dist/services/feedback/tools/submitDeveloperRequest.js +15 -1
  57. package/dist/services/gs25/api.js +2 -2
  58. package/dist/services/gs25/client.js +16 -123
  59. package/dist/services/gs25/errors.js +12 -0
  60. package/dist/services/gs25/index.js +2 -2
  61. package/dist/services/gs25/productSearch.js +1 -0
  62. package/dist/services/gs25/storeStockTransport.js +55 -0
  63. package/dist/services/gs25/tools/checkInventory.js +7 -2
  64. package/dist/services/gs25/tools/findNearbyStores.js +20 -5
  65. package/dist/services/lottecinema/client.js +4 -2
  66. package/dist/services/lottemart/config.js +1 -1
  67. package/dist/services/lottemart/session.js +29 -160
  68. package/dist/services/lottemart/socketTransport.js +149 -0
  69. package/dist/services/oliveyoung/client.js +1 -0
  70. package/dist/services/places/client.js +29 -8
  71. package/dist/services/seveneleven/client.js +18 -8
  72. package/dist/services/seveneleven/index.js +11 -7
  73. package/dist/services/seveneleven/inventory.js +5 -2
  74. package/dist/services/seveneleven/productKeyword.js +3 -2
  75. package/dist/services/seveneleven/tools/checkInventory.js +4 -3
  76. package/dist/services/seveneleven/tools/getCatalogSnapshot.js +4 -3
  77. package/dist/services/seveneleven/tools/getSearchPopwords.js +4 -3
  78. package/dist/services/seveneleven/tools/searchProducts.js +40 -20
  79. package/dist/services/seveneleven/tools/searchStores.js +4 -3
  80. package/dist/utils/format.js +10 -3
  81. package/dist/utils/http.js +38 -10
  82. package/dist/utils/zyte.js +13 -2
  83. package/dist/utils/zyteJsonFallback.js +67 -0
  84. package/openapi.json +51 -3
  85. package/openapi.yaml +36 -3
  86. package/package.json +28 -24
@@ -3,10 +3,11 @@
3
3
  */
4
4
  import * as z from 'zod';
5
5
  import { fetchSevenElevenSearchPopwords } from '../client.js';
6
- async function getSearchPopwords(args) {
6
+ async function getSearchPopwords(args, zyteApiKey) {
7
7
  const { label = 'home', timeoutMs = 15000 } = args;
8
8
  const keywords = await fetchSevenElevenSearchPopwords(label, {
9
9
  timeout: timeoutMs,
10
+ zyteApiKey,
10
11
  });
11
12
  return {
12
13
  content: [
@@ -25,7 +26,7 @@ async function getSearchPopwords(args) {
25
26
  ],
26
27
  };
27
28
  }
28
- export function createGetSearchPopwordsTool() {
29
+ export function createGetSearchPopwordsTool(zyteApiKey) {
29
30
  return {
30
31
  name: 'seveneleven_get_search_popwords',
31
32
  metadata: {
@@ -36,6 +37,6 @@ export function createGetSearchPopwordsTool() {
36
37
  timeoutMs: z.number().optional().default(15000).describe('요청 제한 시간(ms, 기본값: 15000)'),
37
38
  },
38
39
  },
39
- handler: getSearchPopwords,
40
+ handler: (args) => getSearchPopwords(args, zyteApiKey),
40
41
  };
41
42
  }
@@ -9,28 +9,46 @@ function buildTextResponse(payload) {
9
9
  structuredContent: payload,
10
10
  };
11
11
  }
12
- async function searchProducts(args) {
12
+ async function searchProducts(args, zyteApiKey) {
13
13
  const { query, page = 1, size = 20, sort = 'recommend', timeoutMs = 15000 } = args;
14
14
  if (!query || query.trim().length === 0) {
15
15
  throw new Error('상품 검색어(query)를 입력해주세요.');
16
16
  }
17
- const result = await searchSevenElevenProductsWithVariants(query, {
18
- page,
19
- size,
20
- sort,
21
- timeout: timeoutMs,
22
- });
23
- return buildTextResponse({
24
- query: result.query,
25
- page,
26
- size,
27
- sort,
28
- totalCount: result.totalCount,
29
- count: result.products.length,
30
- collectionIds: result.collectionIds,
31
- appliedQueries: result.appliedQueries,
32
- products: result.products,
33
- });
17
+ try {
18
+ const result = await searchSevenElevenProductsWithVariants(query, {
19
+ page,
20
+ size,
21
+ sort,
22
+ timeout: timeoutMs,
23
+ zyteApiKey,
24
+ });
25
+ return buildTextResponse({
26
+ query: result.query,
27
+ page,
28
+ size,
29
+ sort,
30
+ totalCount: result.totalCount,
31
+ count: result.products.length,
32
+ collectionIds: result.collectionIds,
33
+ appliedQueries: result.appliedQueries,
34
+ products: result.products,
35
+ });
36
+ }
37
+ catch (error) {
38
+ return buildTextResponse({
39
+ query,
40
+ page,
41
+ size,
42
+ sort,
43
+ totalCount: 0,
44
+ count: 0,
45
+ collectionIds: [],
46
+ appliedQueries: [query],
47
+ products: [],
48
+ status: 'degraded',
49
+ message: error instanceof Error ? error.message : '세븐일레븐 상품 검색에 실패했습니다.',
50
+ });
51
+ }
34
52
  }
35
53
  const searchProductsOutputSchema = {
36
54
  query: z.string().describe('적용된 검색어'),
@@ -42,8 +60,10 @@ const searchProductsOutputSchema = {
42
60
  collectionIds: z.array(z.string()).describe('검색 컬렉션 ID 목록'),
43
61
  appliedQueries: z.array(z.string()).describe('실제로 시도한 검색어 목록'),
44
62
  products: z.array(z.unknown()).describe('세븐일레븐 상품 검색 결과'),
63
+ status: z.string().optional().describe('외부 API 차단 등으로 결과가 제한된 경우 degraded'),
64
+ message: z.string().optional().describe('degraded 상태의 원인 메시지'),
45
65
  };
46
- export function createSearchProductsTool() {
66
+ export function createSearchProductsTool(zyteApiKey) {
47
67
  return {
48
68
  name: 'seveneleven_search_products',
49
69
  metadata: {
@@ -62,6 +82,6 @@ export function createSearchProductsTool() {
62
82
  },
63
83
  outputSchema: searchProductsOutputSchema,
64
84
  },
65
- handler: searchProducts,
85
+ handler: (args) => searchProducts(args, zyteApiKey),
66
86
  };
67
87
  }
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import * as z from 'zod';
5
5
  import { fetchSevenElevenStoresByKeyword } from '../client.js';
6
- async function searchStores(args) {
6
+ async function searchStores(args, zyteApiKey) {
7
7
  const { keyword, limit = 20, timeoutMs = 15000 } = args;
8
8
  if (!keyword || keyword.trim().length === 0) {
9
9
  throw new Error('매장 검색어(keyword)를 입력해주세요.');
@@ -13,6 +13,7 @@ async function searchStores(args) {
13
13
  limit,
14
14
  }, {
15
15
  timeout: timeoutMs,
16
+ zyteApiKey,
16
17
  });
17
18
  return {
18
19
  content: [
@@ -28,7 +29,7 @@ async function searchStores(args) {
28
29
  ],
29
30
  };
30
31
  }
31
- export function createSearchStoresTool() {
32
+ export function createSearchStoresTool(zyteApiKey) {
32
33
  return {
33
34
  name: 'seveneleven_search_stores',
34
35
  metadata: {
@@ -40,6 +41,6 @@ export function createSearchStoresTool() {
40
41
  timeoutMs: z.number().optional().default(15000).describe('요청 제한 시간(ms, 기본값: 15000)'),
41
42
  },
42
43
  },
43
- handler: searchStores,
44
+ handler: (args) => searchStores(args, zyteApiKey),
44
45
  };
45
46
  }
@@ -24,8 +24,15 @@ export function formatTime(raw) {
24
24
  return raw;
25
25
  }
26
26
  export function toYyyymmdd(value = new Date()) {
27
- const year = value.getFullYear();
28
- const month = `${value.getMonth() + 1}`.padStart(2, '0');
29
- const day = `${value.getDate()}`.padStart(2, '0');
27
+ const parts = new Intl.DateTimeFormat('en-CA', {
28
+ timeZone: 'Asia/Seoul',
29
+ year: 'numeric',
30
+ month: '2-digit',
31
+ day: '2-digit',
32
+ }).formatToParts(value);
33
+ const byType = Object.fromEntries(parts.map((part) => [part.type, part.value]));
34
+ const year = byType.year;
35
+ const month = byType.month;
36
+ const day = byType.day;
30
37
  return `${year}${month}${day}`;
31
38
  }
@@ -15,6 +15,17 @@ export class HttpError extends Error {
15
15
  this.bodyText = bodyText;
16
16
  }
17
17
  }
18
+ /** HTML 원문을 노출하지 않고 예상하지 못한 응답 형식을 구분합니다. */
19
+ export class UnexpectedHtmlResponseError extends Error {
20
+ status;
21
+ contentType;
22
+ constructor(status, contentType) {
23
+ super(`JSON 대신 HTML 응답을 받았습니다 (HTTP ${status}).`);
24
+ this.status = status;
25
+ this.contentType = contentType;
26
+ this.name = 'UnexpectedHtmlResponseError';
27
+ }
28
+ }
18
29
  export function createTimeoutController(timeout) {
19
30
  const controller = new AbortController();
20
31
  const timeoutId = setTimeout(() => controller.abort(), timeout);
@@ -54,19 +65,22 @@ function parseRetryAfterDelayMs(response) {
54
65
  }
55
66
  return null;
56
67
  }
57
- export async function fetchWithTimeout(url, options = {}) {
68
+ async function requestWithTimeout(url, options, readResponse) {
58
69
  const { timeout = 10000, retries = 0, retryDelayMs = 250, retryStatusCodes = DEFAULT_RETRY_STATUS_CODES, retryUnsafeMethods = false, retryMethods = DEFAULT_RETRY_METHODS, onRetry, ...restOptions } = options;
59
70
  const maxAttempts = Math.max(1, Math.trunc(retries) + 1);
60
71
  const method = normalizeMethod(restOptions.method);
61
72
  const retryAllowed = canRetryMethod(method, retryMethods, retryUnsafeMethods);
62
73
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
63
74
  const { controller, timeoutId } = createTimeoutController(timeout);
75
+ let readingResponse = false;
64
76
  try {
65
77
  const response = await fetch(url, {
66
78
  ...restOptions,
67
79
  signal: controller.signal,
68
80
  });
69
- if (retryAllowed && attempt < maxAttempts && isRetryableStatus(response.status, retryStatusCodes)) {
81
+ if (retryAllowed &&
82
+ attempt < maxAttempts &&
83
+ isRetryableStatus(response.status, retryStatusCodes)) {
70
84
  const delayMs = parseRetryAfterDelayMs(response) ?? retryDelayMs;
71
85
  onRetry?.({
72
86
  attempt,
@@ -81,10 +95,11 @@ export async function fetchWithTimeout(url, options = {}) {
81
95
  await wait(delayMs);
82
96
  continue;
83
97
  }
84
- return response;
98
+ readingResponse = true;
99
+ return await readResponse(response);
85
100
  }
86
101
  catch (error) {
87
- if (!retryAllowed || attempt >= maxAttempts) {
102
+ if (readingResponse || !retryAllowed || attempt >= maxAttempts) {
88
103
  throw error;
89
104
  }
90
105
  onRetry?.({
@@ -105,17 +120,30 @@ export async function fetchWithTimeout(url, options = {}) {
105
120
  /* v8 ignore next */
106
121
  throw new Error('API 요청 재시도 처리 중 알 수 없는 오류가 발생했습니다.');
107
122
  }
123
+ export async function fetchWithTimeout(url, options = {}) {
124
+ return requestWithTimeout(url, options, async (response) => response);
125
+ }
126
+ async function readTextResponse(url, options) {
127
+ // 헤더 수신뿐 아니라 본문 읽기에도 같은 요청 제한 시간을 적용합니다.
128
+ return requestWithTimeout(url, options, async (response) => ({
129
+ response,
130
+ body: await response.text(),
131
+ }));
132
+ }
108
133
  export async function fetchJson(url, options = {}) {
109
- const response = await fetchWithTimeout(url, options);
134
+ const { response, body } = await readTextResponse(url, options);
110
135
  if (!response.ok) {
111
- throw new HttpError(response.status, response.statusText, await response.text());
136
+ throw new HttpError(response.status, response.statusText, body);
137
+ }
138
+ if (/^\s*(?:<!doctype\s+html\b|<html\b)/i.test(body)) {
139
+ throw new UnexpectedHtmlResponseError(response.status, response.headers.get('content-type') || '');
112
140
  }
113
- return response.json();
141
+ return JSON.parse(body);
114
142
  }
115
143
  export async function fetchText(url, options = {}) {
116
- const response = await fetchWithTimeout(url, options);
144
+ const { response, body } = await readTextResponse(url, options);
117
145
  if (!response.ok) {
118
- throw new HttpError(response.status, response.statusText, await response.text());
146
+ throw new HttpError(response.status, response.statusText, body);
119
147
  }
120
- return response.text();
148
+ return body;
121
149
  }
@@ -51,6 +51,16 @@ function isRetryableTargetStatus(statusCode) {
51
51
  function isRetryableError(error) {
52
52
  return isAbortError(error) || error instanceof RetryableZyteError;
53
53
  }
54
+ function parseZyteResponseBody(text) {
55
+ try {
56
+ return JSON.parse(text);
57
+ }
58
+ catch {
59
+ return {
60
+ detail: text.trim().replace(/\s+/g, ' ').slice(0, 300),
61
+ };
62
+ }
63
+ }
54
64
  function wait(ms) {
55
65
  if (ms <= 0) {
56
66
  return Promise.resolve();
@@ -60,7 +70,7 @@ function wait(ms) {
60
70
  });
61
71
  }
62
72
  export async function requestByZyte(options) {
63
- const { timeout = 15000, retries = 1, retryDelayMs = 250, url, method = 'GET', headers = [], bodyText, apiKey, } = options;
73
+ const { timeout = 15000, retries = 1, retryDelayMs = 250, url, method = 'GET', headers = [], bodyText, apiKey, tags, } = options;
64
74
  const auth = encodeBasicAuth(resolveZyteApiKey(apiKey));
65
75
  const maxAttempts = Math.max(1, Math.trunc(retries) + 1);
66
76
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
@@ -81,6 +91,7 @@ export async function requestByZyte(options) {
81
91
  customHttpRequestHeaders: headers,
82
92
  httpRequestText: bodyText,
83
93
  httpResponseBody: true,
94
+ ...(tags ? { tags } : {}),
84
95
  }),
85
96
  signal: controller.signal,
86
97
  });
@@ -91,7 +102,7 @@ export async function requestByZyte(options) {
91
102
  }
92
103
  throw error;
93
104
  }
94
- const result = (await response.json());
105
+ const result = parseZyteResponseBody(await response.text());
95
106
  if (!response.ok) {
96
107
  const message = `Zyte API 호출 실패: ${response.status} ${result.detail || result.title || ''}`.trim();
97
108
  if (isRetryableZyteStatus(response.status)) {
@@ -0,0 +1,67 @@
1
+ /**
2
+ * 차단 응답에만 Zyte를 사용하는 JSON 전송 유틸리티
3
+ */
4
+ import { fetchJson, HttpError } from './http.js';
5
+ import { decodeZyteHttpBody, requestByZyte, } from './zyte.js';
6
+ const ZYTE_FALLBACK_STATUSES = new Set([400, 403, 429]);
7
+ const ZYTE_METHODS = new Set([
8
+ 'GET',
9
+ 'POST',
10
+ 'PUT',
11
+ 'PATCH',
12
+ 'DELETE',
13
+ ]);
14
+ function normalizeMethod(method) {
15
+ const normalized = (method || 'GET').toUpperCase();
16
+ if (!ZYTE_METHODS.has(normalized)) {
17
+ throw new Error(`Zyte에서 지원하지 않는 HTTP 메서드입니다: ${normalized}`);
18
+ }
19
+ return normalized;
20
+ }
21
+ function toZyteHeaders(headers) {
22
+ const normalized = new Headers(headers);
23
+ const result = [];
24
+ normalized.forEach((value, name) => {
25
+ result.push({ name, value });
26
+ });
27
+ return result;
28
+ }
29
+ function assertSuccessfulTarget(result) {
30
+ const status = result.statusCode;
31
+ if (typeof status !== 'number' || status < 200 || status >= 300) {
32
+ throw new Error(`Zyte 대상 응답 실패: ${status ?? '알 수 없음'}`);
33
+ }
34
+ }
35
+ function hasZyteApiKey(apiKey) {
36
+ if (apiKey?.trim()) {
37
+ return true;
38
+ }
39
+ /* c8 ignore next */
40
+ return typeof process !== 'undefined' && Boolean(process.env?.ZYTE_API_KEY?.trim());
41
+ }
42
+ export async function fetchJsonWithZyteFallback(url, options = {}) {
43
+ const { zyteApiKey, zyteTags, ...directOptions } = options;
44
+ try {
45
+ return await fetchJson(url, directOptions);
46
+ }
47
+ catch (error) {
48
+ if (!(error instanceof HttpError) || !ZYTE_FALLBACK_STATUSES.has(error.status)) {
49
+ throw error;
50
+ }
51
+ if (!hasZyteApiKey(zyteApiKey)) {
52
+ throw error;
53
+ }
54
+ }
55
+ const result = await requestByZyte({
56
+ apiKey: zyteApiKey,
57
+ url,
58
+ timeout: directOptions.timeout,
59
+ retries: 0,
60
+ method: normalizeMethod(directOptions.method),
61
+ headers: toZyteHeaders(directOptions.headers),
62
+ bodyText: typeof directOptions.body === 'string' ? directOptions.body : undefined,
63
+ tags: zyteTags,
64
+ });
65
+ assertSuccessfulTarget(result);
66
+ return decodeZyteHttpBody(result);
67
+ }
package/openapi.json CHANGED
@@ -22,6 +22,51 @@
22
22
  "summary": "공통 GET 액션 실행",
23
23
  "description": "action 값에 따라 기존 GET API 엔드포인트로 프록시합니다. OpenAI Actions 등록 시 이 단일 엔드포인트를 사용하세요.",
24
24
  "parameters": [
25
+ {
26
+ "name": "cinemaCode",
27
+ "in": "query",
28
+ "required": false,
29
+ "description": "디트릭스 극장 코드",
30
+ "schema": {
31
+ "type": "string"
32
+ }
33
+ },
34
+ {
35
+ "name": "brandCode",
36
+ "in": "query",
37
+ "required": false,
38
+ "description": "디트릭스 브랜드 코드",
39
+ "schema": {
40
+ "type": "string"
41
+ }
42
+ },
43
+ {
44
+ "name": "region",
45
+ "in": "query",
46
+ "required": false,
47
+ "description": "디트릭스 광역 지역명",
48
+ "schema": {
49
+ "type": "string"
50
+ }
51
+ },
52
+ {
53
+ "name": "movieName",
54
+ "in": "query",
55
+ "required": false,
56
+ "description": "디트릭스 영화명 부분 일치",
57
+ "schema": {
58
+ "type": "string"
59
+ }
60
+ },
61
+ {
62
+ "name": "includePlayDates",
63
+ "in": "query",
64
+ "required": false,
65
+ "description": "디트릭스 상영 가능 날짜 포함",
66
+ "schema": {
67
+ "type": "boolean"
68
+ }
69
+ },
25
70
  {
26
71
  "name": "action",
27
72
  "in": "query",
@@ -30,6 +75,9 @@
30
75
  "schema": {
31
76
  "type": "string",
32
77
  "enum": [
78
+ "dtryxListCinemas",
79
+ "dtryxListNowShowing",
80
+ "dtryxGetRemainingSeats",
33
81
  "daisoSearchProducts",
34
82
  "daisoGetProduct",
35
83
  "daisoFindStores",
@@ -551,7 +599,7 @@
551
599
  "name": "timeoutMs",
552
600
  "in": "query",
553
601
  "required": false,
554
- "description": "세븐일레븐 inventory 타임아웃",
602
+ "description": "원본 요청 타임아웃(ms), 디트릭스는 1~60000",
555
603
  "schema": {
556
604
  "type": "integer"
557
605
  }
@@ -2726,11 +2774,11 @@
2726
2774
  },
2727
2775
  "bookedSeats": {
2728
2776
  "type": "integer",
2729
- "example": 28
2777
+ "example": 4
2730
2778
  },
2731
2779
  "remainingSeats": {
2732
2780
  "type": "integer",
2733
- "example": 4
2781
+ "example": 28
2734
2782
  }
2735
2783
  }
2736
2784
  },
package/openapi.yaml CHANGED
@@ -24,6 +24,36 @@ paths:
24
24
  summary: 공통 GET 액션 실행
25
25
  description: action 값에 따라 기존 GET API 엔드포인트로 프록시합니다. OpenAI Actions 등록 시 이 단일 엔드포인트를 사용하세요.
26
26
  parameters:
27
+ - name: cinemaCode
28
+ in: query
29
+ required: false
30
+ description: 디트릭스 극장 코드
31
+ schema:
32
+ type: string
33
+ - name: brandCode
34
+ in: query
35
+ required: false
36
+ description: 디트릭스 브랜드 코드
37
+ schema:
38
+ type: string
39
+ - name: region
40
+ in: query
41
+ required: false
42
+ description: 디트릭스 광역 지역명
43
+ schema:
44
+ type: string
45
+ - name: movieName
46
+ in: query
47
+ required: false
48
+ description: 디트릭스 영화명 부분 일치
49
+ schema:
50
+ type: string
51
+ - name: includePlayDates
52
+ in: query
53
+ required: false
54
+ description: 디트릭스 상영 가능 날짜 포함
55
+ schema:
56
+ type: boolean
27
57
  - name: action
28
58
  in: query
29
59
  required: true
@@ -31,6 +61,9 @@ paths:
31
61
  schema:
32
62
  type: string
33
63
  enum:
64
+ - dtryxListCinemas
65
+ - dtryxListNowShowing
66
+ - dtryxGetRemainingSeats
34
67
  - daisoSearchProducts
35
68
  - daisoGetProduct
36
69
  - daisoFindStores
@@ -389,7 +422,7 @@ paths:
389
422
  - name: timeoutMs
390
423
  in: query
391
424
  required: false
392
- description: 세븐일레븐 inventory 타임아웃
425
+ description: 원본 요청 타임아웃(ms), 디트릭스는 1~60000
393
426
  schema:
394
427
  type: integer
395
428
  responses:
@@ -1920,10 +1953,10 @@ components:
1920
1953
  example: 32
1921
1954
  bookedSeats:
1922
1955
  type: integer
1923
- example: 28
1956
+ example: 4
1924
1957
  remainingSeats:
1925
1958
  type: integer
1926
- example: 4
1959
+ example: 28
1927
1960
  LotteCinemaTheaterSearchResponse:
1928
1961
  type: object
1929
1962
  properties:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "daiso",
3
- "version": "1.0.9",
3
+ "version": "1.1.0",
4
4
  "description": "Korean retail and cinema MCP server, CLI, and Codex Skill for Daiso, convenience stores, marts, Olive Young, and theaters",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -31,6 +31,7 @@
31
31
  "release:dry-run": "npm pack --dry-run",
32
32
  "publish:public": "npm publish --access public",
33
33
  "typecheck": "tsc --noEmit",
34
+ "check:source-lines": "npx tsx scripts/quality/check-source-line-limit.ts",
34
35
  "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.ts\" \"vitest.config.ts\"",
35
36
  "lint:fix": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"scripts/**/*.ts\" \"vitest.config.ts\" --fix",
36
37
  "format": "prettier .github/**/*.md .github/**/*.yml README.md SECURITY.md AGENTS.md CONTRIBUTING.md package.json tsconfig.json .prettierrc.json .mcp.json eslint.config.ts --write",
@@ -39,7 +40,7 @@
39
40
  "test": "vitest run",
40
41
  "test:watch": "vitest",
41
42
  "test:coverage": "vitest run --coverage",
42
- "check": "npm run format:check && npm run lint && npm run lint:biome && npm run typecheck && npm test",
43
+ "check": "npm run format:check && npm run lint && npm run lint:biome && npm run typecheck && npm run check:source-lines && npm test",
43
44
  "lint:biome": "biome lint"
44
45
  },
45
46
  "keywords": [
@@ -76,37 +77,40 @@
76
77
  "url": "https://github.com/hmmhmmhm/daiso-mcp/issues"
77
78
  },
78
79
  "engines": {
79
- "node": ">=20"
80
+ "node": ">=22"
80
81
  },
81
82
  "devDependencies": {
82
- "@biomejs/biome": "^2.4.15",
83
- "@cloudflare/workers-types": "^4.20260526.1",
83
+ "@biomejs/biome": "^2.5.13",
84
+ "@cloudflare/workers-types": "^5.20260914.1",
84
85
  "@eslint/js": "^10.0.1",
85
- "@types/node": "^25.9.1",
86
- "@vitest/coverage-v8": "^4.1.7",
87
- "eslint": "^10.4.0",
86
+ "@types/node": "^26.5.1",
87
+ "@vitest/coverage-v8": "^4.1.11",
88
+ "eslint": "^10.8.0",
88
89
  "eslint-config-prettier": "^10.1.8",
89
- "globals": "^17.4.0",
90
+ "globals": "^17.12.0",
90
91
  "jiti": "^2.7.0",
91
- "playwright": "^1.60.0",
92
- "prettier": "^3.8.3",
93
- "sharp": "^0.34.5",
94
- "tsx": "^4.22.3",
95
- "typescript": "^6.0.3",
96
- "typescript-eslint": "^8.60.0",
97
- "vitest": "^4.1.7",
98
- "wrangler": "^4.95.0"
92
+ "playwright": "^1.63.0",
93
+ "prettier": "^3.9.6",
94
+ "sharp": "^0.35.4",
95
+ "tsx": "^4.23.1",
96
+ "typescript": "6.0.3",
97
+ "typescript-eslint": "^8.65.0",
98
+ "vitest": "^4.1.11",
99
+ "wrangler": "^4.131.2"
99
100
  },
100
101
  "overrides": {
101
- "flatted": "3.4.2",
102
- "undici": "7.24.0",
103
- "brace-expansion": "5.0.6",
104
- "ws": "8.20.1"
102
+ "@modelcontextprotocol/sdk": {
103
+ "@hono/node-server": "2.0.11"
104
+ },
105
+ "miniflare": {
106
+ "sharp": "0.35.4",
107
+ "undici": "7.29.1"
108
+ }
105
109
  },
106
110
  "dependencies": {
107
- "@modelcontextprotocol/sdk": "^1.27.1",
108
- "hono": "^4.12.23",
109
- "proj4": "^2.20.8",
111
+ "@modelcontextprotocol/sdk": "^1.30.0",
112
+ "hono": "^4.13.7",
113
+ "proj4": "^2.22.0",
110
114
  "workers-mcp": "^0.0.13"
111
115
  }
112
116
  }