@mongoui/skills 0.1.8 → 0.2.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/dist/products/index.js +6 -5
- package/package.json +26 -26
- package/skills/flink-flight-api/SKILL.md +63 -63
- package/skills/flink-flight-api/agents/openai.yaml +4 -4
- package/skills/flink-flight-api/references/api-workflows.md +104 -104
- package/skills/flink-flight-api/references/auth-and-credentials.md +136 -136
- package/skills/flink-flight-api/references/endpoint-index.md +92 -92
- package/skills/flink-flight-api/scripts/flink_signed_request.py +188 -188
- package/skills/glink-hotel-api/SKILL.md +153 -152
- package/skills/glink-hotel-api/agents/openai.yaml +4 -4
- package/skills/glink-hotel-api/references/api-workflows.md +168 -158
- package/skills/glink-hotel-api/references/auth-and-credentials.md +144 -144
- package/skills/glink-hotel-api/references/endpoint-index.md +148 -129
- package/skills/glink-hotel-api/references/field-and-enum-notes.md +161 -0
- package/skills/glink-hotel-api/scripts/glink_signed_request.py +199 -199
- package/skills/tmc-api/SKILL.md +63 -0
- package/skills/tmc-api/agents/openai.yaml +4 -0
- package/skills/tmc-api/references/api-workflows.md +365 -0
- package/skills/tmc-api/references/auth-and-calling.md +47 -0
- package/skills/tmc-api/references/endpoint-details.md +25964 -0
- package/skills/tmc-api/references/endpoint-index.md +116 -0
- package/skills/tmc-api/scripts/tmc_signed_request.py +199 -0
- package/skills/tmc-whitelabel/SKILL.md +0 -8
|
@@ -1,199 +1,199 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Generate or send signed requests for the Glink open-platform gateway.
|
|
3
|
-
|
|
4
|
-
Examples:
|
|
5
|
-
python scripts/glink_signed_request.py ^
|
|
6
|
-
--method POST ^
|
|
7
|
-
--path /region/businesses ^
|
|
8
|
-
--body-json "{\"language\":\"zh-CN\",\"cityCode\":\"1\"}" ^
|
|
9
|
-
--app-key open_app_xxx ^
|
|
10
|
-
--app-secret sk_xxx
|
|
11
|
-
|
|
12
|
-
python scripts/glink_signed_request.py ^
|
|
13
|
-
--method POST ^
|
|
14
|
-
--path /search/creditBalance ^
|
|
15
|
-
--body-json "{}" ^
|
|
16
|
-
--app-key open_app_xxx ^
|
|
17
|
-
--app-secret sk_xxx ^
|
|
18
|
-
--send
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
from __future__ import annotations
|
|
22
|
-
|
|
23
|
-
import argparse
|
|
24
|
-
import hashlib
|
|
25
|
-
import hmac
|
|
26
|
-
import json
|
|
27
|
-
import os
|
|
28
|
-
import sys
|
|
29
|
-
import time
|
|
30
|
-
import urllib.parse
|
|
31
|
-
import urllib.request
|
|
32
|
-
import uuid
|
|
33
|
-
from pathlib import Path
|
|
34
|
-
|
|
35
|
-
GATEWAY_PREFIX = "/openapi/v1/glink"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def sha256_hex(data: bytes) -> str:
|
|
39
|
-
return hashlib.sha256(data).hexdigest()
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def normalize_path(path: str) -> str:
|
|
43
|
-
if not path.startswith("/"):
|
|
44
|
-
path = f"/{path}"
|
|
45
|
-
if path == GATEWAY_PREFIX or path.startswith(f"{GATEWAY_PREFIX}/"):
|
|
46
|
-
return path
|
|
47
|
-
return f"{GATEWAY_PREFIX}{path}"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def parse_query_items(items: list[str]) -> list[tuple[str, str]]:
|
|
51
|
-
pairs: list[tuple[str, str]] = []
|
|
52
|
-
for item in items:
|
|
53
|
-
if "=" not in item:
|
|
54
|
-
raise ValueError(f"invalid query item: {item!r}; expected key=value")
|
|
55
|
-
key, value = item.split("=", 1)
|
|
56
|
-
pairs.append((key, value))
|
|
57
|
-
return pairs
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def canonical_query(pairs: list[tuple[str, str]]) -> str:
|
|
61
|
-
ordered = sorted(pairs, key=lambda item: (item[0], item[1]))
|
|
62
|
-
return "&".join(
|
|
63
|
-
f"{urllib.parse.quote(key, safe='~-._')}={urllib.parse.quote(value, safe='~-._')}"
|
|
64
|
-
for key, value in ordered
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def build_sign_payload(
|
|
69
|
-
method: str,
|
|
70
|
-
path: str,
|
|
71
|
-
query_string: str,
|
|
72
|
-
body_bytes: bytes,
|
|
73
|
-
timestamp: str,
|
|
74
|
-
nonce: str,
|
|
75
|
-
app_key: str,
|
|
76
|
-
) -> str:
|
|
77
|
-
return "\n".join(
|
|
78
|
-
[
|
|
79
|
-
method.upper(),
|
|
80
|
-
path,
|
|
81
|
-
query_string,
|
|
82
|
-
sha256_hex(body_bytes),
|
|
83
|
-
timestamp,
|
|
84
|
-
nonce,
|
|
85
|
-
app_key,
|
|
86
|
-
]
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def sign(secret: str, payload: str) -> str:
|
|
91
|
-
return hmac.new(secret.encode("utf-8"), payload.encode("utf-8"), hashlib.sha256).hexdigest()
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def load_body(args: argparse.Namespace) -> bytes:
|
|
95
|
-
if args.body_file:
|
|
96
|
-
return Path(args.body_file).read_bytes()
|
|
97
|
-
if args.body_json is None:
|
|
98
|
-
return b""
|
|
99
|
-
parsed = json.loads(args.body_json)
|
|
100
|
-
return json.dumps(parsed, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
def build_url(base_url: str, path: str, query_string: str) -> str:
|
|
104
|
-
base = base_url.rstrip("/")
|
|
105
|
-
url = f"{base}{path}"
|
|
106
|
-
if query_string:
|
|
107
|
-
url = f"{url}?{query_string}"
|
|
108
|
-
return url
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def build_headers(app_key: str, timestamp: str, nonce: str, signature: str, has_body: bool) -> dict[str, str]:
|
|
112
|
-
headers = {
|
|
113
|
-
"Accept": "application/json",
|
|
114
|
-
"X-OP-App-Key": app_key,
|
|
115
|
-
"X-OP-Timestamp": timestamp,
|
|
116
|
-
"X-OP-Nonce": nonce,
|
|
117
|
-
"X-OP-Sign": signature,
|
|
118
|
-
}
|
|
119
|
-
if has_body:
|
|
120
|
-
headers["Content-Type"] = "application/json"
|
|
121
|
-
return headers
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
def send_request(method: str, url: str, headers: dict[str, str], body_bytes: bytes, timeout: float) -> tuple[int, str]:
|
|
125
|
-
request = urllib.request.Request(url=url, data=body_bytes or None, headers=headers, method=method.upper())
|
|
126
|
-
try:
|
|
127
|
-
with urllib.request.urlopen(request, timeout=timeout) as response:
|
|
128
|
-
return response.status, response.read().decode("utf-8", errors="replace")
|
|
129
|
-
except urllib.error.HTTPError as exc:
|
|
130
|
-
return exc.code, exc.read().decode("utf-8", errors="replace")
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def main() -> int:
|
|
134
|
-
parser = argparse.ArgumentParser(description="Generate or send a signed Glink open-platform request.")
|
|
135
|
-
parser.add_argument("--base-url", default=os.getenv("OPEN_PLATFORM_BASE_URL", "http://127.0.0.1:8671"))
|
|
136
|
-
parser.add_argument("--method", required=True)
|
|
137
|
-
parser.add_argument("--path", required=True, help="Short interface path, such as /search/destination; full gateway paths are also accepted")
|
|
138
|
-
parser.add_argument("--query", action="append", default=[], help="Repeatable key=value query parameter")
|
|
139
|
-
parser.add_argument("--body-json", help="JSON string request body")
|
|
140
|
-
parser.add_argument("--body-file", help="Path to a raw request body file")
|
|
141
|
-
parser.add_argument("--app-key", default=os.getenv("OPEN_PLATFORM_APP_KEY") or os.getenv("OPEN_PLATFORM_OPEN_APP_ID"))
|
|
142
|
-
parser.add_argument("--app-secret", default=os.getenv("OPEN_PLATFORM_APP_SECRET") or os.getenv("OPEN_PLATFORM_OPEN_SECRET"))
|
|
143
|
-
parser.add_argument("--timestamp", default=str(int(time.time())))
|
|
144
|
-
parser.add_argument("--nonce", default=uuid.uuid4().hex)
|
|
145
|
-
parser.add_argument("--send", action="store_true")
|
|
146
|
-
parser.add_argument("--timeout", type=float, default=15.0)
|
|
147
|
-
args = parser.parse_args()
|
|
148
|
-
|
|
149
|
-
if not args.app_key or not args.app_secret:
|
|
150
|
-
print("app key and app secret are required", file=sys.stderr)
|
|
151
|
-
return 2
|
|
152
|
-
|
|
153
|
-
try:
|
|
154
|
-
query_pairs = parse_query_items(args.query)
|
|
155
|
-
body_bytes = load_body(args)
|
|
156
|
-
runtime_path = normalize_path(args.path)
|
|
157
|
-
except Exception as exc:
|
|
158
|
-
print(str(exc), file=sys.stderr)
|
|
159
|
-
return 2
|
|
160
|
-
|
|
161
|
-
query_string = canonical_query(query_pairs)
|
|
162
|
-
payload = build_sign_payload(
|
|
163
|
-
method=args.method,
|
|
164
|
-
path=runtime_path,
|
|
165
|
-
query_string=query_string,
|
|
166
|
-
body_bytes=body_bytes,
|
|
167
|
-
timestamp=args.timestamp,
|
|
168
|
-
nonce=args.nonce,
|
|
169
|
-
app_key=args.app_key,
|
|
170
|
-
)
|
|
171
|
-
signature = sign(args.app_secret, payload)
|
|
172
|
-
headers = build_headers(args.app_key, args.timestamp, args.nonce, signature, bool(body_bytes))
|
|
173
|
-
url = build_url(args.base_url, runtime_path, query_string)
|
|
174
|
-
|
|
175
|
-
print("URL:")
|
|
176
|
-
print(url)
|
|
177
|
-
print()
|
|
178
|
-
print("Canonical String:")
|
|
179
|
-
print(payload)
|
|
180
|
-
print()
|
|
181
|
-
print("Headers:")
|
|
182
|
-
print(json.dumps(headers, ensure_ascii=False, indent=2))
|
|
183
|
-
if body_bytes:
|
|
184
|
-
print()
|
|
185
|
-
print("Body:")
|
|
186
|
-
print(body_bytes.decode("utf-8", errors="replace"))
|
|
187
|
-
|
|
188
|
-
if not args.send:
|
|
189
|
-
return 0
|
|
190
|
-
|
|
191
|
-
status, response_text = send_request(args.method, url, headers, body_bytes, args.timeout)
|
|
192
|
-
print()
|
|
193
|
-
print(f"HTTP {status}")
|
|
194
|
-
print(response_text)
|
|
195
|
-
return 0
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if __name__ == "__main__":
|
|
199
|
-
raise SystemExit(main())
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Generate or send signed requests for the Glink open-platform gateway.
|
|
3
|
+
|
|
4
|
+
Examples:
|
|
5
|
+
python scripts/glink_signed_request.py ^
|
|
6
|
+
--method POST ^
|
|
7
|
+
--path /region/businesses ^
|
|
8
|
+
--body-json "{\"language\":\"zh-CN\",\"cityCode\":\"1\"}" ^
|
|
9
|
+
--app-key open_app_xxx ^
|
|
10
|
+
--app-secret sk_xxx
|
|
11
|
+
|
|
12
|
+
python scripts/glink_signed_request.py ^
|
|
13
|
+
--method POST ^
|
|
14
|
+
--path /search/creditBalance ^
|
|
15
|
+
--body-json "{}" ^
|
|
16
|
+
--app-key open_app_xxx ^
|
|
17
|
+
--app-secret sk_xxx ^
|
|
18
|
+
--send
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import argparse
|
|
24
|
+
import hashlib
|
|
25
|
+
import hmac
|
|
26
|
+
import json
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
import time
|
|
30
|
+
import urllib.parse
|
|
31
|
+
import urllib.request
|
|
32
|
+
import uuid
|
|
33
|
+
from pathlib import Path
|
|
34
|
+
|
|
35
|
+
GATEWAY_PREFIX = "/openapi/v1/glink"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def sha256_hex(data: bytes) -> str:
|
|
39
|
+
return hashlib.sha256(data).hexdigest()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def normalize_path(path: str) -> str:
|
|
43
|
+
if not path.startswith("/"):
|
|
44
|
+
path = f"/{path}"
|
|
45
|
+
if path == GATEWAY_PREFIX or path.startswith(f"{GATEWAY_PREFIX}/"):
|
|
46
|
+
return path
|
|
47
|
+
return f"{GATEWAY_PREFIX}{path}"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def parse_query_items(items: list[str]) -> list[tuple[str, str]]:
|
|
51
|
+
pairs: list[tuple[str, str]] = []
|
|
52
|
+
for item in items:
|
|
53
|
+
if "=" not in item:
|
|
54
|
+
raise ValueError(f"invalid query item: {item!r}; expected key=value")
|
|
55
|
+
key, value = item.split("=", 1)
|
|
56
|
+
pairs.append((key, value))
|
|
57
|
+
return pairs
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def canonical_query(pairs: list[tuple[str, str]]) -> str:
|
|
61
|
+
ordered = sorted(pairs, key=lambda item: (item[0], item[1]))
|
|
62
|
+
return "&".join(
|
|
63
|
+
f"{urllib.parse.quote(key, safe='~-._')}={urllib.parse.quote(value, safe='~-._')}"
|
|
64
|
+
for key, value in ordered
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def build_sign_payload(
|
|
69
|
+
method: str,
|
|
70
|
+
path: str,
|
|
71
|
+
query_string: str,
|
|
72
|
+
body_bytes: bytes,
|
|
73
|
+
timestamp: str,
|
|
74
|
+
nonce: str,
|
|
75
|
+
app_key: str,
|
|
76
|
+
) -> str:
|
|
77
|
+
return "\n".join(
|
|
78
|
+
[
|
|
79
|
+
method.upper(),
|
|
80
|
+
path,
|
|
81
|
+
query_string,
|
|
82
|
+
sha256_hex(body_bytes),
|
|
83
|
+
timestamp,
|
|
84
|
+
nonce,
|
|
85
|
+
app_key,
|
|
86
|
+
]
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def sign(secret: str, payload: str) -> str:
|
|
91
|
+
return hmac.new(secret.encode("utf-8"), payload.encode("utf-8"), hashlib.sha256).hexdigest()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def load_body(args: argparse.Namespace) -> bytes:
|
|
95
|
+
if args.body_file:
|
|
96
|
+
return Path(args.body_file).read_bytes()
|
|
97
|
+
if args.body_json is None:
|
|
98
|
+
return b""
|
|
99
|
+
parsed = json.loads(args.body_json)
|
|
100
|
+
return json.dumps(parsed, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def build_url(base_url: str, path: str, query_string: str) -> str:
|
|
104
|
+
base = base_url.rstrip("/")
|
|
105
|
+
url = f"{base}{path}"
|
|
106
|
+
if query_string:
|
|
107
|
+
url = f"{url}?{query_string}"
|
|
108
|
+
return url
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def build_headers(app_key: str, timestamp: str, nonce: str, signature: str, has_body: bool) -> dict[str, str]:
|
|
112
|
+
headers = {
|
|
113
|
+
"Accept": "application/json",
|
|
114
|
+
"X-OP-App-Key": app_key,
|
|
115
|
+
"X-OP-Timestamp": timestamp,
|
|
116
|
+
"X-OP-Nonce": nonce,
|
|
117
|
+
"X-OP-Sign": signature,
|
|
118
|
+
}
|
|
119
|
+
if has_body:
|
|
120
|
+
headers["Content-Type"] = "application/json"
|
|
121
|
+
return headers
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def send_request(method: str, url: str, headers: dict[str, str], body_bytes: bytes, timeout: float) -> tuple[int, str]:
|
|
125
|
+
request = urllib.request.Request(url=url, data=body_bytes or None, headers=headers, method=method.upper())
|
|
126
|
+
try:
|
|
127
|
+
with urllib.request.urlopen(request, timeout=timeout) as response:
|
|
128
|
+
return response.status, response.read().decode("utf-8", errors="replace")
|
|
129
|
+
except urllib.error.HTTPError as exc:
|
|
130
|
+
return exc.code, exc.read().decode("utf-8", errors="replace")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def main() -> int:
|
|
134
|
+
parser = argparse.ArgumentParser(description="Generate or send a signed Glink open-platform request.")
|
|
135
|
+
parser.add_argument("--base-url", default=os.getenv("OPEN_PLATFORM_BASE_URL", "http://127.0.0.1:8671"))
|
|
136
|
+
parser.add_argument("--method", required=True)
|
|
137
|
+
parser.add_argument("--path", required=True, help="Short interface path, such as /search/destination; full gateway paths are also accepted")
|
|
138
|
+
parser.add_argument("--query", action="append", default=[], help="Repeatable key=value query parameter")
|
|
139
|
+
parser.add_argument("--body-json", help="JSON string request body")
|
|
140
|
+
parser.add_argument("--body-file", help="Path to a raw request body file")
|
|
141
|
+
parser.add_argument("--app-key", default=os.getenv("OPEN_PLATFORM_APP_KEY") or os.getenv("OPEN_PLATFORM_OPEN_APP_ID"))
|
|
142
|
+
parser.add_argument("--app-secret", default=os.getenv("OPEN_PLATFORM_APP_SECRET") or os.getenv("OPEN_PLATFORM_OPEN_SECRET"))
|
|
143
|
+
parser.add_argument("--timestamp", default=str(int(time.time())))
|
|
144
|
+
parser.add_argument("--nonce", default=uuid.uuid4().hex)
|
|
145
|
+
parser.add_argument("--send", action="store_true")
|
|
146
|
+
parser.add_argument("--timeout", type=float, default=15.0)
|
|
147
|
+
args = parser.parse_args()
|
|
148
|
+
|
|
149
|
+
if not args.app_key or not args.app_secret:
|
|
150
|
+
print("app key and app secret are required", file=sys.stderr)
|
|
151
|
+
return 2
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
query_pairs = parse_query_items(args.query)
|
|
155
|
+
body_bytes = load_body(args)
|
|
156
|
+
runtime_path = normalize_path(args.path)
|
|
157
|
+
except Exception as exc:
|
|
158
|
+
print(str(exc), file=sys.stderr)
|
|
159
|
+
return 2
|
|
160
|
+
|
|
161
|
+
query_string = canonical_query(query_pairs)
|
|
162
|
+
payload = build_sign_payload(
|
|
163
|
+
method=args.method,
|
|
164
|
+
path=runtime_path,
|
|
165
|
+
query_string=query_string,
|
|
166
|
+
body_bytes=body_bytes,
|
|
167
|
+
timestamp=args.timestamp,
|
|
168
|
+
nonce=args.nonce,
|
|
169
|
+
app_key=args.app_key,
|
|
170
|
+
)
|
|
171
|
+
signature = sign(args.app_secret, payload)
|
|
172
|
+
headers = build_headers(args.app_key, args.timestamp, args.nonce, signature, bool(body_bytes))
|
|
173
|
+
url = build_url(args.base_url, runtime_path, query_string)
|
|
174
|
+
|
|
175
|
+
print("URL:")
|
|
176
|
+
print(url)
|
|
177
|
+
print()
|
|
178
|
+
print("Canonical String:")
|
|
179
|
+
print(payload)
|
|
180
|
+
print()
|
|
181
|
+
print("Headers:")
|
|
182
|
+
print(json.dumps(headers, ensure_ascii=False, indent=2))
|
|
183
|
+
if body_bytes:
|
|
184
|
+
print()
|
|
185
|
+
print("Body:")
|
|
186
|
+
print(body_bytes.decode("utf-8", errors="replace"))
|
|
187
|
+
|
|
188
|
+
if not args.send:
|
|
189
|
+
return 0
|
|
190
|
+
|
|
191
|
+
status, response_text = send_request(args.method, url, headers, body_bytes, args.timeout)
|
|
192
|
+
print()
|
|
193
|
+
print(f"HTTP {status}")
|
|
194
|
+
print(response_text)
|
|
195
|
+
return 0
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
if __name__ == "__main__":
|
|
199
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tmc-api
|
|
3
|
+
description: Work with the TMC white-label OpenAPI exposed through the Open Platform runtime gateway. Use when Codex needs to read, verify, explain, test, document, or implement against `docs/swagger/tmc-api.json`; choose exact TMC endpoints; include complete request/response fields and types; generate customer-facing TMC onboarding, organization, employee, traveler, approval, travel policy, flight, hotel, payment, attachment, certificate, webhook, and aftersale workflows; or debug Open Platform signing, `token`, sandbox, and production TMC calls.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# TMC API
|
|
7
|
+
|
|
8
|
+
## Quick Start
|
|
9
|
+
|
|
10
|
+
Use this skill when the task is about TMC white-label OpenAPI integration, customer-facing workflow explanation, interface field lookup, signed requests, or implementation against the Open Platform TMC gateway.
|
|
11
|
+
|
|
12
|
+
Start from the smallest reference that answers the request:
|
|
13
|
+
|
|
14
|
+
- Read `references/endpoint-index.md` for the complete 94-operation endpoint map.
|
|
15
|
+
- Read `references/endpoint-details.md` when the answer must include exact request parameters, request body fields, response fields, nested types, examples, or webhook payload fields.
|
|
16
|
+
- Read `references/api-workflows.md` when the user asks for customer process, interaction flow, or test-case-aligned business coverage. It maps the extracted 224 Listify test cases to concrete interfaces.
|
|
17
|
+
- Read `references/auth-and-calling.md` for Open Platform signature rules, runtime path prefixing, token handling, and call validation.
|
|
18
|
+
- Use `scripts/tmc_signed_request.py` to generate headers, inspect the canonical signing string, or send a signed local/sandbox request.
|
|
19
|
+
|
|
20
|
+
## Source Of Truth
|
|
21
|
+
|
|
22
|
+
- Current English swagger: `open-platform-go/docs/swagger/tmc-api.en-US.json`.
|
|
23
|
+
- Simplified Chinese swagger: `open-platform-go/docs/swagger/tmc-api.json`; use it only when the user specifically needs Chinese wording.
|
|
24
|
+
- Customer-facing short paths omit `/openapi/v1/tmc`, for example `/flight/search`.
|
|
25
|
+
- Signing and actual gateway calls must use the full runtime path, for example `/openapi/v1/tmc/flight/search`.
|
|
26
|
+
- The Listify test-case page is Chinese flow evidence, not an interface specification. Preserve source case IDs for traceability, but explain customer workflows in English unless the user asks for Chinese.
|
|
27
|
+
|
|
28
|
+
## Working Rules
|
|
29
|
+
|
|
30
|
+
Follow this workflow:
|
|
31
|
+
|
|
32
|
+
1. Confirm whether the user needs customer documentation, exact field tables, code changes, signed request debugging, or live/local API calls.
|
|
33
|
+
2. Choose the flow area first: identity/org/employee/traveler, approval, travel policy, flight booking, flight aftersale, hotel booking, hotel aftersale, payment, attachments, certificates, or webhook callbacks.
|
|
34
|
+
3. Use `references/api-workflows.md` to connect the business scenario or test case to concrete endpoint paths.
|
|
35
|
+
4. Use `references/endpoint-details.md` for all payload details; do not invent request or response fields.
|
|
36
|
+
5. For signed calls, use `/openapi/v1/tmc` in the canonical path and include `X-OP-App-Key`, `X-OP-Timestamp`, `X-OP-Nonce`, and `X-OP-Sign`.
|
|
37
|
+
6. Call `/auth/token` with `externalUserId` first, then pass the returned TMC `token` header on business APIs.
|
|
38
|
+
7. Evaluate both HTTP status and response envelope `code`; non-zero business `code` is a business failure even when HTTP is 200.
|
|
39
|
+
|
|
40
|
+
## Common Flow Skeletons
|
|
41
|
+
|
|
42
|
+
- Identity and enterprise context: `/auth/token` -> `/departments` -> `/employees` or `/employees/{userId}`.
|
|
43
|
+
- Organization and staff setup: `/departments` -> `/email-domains` -> `/employees` -> `/employees/{userId}` -> certificate APIs when needed.
|
|
44
|
+
- Traveler setup: `/guests/add` -> `/guests` -> `/guests/{id}` -> guest certificate APIs.
|
|
45
|
+
- Approval setup: `/approval-policies` -> `/approval-policies/page` -> `/approval-policies/{policyId}` -> `/approvals/page` -> `/approvals/{approvalNo}` -> `/approvals/audit` or `/approvals/resubmit`.
|
|
46
|
+
- Travel policy setup: hotel and flight policy CRUD/page/enable APIs -> `/travel-policies/users/{employeeId}`.
|
|
47
|
+
- Flight booking: dictionary APIs -> `/booking-users/list` -> `/flight/search` -> `/flight/cabin` -> `/flight/verify` -> `/flight/order/init` -> `/flight/order/policy-check` -> `/flight/order` -> `/payments/payOrder` -> `/flight/orders/{orderCode}`.
|
|
48
|
+
- Flight aftersale: `/flight/aftersale/capability` -> refund or change remaining-query APIs -> apply/detail/confirm-or-cancel APIs.
|
|
49
|
+
- Hotel booking: `/hotel/locations` -> `/hotel/search/filters` -> `/hotel/search` -> `/hotel/{hotelId}` -> `/hotel/products/search` -> `/hotel/check-booking` -> `/hotel/order/policy-check` -> `/hotel/order` -> `/payments/payOrder` -> `/hotel/orders/{orderCode}`.
|
|
50
|
+
- Hotel early checkout: `/hotel/early-checkout/can-apply` -> `/hotel/early-checkout/apply` -> `/hotel/early-checkout/detail`.
|
|
51
|
+
- Attachments: `/attachments/upload` and `/attachments/download`.
|
|
52
|
+
- Webhooks: read the webhook section in `references/endpoint-details.md`; treat callbacks as customer interaction surface even though they are not part of the 94 request operations.
|
|
53
|
+
|
|
54
|
+
## Output Expectations
|
|
55
|
+
|
|
56
|
+
When answering with this skill:
|
|
57
|
+
|
|
58
|
+
- Use concrete method + path pairs, not only abstract process names.
|
|
59
|
+
- Include field names, types, required flags, and nested structures from `endpoint-details.md` when the user asks for parameters.
|
|
60
|
+
- Mention whether a path is a customer-facing short path or a signed runtime path when auth/signing is relevant.
|
|
61
|
+
- Keep test-case-derived process descriptions tied to exact interfaces.
|
|
62
|
+
- State the source used when swagger, local code, and extracted test cases disagree.
|
|
63
|
+
- Never expose or invent credentials, app secrets, tokens, or tenant data.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "TMC API Integration"
|
|
3
|
+
short_description: "TMC white-label OpenAPI auth, fields, workflows, and debugging"
|
|
4
|
+
default_prompt: "Use $tmc-api to explain or implement TMC OpenAPI authentication, endpoint selection, request/response fields, and customer workflows."
|