@josephyan/qingflow-cli 0.2.0-beta.66 → 0.2.0-beta.67

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.66
6
+ npm install @josephyan/qingflow-cli@0.2.0-beta.67
7
7
  ```
8
8
 
9
9
  Run:
10
10
 
11
11
  ```bash
12
- npx -y -p @josephyan/qingflow-cli@0.2.0-beta.66 qingflow
12
+ npx -y -p @josephyan/qingflow-cli@0.2.0-beta.67 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.66",
3
+ "version": "0.2.0-beta.67",
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.0b66"
7
+ version = "0.2.0b67"
8
8
  description = "User-authenticated MCP server for Qingflow"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -192,7 +192,11 @@ class AiBuilderFacade:
192
192
  )
193
193
  items = listing.get("items") if isinstance(listing.get("items"), list) else []
194
194
  matches = [
195
- {"tag_id": item.get("tagId"), "tag_name": item.get("tagName")}
195
+ {
196
+ "tag_id": item.get("tagId"),
197
+ "tag_name": item.get("tagName"),
198
+ "tag_icon": str(item.get("tagIcon") or "").strip() or None,
199
+ }
196
200
  for item in items
197
201
  if isinstance(item, dict) and item.get("tagName") == requested and _coerce_positive_int(item.get("tagId")) is not None
198
202
  ]
@@ -226,12 +230,24 @@ class AiBuilderFacade:
226
230
  "verification": {},
227
231
  "tag_id": match["tag_id"],
228
232
  "tag_name": match["tag_name"],
233
+ "tag_icon": match.get("tag_icon"),
229
234
  "match_mode": "exact",
230
235
  }
231
236
 
232
- def package_create(self, *, profile: str, package_name: str) -> JSONObject:
237
+ def package_create(
238
+ self,
239
+ *,
240
+ profile: str,
241
+ package_name: str,
242
+ icon: str | None = None,
243
+ color: str | None = None,
244
+ ) -> JSONObject:
233
245
  requested = str(package_name or "").strip()
234
- normalized_args = {"package_name": requested}
246
+ normalized_args = {
247
+ "package_name": requested,
248
+ **({"icon": icon} if icon else {}),
249
+ **({"color": color} if color else {}),
250
+ }
235
251
  if not requested:
236
252
  return _failed(
237
253
  "PACKAGE_NAME_REQUIRED",
@@ -239,6 +255,12 @@ class AiBuilderFacade:
239
255
  normalized_args=normalized_args,
240
256
  suggested_next_call=None,
241
257
  )
258
+ desired_tag_icon = encode_workspace_icon_with_defaults(
259
+ icon=icon,
260
+ color=color,
261
+ title=requested,
262
+ fallback_icon_name="files-folder",
263
+ )
242
264
  existing = self.package_resolve(profile=profile, package_name=requested)
243
265
  lookup_permission_blocked = None
244
266
  if existing.get("status") == "success":
@@ -257,6 +279,7 @@ class AiBuilderFacade:
257
279
  "verification": {"existing_package_reused": True},
258
280
  "tag_id": existing.get("tag_id"),
259
281
  "tag_name": existing.get("tag_name"),
282
+ "tag_icon": existing.get("tag_icon"),
260
283
  }
261
284
  if existing.get("error_code") == "AMBIGUOUS_PACKAGE":
262
285
  return existing
@@ -271,7 +294,10 @@ class AiBuilderFacade:
271
294
  elif existing.get("error_code") not in {"PACKAGE_NOT_FOUND"}:
272
295
  return existing
273
296
  try:
274
- created = self.packages.package_create(profile=profile, payload={"tagName": requested})
297
+ created = self.packages.package_create(
298
+ profile=profile,
299
+ payload={"tagName": requested, "tagIcon": desired_tag_icon},
300
+ )
275
301
  except (QingflowApiError, RuntimeError) as error:
276
302
  api_error = _coerce_api_error(error)
277
303
  return _failed_from_api_error(
@@ -282,16 +308,26 @@ class AiBuilderFacade:
282
308
  "package_name": requested,
283
309
  **({"lookup_permission_blocked": lookup_permission_blocked} if lookup_permission_blocked is not None else {}),
284
310
  },
285
- suggested_next_call={"tool_name": "package_create", "arguments": {"profile": profile, "package_name": requested}},
311
+ suggested_next_call={
312
+ "tool_name": "package_create",
313
+ "arguments": {
314
+ "profile": profile,
315
+ "package_name": requested,
316
+ **({"icon": icon} if icon else {}),
317
+ **({"color": color} if color else {}),
318
+ },
319
+ },
286
320
  )
287
321
  result = created.get("result") if isinstance(created.get("result"), dict) else {}
288
322
  tag_id = _coerce_positive_int(result.get("tagId"))
289
323
  tag_name = str(result.get("tagName") or requested).strip() or requested
324
+ tag_icon = str(result.get("tagIcon") or desired_tag_icon or "").strip() or None
290
325
  if tag_id is None:
291
326
  resolved = self.package_resolve(profile=profile, package_name=requested)
292
327
  if resolved.get("status") == "success":
293
328
  tag_id = _coerce_positive_int(resolved.get("tag_id"))
294
329
  tag_name = str(resolved.get("tag_name") or tag_name)
330
+ tag_icon = str(resolved.get("tag_icon") or tag_icon or "").strip() or None
295
331
  verified = tag_id is not None
296
332
  return {
297
333
  "status": "success" if verified else "partial_success",
@@ -310,6 +346,7 @@ class AiBuilderFacade:
310
346
  "verification": {"tag_id_verified": verified},
311
347
  "tag_id": tag_id,
312
348
  "tag_name": tag_name,
349
+ "tag_icon": tag_icon,
313
350
  }
314
351
 
315
352
  def package_list(self, *, profile: str, trial_status: str = "all") -> JSONObject:
@@ -12,10 +12,20 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
12
12
 
13
13
  package = builder_subparsers.add_parser("package", help="应用包")
14
14
  package_subparsers = package.add_subparsers(dest="builder_package_command", required=True)
15
+ package_list = package_subparsers.add_parser("list", help="列出应用包")
16
+ package_list.add_argument("--trial-status", default="all")
17
+ package_list.set_defaults(handler=_handle_package_list, format_hint="builder_summary")
18
+
15
19
  package_resolve = package_subparsers.add_parser("resolve", help="解析应用包")
16
20
  package_resolve.add_argument("--package-name", required=True)
17
21
  package_resolve.set_defaults(handler=_handle_package_resolve, format_hint="builder_summary")
18
22
 
23
+ package_create = package_subparsers.add_parser("create", help="创建应用包")
24
+ package_create.add_argument("--package-name", required=True)
25
+ package_create.add_argument("--icon")
26
+ package_create.add_argument("--color")
27
+ package_create.set_defaults(handler=_handle_package_create, format_hint="builder_summary")
28
+
19
29
  app = builder_subparsers.add_parser("app", help="应用")
20
30
  app_subparsers = app.add_subparsers(dest="builder_app_command", required=True)
21
31
 
@@ -119,10 +129,23 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
119
129
  publish_verify_verify.set_defaults(handler=_handle_publish_verify, format_hint="builder_summary")
120
130
 
121
131
 
132
+ def _handle_package_list(args: argparse.Namespace, context: CliContext) -> dict:
133
+ return context.builder.package_list(profile=args.profile, trial_status=args.trial_status)
134
+
135
+
122
136
  def _handle_package_resolve(args: argparse.Namespace, context: CliContext) -> dict:
123
137
  return context.builder.package_resolve(profile=args.profile, package_name=args.package_name)
124
138
 
125
139
 
140
+ def _handle_package_create(args: argparse.Namespace, context: CliContext) -> dict:
141
+ return context.builder.package_create(
142
+ profile=args.profile,
143
+ package_name=args.package_name,
144
+ icon=args.icon,
145
+ color=args.color,
146
+ )
147
+
148
+
126
149
  def _handle_app_resolve(args: argparse.Namespace, context: CliContext) -> dict:
127
150
  return context.builder.app_resolve(
128
151
  profile=args.profile,
@@ -138,8 +138,13 @@ def build_builder_server() -> FastMCP:
138
138
  return ai_builder.builder_tool_contract(tool_name=tool_name)
139
139
 
140
140
  @server.tool()
141
- def package_create(profile: str = DEFAULT_PROFILE, package_name: str = "") -> dict:
142
- return ai_builder.package_create(profile=profile, package_name=package_name)
141
+ def package_create(
142
+ profile: str = DEFAULT_PROFILE,
143
+ package_name: str = "",
144
+ icon: str | None = None,
145
+ color: str | None = None,
146
+ ) -> dict:
147
+ return ai_builder.package_create(profile=profile, package_name=package_name, icon=icon, color=color)
143
148
 
144
149
  @server.tool()
145
150
  def member_search(
@@ -82,8 +82,13 @@ class AiBuilderTools(ToolBase):
82
82
  return self.builder_tool_contract(tool_name=tool_name)
83
83
 
84
84
  @mcp.tool()
85
- def package_create(profile: str = DEFAULT_PROFILE, package_name: str = "") -> JSONObject:
86
- return self.package_create(profile=profile, package_name=package_name)
85
+ def package_create(
86
+ profile: str = DEFAULT_PROFILE,
87
+ package_name: str = "",
88
+ icon: str | None = None,
89
+ color: str | None = None,
90
+ ) -> JSONObject:
91
+ return self.package_create(profile=profile, package_name=package_name, icon=icon, color=color)
87
92
 
88
93
  @mcp.tool()
89
94
  def member_search(
@@ -425,13 +430,32 @@ class AiBuilderTools(ToolBase):
425
430
  "contract": contract,
426
431
  }
427
432
 
428
- def package_create(self, *, profile: str, package_name: str) -> JSONObject:
429
- normalized_args = {"package_name": package_name}
433
+ def package_create(
434
+ self,
435
+ *,
436
+ profile: str,
437
+ package_name: str,
438
+ icon: str | None = None,
439
+ color: str | None = None,
440
+ ) -> JSONObject:
441
+ normalized_args = {
442
+ "package_name": package_name,
443
+ **({"icon": icon} if icon else {}),
444
+ **({"color": color} if color else {}),
445
+ }
430
446
  return _safe_tool_call(
431
- lambda: self._facade.package_create(profile=profile, package_name=package_name),
447
+ lambda: self._facade.package_create(profile=profile, package_name=package_name, icon=icon, color=color),
432
448
  error_code="PACKAGE_CREATE_FAILED",
433
449
  normalized_args=normalized_args,
434
- suggested_next_call={"tool_name": "package_create", "arguments": {"profile": profile, "package_name": package_name}},
450
+ suggested_next_call={
451
+ "tool_name": "package_create",
452
+ "arguments": {
453
+ "profile": profile,
454
+ "package_name": package_name,
455
+ **({"icon": icon} if icon else {}),
456
+ **({"color": color} if color else {}),
457
+ },
458
+ },
435
459
  )
436
460
 
437
461
  def member_search(
@@ -1708,6 +1732,39 @@ def _public_error_message(error_code: str, error: QingflowApiError) -> str:
1708
1732
 
1709
1733
 
1710
1734
  _BUILDER_TOOL_CONTRACTS: dict[str, JSONObject] = {
1735
+ "package_list": {
1736
+ "allowed_keys": ["trial_status"],
1737
+ "aliases": {"trialStatus": "trial_status"},
1738
+ "allowed_values": {"trial_status": ["all", "trial", "formal"]},
1739
+ "minimal_example": {
1740
+ "profile": "default",
1741
+ "trial_status": "all",
1742
+ },
1743
+ },
1744
+ "package_resolve": {
1745
+ "allowed_keys": ["package_name"],
1746
+ "aliases": {"packageName": "package_name"},
1747
+ "allowed_values": {},
1748
+ "minimal_example": {
1749
+ "profile": "default",
1750
+ "package_name": "PLM(备用,施工中)",
1751
+ },
1752
+ },
1753
+ "package_create": {
1754
+ "allowed_keys": ["package_name", "icon", "color"],
1755
+ "aliases": {
1756
+ "packageName": "package_name",
1757
+ "iconName": "icon",
1758
+ "iconColor": "color",
1759
+ },
1760
+ "allowed_values": {},
1761
+ "minimal_example": {
1762
+ "profile": "default",
1763
+ "package_name": "项目管理",
1764
+ "icon": "files-folder",
1765
+ "color": "azure",
1766
+ },
1767
+ },
1711
1768
  "member_search": {
1712
1769
  "allowed_keys": ["query", "page_num", "page_size", "contain_disable"],
1713
1770
  "aliases": {},
@@ -200,6 +200,7 @@ class PackageTools(ToolBase):
200
200
  compact = {
201
201
  "tagId": result.get("tagId"),
202
202
  "tagName": result.get("tagName"),
203
+ "tagIcon": result.get("tagIcon") if result.get("tagIcon") is not None else permission_source.get("tagIcon"),
203
204
  "publishStatus": result.get("publishStatus") if result.get("publishStatus") is not None else permission_source.get("publishStatus"),
204
205
  "beingTrial": result.get("beingTrial"),
205
206
  "itemCount": len(tag_items),