@mongoui/skills 0.1.4 → 0.1.5

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.
@@ -38,11 +38,33 @@ export async function runInstall(productName, options) {
38
38
  }
39
39
  const installTarget = resolveInstallTarget(options.tool, options.targetDir);
40
40
  const sourceSkillFile = path.join(packageRoot, product.skillFile);
41
+ const sourceSkillDir = product.skillDir ? path.join(packageRoot, product.skillDir) : undefined;
41
42
  const targetSkillFile = resolveTargetSkillFile(product.name, installTarget);
43
+ const targetSkillDir = resolveTargetSkillDir(sourceSkillDir, installTarget);
44
+ await fs.mkdir(installTarget.targetDir, { recursive: true });
45
+ if (sourceSkillDir && shouldInstallAsDirectory(installTarget.tool)) {
46
+ await fs.access(sourceSkillDir);
47
+ await fs.rm(targetSkillDir, { recursive: true, force: true });
48
+ await fs.rm(targetSkillFile, { force: true });
49
+ await copyDirectory(sourceSkillDir, targetSkillDir);
50
+ console.log("Installed skill:");
51
+ console.log("- command: install");
52
+ console.log(`- product: ${product.name}`);
53
+ console.log(`- display name: ${product.displayName}`);
54
+ console.log(`- tool: ${installTarget.tool}`);
55
+ console.log(`- source skill dir: ${sourceSkillDir}`);
56
+ console.log(`- source skill file: ${sourceSkillFile}`);
57
+ console.log(`- target dir: ${installTarget.targetDir}`);
58
+ console.log(`- target source: ${installTarget.source}`);
59
+ console.log(`- target skill dir: ${targetSkillDir}`);
60
+ console.log(`- target skill file: ${path.join(targetSkillDir, "SKILL.md")}`);
61
+ console.log("- target format: directory");
62
+ console.log("- status: installed");
63
+ return;
64
+ }
42
65
  await fs.access(sourceSkillFile);
43
66
  const sourceSkillContent = await fs.readFile(sourceSkillFile, "utf8");
44
67
  const targetSkillContent = renderInstalledContent(installTarget.tool, product.name, product.displayName, sourceSkillContent);
45
- await fs.mkdir(installTarget.targetDir, { recursive: true });
46
68
  await fs.writeFile(targetSkillFile, targetSkillContent, "utf8");
47
69
  console.log("Installed skill:");
48
70
  console.log("- command: install");
@@ -62,7 +84,9 @@ export async function runUninstall(productName, options) {
62
84
  throw new Error(`Missing required option: "--tool <${supportedToolsText()}>".`);
63
85
  }
64
86
  const installTarget = resolveInstallTarget(options.tool, options.targetDir);
87
+ const targetSkillDir = resolveTargetSkillDir(undefined, installTarget, product);
65
88
  const targetSkillFile = resolveTargetSkillFile(product.name, installTarget);
89
+ await fs.rm(targetSkillDir, { recursive: true, force: true });
66
90
  await fs.rm(targetSkillFile, { force: true });
67
91
  console.log("Uninstalled skill:");
68
92
  console.log("- command: uninstall");
@@ -71,6 +95,7 @@ export async function runUninstall(productName, options) {
71
95
  console.log(`- tool: ${installTarget.tool}`);
72
96
  console.log(`- target dir: ${installTarget.targetDir}`);
73
97
  console.log(`- target source: ${installTarget.source}`);
98
+ console.log(`- target skill dir: ${targetSkillDir}`);
74
99
  console.log(`- target skill file: ${targetSkillFile}`);
75
100
  console.log(`- target format: ${installTarget.fileExtension}`);
76
101
  console.log("- status: removed");
@@ -88,3 +113,25 @@ function requireProduct(productName) {
88
113
  function resolveTargetSkillFile(productName, installTarget) {
89
114
  return path.join(installTarget.targetDir, installTarget.fileName ?? `${productName}.${installTarget.fileExtension}`);
90
115
  }
116
+ function resolveTargetSkillDir(sourceSkillDir, installTarget, product) {
117
+ const dirName = sourceSkillDir
118
+ ? path.basename(sourceSkillDir)
119
+ : `${product?.name ?? "skill"}`;
120
+ return path.join(installTarget.targetDir, dirName);
121
+ }
122
+ function shouldInstallAsDirectory(tool) {
123
+ return tool === "codex" || tool === "kiro";
124
+ }
125
+ async function copyDirectory(sourceDir, targetDir) {
126
+ await fs.mkdir(targetDir, { recursive: true });
127
+ const entries = await fs.readdir(sourceDir, { withFileTypes: true });
128
+ for (const entry of entries) {
129
+ const sourcePath = path.join(sourceDir, entry.name);
130
+ const targetPath = path.join(targetDir, entry.name);
131
+ if (entry.isDirectory()) {
132
+ await copyDirectory(sourcePath, targetPath);
133
+ continue;
134
+ }
135
+ await fs.copyFile(sourcePath, targetPath);
136
+ }
137
+ }
@@ -2,12 +2,14 @@ const PRODUCTS = {
2
2
  "glink-hotel-api": {
3
3
  name: "glink-hotel-api",
4
4
  displayName: "Glink Hotel API",
5
- skillFile: "skills/glink-hotel-api/SKILL.md",
5
+ skillFile: "skills/glink-hotel-api/glink-openapi-integration/SKILL.md",
6
+ skillDir: "skills/glink-hotel-api/glink-openapi-integration",
6
7
  },
7
8
  "flink-flight-api": {
8
9
  name: "flink-flight-api",
9
10
  displayName: "Flink Flight API",
10
- skillFile: "skills/flink-flight-api/SKILL.md",
11
+ skillFile: "skills/flink-flight-api/flink-openapi-integration/SKILL.md",
12
+ skillDir: "skills/flink-flight-api/flink-openapi-integration",
11
13
  },
12
14
  "tmc-whitelabel": {
13
15
  name: "tmc-whitelabel",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoui/skills",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "CLI for installing product skills into AI tools",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: flink-openapi-integration
3
+ description: Integrate with the Flink flight OpenAPI exposed through the platform runtime gateway. Use when Codex needs to read or implement against `docs/swagger/flink-api.json`, choose the correct Flink ticket endpoint, generate `X-OP-*` signatures, explain sandbox or production credential acquisition and reset, or drive search, booking, change, refund, airport, airline, nationality, and fields workflows.
4
+ ---
5
+
6
+ # Flink OpenAPI Integration
7
+
8
+ ## Quick Start
9
+
10
+ Use this skill when the task is "call Flink through the open platform" rather than "call Flink supplier APIs directly".
11
+
12
+ Start with the smallest reference that answers the request:
13
+
14
+ - Read `references/auth-and-credentials.md` for signing, headers, key acquisition, reset, or auth troubleshooting.
15
+ - Read `references/api-workflows.md` for business sequencing such as search to booking, change, or refund.
16
+ - Read `references/endpoint-index.md` for exact method, path, required fields, and follow-up identifiers.
17
+ - Use `scripts/flink_signed_request.py` to generate headers, inspect the canonical string, or send a signed request.
18
+
19
+ ## Working Rules
20
+
21
+ Follow this workflow:
22
+
23
+ 1. Confirm the environment: `sandbox` and `production` credentials are separate.
24
+ 2. Confirm the public path under `/openapi/v1/flink/*` and the required `lang` path parameter when present.
25
+ 3. Build the minimum valid request body from `references/endpoint-index.md`.
26
+ 4. Sign the exact request path, sorted query string, and raw body bytes with the platform secret.
27
+ 5. Evaluate both HTTP status and response envelope `code`.
28
+
29
+ Do not violate these rules:
30
+
31
+ - Always send `X-OP-App-Key`, `X-OP-Timestamp`, `X-OP-Nonce`, and `X-OP-Sign`.
32
+ - Never send supplier-side `appid`, `timestamp`, or `sign`; the platform injects them downstream.
33
+ - Treat `code=SUCCESS` as success. Treat `code=SUPPLIER_BIZ_ERROR` as a business failure even if HTTP status is `200`.
34
+ - Keep timestamp skew within `+-5` minutes.
35
+ - Reuse of the same nonce under the same app key can trigger `NONCE_REPLAY`.
36
+ - Preserve bridge identifiers across steps:
37
+ - `priceKey` bridges search or cabin to verify and order creation.
38
+ - `orderNo` bridges order detail, cancel, change, refund, and fields lookup.
39
+ - `changeOrderNo` and `refundOrderNo` drive their detail or cancel or confirm calls.
40
+ - For `GET /openapi/v1/flink/fields`, send at least one of `priceKey` or `orderNo`.
41
+
42
+ ## Typical Requests
43
+
44
+ Use these patterns by default:
45
+
46
+ - Search and booking: `flight.search` -> `flight.cabin` if needed -> `flight.verify` -> `flight.order.create` -> `flight.order.pay` -> `flight.order.detail`
47
+ - Change: `flight.order.detail` -> `flight.change.search` -> `flight.change.apply` -> `flight.order.pay` with `type=1` if payment is required -> `flight.change.detail`
48
+ - Refund: `flight.order.detail` -> `flight.refund.apply` -> `flight.refund.detail` -> `flight.refund.confirm`
49
+ - Base data: `airports.list`, `airlines.list`, `airports.search`, `nationality.list`, `fields`
50
+
51
+ ## Output Expectations
52
+
53
+ When answering or coding with this skill:
54
+
55
+ - Prefer concrete request examples over abstract summaries.
56
+ - State which environment the credential belongs to.
57
+ - State which identifier from the previous step is needed next.
58
+ - Call out auth failures with the likely root cause and the exact header or field to inspect.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Flink OpenAPI 接入"
3
+ short_description: "帮助完成 Flink 机票开放接口鉴权、凭证获取和业务调用"
4
+ default_prompt: "Use $flink-openapi-integration to integrate with the Flink flight open API, including auth, credentials, and endpoint selection."
@@ -0,0 +1,104 @@
1
+ # Flink API Workflows
2
+
3
+ ## Search And Booking
4
+
5
+ Use this sequence for a normal booking flow:
6
+
7
+ 1. `flink.flight.search`
8
+ - Search journeys and offer combinations.
9
+ - Save returned `priceKey` values for the next step.
10
+ 2. `flink.flight.cabin`
11
+ - Use when a selected offer needs more fare or cabin quotes.
12
+ - Input is `priceKey`.
13
+ 3. `flink.flight.verify`
14
+ - Validate the selected offer before order creation.
15
+ - Input is `priceKey`.
16
+ 4. `flink.flight.order.create`
17
+ - Create the pre-booking order with contact and passenger data.
18
+ - Input still depends on `priceKey`.
19
+ 5. `flink.flight.order.pay`
20
+ - Pay the standard order with `type=0`.
21
+ 6. `flink.flight.order.detail`
22
+ - Read status, ticketing progress, and later change or refund context.
23
+ 7. `flink.flight.order.cancel`
24
+ - Use only for cancellable standard orders.
25
+
26
+ Keep these state tokens:
27
+
28
+ - `priceKey`: search or cabin -> verify or order creation
29
+ - `orderNo`: order creation or detail -> pay, cancel, change, refund, fields
30
+
31
+ ## Change Flow
32
+
33
+ Use this sequence for voluntary or involuntary changes:
34
+
35
+ 1. `flink.flight.order.detail`
36
+ - Confirm order status and segment context.
37
+ 2. `flink.flight.change.search`
38
+ - Search eligible changed flights for an existing order and passenger or segment scope.
39
+ 3. `flink.flight.change.apply`
40
+ - Submit the actual change request with `priceKey`, passenger scope, segment scope, reason, and contact info.
41
+ 4. `flink.flight.order.pay`
42
+ - If the change order requires payment, pay with `type=1`.
43
+ 5. `flink.flight.change.detail`
44
+ - Track the change request by `changeOrderNo`.
45
+ 6. `flink.flight.change.cancel`
46
+ - Cancel a pending change request by `changeOrderNo`.
47
+
48
+ Bridge identifiers:
49
+
50
+ - `orderNo` drives change search and apply.
51
+ - `priceKey` from change search is used in change apply.
52
+ - `changeOrderNo` drives detail and cancel.
53
+
54
+ ## Refund Flow
55
+
56
+ Use this sequence for refunds:
57
+
58
+ 1. `flink.flight.order.detail`
59
+ - Confirm the refundable order, passenger, and segment scope.
60
+ 2. `flink.flight.refund.apply`
61
+ - Submit the refund request with passenger scope, segment scope, contact data, and reason.
62
+ 3. `flink.flight.refund.detail`
63
+ - Track the refund request by `refundOrderNo`.
64
+ 4. `flink.flight.refund.confirm`
65
+ - Confirm or cancel the refund request.
66
+
67
+ Bridge identifiers:
68
+
69
+ - `orderNo` drives refund apply.
70
+ - `refundOrderNo` drives detail and confirm.
71
+
72
+ ## Base Data And Helper Calls
73
+
74
+ Use these APIs outside the booking lifecycle:
75
+
76
+ - `flink.airports.list`
77
+ - Paged airport list with optional keyword filtering.
78
+ - `flink.airlines.list`
79
+ - Paged airline list with optional keyword and code filtering.
80
+ - `flink.airports.search`
81
+ - Lightweight airport search by keyword.
82
+ - `flink.nationality.list`
83
+ - Nationality reference data.
84
+ - `flink.fields`
85
+ - Field translations and baggage or travel notice helpers.
86
+ - Provide either `priceKey` or `orderNo`.
87
+
88
+ ## Language Parameter
89
+
90
+ All Flink routes except `flink.fields` require `{lang}` in the path.
91
+
92
+ Common values:
93
+
94
+ - `en`
95
+ - `zh_CN`
96
+ - `zh_TW`
97
+
98
+ Use the same `lang` across related calls unless there is a clear reason to switch.
99
+
100
+ ## Business Handling Rules
101
+
102
+ - Do not treat HTTP `200` as enough; inspect `code`.
103
+ - Do not rebuild supplier headers; the runtime gateway handles supplier authentication.
104
+ - Preserve the original upstream identifiers from earlier responses instead of recomputing them.
@@ -0,0 +1,136 @@
1
+ # Flink Auth And Credentials
2
+
3
+ ## Required Headers
4
+
5
+ Send these headers on every Flink open-platform request:
6
+
7
+ - `X-OP-App-Key`
8
+ - `X-OP-Timestamp`
9
+ - `X-OP-Nonce`
10
+ - `X-OP-Sign`
11
+
12
+ Optional headers that can help tracing or idempotency:
13
+
14
+ - `X-OP-Client-Request-Id`
15
+ - `X-Trace-Id`
16
+ - `X-Idempotency-Key`
17
+
18
+ `X-OP-App-Key` is the issued `open_app_id`.
19
+
20
+ ## Signature Algorithm
21
+
22
+ Build the canonical string in this exact order, separated by newline characters:
23
+
24
+ ```text
25
+ METHOD
26
+ PATH
27
+ CANONICAL_QUERY
28
+ BODY_SHA256
29
+ TIMESTAMP
30
+ NONCE
31
+ APP_KEY
32
+ ```
33
+
34
+ Rules:
35
+
36
+ - Uppercase `METHOD`.
37
+ - Use the exact public request path, for example `/openapi/v1/flink/zh_CN/flight/search`.
38
+ - Sort query parameters by key and then value before joining them as `k=v&k2=v2`.
39
+ - Hash the raw request body bytes with SHA256 and use the lowercase hex digest.
40
+ - Use the request timestamp exactly as sent in `X-OP-Timestamp`.
41
+ - Use the request nonce exactly as sent in `X-OP-Nonce`.
42
+ - Use the same `open_app_id` that is sent in `X-OP-App-Key`.
43
+ - Sign the canonical string with the plain `open_app_secret` using HMAC-SHA256, then output lowercase hex.
44
+
45
+ Notes:
46
+
47
+ - Timestamp may be Unix seconds, Unix milliseconds, or RFC3339.
48
+ - The platform validates timestamp drift within roughly `+-5` minutes.
49
+ - Nonce must be unique for the same app key in a short time window.
50
+ - For empty bodies, hash the empty byte string.
51
+
52
+ Use `scripts/flink_signed_request.py` when the task needs a deterministic implementation or a quick smoke test.
53
+
54
+ ## Credential Acquisition
55
+
56
+ Treat credentials as environment-specific.
57
+
58
+ Sandbox:
59
+
60
+ 1. The developer applies for app access.
61
+ 2. The operator activates the app access with `env_type=sandbox`.
62
+ 3. The platform records sandbox supplier mapping and issues a sandbox `open_app_id/open_app_secret`.
63
+ 4. The app access becomes `sandbox_active`.
64
+
65
+ Production:
66
+
67
+ 1. The developer submits production access after sandbox work is complete.
68
+ 2. The operator approves production with `env_type=production`.
69
+ 3. The platform issues a production `open_app_id/open_app_secret`.
70
+ 4. The production credential becomes usable only after the app access reaches `production_active`.
71
+
72
+ Important constraints:
73
+
74
+ - Sandbox and production credentials are separate and not interchangeable.
75
+ - Developers can see platform credentials only.
76
+ - Developers cannot see supplier-side secrets.
77
+
78
+ ## How To View Or Reset Keys
79
+
80
+ Developer portal APIs:
81
+
82
+ - `GET /api/portal/apps/{app_id}/credentials`
83
+ - Returns the credential list for the app.
84
+ - Includes `open_app_id`, `provider_code`, `env_type`, `open_secret_version`, and masked secret data.
85
+ - Does not return the plain secret in normal list queries.
86
+ - `POST /api/portal/apps/{app_id}/credentials/reset`
87
+ - Resets the platform secret and returns `plain_open_secret`.
88
+ - If the app has more than one credential mapping, pass both `provider_code` and `env_type`.
89
+
90
+ Response fields that matter:
91
+
92
+ - `open_app_id`: the App Key to place into `X-OP-App-Key`
93
+ - `open_secret_masked`: masked secret for display only
94
+ - `open_secret_version`: useful for rotation tracking
95
+ - `plain_open_secret`: returned only on first issuance or reset
96
+
97
+ Practical guidance:
98
+
99
+ - If a developer says "I can only see a masked secret", instruct them to use the reset endpoint or the corresponding portal UI action.
100
+ - If the task is only to call sandbox APIs, do not assume production credentials work.
101
+ - If the app has both sandbox and production mappings, always name the target `env_type` explicitly.
102
+
103
+ ## Common Auth Failures
104
+
105
+ - `APP_KEY_INVALID`
106
+ - Wrong `open_app_id`, wrong environment, or the credential does not exist.
107
+ - `SIGN_ERROR`
108
+ - Wrong secret, wrong path, wrong query ordering, wrong body hash, or newline formatting mismatch.
109
+ - `TIMESTAMP_EXPIRED`
110
+ - Clock skew exceeds the allowed window.
111
+ - `NONCE_REPLAY`
112
+ - Nonce was reused under the same app key.
113
+ - `PRODUCT_FORBIDDEN`
114
+ - The app is not enabled for the product, the mapping is not active, or an IP whitelist rule failed.
115
+
116
+ ## Response Contract
117
+
118
+ The platform wraps business responses in a common envelope:
119
+
120
+ ```json
121
+ {
122
+ "request_id": "req_xxx",
123
+ "trace_id": "trc_xxx",
124
+ "downstream_request_id": "optional",
125
+ "code": "SUCCESS",
126
+ "message": "ok",
127
+ "data": {}
128
+ }
129
+ ```
130
+
131
+ Interpretation:
132
+
133
+ - `request_id` and `trace_id` are platform-side tracing keys.
134
+ - `downstream_request_id` is the supplier request identifier when available.
135
+ - `code=SUCCESS` means the business call succeeded.
136
+ - `code=SUPPLIER_BIZ_ERROR` means the supplier returned a business error and can still arrive with HTTP `200`.
@@ -0,0 +1,92 @@
1
+ # Flink Endpoint Index
2
+
3
+ All routes are public gateway routes. Sign the public path, not the downstream supplier path.
4
+
5
+ ## Flight Search
6
+
7
+ - `flink.flight.search`
8
+ - `POST /openapi/v1/flink/{lang}/flight/search`
9
+ - Required body fields: `cabinClass`, `tripType`, `journeys`, `adultNum`
10
+ - Common optional fields: `childNum`, `infantNum`, `airline`, `transferNumber`, `journeyId`, `corporate`
11
+ - Main output to preserve: `priceKey`
12
+ - `flink.flight.cabin`
13
+ - `POST /openapi/v1/flink/{lang}/flight/cabin`
14
+ - Required body fields: `priceKey`
15
+ - Use to expand or refine fare options for a selected result
16
+ - `flink.flight.verify`
17
+ - `POST /openapi/v1/flink/{lang}/flight/verify`
18
+ - Required body fields: `priceKey`
19
+ - Use before order creation
20
+
21
+ ## Order Management
22
+
23
+ - `flink.flight.order.create`
24
+ - `POST /openapi/v1/flink/{lang}/flight/order/create`
25
+ - Required body fields: `priceKey`, `contactName`, `contactRegion`, `contactPhone`, `contactEmail`, `passenger`
26
+ - Use after verify or directly after search or cabin when allowed by the business flow
27
+ - `flink.flight.order.pay`
28
+ - `POST /openapi/v1/flink/{lang}/flight/order/pay`
29
+ - Required body fields: `orderNo`, `amount`, `type`
30
+ - `type=0` for standard order, `type=1` for change order
31
+ - `flink.flight.order.detail`
32
+ - `POST /openapi/v1/flink/{lang}/flight/order/detail`
33
+ - Required body fields: `orderNo`
34
+ - `flink.flight.order.cancel`
35
+ - `POST /openapi/v1/flink/{lang}/flight/order/cancel`
36
+ - Required body fields: `orderNo`
37
+
38
+ ## Change
39
+
40
+ - `flink.flight.change.search`
41
+ - `POST /openapi/v1/flink/{lang}/flight/change/search`
42
+ - Required body fields: `orderNo`, `cabinClass`, `date`, `passenger`, `segmentId`
43
+ - `flink.flight.change.apply`
44
+ - `POST /openapi/v1/flink/{lang}/flight/change/apply`
45
+ - Required body fields: `orderNo`, `passenger`, `segmentId`, `priceKey`, `changeOrderType`, `changeType`, `contactName`, `contactPhone`, `contactEmail`, `changeReasonType`, `changeReason`
46
+ - `fileList` is required by schema when attachments are involved in involuntary changes
47
+ - `flink.flight.change.cancel`
48
+ - `POST /openapi/v1/flink/{lang}/flight/change/cancel`
49
+ - Required body fields: `changeOrderNo`
50
+ - `flink.flight.change.detail`
51
+ - `POST /openapi/v1/flink/{lang}/flight/change/detail`
52
+ - Required body fields: `changeOrderNo`
53
+
54
+ ## Refund
55
+
56
+ - `flink.flight.refund.apply`
57
+ - `POST /openapi/v1/flink/{lang}/flight/refund/apply`
58
+ - Required body fields: `orderNo`, `orderType`, `passenger`, `segmentId`, `refundType`, `reasonType`, `reason`, `fileList`, `contactName`, `contactRegion`, `contactPhone`, `contactEmail`
59
+ - `flink.flight.refund.detail`
60
+ - `POST /openapi/v1/flink/{lang}/flight/refund/detail`
61
+ - Required body fields: `refundOrderNo`
62
+ - `flink.flight.refund.confirm`
63
+ - `POST /openapi/v1/flink/{lang}/flight/refund/confirm`
64
+ - Required body fields: `refundOrderNo`, `confirm`
65
+
66
+ ## Basic Data
67
+
68
+ - `flink.airports.list`
69
+ - `POST /openapi/v1/flink/{lang}/airports/list`
70
+ - Required body fields: `page`, `pageSize`
71
+ - Common optional fields: `keyword`
72
+ - `flink.airlines.list`
73
+ - `POST /openapi/v1/flink/{lang}/airlines/list`
74
+ - Required body fields: `page`, `pageSize`
75
+ - Common optional fields: `keyword`, `code`
76
+ - `flink.airports.search`
77
+ - `POST /openapi/v1/flink/{lang}/airports/search`
78
+ - Common body field: `keyword`
79
+ - `flink.nationality.list`
80
+ - `POST /openapi/v1/flink/{lang}/nationality/list`
81
+ - No required body fields in the current schema
82
+ - `flink.fields`
83
+ - `GET /openapi/v1/flink/fields`
84
+ - Query parameters: `priceKey` optional, `orderNo` optional
85
+ - Send at least one of `priceKey` or `orderNo`
86
+
87
+ ## Global Notes
88
+
89
+ - `{lang}` is required on every route except `flink.fields`.
90
+ - Platform error responses can return `401`, `403`, `404`, `500`, `502`, or `504`.
91
+ - Common auth-related `401` codes are `APP_KEY_INVALID`, `SIGN_ERROR`, `TIMESTAMP_EXPIRED`, and `NONCE_REPLAY`.
92
+ - Common `403` cause is `PRODUCT_FORBIDDEN`.