@qingflow-tech/qingflow-cli 1.1.41 → 1.1.43

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.
@@ -5,7 +5,8 @@ from contextvars import ContextVar
5
5
  from typing import Any, Callable, TypeVar
6
6
 
7
7
  from ..backend_client import BackendRequestContext, BackendClient
8
- from ..cloud_context import current_cloud_context
8
+ from ..auth_provider import CliSessionFacade
9
+ from ..cloud_context import current_call_platform, current_cloud_context
9
10
  from ..config import (
10
11
  get_credit_meter_enabled,
11
12
  get_credit_usage_base_url,
@@ -21,7 +22,7 @@ _RUN_DEPTH: ContextVar[int] = ContextVar("qingflow_mcp_tool_run_depth", default=
21
22
 
22
23
 
23
24
  class ToolBase:
24
- def __init__(self, sessions: SessionStore, backend: BackendClient) -> None:
25
+ def __init__(self, sessions: SessionStore | CliSessionFacade, backend: BackendClient) -> None:
25
26
  self.sessions = sessions
26
27
  self.backend = backend
27
28
 
@@ -30,7 +31,8 @@ class ToolBase:
30
31
  if cloud_context is not None:
31
32
  return cloud_context.as_tool_context(profile)
32
33
 
33
- self._ensure_environment_credential_matches(profile)
34
+ if not self._uses_oauth(profile):
35
+ self._ensure_environment_credential_matches(profile)
34
36
  session_profile = self.sessions.get_profile(profile)
35
37
  if session_profile is None:
36
38
  raise QingflowApiError.auth_required(profile)
@@ -83,8 +85,18 @@ class ToolBase:
83
85
  return func(refreshed_profile, refreshed_context)
84
86
  except QingflowApiError as refreshed_error:
85
87
  self._handle_error(profile, refreshed_error)
88
+ if error.looks_like_invalid_token() and self._refresh_oauth_session(profile):
89
+ try:
90
+ refreshed_profile, _, refreshed_context = self._require_context(
91
+ profile,
92
+ require_workspace=require_workspace,
93
+ )
94
+ return func(refreshed_profile, refreshed_context)
95
+ except QingflowApiError as refreshed_error:
96
+ self._handle_error(profile, refreshed_error)
86
97
  if (
87
98
  error.looks_like_invalid_token()
99
+ and not self._uses_oauth(profile)
88
100
  and session_profile is not None
89
101
  and backend_session is not None
90
102
  and self._refresh_session_from_credential(
@@ -121,6 +133,19 @@ class ToolBase:
121
133
  http_status=error.http_status,
122
134
  )
123
135
  raise_tool_error(refreshed_error)
136
+ if self._uses_oauth(profile):
137
+ oauth_error = QingflowApiError(
138
+ category="auth",
139
+ message=(
140
+ f"OAuth session for profile '{profile}' has expired and could not be refreshed. "
141
+ "Run auth login --method oauth again."
142
+ ),
143
+ backend_code=error.backend_code,
144
+ request_id=error.request_id,
145
+ http_status=error.http_status,
146
+ details={"reason": "oauth_relogin_required"},
147
+ )
148
+ raise_tool_error(oauth_error)
124
149
  self.sessions.invalidate(profile)
125
150
  error = QingflowApiError(
126
151
  category="auth",
@@ -142,6 +167,20 @@ class ToolBase:
142
167
  current_cloud_context.set(refreshed_context)
143
168
  return True
144
169
 
170
+ def _refresh_oauth_session(self, profile: str) -> bool:
171
+ if not self._uses_oauth(profile):
172
+ return False
173
+ assert isinstance(self.sessions, CliSessionFacade)
174
+ try:
175
+ return self.sessions.refresh_oauth_session(profile, force=True)
176
+ except QingflowApiError as error:
177
+ if (error.details or {}).get("reason") == "oauth_relogin_required":
178
+ raise
179
+ return False
180
+
181
+ def _uses_oauth(self, profile: str) -> bool:
182
+ return isinstance(self.sessions, CliSessionFacade) and self.sessions.uses_oauth(profile)
183
+
145
184
  def _refresh_session_from_credential(
146
185
  self,
147
186
  profile: str,
@@ -323,6 +362,7 @@ class ToolBase:
323
362
  "scene": "MCP",
324
363
  "aiBiz": "MCP",
325
364
  "extraInfo": tool_name,
365
+ "callPlatform": current_call_platform.get(),
326
366
  },
327
367
  )
328
368
 
@@ -535,6 +535,10 @@ class TaskContextTools(ToolBase):
535
535
  task_context=task_context,
536
536
  fields=field_updates,
537
537
  )
538
+ elif normalized_action == "transfer" and body.get("answers") is None:
539
+ record = task_context.get("record") if isinstance(task_context.get("record"), dict) else {}
540
+ current_answers = record.get("answers") if isinstance(record.get("answers"), list) else []
541
+ prepared_fields = {"merged_answers": current_answers}
538
542
  before_apply_status = ((task_context.get("record") or {}).get("apply_status"))
539
543
  runtime_baseline = None
540
544
  if normalized_action != "save_only":
@@ -704,6 +708,8 @@ class TaskContextTools(ToolBase):
704
708
  action_payload["auditFeedback"] = audit_feedback
705
709
  if merged_answers is not None:
706
710
  action_payload["answers"] = merged_answers
711
+ elif payload.get("answers") is not None:
712
+ action_payload["answers"] = payload["answers"]
707
713
  return self._approval_tools.record_transfer(
708
714
  profile=profile,
709
715
  app_key=app_key,