@josephyan/qingflow-cli 0.2.0-beta.81 → 0.2.0-beta.83

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.
@@ -69,13 +69,6 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
69
69
 
70
70
  package = builder_subparsers.add_parser("package", help="应用包")
71
71
  package_subparsers = package.add_subparsers(dest="builder_package_command", required=True)
72
- package_list = package_subparsers.add_parser("list", help="列出应用包")
73
- package_list.add_argument("--trial-status", default="all")
74
- package_list.set_defaults(handler=_handle_package_list, format_hint="builder_summary")
75
-
76
- package_resolve = package_subparsers.add_parser("resolve", help="解析应用包")
77
- package_resolve.add_argument("--package-name", required=True)
78
- package_resolve.set_defaults(handler=_handle_package_resolve, format_hint="builder_summary")
79
72
 
80
73
  solution = builder_subparsers.add_parser("solution", help="解决方案")
81
74
  solution_subparsers = solution.add_subparsers(dest="builder_solution_command", required=True)
@@ -85,30 +78,13 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
85
78
  solution_install.add_argument("--solution-source", default="solutionDetail")
86
79
  solution_install.set_defaults(handler=_handle_solution_install, format_hint="builder_summary")
87
80
 
88
- package_create = package_subparsers.add_parser("create", help="创建应用包")
89
- package_create.add_argument("--package-name", required=True)
90
- package_create.add_argument("--icon")
91
- package_create.add_argument("--color")
92
- package_create.add_argument("--visibility-file")
93
- package_create.set_defaults(handler=_handle_package_create, format_hint="builder_summary")
94
-
95
81
  package_get = package_subparsers.add_parser("get", help="读取应用包详情")
96
- package_get.add_argument("--tag-id", type=int, required=True)
82
+ package_get.add_argument("--package-id", type=int, required=True)
97
83
  package_get.set_defaults(handler=_handle_package_get, format_hint="builder_summary")
98
84
 
99
- package_update = package_subparsers.add_parser("update", help="更新应用包")
100
- package_update.add_argument("--tag-id", type=int, required=True)
101
- package_update.add_argument("--package-name")
102
- package_update.add_argument("--icon")
103
- package_update.add_argument("--color")
104
- package_update.add_argument("--visibility-file")
105
- package_update.set_defaults(handler=_handle_package_update, format_hint="builder_summary")
106
-
107
- package_attach_app = package_subparsers.add_parser("attach-app", help="将应用挂载到应用包")
108
- package_attach_app.add_argument("--tag-id", type=int, required=True)
109
- package_attach_app.add_argument("--app-key", required=True)
110
- package_attach_app.add_argument("--app-title", dest="legacy_app_title", help=argparse.SUPPRESS)
111
- package_attach_app.set_defaults(handler=_handle_package_attach_app, format_hint="builder_summary")
85
+ package_apply = package_subparsers.add_parser("apply", help="创建或更新应用包配置")
86
+ package_apply.add_argument("--config-file", required=True)
87
+ package_apply.set_defaults(handler=_handle_package_apply, format_hint="builder_summary")
112
88
 
113
89
  app = builder_subparsers.add_parser("app", help="应用")
114
90
  app_subparsers = app.add_subparsers(dest="builder_app_command", required=True)
@@ -116,7 +92,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
116
92
  app_resolve = app_subparsers.add_parser("resolve", help="解析应用")
117
93
  app_resolve.add_argument("--app-key", default="")
118
94
  app_resolve.add_argument("--app-name", default="")
119
- app_resolve.add_argument("--package-tag-id", type=int)
95
+ app_resolve.add_argument("--package-id", type=int)
120
96
  app_resolve.set_defaults(handler=_handle_app_resolve, format_hint="builder_summary")
121
97
 
122
98
  app_release_lock = app_subparsers.add_parser("release-edit-lock-if-mine", help="释放当前账号持有的编辑锁")
@@ -182,7 +158,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
182
158
  portal_apply = portal_subparsers.add_parser("apply", help="更新门户")
183
159
  portal_apply.add_argument("--dash-key", default="")
184
160
  portal_apply.add_argument("--dash-name", default="")
185
- portal_apply.add_argument("--package-tag-id", type=int)
161
+ portal_apply.add_argument("--package-id", type=int)
186
162
  portal_apply.add_argument("--publish", action=argparse.BooleanOptionalAction, default=True)
187
163
  portal_apply.add_argument("--sections-file", required=True)
188
164
  portal_apply.add_argument("--visibility-file")
@@ -198,7 +174,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
198
174
  schema_apply_subparsers = schema_apply.add_subparsers(dest="builder_schema_command", required=True)
199
175
  schema_apply_apply = schema_apply_subparsers.add_parser("apply", help="执行字段变更")
200
176
  schema_apply_apply.add_argument("--app-key", default="")
201
- schema_apply_apply.add_argument("--package-tag-id", type=int)
177
+ schema_apply_apply.add_argument("--package-id", type=int)
202
178
  schema_apply_apply.add_argument("--app-name", default="")
203
179
  schema_apply_apply.add_argument("--app-title", default="")
204
180
  schema_apply_apply.add_argument("--icon")
@@ -252,7 +228,7 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
252
228
  publish_verify_subparsers = publish_verify.add_subparsers(dest="builder_publish_command", required=True)
253
229
  publish_verify_verify = publish_verify_subparsers.add_parser("verify", help="校验应用发布")
254
230
  publish_verify_verify.add_argument("--app-key", required=True)
255
- publish_verify_verify.add_argument("--expected-package-tag-id", type=int)
231
+ publish_verify_verify.add_argument("--expected-package-id", type=int)
256
232
  publish_verify_verify.set_defaults(handler=_handle_publish_verify, format_hint="builder_summary")
257
233
 
258
234
  view = builder_subparsers.add_parser("view", help="视图详情")
@@ -268,10 +244,6 @@ def register(subparsers: argparse._SubParsersAction[argparse.ArgumentParser]) ->
268
244
  chart_get.set_defaults(handler=_handle_chart_get, format_hint="builder_summary")
269
245
 
270
246
 
271
- def _handle_package_list(args: argparse.Namespace, context: CliContext) -> dict:
272
- return context.builder.package_list(profile=args.profile, trial_status=args.trial_status)
273
-
274
-
275
247
  def _handle_file_upload_local(args: argparse.Namespace, context: CliContext) -> dict:
276
248
  return context.files.file_upload_local(
277
249
  profile=args.profile,
@@ -335,10 +307,6 @@ def _handle_role_create(args: argparse.Namespace, context: CliContext) -> dict:
335
307
  )
336
308
 
337
309
 
338
- def _handle_package_resolve(args: argparse.Namespace, context: CliContext) -> dict:
339
- return context.builder.package_resolve(profile=args.profile, package_name=args.package_name)
340
-
341
-
342
310
  def _handle_solution_install(args: argparse.Namespace, context: CliContext) -> dict:
343
311
  return context.builder.solution_install(
344
312
  profile=args.profile,
@@ -348,63 +316,36 @@ def _handle_solution_install(args: argparse.Namespace, context: CliContext) -> d
348
316
  )
349
317
 
350
318
 
351
- def _handle_package_create(args: argparse.Namespace, context: CliContext) -> dict:
352
- return context.builder.package_create(
353
- profile=args.profile,
354
- package_name=args.package_name,
355
- icon=args.icon,
356
- color=args.color,
357
- visibility=load_object_arg(args.visibility_file, option_name="--visibility-file"),
358
- )
359
-
360
-
361
319
  def _handle_package_get(args: argparse.Namespace, context: CliContext) -> dict:
362
- return context.builder.package_get(profile=args.profile, tag_id=args.tag_id)
363
-
320
+ return context.builder.package_get(profile=args.profile, package_id=args.package_id)
364
321
 
365
- def _handle_package_update(args: argparse.Namespace, context: CliContext) -> dict:
366
- return context.builder.package_update(
367
- profile=args.profile,
368
- tag_id=args.tag_id,
369
- package_name=args.package_name,
370
- icon=args.icon,
371
- color=args.color,
372
- visibility=load_object_arg(args.visibility_file, option_name="--visibility-file"),
373
- )
374
322
 
375
-
376
- def _handle_package_attach_app(args: argparse.Namespace, context: CliContext) -> dict:
377
- if (args.legacy_app_title or "").strip():
378
- raise_config_error(
379
- "package attach-app no longer accepts app_title.",
380
- fix_hint="Use `--app-key` to identify the app to attach.",
381
- )
382
- return context.builder.package_attach_app(
383
- profile=args.profile,
384
- tag_id=args.tag_id,
385
- app_key=args.app_key,
386
- )
323
+ def _handle_package_apply(args: argparse.Namespace, context: CliContext) -> dict:
324
+ config = load_object_arg(args.config_file, option_name="--config-file")
325
+ if not isinstance(config, dict):
326
+ raise_config_error("package apply --config-file must contain a JSON object.")
327
+ return context.builder.package_apply(profile=args.profile, **config)
387
328
 
388
329
 
389
330
  def _handle_app_resolve(args: argparse.Namespace, context: CliContext) -> dict:
390
331
  has_app_key = bool((args.app_key or "").strip())
391
332
  has_app_name = bool((args.app_name or "").strip())
392
- has_package_tag_id = args.package_tag_id is not None
393
- if has_app_key and (has_app_name or has_package_tag_id):
333
+ has_package_id = args.package_id is not None
334
+ if has_app_key and (has_app_name or has_package_id):
394
335
  raise_config_error(
395
336
  "app resolve accepts exactly one selector mode.",
396
- fix_hint="Use only `--app-key`, or use `--app-name` together with `--package-tag-id`.",
337
+ fix_hint="Use only `--app-key`, or use `--app-name` together with `--package-id`.",
397
338
  )
398
- if not has_app_key and not (has_app_name and has_package_tag_id):
339
+ if not has_app_key and not (has_app_name and has_package_id):
399
340
  raise_config_error(
400
- "app resolve requires either --app-key, or --app-name together with --package-tag-id.",
401
- fix_hint="For an existing known app, pass `--app-key`. For package-scoped lookup, pass both `--app-name` and `--package-tag-id`.",
341
+ "app resolve requires either --app-key, or --app-name together with --package-id.",
342
+ fix_hint="For an existing known app, pass `--app-key`. For package-scoped lookup, pass both `--app-name` and `--package-id`.",
402
343
  )
403
344
  return context.builder.app_resolve(
404
345
  profile=args.profile,
405
346
  app_key=args.app_key,
406
347
  app_name=args.app_name,
407
- package_tag_id=args.package_tag_id,
348
+ package_id=args.package_id,
408
349
  )
409
350
 
410
351
 
@@ -487,23 +428,23 @@ def _handle_schema_apply(args: argparse.Namespace, context: CliContext) -> dict:
487
428
  has_app_key = bool((args.app_key or "").strip())
488
429
  has_app_name = bool((args.app_name or "").strip())
489
430
  has_app_title = bool((args.app_title or "").strip())
490
- has_package_tag_id = args.package_tag_id is not None
431
+ has_package_id = args.package_id is not None
491
432
  if has_app_key:
492
- if args.create_if_missing or has_package_tag_id:
433
+ if args.create_if_missing or has_package_id:
493
434
  raise_config_error(
494
435
  "schema apply edit mode accepts --app-key and optional --app-name only.",
495
- fix_hint="For existing apps, use `--app-key` and optionally `--app-name`. For create mode, use `--package-tag-id --app-name --create-if-missing`.",
436
+ fix_hint="For existing apps, use `--app-key` and optionally `--app-name`. For create mode, use `--package-id --app-name --create-if-missing`.",
496
437
  )
497
438
  else:
498
- if not args.create_if_missing or not has_package_tag_id or not (has_app_name or has_app_title):
439
+ if not args.create_if_missing or not has_package_id or not (has_app_name or has_app_title):
499
440
  raise_config_error(
500
- "schema apply create mode requires --package-tag-id, --app-name, and --create-if-missing.",
501
- fix_hint="Use `--app-key` for existing apps, or pass `--package-tag-id --app-name --create-if-missing` to create a new app.",
441
+ "schema apply create mode requires --package-id, --app-name, and --create-if-missing.",
442
+ fix_hint="Use `--app-key` for existing apps, or pass `--package-id --app-name --create-if-missing` to create a new app.",
502
443
  )
503
444
  return context.builder.app_schema_apply(
504
445
  profile=args.profile,
505
446
  app_key=args.app_key,
506
- package_tag_id=args.package_tag_id,
447
+ package_id=args.package_id,
507
448
  app_name=args.app_name,
508
449
  app_title=args.app_title,
509
450
  icon=args.icon,
@@ -561,22 +502,22 @@ def _handle_charts_apply(args: argparse.Namespace, context: CliContext) -> dict:
561
502
  def _handle_portal_apply(args: argparse.Namespace, context: CliContext) -> dict:
562
503
  has_dash_key = bool((args.dash_key or "").strip())
563
504
  has_dash_name = bool((args.dash_name or "").strip())
564
- has_package_tag_id = args.package_tag_id is not None
565
- if has_dash_key and has_package_tag_id:
505
+ has_package_id = args.package_id is not None
506
+ if has_dash_key and has_package_id:
566
507
  raise_config_error(
567
508
  "portal apply accepts exactly one selector mode.",
568
- fix_hint="Use `--dash-key` to update an existing portal, or use `--package-tag-id --dash-name` to create a new portal.",
509
+ fix_hint="Use `--dash-key` to update an existing portal, or use `--package-id --dash-name` to create a new portal.",
569
510
  )
570
- if not has_dash_key and not (has_package_tag_id and has_dash_name):
511
+ if not has_dash_key and not (has_package_id and has_dash_name):
571
512
  raise_config_error(
572
- "portal apply requires either --dash-key, or --package-tag-id together with --dash-name.",
573
- fix_hint="Use `--dash-key` for an existing portal. For create mode, pass `--package-tag-id --dash-name`.",
513
+ "portal apply requires either --dash-key, or --package-id together with --dash-name.",
514
+ fix_hint="Use `--dash-key` for an existing portal. For create mode, pass `--package-id --dash-name`.",
574
515
  )
575
516
  return context.builder.portal_apply(
576
517
  profile=args.profile,
577
518
  dash_key=args.dash_key,
578
519
  dash_name=args.dash_name,
579
- package_tag_id=args.package_tag_id,
520
+ package_id=args.package_id,
580
521
  publish=bool(args.publish),
581
522
  sections=require_list_arg(args.sections_file, option_name="--sections-file"),
582
523
  visibility=load_object_arg(args.visibility_file, option_name="--visibility-file"),
@@ -593,5 +534,5 @@ def _handle_publish_verify(args: argparse.Namespace, context: CliContext) -> dic
593
534
  return context.builder.app_publish_verify(
594
535
  profile=args.profile,
595
536
  app_key=args.app_key,
596
- expected_package_tag_id=args.expected_package_tag_id,
537
+ expected_package_id=args.expected_package_id,
597
538
  )
@@ -115,17 +115,13 @@ BUILDER_PUBLIC_TOOL_SPECS: tuple[PublicToolSpec, ...] = (
115
115
  PublicToolSpec(BUILDER_DOMAIN, "workspace_select", ("workspace_select",), ("builder", "workspace", "select"), cli_public=False),
116
116
  PublicToolSpec(BUILDER_DOMAIN, "file_upload_local", ("file_upload_local",), ("builder", "file", "upload-local"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
117
117
  PublicToolSpec(BUILDER_DOMAIN, "feedback_submit", ("feedback_submit",), ("builder", "feedback", "submit"), has_contract=True),
118
- PublicToolSpec(BUILDER_DOMAIN, "package_list", ("package_list",), ("builder", "package", "list"), has_contract=True, cli_show_effective_context=True),
119
- PublicToolSpec(BUILDER_DOMAIN, "package_resolve", ("package_resolve",), ("builder", "package", "resolve"), has_contract=True, cli_show_effective_context=True),
120
118
  PublicToolSpec(BUILDER_DOMAIN, "builder_tool_contract", ("builder_tool_contract",), ("builder", "contract"), has_contract=False),
121
- PublicToolSpec(BUILDER_DOMAIN, "package_create", ("package_create",), ("builder", "package", "create"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
122
119
  PublicToolSpec(BUILDER_DOMAIN, "package_get", ("package_get",), ("builder", "package", "get"), has_contract=True, cli_show_effective_context=True),
123
- PublicToolSpec(BUILDER_DOMAIN, "package_update", ("package_update",), ("builder", "package", "update"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
120
+ PublicToolSpec(BUILDER_DOMAIN, "package_apply", ("package_apply",), ("builder", "package", "apply"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
124
121
  PublicToolSpec(BUILDER_DOMAIN, "solution_install", ("solution_install",), ("builder", "solution", "install"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
125
122
  PublicToolSpec(BUILDER_DOMAIN, "member_search", ("member_search",), ("builder", "member", "search"), has_contract=True, cli_show_effective_context=True),
126
123
  PublicToolSpec(BUILDER_DOMAIN, "role_search", ("role_search",), ("builder", "role", "search"), has_contract=True, cli_show_effective_context=True),
127
124
  PublicToolSpec(BUILDER_DOMAIN, "role_create", ("role_create",), ("builder", "role", "create"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
128
- PublicToolSpec(BUILDER_DOMAIN, "package_attach_app", ("package_attach_app",), ("builder", "package", "attach-app"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
129
125
  PublicToolSpec(BUILDER_DOMAIN, "app_release_edit_lock_if_mine", ("app_release_edit_lock_if_mine",), ("builder", "app", "release-edit-lock-if-mine"), has_contract=True, cli_show_effective_context=True, cli_context_write=True),
130
126
  PublicToolSpec(BUILDER_DOMAIN, "app_resolve", ("app_resolve",), ("builder", "app", "resolve"), has_contract=True, cli_show_effective_context=True),
131
127
  PublicToolSpec(BUILDER_DOMAIN, "app_custom_button_list", ("app_custom_button_list",), ("builder", "button", "list"), has_contract=True, cli_show_effective_context=True),
@@ -445,15 +445,13 @@ _register_policy(
445
445
  _register_policy(
446
446
  (BUILDER_DOMAIN,),
447
447
  (
448
- "package_list",
449
- "package_resolve",
450
448
  "builder_tool_contract",
449
+ "package_get",
450
+ "package_apply",
451
451
  "solution_install",
452
- "package_create",
453
452
  "member_search",
454
453
  "role_search",
455
454
  "role_create",
456
- "package_attach_app",
457
455
  "app_release_edit_lock_if_mine",
458
456
  "app_resolve",
459
457
  "app_custom_button_list",
@@ -32,15 +32,15 @@ def build_builder_server() -> FastMCP:
32
32
  instructions=(
33
33
  "Use this server for AI-native Qingflow builder workflows. "
34
34
  "`feedback_submit` is always available as a cross-cutting helper when the current capability is unsupported, awkward, or still cannot satisfy the user's need after reasonable use; it does not require Qingflow login or workspace selection, and it should be called only after explicit user confirmation. "
35
- "Follow the resource path resolve -> summary read -> apply -> attach -> publish_verify. "
35
+ "Follow the resource path resolve -> summary read -> apply -> publish_verify. "
36
36
  "Use builder_tool_contract when you need a machine-readable contract, aliases, allowed enums, or a minimal valid example for a public builder tool. "
37
37
  "Use solution_install when the user explicitly wants to install a packaged solution/template by solution_key, optionally copying bundled demo data. "
38
- "If creating a new package may be appropriate, ask the user to confirm package creation before calling package_create; otherwise use package_resolve/package_list and app_resolve to locate resources, "
38
+ "If creating or updating an app package may be appropriate, use package_apply with explicit user intent; otherwise use package_get and app_resolve to locate resources, "
39
39
  "app_get/app_get_fields/app_repair_code_blocks/app_get_layout/app_get_views/app_get_flow/app_get_charts/portal_list/portal_get/view_get/chart_get for configuration reads, "
40
40
  "member_search/role_search/role_create when workflow assignees must come from the directory or role catalog, preferring roles over explicit members unless the user explicitly names members, "
41
41
  "then app_schema_apply/app_layout_apply/app_flow_apply/app_views_apply/app_charts_apply/portal_apply to execute normalized patches; these apply tools perform planning, normalization, and dependency checks internally where applicable. Schema/layout/views noop requests skip publish, charts are immediate-live without publish and resolve targets by chart_id first then exact unique chart name, portal updates are replace-only and publish=false only guarantees draft/base-info updates, and flow should use publish=false whenever you only want draft/precheck behavior. "
42
42
  "For code_block fields with output bindings, always use qf_output assignment rather than const/let qf_output, and use app_repair_code_blocks when an existing form hangs because output-bound fields stay loading. "
43
- "Use package_attach_app to attach apps to packages, and app_publish_verify for explicit final publish verification. "
43
+ "Use package_apply to manage package metadata, visibility, grouping, and ordering, and app_publish_verify for explicit final publish verification. "
44
44
  "For workflow edits, keep the public builder surface on stable linear flows only: start/approve/fill/copy/webhook/end. Branch and condition nodes are intentionally disabled because the backend workflow route is not front-end stable for those node types. Declare node assignees and editable fields explicitly. "
45
45
  "If builder writes are blocked by the current user's own edit lock, use app_release_edit_lock_if_mine with the lock owner details from the failed result. "
46
46
  "Do not handcraft internal solution payloads or rely on build_id/stage/repair. "
@@ -179,48 +179,36 @@ def build_builder_server() -> FastMCP:
179
179
  raise RuntimeError(json.dumps(trim_error_response(payload), ensure_ascii=False)) from None
180
180
  raise
181
181
 
182
- @server.tool()
183
- def package_list(profile: str = DEFAULT_PROFILE, trial_status: str = "all") -> dict:
184
- return ai_builder.package_list(profile=profile, trial_status=trial_status)
185
-
186
- @server.tool()
187
- def package_resolve(profile: str = DEFAULT_PROFILE, package_name: str = "") -> dict:
188
- return ai_builder.package_resolve(profile=profile, package_name=package_name)
189
-
190
182
  @server.tool()
191
183
  def builder_tool_contract(tool_name: str = "") -> dict:
192
184
  return ai_builder.builder_tool_contract(tool_name=tool_name)
193
185
 
194
186
  @server.tool()
195
- def package_create(
196
- profile: str = DEFAULT_PROFILE,
197
- package_name: str = "",
198
- icon: str | None = None,
199
- color: str | None = None,
200
- visibility: dict | None = None,
201
- ) -> dict:
202
- return ai_builder.package_create(profile=profile, package_name=package_name, icon=icon, color=color, visibility=visibility)
203
-
204
- @server.tool()
205
- def package_get(profile: str = DEFAULT_PROFILE, tag_id: int = 0) -> dict:
206
- return ai_builder.package_get(profile=profile, tag_id=tag_id)
187
+ def package_get(profile: str = DEFAULT_PROFILE, package_id: int = 0) -> dict:
188
+ return ai_builder.package_get(profile=profile, package_id=package_id)
207
189
 
208
190
  @server.tool()
209
- def package_update(
191
+ def package_apply(
210
192
  profile: str = DEFAULT_PROFILE,
211
- tag_id: int = 0,
193
+ package_id: int | None = None,
212
194
  package_name: str | None = None,
195
+ create_if_missing: bool = False,
213
196
  icon: str | None = None,
214
197
  color: str | None = None,
215
198
  visibility: dict | None = None,
199
+ items: list[dict] | None = None,
200
+ allow_detach: bool = False,
216
201
  ) -> dict:
217
- return ai_builder.package_update(
202
+ return ai_builder.package_apply(
218
203
  profile=profile,
219
- tag_id=tag_id,
204
+ package_id=package_id,
220
205
  package_name=package_name,
206
+ create_if_missing=create_if_missing,
221
207
  icon=icon,
222
208
  color=color,
223
209
  visibility=visibility,
210
+ items=items,
211
+ allow_detach=allow_detach,
224
212
  )
225
213
 
226
214
  @server.tool()
@@ -280,14 +268,6 @@ def build_builder_server() -> FastMCP:
280
268
  role_icon=role_icon,
281
269
  )
282
270
 
283
- @server.tool()
284
- def package_attach_app(
285
- profile: str = DEFAULT_PROFILE,
286
- tag_id: int = 0,
287
- app_key: str = "",
288
- ) -> dict:
289
- return ai_builder.package_attach_app(profile=profile, tag_id=tag_id, app_key=app_key)
290
-
291
271
  @server.tool()
292
272
  def app_release_edit_lock_if_mine(
293
273
  profile: str = DEFAULT_PROFILE,
@@ -307,22 +287,22 @@ def build_builder_server() -> FastMCP:
307
287
  profile: str = DEFAULT_PROFILE,
308
288
  app_key: str = "",
309
289
  app_name: str = "",
310
- package_tag_id: int | None = None,
290
+ package_id: int | None = None,
311
291
  ) -> dict:
312
292
  has_app_key = bool((app_key or "").strip())
313
293
  has_app_name = bool((app_name or "").strip())
314
- has_package_tag_id = package_tag_id is not None
315
- if has_app_key and (has_app_name or has_package_tag_id):
294
+ has_package_id = package_id is not None
295
+ if has_app_key and (has_app_name or has_package_id):
316
296
  return _config_failure(
317
297
  "app_resolve accepts exactly one selector mode.",
318
- fix_hint="Use only `app_key`, or use `app_name` together with `package_tag_id`.",
298
+ fix_hint="Use only `app_key`, or use `app_name` together with `package_id`.",
319
299
  )
320
- if not has_app_key and not (has_app_name and has_package_tag_id):
300
+ if not has_app_key and not (has_app_name and has_package_id):
321
301
  return _config_failure(
322
- "app_resolve requires either app_key, or app_name together with package_tag_id.",
323
- fix_hint="For an existing known app, pass `app_key`. For package-scoped lookup, pass both `app_name` and `package_tag_id`.",
302
+ "app_resolve requires either app_key, or app_name together with package_id.",
303
+ fix_hint="For an existing known app, pass `app_key`. For package-scoped lookup, pass both `app_name` and `package_id`.",
324
304
  )
325
- return ai_builder.app_resolve(profile=profile, app_key=app_key, app_name=app_name, package_tag_id=package_tag_id)
305
+ return ai_builder.app_resolve(profile=profile, app_key=app_key, app_name=app_name, package_id=package_id)
326
306
 
327
307
  @server.tool()
328
308
  def app_custom_button_list(profile: str = DEFAULT_PROFILE, app_key: str = "") -> dict:
@@ -406,7 +386,7 @@ def build_builder_server() -> FastMCP:
406
386
  def app_schema_apply(
407
387
  profile: str = DEFAULT_PROFILE,
408
388
  app_key: str = "",
409
- package_tag_id: int | None = None,
389
+ package_id: int | None = None,
410
390
  app_name: str = "",
411
391
  app_title: str = "",
412
392
  icon: str = "",
@@ -421,22 +401,22 @@ def build_builder_server() -> FastMCP:
421
401
  has_app_key = bool((app_key or "").strip())
422
402
  has_app_name = bool((app_name or "").strip())
423
403
  has_app_title = bool((app_title or "").strip())
424
- has_package_tag_id = package_tag_id is not None
404
+ has_package_id = package_id is not None
425
405
  if has_app_key:
426
- if create_if_missing or has_package_tag_id:
406
+ if create_if_missing or has_package_id:
427
407
  return _config_failure(
428
408
  "app_schema_apply edit mode accepts app_key and optional app_name rename only.",
429
- fix_hint="For existing apps, use `app_key` and optionally `app_name`. For create mode, use `package_tag_id + app_name + create_if_missing=true`.",
409
+ fix_hint="For existing apps, use `app_key` and optionally `app_name`. For create mode, use `package_id + app_name + create_if_missing=true`.",
430
410
  )
431
- elif not (create_if_missing and has_package_tag_id and (has_app_name or has_app_title)):
411
+ elif not (create_if_missing and has_package_id and (has_app_name or has_app_title)):
432
412
  return _config_failure(
433
- "app_schema_apply create mode requires package_tag_id, app_name, and create_if_missing=true.",
434
- fix_hint="Use `app_key` for existing apps, or pass `package_tag_id + app_name + create_if_missing=true` to create a new app.",
413
+ "app_schema_apply create mode requires package_id, app_name, and create_if_missing=true.",
414
+ fix_hint="Use `app_key` for existing apps, or pass `package_id + app_name + create_if_missing=true` to create a new app.",
435
415
  )
436
416
  return ai_builder.app_schema_apply(
437
417
  profile=profile,
438
418
  app_key=app_key,
439
- package_tag_id=package_tag_id,
419
+ package_id=package_id,
440
420
  app_name=app_name,
441
421
  app_title=app_title,
442
422
  icon=icon,
@@ -514,7 +494,7 @@ def build_builder_server() -> FastMCP:
514
494
  profile: str = DEFAULT_PROFILE,
515
495
  dash_key: str = "",
516
496
  dash_name: str = "",
517
- package_tag_id: int | None = None,
497
+ package_id: int | None = None,
518
498
  publish: bool = True,
519
499
  sections: list[dict] | None = None,
520
500
  visibility: dict | None = None,
@@ -527,22 +507,22 @@ def build_builder_server() -> FastMCP:
527
507
  ) -> dict:
528
508
  has_dash_key = bool((dash_key or "").strip())
529
509
  has_dash_name = bool((dash_name or "").strip())
530
- has_package_tag_id = package_tag_id is not None
531
- if has_dash_key and has_package_tag_id:
510
+ has_package_id = package_id is not None
511
+ if has_dash_key and has_package_id:
532
512
  return _config_failure(
533
513
  "portal_apply accepts exactly one selector mode.",
534
- fix_hint="Use `dash_key` to update an existing portal, or use `package_tag_id + dash_name` to create a new portal.",
514
+ fix_hint="Use `dash_key` to update an existing portal, or use `package_id + dash_name` to create a new portal.",
535
515
  )
536
- if not has_dash_key and not (has_package_tag_id and has_dash_name):
516
+ if not has_dash_key and not (has_package_id and has_dash_name):
537
517
  return _config_failure(
538
- "portal_apply requires either dash_key, or package_tag_id together with dash_name.",
539
- fix_hint="Use `dash_key` for an existing portal. For create mode, pass `package_tag_id + dash_name`.",
518
+ "portal_apply requires either dash_key, or package_id together with dash_name.",
519
+ fix_hint="Use `dash_key` for an existing portal. For create mode, pass `package_id + dash_name`.",
540
520
  )
541
521
  return ai_builder.portal_apply(
542
522
  profile=profile,
543
523
  dash_key=dash_key,
544
524
  dash_name=dash_name,
545
- package_tag_id=package_tag_id,
525
+ package_id=package_id,
546
526
  publish=publish,
547
527
  sections=sections or [],
548
528
  visibility=visibility,
@@ -558,12 +538,12 @@ def build_builder_server() -> FastMCP:
558
538
  def app_publish_verify(
559
539
  profile: str = DEFAULT_PROFILE,
560
540
  app_key: str = "",
561
- expected_package_tag_id: int | None = None,
541
+ expected_package_id: int | None = None,
562
542
  ) -> dict:
563
543
  return ai_builder.app_publish_verify(
564
544
  profile=profile,
565
545
  app_key=app_key,
566
- expected_package_tag_id=expected_package_tag_id,
546
+ expected_package_id=expected_package_id,
567
547
  )
568
548
 
569
549
  return server