smart-web-mcp 0.9.2 → 0.10.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.
- package/CHANGELOG.md +9 -0
- package/dist/korea/coupang.d.ts +46 -0
- package/dist/korea/coupang.js +165 -0
- package/dist/korea/coupang.js.map +1 -0
- package/dist/korea/dart.d.ts +53 -0
- package/dist/korea/dart.js +105 -0
- package/dist/korea/dart.js.map +1 -0
- package/dist/korea/drug-safety.d.ts +33 -0
- package/dist/korea/drug-safety.js +60 -0
- package/dist/korea/drug-safety.js.map +1 -0
- package/dist/korea/fine-dust.d.ts +27 -0
- package/dist/korea/fine-dust.js +88 -0
- package/dist/korea/fine-dust.js.map +1 -0
- package/dist/korea/food-safety.d.ts +21 -0
- package/dist/korea/food-safety.js +50 -0
- package/dist/korea/food-safety.js.map +1 -0
- package/dist/korea/geeknews.d.ts +31 -0
- package/dist/korea/geeknews.js +144 -0
- package/dist/korea/geeknews.js.map +1 -0
- package/dist/korea/index.d.ts +16 -0
- package/dist/korea/index.js +17 -0
- package/dist/korea/index.js.map +1 -0
- package/dist/korea/law.d.ts +41 -0
- package/dist/korea/law.js +85 -0
- package/dist/korea/law.js.map +1 -0
- package/dist/korea/naver-news.d.ts +38 -0
- package/dist/korea/naver-news.js +67 -0
- package/dist/korea/naver-news.js.map +1 -0
- package/dist/korea/naver-shopping.d.ts +39 -0
- package/dist/korea/naver-shopping.js +51 -0
- package/dist/korea/naver-shopping.js.map +1 -0
- package/dist/korea/proxy-client.d.ts +20 -0
- package/dist/korea/proxy-client.js +88 -0
- package/dist/korea/proxy-client.js.map +1 -0
- package/dist/korea/public-restroom.d.ts +44 -0
- package/dist/korea/public-restroom.js +184 -0
- package/dist/korea/public-restroom.js.map +1 -0
- package/dist/korea/real-estate.d.ts +56 -0
- package/dist/korea/real-estate.js +95 -0
- package/dist/korea/real-estate.js.map +1 -0
- package/dist/korea/spellcheck.d.ts +32 -0
- package/dist/korea/spellcheck.js +142 -0
- package/dist/korea/spellcheck.js.map +1 -0
- package/dist/korea/stock.d.ts +59 -0
- package/dist/korea/stock.js +84 -0
- package/dist/korea/stock.js.map +1 -0
- package/dist/korea/subway.d.ts +29 -0
- package/dist/korea/subway.js +47 -0
- package/dist/korea/subway.js.map +1 -0
- package/dist/korea/weather.d.ts +35 -0
- package/dist/korea/weather.js +92 -0
- package/dist/korea/weather.js.map +1 -0
- package/dist/korea/zipcode.d.ts +25 -0
- package/dist/korea/zipcode.js +70 -0
- package/dist/korea/zipcode.js.map +1 -0
- package/dist/mcp-server.js +386 -0
- package/dist/mcp-server.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naver-shopping.js","sourceRoot":"","sources":["../../src/korea/naver-shopping.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared HTTP client for k-skill-proxy and Korean public APIs.
|
|
3
|
+
* Proxy base URL: KSKILL_PROXY_BASE_URL env var, default https://k-skill-proxy.nomadamas.org
|
|
4
|
+
*/
|
|
5
|
+
export declare function proxyBase(): string;
|
|
6
|
+
export type KrError = {
|
|
7
|
+
ok: false;
|
|
8
|
+
status: number;
|
|
9
|
+
message: string;
|
|
10
|
+
code: string;
|
|
11
|
+
};
|
|
12
|
+
export type KrResult<T> = {
|
|
13
|
+
ok: true;
|
|
14
|
+
data: T;
|
|
15
|
+
} | KrError;
|
|
16
|
+
export declare function proxyGet<T = unknown>(path: string, params?: Record<string, string>, timeoutMs?: number): Promise<KrResult<T>>;
|
|
17
|
+
export declare function httpGetText(url: string, headers?: Record<string, string>, timeoutMs?: number): Promise<KrResult<string>>;
|
|
18
|
+
export declare function httpGet<T = unknown>(url: string, headers?: Record<string, string>, timeoutMs?: number): Promise<KrResult<T>>;
|
|
19
|
+
export declare function httpPost<T = unknown>(url: string, body: string, headers?: Record<string, string>, timeoutMs?: number): Promise<KrResult<T>>;
|
|
20
|
+
export declare function formatError(err: KrError): string;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared HTTP client for k-skill-proxy and Korean public APIs.
|
|
3
|
+
* Proxy base URL: KSKILL_PROXY_BASE_URL env var, default https://k-skill-proxy.nomadamas.org
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_PROXY = "https://k-skill-proxy.nomadamas.org";
|
|
6
|
+
const DEFAULT_TIMEOUT_MS = 15000;
|
|
7
|
+
const BROWSER_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36";
|
|
8
|
+
export function proxyBase() {
|
|
9
|
+
return (process.env["KSKILL_PROXY_BASE_URL"] || DEFAULT_PROXY).replace(/\/$/, "");
|
|
10
|
+
}
|
|
11
|
+
export async function proxyGet(path, params = {}, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
12
|
+
const base = proxyBase();
|
|
13
|
+
const url = new URL(`${base}${path}`);
|
|
14
|
+
for (const [key, value] of Object.entries(params)) {
|
|
15
|
+
if (value !== undefined && value !== "")
|
|
16
|
+
url.searchParams.set(key, value);
|
|
17
|
+
}
|
|
18
|
+
return httpGet(url.toString(), {}, timeoutMs);
|
|
19
|
+
}
|
|
20
|
+
export async function httpGetText(url, headers = {}, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
21
|
+
const controller = new AbortController();
|
|
22
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
23
|
+
try {
|
|
24
|
+
const res = await fetch(url, {
|
|
25
|
+
signal: controller.signal,
|
|
26
|
+
headers: {
|
|
27
|
+
"user-agent": BROWSER_UA,
|
|
28
|
+
"accept-language": "ko-KR,ko;q=0.9,en;q=0.8",
|
|
29
|
+
...headers,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
const text = await res.text();
|
|
33
|
+
if (!res.ok) {
|
|
34
|
+
return { ok: false, status: res.status, message: text.slice(0, 500), code: `http_${res.status}` };
|
|
35
|
+
}
|
|
36
|
+
return { ok: true, data: text };
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
40
|
+
return { ok: false, status: 0, message, code: err instanceof Error && err.name === "AbortError" ? "timeout" : "network_error" };
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export async function httpGet(url, headers = {}, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
47
|
+
const result = await httpGetText(url, { accept: "application/json, */*", ...headers }, timeoutMs);
|
|
48
|
+
if (!result.ok)
|
|
49
|
+
return result;
|
|
50
|
+
try {
|
|
51
|
+
return { ok: true, data: JSON.parse(result.data) };
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return { ok: false, status: 0, message: "Response is not valid JSON", code: "parse_error" };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function httpPost(url, body, headers = {}, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
58
|
+
const controller = new AbortController();
|
|
59
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
60
|
+
try {
|
|
61
|
+
const res = await fetch(url, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
body,
|
|
64
|
+
signal: controller.signal,
|
|
65
|
+
headers: {
|
|
66
|
+
"user-agent": BROWSER_UA,
|
|
67
|
+
"accept-language": "ko-KR,ko;q=0.9,en;q=0.8",
|
|
68
|
+
...headers,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
const text = await res.text();
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
return { ok: false, status: res.status, message: text.slice(0, 500), code: `http_${res.status}` };
|
|
74
|
+
}
|
|
75
|
+
return { ok: true, data: text };
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
79
|
+
return { ok: false, status: 0, message, code: err instanceof Error && err.name === "AbortError" ? "timeout" : "network_error" };
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function formatError(err) {
|
|
86
|
+
return `[${err.code}] ${err.message}`;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=proxy-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-client.js","sourceRoot":"","sources":["../../src/korea/proxy-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nearby public restrooms using the official nationwide standard dataset
|
|
3
|
+
* + Kakao Map anchor resolution for location strings.
|
|
4
|
+
*
|
|
5
|
+
* Data source: https://file.localdata.go.kr/file/download/public_restroom_info/info
|
|
6
|
+
* Kakao anchor: https://place-api.map.kakao.com/places/panel3/<confirmId>
|
|
7
|
+
*
|
|
8
|
+
* This tool downloads a regional CSV on first call (cached in memory for the session).
|
|
9
|
+
* CSV is ~2-5 MB per region; nationwide is ~15 MB.
|
|
10
|
+
*/
|
|
11
|
+
export type RestroomRow = {
|
|
12
|
+
name: string;
|
|
13
|
+
kind: string;
|
|
14
|
+
address: string;
|
|
15
|
+
lat: number;
|
|
16
|
+
lon: number;
|
|
17
|
+
open_hours?: string;
|
|
18
|
+
phone?: string;
|
|
19
|
+
};
|
|
20
|
+
export type RestroomResult = {
|
|
21
|
+
source: "public-restroom";
|
|
22
|
+
location_hint: string;
|
|
23
|
+
anchor_lat?: number;
|
|
24
|
+
anchor_lon?: number;
|
|
25
|
+
radius_km: number;
|
|
26
|
+
total: number;
|
|
27
|
+
items: NearbyRestroom[];
|
|
28
|
+
};
|
|
29
|
+
export type NearbyRestroom = RestroomRow & {
|
|
30
|
+
distance_m: number;
|
|
31
|
+
map_url: string;
|
|
32
|
+
};
|
|
33
|
+
export declare function runPublicRestroom(options: {
|
|
34
|
+
location: string;
|
|
35
|
+
lat?: number;
|
|
36
|
+
lon?: number;
|
|
37
|
+
radiusKm?: number;
|
|
38
|
+
limit?: number;
|
|
39
|
+
timeoutMs?: number;
|
|
40
|
+
}): Promise<RestroomResult | {
|
|
41
|
+
ok: false;
|
|
42
|
+
message: string;
|
|
43
|
+
}>;
|
|
44
|
+
export declare function renderPublicRestroom(result: RestroomResult): string;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nearby public restrooms using the official nationwide standard dataset
|
|
3
|
+
* + Kakao Map anchor resolution for location strings.
|
|
4
|
+
*
|
|
5
|
+
* Data source: https://file.localdata.go.kr/file/download/public_restroom_info/info
|
|
6
|
+
* Kakao anchor: https://place-api.map.kakao.com/places/panel3/<confirmId>
|
|
7
|
+
*
|
|
8
|
+
* This tool downloads a regional CSV on first call (cached in memory for the session).
|
|
9
|
+
* CSV is ~2-5 MB per region; nationwide is ~15 MB.
|
|
10
|
+
*/
|
|
11
|
+
import { httpGet } from "./proxy-client.js";
|
|
12
|
+
const CSV_BASE = "https://file.localdata.go.kr/file/download/public_restroom_info";
|
|
13
|
+
const KAKAO_SEARCH = "https://dapi.kakao.com/v2/local/search/keyword.json";
|
|
14
|
+
// In-memory cache: regionCode → parsed rows
|
|
15
|
+
const csvCache = new Map();
|
|
16
|
+
function haversineM(lat1, lon1, lat2, lon2) {
|
|
17
|
+
const R = 6371000;
|
|
18
|
+
const dLat = ((lat2 - lat1) * Math.PI) / 180;
|
|
19
|
+
const dLon = ((lon2 - lon1) * Math.PI) / 180;
|
|
20
|
+
const a = Math.sin(dLat / 2) ** 2 + Math.cos((lat1 * Math.PI) / 180) * Math.cos((lat2 * Math.PI) / 180) * Math.sin(dLon / 2) ** 2;
|
|
21
|
+
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
22
|
+
}
|
|
23
|
+
function parseCSV(text) {
|
|
24
|
+
const lines = text.split(/\r?\n/);
|
|
25
|
+
if (lines.length < 2)
|
|
26
|
+
return [];
|
|
27
|
+
const header = (lines[0] ?? "").split(",").map((h) => h.trim().replace(/^"|"$/g, "").toLowerCase());
|
|
28
|
+
const rows = [];
|
|
29
|
+
// Column index helpers
|
|
30
|
+
const col = (names) => names.map((n) => header.findIndex((h) => h.includes(n))).find((i) => i >= 0) ?? -1;
|
|
31
|
+
const nameIdx = col(["화장실명", "명칭", "name"]);
|
|
32
|
+
const kindIdx = col(["구분명", "종류", "kind"]);
|
|
33
|
+
const addrIdx = col(["소재지도로명주소", "도로명주소", "address", "주소"]);
|
|
34
|
+
const latIdx = col(["위도", "lat", "y좌표", "wgs84위도"]);
|
|
35
|
+
const lonIdx = col(["경도", "lon", "x좌표", "wgs84경도"]);
|
|
36
|
+
const hoursIdx = col(["개방시간", "운영시간", "hours"]);
|
|
37
|
+
const phoneIdx = col(["전화번호", "phone", "연락처"]);
|
|
38
|
+
for (const line of lines.slice(1)) {
|
|
39
|
+
if (!line.trim())
|
|
40
|
+
continue;
|
|
41
|
+
const cells = line.split(",").map((c) => c.trim().replace(/^"|"$/g, ""));
|
|
42
|
+
const lat = parseFloat(cells[latIdx] ?? "");
|
|
43
|
+
const lon = parseFloat(cells[lonIdx] ?? "");
|
|
44
|
+
if (!isFinite(lat) || !isFinite(lon) || lat === 0 || lon === 0)
|
|
45
|
+
continue;
|
|
46
|
+
const hours = hoursIdx >= 0 ? cells[hoursIdx] : undefined;
|
|
47
|
+
const phone = phoneIdx >= 0 ? cells[phoneIdx] : undefined;
|
|
48
|
+
rows.push({
|
|
49
|
+
name: cells[nameIdx] ?? "",
|
|
50
|
+
kind: cells[kindIdx] ?? "공중화장실",
|
|
51
|
+
address: cells[addrIdx] ?? "",
|
|
52
|
+
lat,
|
|
53
|
+
lon,
|
|
54
|
+
...(hours !== undefined ? { open_hours: hours } : {}),
|
|
55
|
+
...(phone !== undefined ? { phone } : {}),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return rows;
|
|
59
|
+
}
|
|
60
|
+
async function fetchCSV(regionCode, timeoutMs = 30000) {
|
|
61
|
+
const cacheKey = regionCode ?? "nationwide";
|
|
62
|
+
if (csvCache.has(cacheKey))
|
|
63
|
+
return csvCache.get(cacheKey);
|
|
64
|
+
const url = regionCode
|
|
65
|
+
? `${CSV_BASE}/info?orgCode=${regionCode}`
|
|
66
|
+
: `${CSV_BASE}/info`;
|
|
67
|
+
const result = await httpGet(url, { accept: "text/csv, text/plain, */*" }, timeoutMs);
|
|
68
|
+
if (!result.ok)
|
|
69
|
+
return [];
|
|
70
|
+
const text = result.data;
|
|
71
|
+
const rows = parseCSV(String(text));
|
|
72
|
+
csvCache.set(cacheKey, rows);
|
|
73
|
+
return rows;
|
|
74
|
+
}
|
|
75
|
+
async function resolveLocationCoords(location, kakaoApiKey, timeoutMs = 10000) {
|
|
76
|
+
const key = kakaoApiKey ?? process.env["KAKAO_REST_API_KEY"] ?? "";
|
|
77
|
+
if (!key)
|
|
78
|
+
return null;
|
|
79
|
+
const url = `${KAKAO_SEARCH}?query=${encodeURIComponent(location)}&size=1`;
|
|
80
|
+
const result = await httpGet(url, { Authorization: `KakaoAK ${key}` }, timeoutMs);
|
|
81
|
+
if (!result.ok)
|
|
82
|
+
return null;
|
|
83
|
+
const docs = result.data["documents"]?.[0];
|
|
84
|
+
if (!docs)
|
|
85
|
+
return null;
|
|
86
|
+
const lat = parseFloat(String(docs["y"] ?? ""));
|
|
87
|
+
const lon = parseFloat(String(docs["x"] ?? ""));
|
|
88
|
+
if (!isFinite(lat) || !isFinite(lon))
|
|
89
|
+
return null;
|
|
90
|
+
return { lat, lon };
|
|
91
|
+
}
|
|
92
|
+
// Simple region code inference from Korean city names
|
|
93
|
+
function inferRegionCode(location) {
|
|
94
|
+
const map = {
|
|
95
|
+
서울: "11",
|
|
96
|
+
부산: "26",
|
|
97
|
+
대구: "27",
|
|
98
|
+
인천: "28",
|
|
99
|
+
광주: "29",
|
|
100
|
+
대전: "30",
|
|
101
|
+
울산: "31",
|
|
102
|
+
세종: "36",
|
|
103
|
+
경기: "41",
|
|
104
|
+
강원: "42",
|
|
105
|
+
충북: "43",
|
|
106
|
+
충남: "44",
|
|
107
|
+
전북: "45",
|
|
108
|
+
전남: "46",
|
|
109
|
+
경북: "47",
|
|
110
|
+
경남: "48",
|
|
111
|
+
제주: "50",
|
|
112
|
+
};
|
|
113
|
+
for (const [city, code] of Object.entries(map)) {
|
|
114
|
+
if (location.includes(city))
|
|
115
|
+
return code;
|
|
116
|
+
}
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
export async function runPublicRestroom(options) {
|
|
120
|
+
const { location, lat: inputLat, lon: inputLon, radiusKm = 1.0, limit = 5, timeoutMs = 30000 } = options;
|
|
121
|
+
let anchorLat = inputLat;
|
|
122
|
+
let anchorLon = inputLon;
|
|
123
|
+
// Try to resolve via Kakao if no coordinates given
|
|
124
|
+
if (anchorLat === undefined || anchorLon === undefined) {
|
|
125
|
+
const coords = await resolveLocationCoords(location, undefined, timeoutMs);
|
|
126
|
+
if (coords) {
|
|
127
|
+
anchorLat = coords.lat;
|
|
128
|
+
anchorLon = coords.lon;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
return { ok: false, message: `위치 "${location}"의 좌표를 확인할 수 없습니다. KAKAO_REST_API_KEY가 필요하거나, 위도/경도를 직접 입력해 주세요.` };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const regionCode = inferRegionCode(location);
|
|
135
|
+
const rows = await fetchCSV(regionCode, timeoutMs);
|
|
136
|
+
if (rows.length === 0) {
|
|
137
|
+
return { ok: false, message: "공중화장실 데이터를 불러오지 못했습니다. 잠시 후 다시 시도하세요." };
|
|
138
|
+
}
|
|
139
|
+
const radiusM = radiusKm * 1000;
|
|
140
|
+
const nearby = rows
|
|
141
|
+
.map((row) => ({
|
|
142
|
+
...row,
|
|
143
|
+
distance_m: haversineM(anchorLat, anchorLon, row.lat, row.lon),
|
|
144
|
+
map_url: `https://map.kakao.com/link/map/${encodeURIComponent(row.name)},${row.lat},${row.lon}`,
|
|
145
|
+
}))
|
|
146
|
+
.filter((r) => r.distance_m <= radiusM)
|
|
147
|
+
.sort((a, b) => a.distance_m - b.distance_m)
|
|
148
|
+
.slice(0, limit);
|
|
149
|
+
return {
|
|
150
|
+
source: "public-restroom",
|
|
151
|
+
location_hint: location,
|
|
152
|
+
anchor_lat: anchorLat,
|
|
153
|
+
anchor_lon: anchorLon,
|
|
154
|
+
radius_km: radiusKm,
|
|
155
|
+
total: nearby.length,
|
|
156
|
+
items: nearby,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
export function renderPublicRestroom(result) {
|
|
160
|
+
const lines = [
|
|
161
|
+
`# 근처 공중화장실 — ${result.location_hint}`,
|
|
162
|
+
`검색 반경: ${result.radius_km}km | ${result.total}개 발견`,
|
|
163
|
+
"",
|
|
164
|
+
];
|
|
165
|
+
if (result.total === 0) {
|
|
166
|
+
lines.push(`반경 ${result.radius_km}km 내 공중화장실이 없습니다. 반경을 늘려서 다시 시도해보세요.`);
|
|
167
|
+
return lines.join("\n");
|
|
168
|
+
}
|
|
169
|
+
for (const r of result.items) {
|
|
170
|
+
lines.push(`## ${r.name} (${Math.round(r.distance_m)}m)`);
|
|
171
|
+
lines.push(`- 구분: ${r.kind}`);
|
|
172
|
+
if (r.address)
|
|
173
|
+
lines.push(`- 주소: ${r.address}`);
|
|
174
|
+
if (r.open_hours)
|
|
175
|
+
lines.push(`- 개방시간: ${r.open_hours}`);
|
|
176
|
+
if (r.phone)
|
|
177
|
+
lines.push(`- 전화: ${r.phone}`);
|
|
178
|
+
lines.push(`- [지도 보기](${r.map_url})`);
|
|
179
|
+
lines.push("");
|
|
180
|
+
}
|
|
181
|
+
lines.push("_공공데이터포털 공중화장실 표준데이터 기준 / 실시간 점유상태 미반영_");
|
|
182
|
+
return lines.join("\n");
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=public-restroom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-restroom.js","sourceRoot":"","sources":["../../src/korea/public-restroom.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE3C,MAAM,QAAQ,GAAG,iEAAiE,CAAA;AAClF,MAAM,YAAY,GAAG,qDAAqD,CAAA;AAE1E,4CAA4C;AAC5C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAA;AA2BjD,SAAS,UAAU,CAAC,IAAY,EAAE,IAAY,EAAE,IAAY,EAAE,IAAY;IACxE,MAAM,CAAC,GAAG,OAAO,CAAA;IACjB,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAA;IAC5C,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAA;IAC5C,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,CAAA;IACjI,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;AAC3D,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACjC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,CAAA;IAE/B,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAA;IACnG,MAAM,IAAI,GAAkB,EAAE,CAAA;IAE9B,uBAAuB;IACvB,MAAM,GAAG,GAAG,CAAC,KAAe,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;IAEnH,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAA;IAC3C,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAA;IAC1C,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAA;IAC3D,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAA;IACnD,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAA;IACnD,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAC/C,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAA;IAE9C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,SAAQ;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAA;QACxE,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;QAC3C,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;QAC3C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC;YAAE,SAAQ;QACxE,MAAM,KAAK,GAAG,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QACzD,MAAM,KAAK,GAAG,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QACzD,IAAI,CAAC,IAAI,CAAC;YACR,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE;YAC1B,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,OAAO;YAC/B,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE;YAC7B,GAAG;YACH,GAAG;YACH,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACrD,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1C,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,UAAmB,EAAE,SAAS,GAAG,KAAK;IAC5D,MAAM,QAAQ,GAAG,UAAU,IAAI,YAAY,CAAA;IAC3C,IAAI,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAA;IAE1D,MAAM,GAAG,GAAG,UAAU;QACpB,CAAC,CAAC,GAAG,QAAQ,iBAAiB,UAAU,EAAE;QAC1C,CAAC,CAAC,GAAG,QAAQ,OAAO,CAAA;IAEtB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAS,GAAG,EAAE,EAAE,MAAM,EAAE,2BAA2B,EAAE,EAAE,SAAS,CAAC,CAAA;IAC7F,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,CAAA;IAEzB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAyB,CAAA;IAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IACnC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;IAC5B,OAAO,IAAI,CAAA;AACb,CAAC;AAED,KAAK,UAAU,qBAAqB,CAAC,QAAgB,EAAE,WAAoB,EAAE,SAAS,GAAG,KAAK;IAC5F,MAAM,GAAG,GAAG,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAA;IAClE,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAA;IAErB,MAAM,GAAG,GAAG,GAAG,YAAY,UAAU,kBAAkB,CAAC,QAAQ,CAAC,SAAS,CAAA;IAC1E,MAAM,MAAM,GAAG,MAAM,OAAO,CAA0B,GAAG,EAAE,EAAE,aAAa,EAAE,WAAW,GAAG,EAAE,EAAE,EAAE,SAAS,CAAC,CAAA;IAC1G,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,IAAI,CAAA;IAE3B,MAAM,IAAI,GAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAA2C,EAAE,CAAC,CAAC,CAAC,CAAA;IACrF,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;IAC/C,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;IAC/C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IACjD,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;AACrB,CAAC;AAED,sDAAsD;AACtD,SAAS,eAAe,CAAC,QAAgB;IACvC,MAAM,GAAG,GAA2B;QAClC,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;QACR,EAAE,EAAE,IAAI;KACT,CAAA;IACD,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;IAC1C,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAOvC;IACC,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,GAAG,GAAG,EAAE,KAAK,GAAG,CAAC,EAAE,SAAS,GAAG,KAAK,EAAE,GAAG,OAAO,CAAA;IAExG,IAAI,SAAS,GAAG,QAAQ,CAAA;IACxB,IAAI,SAAS,GAAG,QAAQ,CAAA;IAExB,mDAAmD;IACnD,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QACvD,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,CAAA;QAC1E,IAAI,MAAM,EAAE,CAAC;YACX,SAAS,GAAG,MAAM,CAAC,GAAG,CAAA;YACtB,SAAS,GAAG,MAAM,CAAC,GAAG,CAAA;QACxB,CAAC;aAAM,CAAC;YACN,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,QAAQ,kEAAkE,EAAE,CAAA;QAClH,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAA;IAC5C,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;IAElD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,uCAAuC,EAAE,CAAA;IACxE,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAA;IAC/B,MAAM,MAAM,GAAqB,IAAI;SAClC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACb,GAAG,GAAG;QACN,UAAU,EAAE,UAAU,CAAC,SAAU,EAAE,SAAU,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC;QAChE,OAAO,EAAE,kCAAkC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE;KAChG,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,OAAO,CAAC;SACtC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;SAC3C,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;IAElB,OAAO;QACL,MAAM,EAAE,iBAAiB;QACzB,aAAa,EAAE,QAAQ;QACvB,UAAU,EAAE,SAAS;QACrB,UAAU,EAAE,SAAS;QACrB,SAAS,EAAE,QAAQ;QACnB,KAAK,EAAE,MAAM,CAAC,MAAM;QACpB,KAAK,EAAE,MAAM;KACd,CAAA;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAsB;IACzD,MAAM,KAAK,GAAG;QACZ,gBAAgB,MAAM,CAAC,aAAa,EAAE;QACtC,UAAU,MAAM,CAAC,SAAS,UAAU,MAAM,CAAC,KAAK,MAAM;QACtD,EAAE;KACH,CAAA;IAED,IAAI,MAAM,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,MAAM,MAAM,CAAC,SAAS,sCAAsC,CAAC,CAAA;QACxE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QACzD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;QAC7B,IAAI,CAAC,CAAC,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;QAC/C,IAAI,CAAC,CAAC,UAAU;YAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,UAAU,EAAE,CAAC,CAAA;QACvD,IAAI,CAAC,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC,CAAA;QAC3C,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,GAAG,CAAC,CAAA;QACrC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAChB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAA;IACrD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Korean real estate transaction prices via k-skill-proxy
|
|
3
|
+
* Endpoints: /v1/real-estate/region-code, /v1/real-estate/:assetType/:dealType
|
|
4
|
+
*/
|
|
5
|
+
export type RegionCode = {
|
|
6
|
+
lawd_cd: string;
|
|
7
|
+
name: string;
|
|
8
|
+
};
|
|
9
|
+
export type RealEstateDeal = {
|
|
10
|
+
name?: string;
|
|
11
|
+
district?: string;
|
|
12
|
+
area_m2?: number;
|
|
13
|
+
floor?: number;
|
|
14
|
+
price_10k?: number;
|
|
15
|
+
deposit_10k?: number;
|
|
16
|
+
monthly_rent_10k?: number;
|
|
17
|
+
deal_date?: string;
|
|
18
|
+
build_year?: number;
|
|
19
|
+
deal_type?: string;
|
|
20
|
+
contract_type?: string;
|
|
21
|
+
};
|
|
22
|
+
export type RealEstateSummary = {
|
|
23
|
+
median_price_10k?: number;
|
|
24
|
+
min_price_10k?: number;
|
|
25
|
+
max_price_10k?: number;
|
|
26
|
+
sample_count: number;
|
|
27
|
+
median_deposit_10k?: number;
|
|
28
|
+
monthly_rent_avg_10k?: number;
|
|
29
|
+
};
|
|
30
|
+
export type RealEstateResult = {
|
|
31
|
+
source: "real-estate";
|
|
32
|
+
action: "region-code" | "transactions";
|
|
33
|
+
asset_type?: string;
|
|
34
|
+
deal_type?: string;
|
|
35
|
+
lawd_cd?: string;
|
|
36
|
+
deal_ymd?: string;
|
|
37
|
+
region_codes?: RegionCode[];
|
|
38
|
+
items?: RealEstateDeal[];
|
|
39
|
+
summary?: RealEstateSummary;
|
|
40
|
+
};
|
|
41
|
+
export type AssetType = "apartment" | "officetel" | "villa" | "single-house" | "commercial";
|
|
42
|
+
export type DealType = "trade" | "rent";
|
|
43
|
+
export declare function runRealEstate(options: {
|
|
44
|
+
action: "region-code" | "transactions";
|
|
45
|
+
q?: string;
|
|
46
|
+
lawd_cd?: string;
|
|
47
|
+
asset_type?: AssetType;
|
|
48
|
+
deal_type?: DealType;
|
|
49
|
+
deal_ymd?: string;
|
|
50
|
+
num_of_rows?: number;
|
|
51
|
+
timeoutMs?: number;
|
|
52
|
+
}): Promise<RealEstateResult | {
|
|
53
|
+
ok: false;
|
|
54
|
+
message: string;
|
|
55
|
+
}>;
|
|
56
|
+
export declare function renderRealEstate(result: RealEstateResult): string;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Korean real estate transaction prices via k-skill-proxy
|
|
3
|
+
* Endpoints: /v1/real-estate/region-code, /v1/real-estate/:assetType/:dealType
|
|
4
|
+
*/
|
|
5
|
+
import { proxyGet } from "./proxy-client.js";
|
|
6
|
+
function prevMonthYmd() {
|
|
7
|
+
const d = new Date();
|
|
8
|
+
d.setMonth(d.getMonth() - 1);
|
|
9
|
+
return d.toISOString().slice(0, 7).replace("-", "");
|
|
10
|
+
}
|
|
11
|
+
export async function runRealEstate(options) {
|
|
12
|
+
const { action, q, lawd_cd, asset_type = "apartment", deal_type = "trade", deal_ymd, num_of_rows = 100, timeoutMs = 20000 } = options;
|
|
13
|
+
if (action === "region-code") {
|
|
14
|
+
if (!q)
|
|
15
|
+
return { ok: false, message: "지역명(q)이 필요합니다." };
|
|
16
|
+
const result = await proxyGet("/v1/real-estate/region-code", { q }, timeoutMs);
|
|
17
|
+
if (!result.ok)
|
|
18
|
+
return { ok: false, message: `Region code lookup failed: ${result.message}` };
|
|
19
|
+
const regions = (result.data["results"] ?? []);
|
|
20
|
+
return { source: "real-estate", action: "region-code", region_codes: regions };
|
|
21
|
+
}
|
|
22
|
+
// transactions
|
|
23
|
+
if (!lawd_cd)
|
|
24
|
+
return { ok: false, message: "lawd_cd(법정동코드 5자리)가 필요합니다. 먼저 region-code로 확인하세요." };
|
|
25
|
+
const ymd = deal_ymd || prevMonthYmd();
|
|
26
|
+
const path = `/v1/real-estate/${asset_type}/${deal_type}`;
|
|
27
|
+
const result = await proxyGet(path, { lawd_cd, deal_ymd: ymd, num_of_rows: String(num_of_rows) }, timeoutMs);
|
|
28
|
+
if (!result.ok)
|
|
29
|
+
return { ok: false, message: `Real estate lookup failed: ${result.message}` };
|
|
30
|
+
const d = result.data;
|
|
31
|
+
const items = (d["items"] ?? []);
|
|
32
|
+
const summary = d["summary"];
|
|
33
|
+
return {
|
|
34
|
+
source: "real-estate",
|
|
35
|
+
action: "transactions",
|
|
36
|
+
asset_type,
|
|
37
|
+
deal_type,
|
|
38
|
+
lawd_cd,
|
|
39
|
+
deal_ymd: ymd,
|
|
40
|
+
items,
|
|
41
|
+
...(summary !== undefined ? { summary } : {}),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function formatPrice(won10k) {
|
|
45
|
+
if (won10k === undefined)
|
|
46
|
+
return "-";
|
|
47
|
+
const eok = Math.floor(won10k / 10000);
|
|
48
|
+
const man = won10k % 10000;
|
|
49
|
+
if (eok > 0 && man > 0)
|
|
50
|
+
return `${eok}억 ${man.toLocaleString("ko")}만원`;
|
|
51
|
+
if (eok > 0)
|
|
52
|
+
return `${eok}억원`;
|
|
53
|
+
return `${won10k.toLocaleString("ko")}만원`;
|
|
54
|
+
}
|
|
55
|
+
export function renderRealEstate(result) {
|
|
56
|
+
if (result.action === "region-code") {
|
|
57
|
+
const lines = ["# 지역코드 조회", ""];
|
|
58
|
+
for (const r of result.region_codes ?? []) {
|
|
59
|
+
lines.push(`- **${r.name}**: \`${r.lawd_cd}\``);
|
|
60
|
+
}
|
|
61
|
+
if ((result.region_codes?.length ?? 0) === 0)
|
|
62
|
+
lines.push("검색 결과가 없습니다.");
|
|
63
|
+
lines.push("\n`lawd_cd`를 transactions 조회에 사용하세요.");
|
|
64
|
+
return lines.join("\n");
|
|
65
|
+
}
|
|
66
|
+
const s = result.summary;
|
|
67
|
+
const lines = [
|
|
68
|
+
`# 부동산 실거래가 — ${result.lawd_cd} (${result.asset_type} ${result.deal_type})`,
|
|
69
|
+
`기준: ${result.deal_ymd}`,
|
|
70
|
+
"",
|
|
71
|
+
];
|
|
72
|
+
if (s) {
|
|
73
|
+
lines.push("## 요약");
|
|
74
|
+
lines.push(`- 거래건수: ${s.sample_count}건`);
|
|
75
|
+
if (s.median_price_10k !== undefined)
|
|
76
|
+
lines.push(`- 중위가: ${formatPrice(s.median_price_10k)}`);
|
|
77
|
+
if (s.min_price_10k !== undefined)
|
|
78
|
+
lines.push(`- 최저: ${formatPrice(s.min_price_10k)}`);
|
|
79
|
+
if (s.max_price_10k !== undefined)
|
|
80
|
+
lines.push(`- 최고: ${formatPrice(s.max_price_10k)}`);
|
|
81
|
+
if (s.median_deposit_10k !== undefined)
|
|
82
|
+
lines.push(`- 중위보증금: ${formatPrice(s.median_deposit_10k)}`);
|
|
83
|
+
if (s.monthly_rent_avg_10k !== undefined)
|
|
84
|
+
lines.push(`- 평균월세: ${formatPrice(s.monthly_rent_avg_10k)}`);
|
|
85
|
+
lines.push("");
|
|
86
|
+
}
|
|
87
|
+
lines.push("## 상위 거래");
|
|
88
|
+
for (const item of (result.items ?? []).slice(0, 10)) {
|
|
89
|
+
const price = item.price_10k !== undefined ? formatPrice(item.price_10k) : item.deposit_10k !== undefined ? `보증금 ${formatPrice(item.deposit_10k)}` : "";
|
|
90
|
+
lines.push(`- ${item.name ?? "?"} ${item.area_m2 ?? "?"}㎡ ${item.floor ?? "?"}층 ${price} (${item.deal_date ?? "?"})`);
|
|
91
|
+
}
|
|
92
|
+
lines.push("\n_국토교통부 실거래가 신고 기준_");
|
|
93
|
+
return lines.join("\n");
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=real-estate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"real-estate.js","sourceRoot":"","sources":["../../src/korea/real-estate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AA0C5C,SAAS,YAAY;IACnB,MAAM,CAAC,GAAG,IAAI,IAAI,EAAE,CAAA;IACpB,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAA;IAC5B,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAA;AACrD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OASnC;IACC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,UAAU,GAAG,WAAW,EAAE,SAAS,GAAG,OAAO,EAAE,QAAQ,EAAE,WAAW,GAAG,GAAG,EAAE,SAAS,GAAG,KAAK,EAAE,GAAG,OAAO,CAAA;IAErI,IAAI,MAAM,KAAK,aAAa,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAA;QACvD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAA0B,6BAA6B,EAAE,EAAE,CAAC,EAAE,EAAE,SAAS,CAAC,CAAA;QACvG,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,8BAA8B,MAAM,CAAC,OAAO,EAAE,EAAE,CAAA;QAC7F,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAiB,CAAA;QAC9D,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,OAAO,EAAE,CAAA;IAChF,CAAC;IAED,eAAe;IACf,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,mDAAmD,EAAE,CAAA;IAChG,MAAM,GAAG,GAAG,QAAQ,IAAI,YAAY,EAAE,CAAA;IACtC,MAAM,IAAI,GAAG,mBAAmB,UAAU,IAAI,SAAS,EAAE,CAAA;IACzD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAA0B,IAAI,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,CAAC,CAAA;IACrI,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,8BAA8B,MAAM,CAAC,OAAO,EAAE,EAAE,CAAA;IAE7F,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAA;IACrB,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,CAAqB,CAAA;IACpD,MAAM,OAAO,GAAG,CAAC,CAAC,SAAS,CAAkC,CAAA;IAE7D,OAAO;QACL,MAAM,EAAE,aAAa;QACrB,MAAM,EAAE,cAAc;QACtB,UAAU;QACV,SAAS;QACT,OAAO;QACP,QAAQ,EAAE,GAAG;QACb,KAAK;QACL,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC9C,CAAA;AACH,CAAC;AAED,SAAS,WAAW,CAAC,MAA0B;IAC7C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,GAAG,CAAA;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,CAAA;IACtC,MAAM,GAAG,GAAG,MAAM,GAAG,KAAK,CAAA;IAC1B,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC;QAAE,OAAO,GAAG,GAAG,KAAK,GAAG,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAA;IACtE,IAAI,GAAG,GAAG,CAAC;QAAE,OAAO,GAAG,GAAG,IAAI,CAAA;IAC9B,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAA;AAC3C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAwB;IACvD,IAAI,MAAM,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;QAC/B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,YAAY,IAAI,EAAE,EAAE,CAAC;YAC1C,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,OAAO,IAAI,CAAC,CAAA;QACjD,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;QACxE,KAAK,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAA;QAClD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAA;IACxB,MAAM,KAAK,GAAG;QACZ,gBAAgB,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,SAAS,GAAG;QAC3E,OAAO,MAAM,CAAC,QAAQ,EAAE;QACxB,EAAE;KACH,CAAA;IAED,IAAI,CAAC,EAAE,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACnB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,YAAY,GAAG,CAAC,CAAA;QACxC,IAAI,CAAC,CAAC,gBAAgB,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,UAAU,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAA;QAC7F,IAAI,CAAC,CAAC,aAAa,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;QACtF,IAAI,CAAC,CAAC,aAAa,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;QACtF,IAAI,CAAC,CAAC,kBAAkB,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,YAAY,WAAW,CAAC,CAAC,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAA;QACnG,IAAI,CAAC,CAAC,oBAAoB,KAAK,SAAS;YAAE,KAAK,CAAC,IAAI,CAAC,WAAW,WAAW,CAAC,CAAC,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAA;QACtG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAChB,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACtB,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QACrD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACvJ,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,GAAG,KAAK,IAAI,CAAC,OAAO,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,IAAI,GAAG,MAAM,KAAK,MAAM,IAAI,CAAC,SAAS,IAAI,GAAG,GAAG,CAAC,CAAA;IAC3H,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAA;IAClC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Korean spell check via nara-speller.co.kr (공개 표면, 저빈도·비상업적 용도에 한함)
|
|
3
|
+
* POST https://nara-speller.co.kr/old_speller/results
|
|
4
|
+
*
|
|
5
|
+
* Policy enforced here:
|
|
6
|
+
* - Max 3000 chars per call (chunked internally)
|
|
7
|
+
* - Non-commercial / low-frequency only — this is stated in the upstream service ToS
|
|
8
|
+
* - May return 403 if Cloudflare blocks; caller should retry later
|
|
9
|
+
*/
|
|
10
|
+
export type SpellIssue = {
|
|
11
|
+
original: string;
|
|
12
|
+
suggestions: string[];
|
|
13
|
+
reason: string;
|
|
14
|
+
};
|
|
15
|
+
export type SpellCheckResult = {
|
|
16
|
+
source: "spell-check";
|
|
17
|
+
original_text: string;
|
|
18
|
+
corrected_text: string;
|
|
19
|
+
issue_count: number;
|
|
20
|
+
issues: SpellIssue[];
|
|
21
|
+
chunk_count: number;
|
|
22
|
+
note: string;
|
|
23
|
+
};
|
|
24
|
+
export declare function runSpellCheck(options: {
|
|
25
|
+
text: string;
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
throttleMs?: number;
|
|
28
|
+
}): Promise<SpellCheckResult | {
|
|
29
|
+
ok: false;
|
|
30
|
+
message: string;
|
|
31
|
+
}>;
|
|
32
|
+
export declare function renderSpellCheck(result: SpellCheckResult): string;
|