@josephyan/qingflow-cli 0.2.0-beta.72 → 0.2.0-beta.74

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/README.md CHANGED
@@ -3,13 +3,13 @@
3
3
  Install:
4
4
 
5
5
  ```bash
6
- npm install @josephyan/qingflow-cli@0.2.0-beta.72
6
+ npm install @josephyan/qingflow-cli@0.2.0-beta.74
7
7
  ```
8
8
 
9
9
  Run:
10
10
 
11
11
  ```bash
12
- npx -y -p @josephyan/qingflow-cli@0.2.0-beta.72 qingflow
12
+ npx -y -p @josephyan/qingflow-cli@0.2.0-beta.74 qingflow
13
13
  ```
14
14
 
15
15
  Environment:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@josephyan/qingflow-cli",
3
- "version": "0.2.0-beta.72",
3
+ "version": "0.2.0-beta.74",
4
4
  "description": "Human-friendly Qingflow command line interface for auth, record operations, import, tasks, and stable builder flows.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qingflow-mcp"
7
- version = "0.2.0b72"
7
+ version = "0.2.0b74"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -88,6 +88,30 @@ class BackendClient:
88
88
  unwrap=unwrap,
89
89
  )
90
90
 
91
+ def public_request_with_headers(
92
+ self,
93
+ method: str,
94
+ base_url: str,
95
+ path: str,
96
+ *,
97
+ params: JSONObject | None = None,
98
+ json_body: JSONValue = None,
99
+ unwrap: bool = True,
100
+ qf_version: str | None = None,
101
+ headers: dict[str, str] | None = None,
102
+ ) -> BackendResponse:
103
+ request_headers = self._base_headers(None, None, qf_version=qf_version)
104
+ if headers:
105
+ request_headers.update({key: value for key, value in headers.items() if value is not None})
106
+ return self._request_with_meta(
107
+ method,
108
+ self._build_url(base_url, path),
109
+ params=params,
110
+ json_body=json_body,
111
+ headers=request_headers,
112
+ unwrap=unwrap,
113
+ )
114
+
91
115
  def request(
92
116
  self,
93
117
  method: str,
@@ -131,6 +155,54 @@ class BackendClient:
131
155
  unwrap=unwrap,
132
156
  )
133
157
 
158
+ def stream_request(
159
+ self,
160
+ method: str,
161
+ context: BackendRequestContext,
162
+ path: str,
163
+ *,
164
+ params: JSONObject | None = None,
165
+ json_body: JSONValue = None,
166
+ headers: dict[str, str] | None = None,
167
+ ) -> list[str]:
168
+ request_headers = self._base_headers(
169
+ context.token,
170
+ context.ws_id,
171
+ context.qf_request_id,
172
+ qf_version=context.qf_version,
173
+ )
174
+ if headers:
175
+ request_headers.update({key: value for key, value in headers.items() if value is not None})
176
+ return self._stream_lines(
177
+ method,
178
+ self._build_url(context.base_url, path),
179
+ params=params,
180
+ json_body=json_body,
181
+ headers=request_headers,
182
+ )
183
+
184
+ def public_stream_request(
185
+ self,
186
+ method: str,
187
+ base_url: str,
188
+ path: str,
189
+ *,
190
+ params: JSONObject | None = None,
191
+ json_body: JSONValue = None,
192
+ headers: dict[str, str] | None = None,
193
+ qf_version: str | None = None,
194
+ ) -> list[str]:
195
+ request_headers = self._base_headers(None, None, qf_version=qf_version)
196
+ if headers:
197
+ request_headers.update({key: value for key, value in headers.items() if value is not None})
198
+ return self._stream_lines(
199
+ method,
200
+ self._build_url(base_url, path),
201
+ params=params,
202
+ json_body=json_body,
203
+ headers=request_headers,
204
+ )
205
+
134
206
  def describe_route(self, context: BackendRequestContext) -> JSONObject:
135
207
  qf_version, source = self._resolve_qf_version(context.qf_version)
136
208
  if context.qf_version is not None and context.qf_version_source:
@@ -430,6 +502,36 @@ class BackendClient:
430
502
  assert last_error is not None
431
503
  raise last_error
432
504
 
505
+ def _stream_lines(
506
+ self,
507
+ method: str,
508
+ url: str,
509
+ *,
510
+ params: JSONObject | None,
511
+ json_body: JSONValue,
512
+ headers: dict[str, str],
513
+ ) -> list[str]:
514
+ try:
515
+ with self._client.stream(method.upper(), url, params=params, json=json_body, headers=headers) as response:
516
+ request_id = headers["Qf-Request-Id"]
517
+ if response.status_code >= 400:
518
+ raw_bytes = response.read()
519
+ payload: JSONValue
520
+ try:
521
+ payload = response.json()
522
+ except ValueError:
523
+ payload = raw_bytes.decode("utf-8", errors="replace")
524
+ raise QingflowApiError(
525
+ category="http",
526
+ message=self._extract_message(payload) or f"HTTP {response.status_code}",
527
+ backend_code=self._extract_code(payload),
528
+ request_id=request_id,
529
+ http_status=response.status_code,
530
+ )
531
+ return [line for line in response.iter_lines()]
532
+ except httpx.RequestError as exc:
533
+ raise QingflowApiError(category="network", message=str(exc), request_id=headers["Qf-Request-Id"])
534
+
433
535
  def _parse_response(self, response: httpx.Response, request_id: str, *, unwrap: bool) -> JSONValue:
434
536
  payload: JSONValue
435
537
  try:
@@ -187,7 +187,8 @@ class FlowConditionRulePatch(StrictModel):
187
187
  return value
188
188
  payload = dict(value)
189
189
  if "value" in payload and "values" not in payload:
190
- payload["values"] = [payload.pop("value")]
190
+ raw_value = payload.pop("value")
191
+ payload["values"] = list(raw_value) if isinstance(raw_value, list) else [raw_value]
191
192
  raw_operator = payload.get("operator", payload.get("op"))
192
193
  if isinstance(raw_operator, str):
193
194
  normalized = raw_operator.strip().lower()
@@ -238,7 +239,8 @@ class ViewFilterRulePatch(StrictModel):
238
239
  return value
239
240
  payload = dict(value)
240
241
  if "value" in payload and "values" not in payload:
241
- payload["values"] = [payload.pop("value")]
242
+ raw_value = payload.pop("value")
243
+ payload["values"] = list(raw_value) if isinstance(raw_value, list) else [raw_value]
242
244
  raw_operator = payload.get("operator", payload.get("op"))
243
245
  if isinstance(raw_operator, str):
244
246
  normalized = raw_operator.strip().lower()
@@ -1092,7 +1094,8 @@ class ChartFilterRulePatch(StrictModel):
1092
1094
  return value
1093
1095
  payload = dict(value)
1094
1096
  if "value" in payload and "values" not in payload:
1095
- payload["values"] = [payload.pop("value")]
1097
+ raw_value = payload.pop("value")
1098
+ payload["values"] = list(raw_value) if isinstance(raw_value, list) else [raw_value]
1096
1099
  raw_operator = payload.get("operator", payload.get("op"))
1097
1100
  if isinstance(raw_operator, str):
1098
1101
  normalized = raw_operator.strip().lower()