
Express Bus Booking
- 1.7k installs
- 6.5k repo stars
- Updated July 27, 2026
- nomadamas/k-skill
express-bus-booking is an agent skill for search and assist korean 고속버스/kobus bookings using official http/api-first flows; use for 고속버스 예매, 시간표, 좌석 조회, 임시 선점, and official checkout-entry handoff.
About
The express-bus-booking skill is designed for search and assist Korean 고속버스/KOBUS bookings using official HTTP/API-first flows; use for 고속버스 예매, 시간표, 좌석 조회, 임시 선점, and official checkout-entry handoff. Express Bus Booking (KOBUS) Overview Use this skill for Korean 고속버스 / KOBUS timetable lookup and reservation assistance. The preferred workflow is HTTP/API-first: resolve routes, query timetables, inspect remaining seats and fares, and only use browser automation when endpoint discovery or an official web-only step blocks progress. Invoke when the user asks about express bus booking or related SKILL.md workflows.
- The user asks for 고속버스 예매, 고속버스 시간표, 고속버스 예약, 프리미엄 고속버스, or 우등 고속버스.
- The route is clearly a KOBUS 고속버스 route or the user names KOBUS/코버스/고속버스통합예매.
- The user wants assisted reservation up to seat selection, temporary hold, or a payment-entry link.
- 시외버스/Tmoney routes — use intercity-bus-booking.
- Final card submission or payment without explicit, narrowly scoped confirmation.
Express Bus Booking by the numbers
- 1,685 all-time installs (skills.sh)
- +131 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #203 of 2,742 Automation & Workflows skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
express-bus-booking capabilities & compatibility
- Capabilities
- the user asks for 고속버스 예매, 고속버스 시간표, 고속버스 예약, 프리 · the route is clearly a kobus 고속버스 route or the u · the user wants assisted reservation up to seat s · 시외버스/tmoney routes — use intercity bus booking
What express-bus-booking says it does
Search and assist Korean 고속버스/KOBUS bookings using official HTTP/API-first flows; use for 고속버스 예매, 시간표, 좌석 조회, 임시 선점, and official checkout-entry handoff.
Search and assist Korean 고속버스/KOBUS bookings using official HTTP/API-first flows; use for 고속버스 예매, 시간표, 좌석 조회, 임시 선점, and official checkout-entry handoff.
npx skills add https://github.com/nomadamas/k-skill --skill express-bus-bookingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.7k |
|---|---|
| repo stars | ★ 6.5k |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 27, 2026 |
| Repository | nomadamas/k-skill ↗ |
How do I search and assist korean 고속버스/kobus bookings using official http/api-first flows; use for 고속버스 예매, 시간표, 좌석 조회, 임시 선점, and official checkout-entry handoff?
Search and assist Korean 고속버스/KOBUS bookings using official HTTP/API-first flows; use for 고속버스 예매, 시간표, 좌석 조회, 임시 선점, and official checkout-entry handoff.
Who is it for?
Developers using express bus booking workflows documented in SKILL.md.
Skip if: Skip when the task falls outside express-bus-booking scope or needs a different stack.
When should I use this skill?
User asks about express bus booking or related SKILL.md workflows.
What you get
Completed express-bus-booking workflow with documented commands, files, and expected deliverables.
- API probe notes
- route and timetable JSON mappings
By the numbers
- One route probe returned about 1,208 records in rotInfList
- Seoul-to-Busan test returned 42 schedule links and 25 selectable seat snippets
Files
Express Bus Booking (KOBUS)
Overview
Use this skill for Korean 고속버스 / KOBUS timetable lookup and reservation assistance. The preferred workflow is HTTP/API-first: resolve routes, query timetables, inspect remaining seats and fares, and only use browser automation when endpoint discovery or an official web-only step blocks progress.
This skill is intentionally separate from 시외버스. KOBUS terminal codes, route structures, and checkout pages differ from Tmoney 시외버스. Use intercity-bus-booking for 시외버스/Tmoney flows.
When to Use
- The user asks for
고속버스 예매,고속버스 시간표,고속버스 예약,프리미엄 고속버스, or우등 고속버스. - The route is clearly a KOBUS 고속버스 route or the user names KOBUS/코버스/고속버스통합예매.
- The user wants assisted reservation up to seat selection, temporary hold, or a payment-entry link.
Do not use this for:
- 시외버스/Tmoney routes — use
intercity-bus-booking. - Final card submission or payment without explicit, narrowly scoped confirmation.
- Blind browser automation before trying the official HTTP flow.
Core Principles
- Use official KOBUS surfaces:
https://www.kobus.co.kr. - Keep a cookie jar and reuse referers. Stateless one-off POSTs are less reliable.
- Prefer desktop User-Agent and HTTP/1.1 during probing if the server behaves differently under HTTP/2.
- Default scope is assisted manual checkout: lookup, candidate presentation, seat-stage readiness, temporary seat hold, and official payment-entry page handoff.
- Do not submit card fields, agree to terms, or complete payment unless the user explicitly confirms that exact action.
Known HTTP Flow
See references/kobus-http-flow.md for session-proven endpoint details and parameter examples.
1. Start a Session
Fetch either the main page or route/search page with cookies enabled:
GET https://www.kobus.co.kr/main.do
GET https://www.kobus.co.kr/mrs/rotinf.doUse a realistic desktop User-Agent and a cookie jar.
2. Resolve Route / Terminal Candidates
POST /mrs/readRotLinInf.ajaxThe response is JSON and can include rotInfList, tfrInfList, len, and codeYn. Prefer code-confirmed routes over display-name matching because terminal names are not always unique.
3. Query Timetable
POST /mrs/alcnSrch.doTypical fields include:
deprCd=010
arvlCd=700
pathDvs=sngl
pathStep=1
deprDtm=YYYYMMDD
busClsCd=0
rtrpChc=1
timeLinkMin=00
timeLinkMax=23Parse the returned HTML for schedule rows/cards and fnSatsChc(...) onclick arguments. Respect mrsPsbYn=N or any equivalent booking-disabled marker.
4. Enter Seat / Fare Stage
POST /mrs/satschc.doSend the original search form fields plus selected values from fnSatsChc(...), commonly including:
deprTime
alcnDeprTime
alcnDeprTrmlNo
alcnArvlTrmlNo
indVBusClsCd
cacmCd
dcDvsCd
prvtBbizEmpAcmtRt
chldSftySatsYn
dsprSatsYnThe response should include form#satsChcFrm and hidden fare/seat values such as adltFee, rmnSatsNum, totSatsNum, and terminal/time fields.
5. Temporary Seat Hold
POST /mrs/setPcpy.ajaxSubmit the satsChcFrm hidden fields plus selected seat/count fields. A successful response includes:
MSG_CD=S0000
pcpyNoAll
satsNoAll
ESTM_AMT
DC_AMT
TISSU_AMTTreat the hold as short-lived. If testing, if the user does not proceed, or if a new seat is chosen, release it explicitly.
6. Release Temporary Hold
POST /mrs/cancPcpy.ajaxUse the same relevant form fields plus the returned pcpyNoAll and satsNoAll. A successful response returns MSG_CD=S0000.
Helper Script
Use the bundled helper for KOBUS lookup and optional temporary holds:
python3 express-bus-booking/scripts/kobus_express_booking.py \
--depart-code 021 \
--arrive-code 500 \
--date 20260520 \
--select-index 1 \
--hold-first-seat \
--output-dir /tmp/kobus-holdFor Seoul to Gwangju, the verified KOBUS route is 센트럴시티(서울) code 021 to 광주(유·스퀘어) code 500. A successful hold returns MSG_CD=S0000, pcpyNoAll, satsNoAll, fare amounts, and saves a local auto-submit helper for the official KOBUS payment-information page. Final card entry and payment remain manual. Cancel abandoned holds with /mrs/cancPcpy.ajax using the saved cancel fields.
Checkout-Entry Link Helper
A plain official checkout URL is not enough because KOBUS expects a POST body containing the selected schedule, seat, fare, and hold identifiers. The practical user-facing pattern is:
1. Create the temporary hold server-side via setPcpy.ajax. 2. Generate a short helper page that auto-submits a POST form to the official KOBUS checkout endpoint. 3. Send the helper link to the user. 4. The user completes card/payment fields manually on the official KOBUS page.
Desktop action:
https://www.kobus.co.kr/mrs/stplcfmpym.doMobile-friendly action:
https://www.kobus.co.kr/mrs/stplcfmpym.do?keep=/mrs/payThe POST body should include the original seat form fields plus returned hold values such as pcpyNoAll, satsNoAll, estmAmt, dcAmt, tissuAmt, and nonMbrsYn=Y when using a non-member checkout flow.
Mobile Redirect Caveat
KOBUS common JavaScript can redirect narrow/mobile screens to /mblIdx.do unless location.href contains a mobile-allowed path fragment such as /mrs/pay. In testing, posting to:
/mrs/stplcfmpym.do?keep=/mrs/paypreserved the same checkout POST body while making the final browser URL contain /mrs/pay, avoiding the client-side mobile-main redirect condition. Use this variant for Discord/mobile users, but still verify with the user because in-app browsers may add their own quirks.
If mobile still lands on the homepage, recommend opening the helper link in a normal external browser or desktop browser. Do not keep retrying holds indefinitely; cancel stale holds.
Suggested Output Format
Keep candidate lists concise and actionable:
고속버스 서울경부 → 부산 / 2026-05-09
1. 00:30 심야우등 / 천일고속 / 잔여 10석 / 성인 47,600원
2. 13:50 우등 / ...When a hold/checkout helper is created, state that the next step opens the official KOBUS payment page and that payment remains manual.
For Discord/mobile, provide links as normal text links, not fenced code blocks, so the user can tap them directly.
Common Pitfalls
1. Mixing KOBUS and Tmoney codes. KOBUS terminal codes are not Tmoney 시외버스 terminal codes. 2. Assuming a cart exists. KOBUS does not expose a shopping-cart style hold list in the tested web flow. The realistic UX is temporary seat hold plus checkout-entry handoff. 3. Ignoring hold cleanup. Always cancel test holds or abandoned holds with cancPcpy.ajax. 4. Treating lookup success as payment permission. Lookup, seat-stage entry, and temporary hold are not authorization to submit payment. 5. Mobile homepage redirects. Use the ?keep=/mrs/pay helper action for mobile; otherwise KOBUS JS may send the user to /mblIdx.do. 6. Browser automation too early. Try direct HTTP first. Use browser tooling only to discover changed endpoints or verify final user-facing behavior.
Verification Checklist
- [ ] Route/terminal codes were resolved from KOBUS, not guessed.
- [ ] Timetable was queried with cookies and a realistic User-Agent.
- [ ] Candidate output includes date, departure/arrival terminals, time, class/operator when available, fare, and remaining seats.
- [ ] Seat-stage response contains expected hidden fields before attempting a hold.
- [ ] Temporary hold success was confirmed with
MSG_CD=S0000before sending a checkout helper link. - [ ] Stale/test holds were cancelled with
cancPcpy.ajax. - [ ] Payment/card fields were left for the user unless they explicitly confirmed otherwise.
- [ ] Mobile helper links use the
/mrs/paymarker variant when sent to mobile-heavy platforms.
KOBUS HTTP/API Probe Notes
Session-proven on 2026-05-08. Goal: avoid browser automation where possible.
Base
https://www.kobus.co.krUse a desktop User-Agent, HTTP/1.1 if needed, a cookie jar, and referers.
Tested Flow
Route / Terminal Candidates
POST /mrs/readRotLinInf.ajaxObserved JSON keys:
tfrLen
tfrInfList
len
codeYn
rotInfListOne probe returned about 1,208 route records in rotInfList.
Timetable
POST /mrs/alcnSrch.doExample tested route/date:
서울경부(010) -> 부산(700), 2026-05-09Observed result:
42 schedule links/cards
25 selectable seat snippets
first selectable: 00:30 / 천일고속 / 심야우등 / 10 seatsTypical POST fields:
deprCd=010
arvlCd=700
pathDvs=sngl
pathStep=1
deprDtm=YYYYMMDD
busClsCd=0
rtrpChc=1
timeLinkMin=00
timeLinkMax=23Parse fnSatsChc(...) onclick values for the next step. Example:
fnSatsChc('20260509','003000','003000','010','700','3','07','0','Y','N','010','700','N','N','N','N')Seat / Fare Stage
POST /mrs/satschc.doSend the original alcnSrchFrm hidden fields plus selected values from fnSatsChc(...), including values such as:
deprTime=003000
alcnDeprTime=003000
alcnDeprTrmlNo=010
alcnArvlTrmlNo=700
indVBusClsCd=3
cacmCd=07
dcDvsCd=0
prvtBbizEmpAcmtRt=N
chldSftySatsYn=N
dsprSatsYn=NObserved response contained form#satsChcFrm and hidden values:
{
"deprTime": "003000",
"alcnDeprTrmlNo": "010",
"alcnArvlTrmlNo": "700",
"adltFee": "47600",
"rmnSatsNum": "10",
"totSatsNum": "28"
}Temporary Hold
POST /mrs/setPcpy.ajaxObserved successful response markers:
MSG_CD=S0000
pcpyNoAll
satsNoAll
ESTM_AMT
DC_AMT
TISSU_AMTHold Cancellation
POST /mrs/cancPcpy.ajaxObserved success marker:
MSG_CD=S00002026-05-13 서울→광주 re-verification: 센트럴시티(서울)(021) -> 광주(유·스퀘어)(500), 2026-05-20 00:45 중앙고속 심야우등, seat 1. /mrs/setPcpy.ajax returned MSG_CD=S0000, pcpyNoAll, satsNoAll=01, TISSU_AMT=36900; /mrs/stplcfmpym.do?keep=/mrs/pay rendered the official payment-information page; /mrs/cancPcpy.ajax returned MSG_CD=S0000.
Checkout Entry
POST /mrs/stplcfmpym.doThe POST body must include the selected schedule/seat form values plus temporary hold identifiers and fare amounts. A helper page can auto-submit this form to the official KOBUS endpoint.
For mobile browsers, use:
POST /mrs/stplcfmpym.do?keep=/mrs/payThis preserves the POST body while placing /mrs/pay in location.href, which avoids a KOBUS client-side mobile redirect condition observed in the common JavaScript.
Interpretation
- Login was not required for route lookup, timetable lookup, seat-selection-page entry, temporary hold, or checkout-entry page display in the tested flow.
- Page HTML can include login or
grecaptchaTokenforms, but these did not block the tested lookup/seat-stage path. - Final payment should remain a manual, explicitly confirmed stage.
- KOBUS mobile behavior is less stable than desktop because common JavaScript can redirect narrow screens to the mobile main page.
#!/usr/bin/env python3
"""KOBUS timetable lookup and temporary hold helper.
Default mode searches timetables. With --hold-first-seat or --hold-seat it creates
a temporary seat hold through /mrs/setPcpy.ajax and saves a local auto-submit
HTML helper for the official KOBUS payment-information page. It never submits
card fields or final payment.
"""
from __future__ import annotations
import argparse
import html
import http.cookiejar
import json
import re
import ssl
import sys
import tempfile
import urllib.parse
import urllib.request
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Iterable
BASE_URL = "https://www.kobus.co.kr"
UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/125 Safari/537.36"
FN_SATS_RE = re.compile(r"fnSatsChc\((.*?)\)", re.DOTALL)
ARG_RE = re.compile(r"'([^']*)'")
FORM_RE = re.compile(r"<form\b([^>]*)>(.*?)</form>", re.DOTALL | re.IGNORECASE)
INPUT_RE = re.compile(r"<input\b([^>]+)>", re.DOTALL | re.IGNORECASE)
ATTR_RE = re.compile(r"([\w:-]+)=[\"']([^\"']*)[\"']")
SEAT_RE = re.compile(r'<input\b([^>]*name=["\']seatBoxDtl["\'][^>]*)>', re.DOTALL | re.IGNORECASE)
TAG_RE = re.compile(r"<[^>]+>")
@dataclass
class Schedule:
index: int
departure_time: str | None
company: str | None
bus_class: str | None
remaining_text: str | None
raw_args: list[str]
@dataclass
class Hold:
success: bool
pcpy_no_all: str | None
sats_no_all: str | None
seat: str | None
estm_amt: str | None
dc_amt: str | None
tissu_amt: str | None
checkout_helper_path: str | None
checkout_response_path: str | None
cancel_fields_path: str | None
raw_response: dict[str, object]
def opener() -> urllib.request.OpenerDirector:
jar = http.cookiejar.CookieJar()
ctx = ssl._create_unverified_context()
try:
ctx.set_ciphers("DEFAULT@SECLEVEL=1")
except ssl.SSLError:
pass
return urllib.request.build_opener(
urllib.request.HTTPCookieProcessor(jar),
urllib.request.HTTPSHandler(context=ctx),
)
def request(url: str, data: dict[str, str] | list[tuple[str, str]] | None = None, referer: str | None = None) -> urllib.request.Request:
headers = {"User-Agent": UA}
if referer:
headers["Referer"] = referer
if data is None:
return urllib.request.Request(url, headers=headers, method="GET")
headers["Content-Type"] = "application/x-www-form-urlencoded"
return urllib.request.Request(url, data=urllib.parse.urlencode(data).encode(), headers=headers, method="POST")
def open_text(op: urllib.request.OpenerDirector, req: urllib.request.Request, timeout: int) -> str:
with op.open(req, timeout=timeout) as resp:
return resp.read().decode(resp.headers.get_content_charset() or "utf-8", errors="replace")
def attrs(fragment: str) -> dict[str, str]:
return {k.lower(): html.unescape(v) for k, v in ATTR_RE.findall(fragment)}
def strip_tags(s: str) -> str:
return re.sub(r"\s+", " ", html.unescape(TAG_RE.sub(" ", s))).strip()
def parse_form(body: str, form_id: str) -> list[tuple[str, str]]:
for attr_text, form_body in FORM_RE.findall(body):
a = attrs(attr_text)
if a.get("id") == form_id or a.get("name") == form_id:
fields = []
for input_text in INPUT_RE.findall(form_body):
ia = attrs(input_text)
if ia.get("name"):
fields.append((ia["name"], ia.get("value", "")))
return fields
return []
def search(op: urllib.request.OpenerDirector, depart: str, arrive: str, date: str, timeout: int) -> tuple[str, list[Schedule]]:
open_text(op, request(f"{BASE_URL}/main.do"), timeout)
body = open_text(
op,
request(
f"{BASE_URL}/mrs/alcnSrch.do",
{
"deprCd": depart,
"arvlCd": arrive,
"pathDvs": "sngl",
"pathStep": "1",
"deprDtm": date,
"busClsCd": "0",
"rtrpChc": "1",
"timeLinkMin": "00",
"timeLinkMax": "23",
},
f"{BASE_URL}/main.do",
),
timeout,
)
schedules: list[Schedule] = []
for idx, m in enumerate(FN_SATS_RE.finditer(body), 1):
args = ARG_RE.findall(m.group(1))
context = strip_tags(body[max(0, m.start() - 900) : m.start() + 900])
departure = args[1][:2] + ":" + args[1][2:4] if len(args) > 1 and len(args[1]) >= 4 else None
schedules.append(
Schedule(
index=idx,
departure_time=departure,
company=(re.search(r"\((?:주|유)\)[^\s]+|[가-힣]+고속", context) or [None])[0],
bus_class=(re.search(r"심야우등|우등|프리미엄|고속", context) or [None])[0],
remaining_text=(re.search(r"잔여\s*\d+석|\d+\s*/\s*\d+", context) or [None])[0],
raw_args=args,
)
)
return body, schedules
def seat_stage_fields(search_form: list[tuple[str, str]], schedule: Schedule) -> list[tuple[str, str]]:
a = schedule.raw_args
values = dict(search_form)
updates = {
"deprTime": a[1],
"alcnDeprTime": a[2],
"alcnDeprTrmlNo": a[3],
"alcnArvlTrmlNo": a[4],
"indVBusClsCd": a[5],
"cacmCd": a[6],
"dcDvsCd": a[7],
"prvtBbizEmpAcmtRt": a[8],
"chldSftySatsYn": a[12],
"dsprSatsYn": a[13],
}
return [(k, updates.get(k, v)) for k, v in search_form]
def hold(op: urllib.request.OpenerDirector, alcn_body: str, schedule: Schedule, seat: str | None, out: Path, timeout: int) -> Hold:
search_form = parse_form(alcn_body, "alcnSrchFrm")
seat_body = open_text(op, request(f"{BASE_URL}/mrs/satschc.do", seat_stage_fields(search_form, schedule), f"{BASE_URL}/mrs/alcnSrch.do"), timeout)
fields = parse_form(seat_body, "satsChcFrm")
field_map = dict(fields)
seats = []
for input_text in SEAT_RE.findall(seat_body):
a = attrs(input_text)
if "disabled" not in input_text and a.get("value"):
seats.append(a["value"])
selected = seat or (seats[0] if seats else None)
if not selected:
raise RuntimeError("No selectable KOBUS seat found")
def set_field(items: list[tuple[str, str]], key: str, val: str) -> list[tuple[str, str]]:
return [(k, val if k == key else v) for k, v in items]
for key, val in {
"selSeatNum": selected,
"selSeatCnt": "1",
"selAdltCnt": "1",
"selAdltDcCnt": "0",
"prmmDcDvsCd": field_map.get("prmmDcDvsCd") or "0",
}.items():
fields = set_field(fields, key, val)
raw = json.loads(open_text(op, request(f"{BASE_URL}/mrs/setPcpy.ajax", fields, f"{BASE_URL}/mrs/satschc.do"), timeout))
success = raw.get("MSG_CD") == "S0000"
if not success:
return Hold(False, None, None, selected, None, None, None, None, None, None, raw)
for key, val in {
"satsNoAll": str(raw.get("satsNoAll", "")),
"pcpyNoAll": str(raw.get("pcpyNoAll", "")),
"estmAmt": str(raw.get("ESTM_AMT", "")),
"dcAmt": str(raw.get("DC_AMT", "")),
"tissuAmt": str(raw.get("TISSU_AMT", "")),
"nonMbrsYn": "Y",
}.items():
fields = set_field(fields, key, val)
out.mkdir(parents=True, exist_ok=True)
checkout = open_text(op, request(f"{BASE_URL}/mrs/stplcfmpym.do?keep=/mrs/pay", fields, f"{BASE_URL}/mrs/satschc.do"), timeout)
checkout_path = out / "kobus-checkout-response.html"
checkout_path.write_text(checkout)
helper_path = out / "kobus-payment-autosubmit.html"
inputs = "\n".join(f'<input type="hidden" name="{html.escape(k)}" value="{html.escape(v, quote=True)}">' for k, v in fields)
helper_path.write_text(f'<!doctype html><meta charset="utf-8"><p>공식 KOBUS 결제정보 입력 페이지로 이동합니다. 결제는 직접 진행하세요.</p><form id="f" method="post" action="{BASE_URL}/mrs/stplcfmpym.do?keep=/mrs/pay">{inputs}</form><script>document.getElementById("f").submit();</script>')
cancel_path = out / "kobus-cancel-fields.txt"
cancel_path.write_text("\n".join(f"{k}={v}" for k, v in fields))
return Hold(True, str(raw.get("pcpyNoAll")), str(raw.get("satsNoAll")), selected, str(raw.get("ESTM_AMT")), str(raw.get("DC_AMT")), str(raw.get("TISSU_AMT")), str(helper_path), str(checkout_path), str(cancel_path), raw)
def main(argv: Iterable[str] | None = None) -> int:
p = argparse.ArgumentParser()
p.add_argument("--depart-code", required=True)
p.add_argument("--arrive-code", required=True)
p.add_argument("--date", required=True)
p.add_argument("--select-index", type=int, default=1)
p.add_argument("--hold-first-seat", action="store_true")
p.add_argument("--hold-seat")
p.add_argument("--output-dir")
p.add_argument("--limit", type=int, default=20)
p.add_argument("--timeout", type=int, default=20)
args = p.parse_args(argv)
op = opener()
body, schedules = search(op, args.depart_code, args.arrive_code, args.date, args.timeout)
result: dict[str, object] = {"route": {"depart_code": args.depart_code, "arrive_code": args.arrive_code, "date": args.date}, "count": len(schedules), "items": [asdict(s) for s in schedules[: args.limit]]}
if (args.hold_first_seat or args.hold_seat) and schedules:
out = Path(args.output_dir) if args.output_dir else Path(tempfile.mkdtemp(prefix="kobus-hold-"))
result["hold"] = asdict(hold(op, body, schedules[args.select_index - 1], args.hold_seat, out, args.timeout))
result["payment_note"] = "Opened/saved the official KOBUS payment-information page; final card entry/payment remains manual."
print(json.dumps(result, ensure_ascii=False, indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())
Related skills
FAQ
What does express-bus-booking do?
Search and assist Korean 고속버스/KOBUS bookings using official HTTP/API-first flows; use for 고속버스 예매, 시간표, 좌석 조회, 임시 선점, and official checkout-entry handoff.
When should I use express-bus-booking?
User asks about express bus booking or related SKILL.md workflows.
Is express-bus-booking safe to install?
Review the Security Audits panel on this page before installing in production.