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

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.74
6
+ npm install @josephyan/qingflow-cli@0.2.0-beta.76
7
7
  ```
8
8
 
9
9
  Run:
10
10
 
11
11
  ```bash
12
- npx -y -p @josephyan/qingflow-cli@0.2.0-beta.74 qingflow
12
+ npx -y -p @josephyan/qingflow-cli@0.2.0-beta.76 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.74",
3
+ "version": "0.2.0-beta.76",
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.0b74"
7
+ version = "0.2.0b76"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -35,6 +35,11 @@ class PublicRelationMode(str, Enum):
35
35
  multiple = "multiple"
36
36
 
37
37
 
38
+ class PublicDepartmentScopeMode(str, Enum):
39
+ all = "all"
40
+ custom = "custom"
41
+
42
+
38
43
  FIELD_TYPE_ALIASES: dict[str, PublicFieldType] = {
39
44
  "textarea": PublicFieldType.long_text,
40
45
  "amount": PublicFieldType.amount,
@@ -291,6 +296,56 @@ class FieldSelector(StrictModel):
291
296
  return self
292
297
 
293
298
 
299
+ class DepartmentSelectorPatch(StrictModel):
300
+ dept_id: int | None = Field(default=None, validation_alias=AliasChoices("dept_id", "deptId", "id"))
301
+ dept_name: str | None = Field(default=None, validation_alias=AliasChoices("dept_name", "deptName", "name", "value"))
302
+
303
+ @model_validator(mode="after")
304
+ def validate_selector(self) -> "DepartmentSelectorPatch":
305
+ if self.dept_id is None and not str(self.dept_name or "").strip():
306
+ raise ValueError("department selector must include dept_id or dept_name")
307
+ return self
308
+
309
+
310
+ class DepartmentScopePatch(StrictModel):
311
+ mode: PublicDepartmentScopeMode | None = None
312
+ departments: list[DepartmentSelectorPatch] = Field(
313
+ default_factory=list,
314
+ validation_alias=AliasChoices("departments", "departs", "depart"),
315
+ )
316
+ include_sub_departs: bool | None = Field(
317
+ default=None,
318
+ validation_alias=AliasChoices("include_sub_departs", "includeSubDeparts"),
319
+ )
320
+
321
+ @model_validator(mode="before")
322
+ @classmethod
323
+ def normalize_aliases(cls, value: Any) -> Any:
324
+ if not isinstance(value, dict):
325
+ return value
326
+ payload = dict(value)
327
+ if "depart" in payload and "departments" not in payload:
328
+ payload["departments"] = payload.pop("depart")
329
+ if "departs" in payload and "departments" not in payload:
330
+ payload["departments"] = payload.pop("departs")
331
+ normalized_mode = _normalize_public_department_scope_mode(payload.get("mode"))
332
+ if normalized_mode is None:
333
+ if "departments" in payload:
334
+ normalized_mode = PublicDepartmentScopeMode.custom.value
335
+ else:
336
+ normalized_mode = PublicDepartmentScopeMode.all.value
337
+ payload["mode"] = normalized_mode
338
+ return payload
339
+
340
+ @model_validator(mode="after")
341
+ def validate_shape(self) -> "DepartmentScopePatch":
342
+ if self.mode == PublicDepartmentScopeMode.custom and not self.departments:
343
+ raise ValueError("custom department scope requires departments")
344
+ if self.mode == PublicDepartmentScopeMode.custom and any(item.dept_id is None for item in self.departments):
345
+ raise ValueError("custom department scope requires departments[].dept_id")
346
+ return self
347
+
348
+
294
349
  class CodeBlockAliasPathPatch(StrictModel):
295
350
  alias_name: str = Field(validation_alias=AliasChoices("alias_name", "aliasName"))
296
351
  alias_path: str = Field(validation_alias=AliasChoices("alias_path", "aliasPath"))
@@ -555,6 +610,10 @@ class FieldPatch(StrictModel):
555
610
  display_field: FieldSelector | None = None
556
611
  visible_fields: list[FieldSelector] = Field(default_factory=list)
557
612
  relation_mode: PublicRelationMode | None = Field(default=None, validation_alias=AliasChoices("relation_mode", "relationMode", "selection_mode", "selectionMode"))
613
+ department_scope: DepartmentScopePatch | None = Field(
614
+ default=None,
615
+ validation_alias=AliasChoices("department_scope", "departmentScope"),
616
+ )
558
617
  remote_lookup_config: RemoteLookupConfigPatch | None = Field(
559
618
  default=None,
560
619
  validation_alias=AliasChoices("remote_lookup_config", "remoteLookupConfig"),
@@ -586,10 +645,16 @@ class FieldPatch(StrictModel):
586
645
  def validate_shape(self) -> "FieldPatch":
587
646
  if self.type == PublicFieldType.relation and not self.target_app_key:
588
647
  raise ValueError("relation field requires target_app_key")
648
+ if self.type == PublicFieldType.relation and self.display_field is None:
649
+ raise ValueError("relation field requires display_field")
650
+ if self.type == PublicFieldType.relation and not self.visible_fields:
651
+ raise ValueError("relation field requires visible_fields")
589
652
  if self.type != PublicFieldType.relation and self.target_app_key:
590
653
  raise ValueError("target_app_key is only allowed for relation fields")
591
654
  if self.type != PublicFieldType.relation and (self.display_field is not None or self.visible_fields or self.relation_mode is not None):
592
655
  raise ValueError("display_field, visible_fields, and relation_mode are only allowed for relation fields")
656
+ if self.type != PublicFieldType.department and self.department_scope is not None:
657
+ raise ValueError("department_scope is only allowed for department fields")
593
658
  if self.type != PublicFieldType.q_linker and (
594
659
  self.remote_lookup_config is not None
595
660
  or self.q_linker_binding is not None
@@ -625,6 +690,10 @@ class FieldMutation(StrictModel):
625
690
  display_field: FieldSelector | None = None
626
691
  visible_fields: list[FieldSelector] | None = None
627
692
  relation_mode: PublicRelationMode | None = Field(default=None, validation_alias=AliasChoices("relation_mode", "relationMode", "selection_mode", "selectionMode"))
693
+ department_scope: DepartmentScopePatch | None = Field(
694
+ default=None,
695
+ validation_alias=AliasChoices("department_scope", "departmentScope"),
696
+ )
628
697
  remote_lookup_config: RemoteLookupConfigPatch | None = Field(
629
698
  default=None,
630
699
  validation_alias=AliasChoices("remote_lookup_config", "remoteLookupConfig"),
@@ -656,8 +725,17 @@ class FieldMutation(StrictModel):
656
725
  def validate_shape(self) -> "FieldMutation":
657
726
  if self.type == PublicFieldType.relation and not self.target_app_key:
658
727
  raise ValueError("relation field requires target_app_key")
728
+ relation_patch = (
729
+ self.type == PublicFieldType.relation
730
+ or self.target_app_key is not None
731
+ or self.display_field is not None
732
+ or self.visible_fields is not None
733
+ or self.relation_mode is not None
734
+ )
659
735
  if self.type is not None and self.type != PublicFieldType.relation and (self.display_field is not None or self.visible_fields or self.relation_mode is not None):
660
736
  raise ValueError("display_field, visible_fields, and relation_mode are only allowed for relation fields")
737
+ if self.type is not None and self.type != PublicFieldType.department and self.department_scope is not None:
738
+ raise ValueError("department_scope is only allowed for department fields")
661
739
  if self.type is not None and self.type != PublicFieldType.q_linker and (
662
740
  self.remote_lookup_config is not None
663
741
  or self.q_linker_binding is not None
@@ -981,6 +1059,17 @@ class CustomButtonWingsConfigPatch(StrictModel):
981
1059
  return payload
982
1060
 
983
1061
 
1062
+ def _is_white_button_color(value: str | None) -> bool:
1063
+ normalized = str(value or "").strip().lower().replace(" ", "")
1064
+ return normalized in {
1065
+ "#fff",
1066
+ "#ffffff",
1067
+ "white",
1068
+ "rgb(255,255,255)",
1069
+ "rgba(255,255,255,1)",
1070
+ }
1071
+
1072
+
984
1073
  class CustomButtonPatch(StrictModel):
985
1074
  button_text: str = Field(validation_alias=AliasChoices("button_text", "buttonText"))
986
1075
  background_color: str = Field(validation_alias=AliasChoices("background_color", "backgroundColor"))
@@ -1017,6 +1106,8 @@ class CustomButtonPatch(StrictModel):
1017
1106
  raise ValueError("qRobot buttons require external_qrobot_config")
1018
1107
  if self.trigger_action == PublicButtonTriggerAction.wings and self.trigger_wings_config is None:
1019
1108
  raise ValueError("wings buttons require trigger_wings_config")
1109
+ if _is_white_button_color(self.background_color) and _is_white_button_color(self.text_color):
1110
+ raise ValueError("background_color and text_color cannot both be white")
1020
1111
  return self
1021
1112
 
1022
1113
 
@@ -1079,6 +1170,8 @@ class ViewButtonBindingPatch(StrictModel):
1079
1170
  ]
1080
1171
  if missing:
1081
1172
  raise ValueError(f"system button bindings require {', '.join(missing)}")
1173
+ if _is_white_button_color(self.background_color) and _is_white_button_color(self.text_color):
1174
+ raise ValueError("background_color and text_color cannot both be white")
1082
1175
  return self
1083
1176
 
1084
1177
 
@@ -1599,6 +1692,34 @@ def _normalize_public_relation_mode(value: Any) -> str | None:
1599
1692
  return None
1600
1693
 
1601
1694
 
1695
+ def _normalize_public_department_scope_mode(value: Any) -> str | None:
1696
+ if value is None:
1697
+ return None
1698
+ if isinstance(value, int):
1699
+ if value == 1:
1700
+ return PublicDepartmentScopeMode.all.value
1701
+ if value == 2:
1702
+ return PublicDepartmentScopeMode.custom.value
1703
+ return None
1704
+ if isinstance(value, str):
1705
+ normalized = value.strip().lower()
1706
+ aliases = {
1707
+ "all": PublicDepartmentScopeMode.all.value,
1708
+ "workspace_all": PublicDepartmentScopeMode.all.value,
1709
+ "workspace-all": PublicDepartmentScopeMode.all.value,
1710
+ "default": PublicDepartmentScopeMode.all.value,
1711
+ "default_all": PublicDepartmentScopeMode.all.value,
1712
+ "default-all": PublicDepartmentScopeMode.all.value,
1713
+ "1": PublicDepartmentScopeMode.all.value,
1714
+ "custom": PublicDepartmentScopeMode.custom.value,
1715
+ "explicit": PublicDepartmentScopeMode.custom.value,
1716
+ "selected": PublicDepartmentScopeMode.custom.value,
1717
+ "2": PublicDepartmentScopeMode.custom.value,
1718
+ }
1719
+ return aliases.get(normalized, normalized or None)
1720
+ return None
1721
+
1722
+
1602
1723
  CustomButtonMatchRulePatch.model_rebuild()
1603
1724
  CustomButtonAddDataConfigPatch.model_rebuild()
1604
1725
  CodeBlockAliasPathPatch.model_rebuild()