@mongoui/skills 0.1.8 → 0.1.9

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.
@@ -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())
@@ -1,8 +1,8 @@
1
- ---
2
- name: tmc-whitelabel
3
- description: Placeholder skill for TMC Whitelabel.
4
- ---
5
-
6
- # TMC Whitelabel
7
-
8
- 内容待补充。
1
+ ---
2
+ name: tmc-whitelabel
3
+ description: Placeholder skill for TMC Whitelabel.
4
+ ---
5
+
6
+ # TMC Whitelabel
7
+
8
+ 内容待补充。