kwcli 0.1.0__py3-none-any.whl
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.
- kiwoom/__init__.py +47 -0
- kiwoom/_data/kiwoom_api_spec.json +66372 -0
- kiwoom/core/__init__.py +6 -0
- kiwoom/core/auth.py +349 -0
- kiwoom/core/client.py +244 -0
- kiwoom/core/errors.py +177 -0
- kiwoom/core/platform_paths.py +68 -0
- kiwoom/core/profiles.py +143 -0
- kiwoom/core/runtime.py +153 -0
- kiwoom/core/secrets.py +217 -0
- kiwoom/core/settings.py +64 -0
- kiwoom/core/token_store.py +186 -0
- kiwoom/core/types.py +28 -0
- kiwoom/core/ws_client.py +262 -0
- kiwoom/realtime/__init__.py +30 -0
- kiwoom/realtime/decoders.py +171 -0
- kiwoom/realtime/events.py +98 -0
- kiwoom/realtime/packets.py +65 -0
- kiwoom/realtime/schemas.py +76 -0
- kiwoom/realtime/stream.py +213 -0
- kiwoom/specs.py +272 -0
- kiwoom_cli/README.md +671 -0
- kiwoom_cli/__init__.py +9 -0
- kiwoom_cli/__main__.py +5 -0
- kiwoom_cli/argument_maps.py +561 -0
- kiwoom_cli/arguments.py +147 -0
- kiwoom_cli/auth_context.py +64 -0
- kiwoom_cli/banner.py +125 -0
- kiwoom_cli/commands/__init__.py +1 -0
- kiwoom_cli/commands/auth.py +406 -0
- kiwoom_cli/commands/groups.py +281 -0
- kiwoom_cli/commands/mapped.py +74 -0
- kiwoom_cli/commands/orders.py +158 -0
- kiwoom_cli/commands/spec.py +98 -0
- kiwoom_cli/commands/stocks.py +100 -0
- kiwoom_cli/commands/streams.py +470 -0
- kiwoom_cli/doctor.py +324 -0
- kiwoom_cli/errors.py +24 -0
- kiwoom_cli/executor/__init__.py +33 -0
- kiwoom_cli/executor/condition.py +374 -0
- kiwoom_cli/executor/rest.py +132 -0
- kiwoom_cli/executor/waits.py +34 -0
- kiwoom_cli/executor/websocket.py +131 -0
- kiwoom_cli/main.py +203 -0
- kiwoom_cli/maps/README.md +99 -0
- kiwoom_cli/maps/api_commands.csv +209 -0
- kiwoom_cli/maps/arguments.csv +731 -0
- kiwoom_cli/maps/order_confirmation_commands.csv +13 -0
- kiwoom_cli/maps/order_confirmation_fields.csv +71 -0
- kiwoom_cli/maps/order_price_policies.csv +47 -0
- kiwoom_cli/maps/order_value_labels.csv +28 -0
- kiwoom_cli/maps/positional_arguments.csv +21 -0
- kiwoom_cli/order_confirmation.py +167 -0
- kiwoom_cli/output.py +136 -0
- kiwoom_cli/registry.py +95 -0
- kiwoom_cli/safety.py +30 -0
- kiwoom_cli/setup.py +533 -0
- kwcli-0.1.0.dist-info/METADATA +215 -0
- kwcli-0.1.0.dist-info/RECORD +62 -0
- kwcli-0.1.0.dist-info/WHEEL +4 -0
- kwcli-0.1.0.dist-info/entry_points.txt +2 -0
- kwcli-0.1.0.dist-info/licenses/LICENSE.md +36 -0
kiwoom_cli/doctor.py
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
import platform
|
|
6
|
+
import shlex
|
|
7
|
+
import shutil
|
|
8
|
+
import stat
|
|
9
|
+
import sys
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from kiwoom.core.errors import KiwoomError, ModeNotConfiguredError
|
|
14
|
+
from kiwoom.core.platform_paths import cache_dir
|
|
15
|
+
from kiwoom.core.profiles import get_current_profile, load_profiles
|
|
16
|
+
from kiwoom.core.runtime import describe_selection
|
|
17
|
+
from kiwoom.core.secrets import env_var_names
|
|
18
|
+
from kiwoom.core.settings import get_mode_from_env, get_profile_from_env, settings_path
|
|
19
|
+
from kiwoom_cli.auth_context import build_auth_context
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def add_doctor_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
23
|
+
doctor_parser = subparsers.add_parser(
|
|
24
|
+
"doctor", help="설치/인증 컨텍스트 현재 상태를 진단합니다."
|
|
25
|
+
)
|
|
26
|
+
doctor_parser.set_defaults(handler=handle_doctor)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def handle_doctor(args: argparse.Namespace) -> None:
|
|
30
|
+
print_doctor()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# 설치된 kiwoom은 자체 격리 런타임에서 돌기 때문에 이 도구들은 실행 필수가 아니라
|
|
34
|
+
# 설치/업데이트나 부가 기능(예: auth export의 .gitignore 등록)용 온보딩 항목이다.
|
|
35
|
+
# 따라서 Windows에서만 존재 여부를 경고성으로 안내한다.
|
|
36
|
+
WINDOWS_ONBOARDING_TOOLS = ("git", "python", "uv")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def os_label() -> str:
|
|
40
|
+
system = platform.system()
|
|
41
|
+
if system == "Darwin":
|
|
42
|
+
return f"macOS {platform.mac_ver()[0] or platform.release()}".strip()
|
|
43
|
+
if system == "Windows":
|
|
44
|
+
return f"Windows {platform.release()}".strip()
|
|
45
|
+
if system == "Linux":
|
|
46
|
+
return f"Linux {platform.release()}".strip()
|
|
47
|
+
return system or "알 수 없음"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _tool_found(tool: str) -> bool:
|
|
51
|
+
# Windows에서 python은 python/python3 외에 py 런처로도 제공된다.
|
|
52
|
+
candidates = {"python": ("python", "python3", "py")}.get(tool, (tool,))
|
|
53
|
+
return any(shutil.which(candidate) for candidate in candidates)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def onboarding_tool_report() -> list[tuple[str, bool]]:
|
|
57
|
+
"""Windows에서만 (도구, 발견여부) 목록을 돌려준다. 다른 OS에서는 빈 목록."""
|
|
58
|
+
if os.name != "nt":
|
|
59
|
+
return []
|
|
60
|
+
return [(tool, _tool_found(tool)) for tool in WINDOWS_ONBOARDING_TOOLS]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def command_path() -> str:
|
|
64
|
+
return shutil.which("kiwoom") or "없음"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def current_executable() -> str:
|
|
68
|
+
raw = sys.argv[0] or ""
|
|
69
|
+
if not raw:
|
|
70
|
+
return "알 수 없음"
|
|
71
|
+
path = Path(raw)
|
|
72
|
+
if not path.is_absolute():
|
|
73
|
+
path = Path.cwd() / path
|
|
74
|
+
return str(path)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def resolved_current_executable() -> str:
|
|
78
|
+
raw = current_executable()
|
|
79
|
+
if raw in {"", "알 수 없음"}:
|
|
80
|
+
return raw
|
|
81
|
+
return str(Path(raw).resolve())
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def path_kiwoom_entries() -> list[str]:
|
|
85
|
+
entries: list[str] = []
|
|
86
|
+
seen: set[str] = set()
|
|
87
|
+
for directory in os.environ.get("PATH", "").split(os.pathsep):
|
|
88
|
+
if not directory:
|
|
89
|
+
continue
|
|
90
|
+
candidate = Path(directory) / "kiwoom"
|
|
91
|
+
try:
|
|
92
|
+
candidate_stat = candidate.stat()
|
|
93
|
+
except OSError:
|
|
94
|
+
continue
|
|
95
|
+
if not stat.S_ISREG(candidate_stat.st_mode):
|
|
96
|
+
continue
|
|
97
|
+
if not os.access(candidate, os.X_OK):
|
|
98
|
+
continue
|
|
99
|
+
rendered = str(candidate)
|
|
100
|
+
if rendered not in seen:
|
|
101
|
+
entries.append(rendered)
|
|
102
|
+
seen.add(rendered)
|
|
103
|
+
return entries
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass
|
|
107
|
+
class _Finding:
|
|
108
|
+
"""A diagnosed problem: symptom plus the exact command(s) to fix it."""
|
|
109
|
+
|
|
110
|
+
symptom: str
|
|
111
|
+
fixes: list[str] = field(default_factory=list)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def print_doctor() -> None:
|
|
115
|
+
"""Diagnose the current auth context. Verdict-first and never crashes.
|
|
116
|
+
|
|
117
|
+
Every gathering step is defensive: a broken settings file or a conflicting
|
|
118
|
+
environment becomes a reported finding, not a traceback — because doctor is
|
|
119
|
+
exactly the command a user runs when those things are wrong.
|
|
120
|
+
"""
|
|
121
|
+
print("Kiwoom CLI 진단")
|
|
122
|
+
print()
|
|
123
|
+
|
|
124
|
+
findings: list[_Finding] = []
|
|
125
|
+
target_label, can_call_now, cause = _diagnose_target(findings)
|
|
126
|
+
_diagnose_install(findings)
|
|
127
|
+
|
|
128
|
+
_print_verdict(target_label, can_call_now, cause)
|
|
129
|
+
print()
|
|
130
|
+
_print_findings(findings)
|
|
131
|
+
print()
|
|
132
|
+
_print_detail()
|
|
133
|
+
print()
|
|
134
|
+
_print_environment_summary()
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _print_verdict(target_label: str, can_call_now: bool | None, cause: str | None) -> None:
|
|
138
|
+
print("판정")
|
|
139
|
+
print(f" 유효 대상: {target_label}")
|
|
140
|
+
if can_call_now is None:
|
|
141
|
+
print(" 지금 호출 가능: 알 수 없음")
|
|
142
|
+
else:
|
|
143
|
+
print(f" 지금 호출 가능: {'예' if can_call_now else '아니오'}")
|
|
144
|
+
if cause:
|
|
145
|
+
print(f" 원인: {cause}")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _print_findings(findings: list[_Finding]) -> None:
|
|
149
|
+
print("발견된 문제")
|
|
150
|
+
if not findings:
|
|
151
|
+
print(" 특이 사항이 없습니다.")
|
|
152
|
+
return
|
|
153
|
+
for finding in findings:
|
|
154
|
+
print(f" • {finding.symptom}")
|
|
155
|
+
for fix in finding.fixes:
|
|
156
|
+
print(f" 고치려면: {fix}")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _diagnose_target(findings: list[_Finding]) -> tuple[str, bool | None, str | None]:
|
|
160
|
+
"""Resolve the default target and judge whether it can call now.
|
|
161
|
+
|
|
162
|
+
Returns (target_label, can_call_now, cause). can_call_now is None when the
|
|
163
|
+
target cannot be resolved (not configured / conflicting / unreadable).
|
|
164
|
+
"""
|
|
165
|
+
current = _safe_current_profile(findings)
|
|
166
|
+
|
|
167
|
+
try:
|
|
168
|
+
selection = describe_selection()
|
|
169
|
+
except ModeNotConfiguredError:
|
|
170
|
+
findings.append(_Finding("기본 실행 대상이 설정되지 않았습니다.", ["kiwoomcli setup"]))
|
|
171
|
+
return ("없음", None, "설정된 계좌 별칭 또는 모드가 없습니다")
|
|
172
|
+
except ValueError as exc:
|
|
173
|
+
# KIWOOM_PROFILE + KIWOOM_MODE point at conflicting modes.
|
|
174
|
+
findings.append(
|
|
175
|
+
_Finding(
|
|
176
|
+
f"환경변수 충돌: {exc}",
|
|
177
|
+
["KIWOOM_MODE 또는 KIWOOM_PROFILE 중 하나를 unset 하세요"],
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
return ("알 수 없음", None, "KIWOOM_PROFILE 과 KIWOOM_MODE 가 충돌합니다")
|
|
181
|
+
except KiwoomError as exc:
|
|
182
|
+
# e.g. KIWOOM_PROFILE names a missing profile, or settings unreadable.
|
|
183
|
+
findings.append(_Finding(f"실행 대상을 해석할 수 없습니다: {exc}", ["kiwoomcli setup", "또는 KIWOOM_PROFILE 값 확인"]))
|
|
184
|
+
return ("알 수 없음", None, str(exc))
|
|
185
|
+
|
|
186
|
+
_append_override_finding(findings, current, selection)
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
context = build_auth_context()
|
|
190
|
+
except KiwoomError as exc:
|
|
191
|
+
return (selection.target_label, None, f"상태 확인 실패: {exc}")
|
|
192
|
+
|
|
193
|
+
if context.can_call_now:
|
|
194
|
+
return (selection.target_label, True, None)
|
|
195
|
+
|
|
196
|
+
status = context.status
|
|
197
|
+
cause = "키/시크릿이 없고 사용 가능한 토큰도 없습니다"
|
|
198
|
+
if selection.uses_profile:
|
|
199
|
+
alias_arg = shlex.quote(str(selection.profile))
|
|
200
|
+
fixes = [
|
|
201
|
+
f"kiwoomcli auth login --alias {alias_arg} --mode {selection.mode}",
|
|
202
|
+
f"kiwoomcli auth status --profile {alias_arg}",
|
|
203
|
+
]
|
|
204
|
+
if status.has_token and not status.token_reusable and not status.has_credentials:
|
|
205
|
+
symptom = (
|
|
206
|
+
f"계좌 별칭 '{selection.profile}' 은 남아 있지만 저장된 키/시크릿을 "
|
|
207
|
+
"읽을 수 없고 토큰도 재사용할 수 없습니다."
|
|
208
|
+
)
|
|
209
|
+
else:
|
|
210
|
+
symptom = f"유효 대상 '{selection.profile}' 으로 지금 호출할 수 없습니다."
|
|
211
|
+
findings.append(
|
|
212
|
+
_Finding(
|
|
213
|
+
symptom,
|
|
214
|
+
fixes,
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
else:
|
|
218
|
+
appkey_var, secretkey_var = env_var_names(selection.mode)
|
|
219
|
+
findings.append(
|
|
220
|
+
_Finding(
|
|
221
|
+
f"유효 대상 {selection.mode} mode 로 지금 호출할 수 없습니다 (키/시크릿 없음).",
|
|
222
|
+
["kiwoomcli setup", f"{appkey_var} / {secretkey_var} 환경변수 설정"],
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
return (selection.target_label, False, cause)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _append_override_finding(findings: list[_Finding], current, selection) -> None:
|
|
229
|
+
"""Flag when an environment variable overrides the configured current profile.
|
|
230
|
+
|
|
231
|
+
This is the "demo인데 왜 real?" surprise: a stored current_profile is
|
|
232
|
+
silently bypassed because KIWOOM_MODE / KIWOOM_PROFILE wins the precedence.
|
|
233
|
+
"""
|
|
234
|
+
if current is None:
|
|
235
|
+
return
|
|
236
|
+
if selection.selection_source not in {"KIWOOM_MODE", "KIWOOM_PROFILE"}:
|
|
237
|
+
return
|
|
238
|
+
if selection.uses_profile and selection.profile == current.alias:
|
|
239
|
+
return
|
|
240
|
+
findings.append(
|
|
241
|
+
_Finding(
|
|
242
|
+
f"{selection.selection_source} 가 current_profile {current.alias}({current.mode})"
|
|
243
|
+
f" 보다 우선 적용되어 유효 대상이 {selection.target_label} 입니다.",
|
|
244
|
+
[
|
|
245
|
+
f"unset {selection.selection_source} (current_profile {current.alias} 사용)",
|
|
246
|
+
f"kiwoomcli auth switch {current.alias} (현재 유효 대상을 기본값으로 굳히기)",
|
|
247
|
+
],
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def _diagnose_install(findings: list[_Finding]) -> None:
|
|
253
|
+
entries = path_kiwoom_entries()
|
|
254
|
+
if len(entries) > 1:
|
|
255
|
+
finding = _Finding(
|
|
256
|
+
f"PATH 에 kiwoom 실행 파일이 {len(entries)}개 있어 어떤 것이 실행될지 모호합니다.",
|
|
257
|
+
["which -a kiwoom 로 우선순위를 확인하세요"],
|
|
258
|
+
)
|
|
259
|
+
finding.fixes.extend(f"- {entry}" for entry in entries)
|
|
260
|
+
findings.append(finding)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _print_detail() -> None:
|
|
264
|
+
current = _safe_current_profile(None)
|
|
265
|
+
print("상세(근거)")
|
|
266
|
+
print(" 선택 규칙 입력")
|
|
267
|
+
print(f" current_profile: {current.alias if current else '없음'}")
|
|
268
|
+
print(f" KIWOOM_MODE: {get_mode_from_env() or '없음'}")
|
|
269
|
+
print(f" KIWOOM_PROFILE: {get_profile_from_env() or '없음'}")
|
|
270
|
+
print(" 저장된 프로필")
|
|
271
|
+
try:
|
|
272
|
+
profiles = load_profiles()
|
|
273
|
+
except KiwoomError as exc:
|
|
274
|
+
print(f" 프로필을 읽을 수 없습니다: {exc}")
|
|
275
|
+
return
|
|
276
|
+
if not profiles:
|
|
277
|
+
print(" 저장된 계좌 별칭이 없습니다.")
|
|
278
|
+
return
|
|
279
|
+
for alias in sorted(profiles):
|
|
280
|
+
try:
|
|
281
|
+
# Pass the profile's own mode so ambient KIWOOM_MODE can't trigger
|
|
282
|
+
# a mode-mismatch while enumerating stored profiles.
|
|
283
|
+
context = build_auth_context(mode=profiles[alias].mode, profile=alias)
|
|
284
|
+
except KiwoomError as exc:
|
|
285
|
+
print(f" {alias}: 상태 확인 실패 ({exc})")
|
|
286
|
+
continue
|
|
287
|
+
status = context.status
|
|
288
|
+
token_label = "사용 가능" if status.token_reusable else ("있음" if status.has_token else "없음")
|
|
289
|
+
marker = " (current)" if current is not None and current.alias == alias else ""
|
|
290
|
+
print(f" {alias}{marker}: 서버={status.mode} 키/시크릿={'있음' if status.has_credentials else '없음'} "
|
|
291
|
+
f"토큰={token_label} 호출={'가능' if context.can_call_now else '불가'} "
|
|
292
|
+
f"재발급={'가능' if context.can_refresh else '불가'}")
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _print_environment_summary() -> None:
|
|
296
|
+
print("실행 환경")
|
|
297
|
+
print(f" 운영체제: {os_label()}")
|
|
298
|
+
tools = onboarding_tool_report()
|
|
299
|
+
if tools:
|
|
300
|
+
rendered = " · ".join(
|
|
301
|
+
f"{tool} {'있음' if found else '없음'}" for tool, found in tools
|
|
302
|
+
)
|
|
303
|
+
print(f" 도구: {rendered}")
|
|
304
|
+
print(f" 실행 대상: {resolved_current_executable()}")
|
|
305
|
+
print(f" 설정 파일: {settings_path()}")
|
|
306
|
+
print(f" 토큰 캐시: {cache_dir()}")
|
|
307
|
+
# Detailed PATH/package listing only matters when there is an anomaly; the
|
|
308
|
+
# multiple-kiwoom case is already surfaced as a finding above.
|
|
309
|
+
entries = path_kiwoom_entries()
|
|
310
|
+
if len(entries) > 1:
|
|
311
|
+
print(f" PATH 의 kiwoom: {len(entries)}개")
|
|
312
|
+
for entry in entries:
|
|
313
|
+
print(f" - {entry}")
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def _safe_current_profile(findings: list[_Finding] | None):
|
|
317
|
+
try:
|
|
318
|
+
return get_current_profile()
|
|
319
|
+
except KiwoomError as exc:
|
|
320
|
+
if findings is not None:
|
|
321
|
+
findings.append(
|
|
322
|
+
_Finding(f"current_profile 을 읽을 수 없습니다: {exc}", ["settings.json 확인 또는 kiwoomcli setup"])
|
|
323
|
+
)
|
|
324
|
+
return None
|
kiwoom_cli/errors.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""CLI-owned exception taxonomy.
|
|
2
|
+
|
|
3
|
+
Separates user-fixable input/policy violations from should-be-unreachable
|
|
4
|
+
internal assertions so ``main()`` can present each differently: input errors
|
|
5
|
+
get a single Korean line the user can act on, while internal errors surface as
|
|
6
|
+
"내부 오류" with a traceback instead of being mistaken for user mistakes.
|
|
7
|
+
|
|
8
|
+
API/server errors stay as ``kiwoom.core`` ``KiwoomError`` subclasses and are
|
|
9
|
+
passed through unchanged; they are intentionally NOT remapped here.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CliError(Exception):
|
|
16
|
+
"""Base class for errors owned by the CLI layer."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CliInputError(CliError):
|
|
20
|
+
"""User input or policy violation (a single Korean message the user can fix)."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CliInternalError(CliError):
|
|
24
|
+
"""Should-be-unreachable internal assertion (reported as 내부 오류)."""
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Command execution through the package runtime facade.
|
|
2
|
+
|
|
3
|
+
Public API for command modules; implementations live in the sibling modules
|
|
4
|
+
(``rest``, ``websocket``, ``condition``, ``waits``).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from kiwoom_cli.executor.condition import (
|
|
8
|
+
DEFAULT_CONDITION_MAX_WAIT_SECONDS,
|
|
9
|
+
execute_condition_command,
|
|
10
|
+
)
|
|
11
|
+
from kiwoom_cli.executor.rest import (
|
|
12
|
+
execute_rest_command,
|
|
13
|
+
preview_rest_command,
|
|
14
|
+
validate_preview_request,
|
|
15
|
+
validate_request_body,
|
|
16
|
+
)
|
|
17
|
+
from kiwoom_cli.executor.websocket import (
|
|
18
|
+
DEFAULT_STREAM_MAX_WAIT_SECONDS,
|
|
19
|
+
execute_websocket_request_once,
|
|
20
|
+
execute_websocket_stream,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"DEFAULT_CONDITION_MAX_WAIT_SECONDS",
|
|
25
|
+
"DEFAULT_STREAM_MAX_WAIT_SECONDS",
|
|
26
|
+
"execute_condition_command",
|
|
27
|
+
"execute_rest_command",
|
|
28
|
+
"execute_websocket_request_once",
|
|
29
|
+
"execute_websocket_stream",
|
|
30
|
+
"preview_rest_command",
|
|
31
|
+
"validate_preview_request",
|
|
32
|
+
"validate_request_body",
|
|
33
|
+
]
|