unique-sdk 2026.26.0.dev7__tar.gz → 2026.26.0.dev8__tar.gz

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.
Files changed (84) hide show
  1. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/PKG-INFO +1 -1
  2. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/pyproject.toml +1 -1
  3. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/__init__.py +3 -0
  4. unique_sdk-2026.26.0.dev8/unique_sdk/api_resources/_dynamic_frontend.py +81 -0
  5. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/cli.py +79 -0
  6. unique_sdk-2026.26.0.dev8/unique_sdk/cli/commands/dynamic_frontend.py +116 -0
  7. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/README.md +0 -0
  8. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_api_requestor.py +0 -0
  9. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_api_resource.py +0 -0
  10. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_api_version.py +0 -0
  11. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_error.py +0 -0
  12. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_http_client.py +0 -0
  13. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_list_object.py +0 -0
  14. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_object_classes.py +0 -0
  15. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_request_options.py +0 -0
  16. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_unique_object.py +0 -0
  17. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_unique_ql.py +0 -0
  18. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_unique_response.py +0 -0
  19. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_util.py +0 -0
  20. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_version.py +0 -0
  21. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/_webhook.py +0 -0
  22. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/__init__.py +0 -0
  23. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_acronyms.py +0 -0
  24. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_agentic_table.py +0 -0
  25. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_analytics_order.py +0 -0
  26. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_benchmarking.py +0 -0
  27. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_briefing.py +0 -0
  28. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_chat_completion.py +0 -0
  29. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_content.py +0 -0
  30. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_elicitation.py +0 -0
  31. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_embedding.py +0 -0
  32. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_event.py +0 -0
  33. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_folder.py +0 -0
  34. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_group.py +0 -0
  35. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_integrated.py +0 -0
  36. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_llm_models.py +0 -0
  37. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_mcp.py +0 -0
  38. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_message.py +0 -0
  39. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_message_assessment.py +0 -0
  40. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_message_execution.py +0 -0
  41. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_message_log.py +0 -0
  42. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_message_tool.py +0 -0
  43. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_module.py +0 -0
  44. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_scheduled_task.py +0 -0
  45. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_search.py +0 -0
  46. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_search_string.py +0 -0
  47. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_short_term_memory.py +0 -0
  48. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_space.py +0 -0
  49. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_user.py +0 -0
  50. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/api_resources/_web_search.py +0 -0
  51. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/__init__.py +0 -0
  52. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/__main__.py +0 -0
  53. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/__init__.py +0 -0
  54. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/_citation_manifest.py +0 -0
  55. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/cite_file.py +0 -0
  56. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/elicitation.py +0 -0
  57. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/files.py +0 -0
  58. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/folders.py +0 -0
  59. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/mcp.py +0 -0
  60. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/navigation.py +0 -0
  61. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/read.py +0 -0
  62. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/scheduled_tasks.py +0 -0
  63. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/search.py +0 -0
  64. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/subagent.py +0 -0
  65. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/web_search.py +0 -0
  66. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/commands/web_search_config.py +0 -0
  67. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/config.py +0 -0
  68. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/formatting.py +0 -0
  69. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/shell.py +0 -0
  70. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/skills/unique-cli-elicitation/SKILL.md +0 -0
  71. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/skills/unique-cli-file-management/SKILL.md +0 -0
  72. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/skills/unique-cli-mcp/SKILL.md +0 -0
  73. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/skills/unique-cli-scheduled-tasks/SKILL.md +0 -0
  74. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/skills/unique-cli-search/SKILL.md +0 -0
  75. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/skills/unique-cli-subagent/SKILL.md +0 -0
  76. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/skills/unique-cli-web-search/SKILL.md +0 -0
  77. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/cli/state.py +0 -0
  78. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/utils/analytics_order_run.py +0 -0
  79. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/utils/benchmarking_run.py +0 -0
  80. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/utils/chat_history.py +0 -0
  81. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/utils/chat_in_space.py +0 -0
  82. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/utils/file_io.py +0 -0
  83. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/utils/sources.py +0 -0
  84. {unique_sdk-2026.26.0.dev7 → unique_sdk-2026.26.0.dev8}/unique_sdk/utils/token.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-sdk
3
- Version: 2026.26.0.dev7
3
+ Version: 2026.26.0.dev8
4
4
  Summary:
5
5
  Author: Martin Fadler, Konstantin Krauss, Andreas Hauri
6
6
  Author-email: Martin Fadler <martin.fadler@unique.ch>, Konstantin Krauss <konstantin@unique.ch>, Andreas Hauri <andreas@unique.ch>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique_sdk"
3
- version = "2026.26.0.dev7"
3
+ version = "2026.26.0.dev8"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -77,6 +77,9 @@ from unique_sdk.api_resources._message import Message as Message
77
77
  from unique_sdk.api_resources._integrated import Integrated as Integrated
78
78
  from unique_sdk.api_resources._search import Search as Search
79
79
  from unique_sdk.api_resources._content import Content as Content
80
+ from unique_sdk.api_resources._dynamic_frontend import (
81
+ DynamicFrontend as DynamicFrontend,
82
+ )
80
83
  from unique_sdk.api_resources._search_string import SearchString as SearchString
81
84
  from unique_sdk.api_resources._short_term_memory import (
82
85
  ShortTermMemory as ShortTermMemory,
@@ -0,0 +1,81 @@
1
+ from typing import Literal, cast
2
+
3
+ from typing_extensions import NotRequired, Unpack
4
+
5
+ from unique_sdk._api_resource import APIResource
6
+ from unique_sdk._request_options import RequestOptions
7
+ from unique_sdk._util import classproperty
8
+
9
+
10
+ class DynamicFrontend(APIResource["DynamicFrontend"]):
11
+ @classproperty
12
+ def OBJECT_NAME(cls) -> Literal["dynamic-frontend"]:
13
+ return "dynamic-frontend"
14
+
15
+ id: str
16
+ spaceId: str
17
+ name: str
18
+ contentId: str
19
+ status: dict[str, object] | None
20
+
21
+ class CreateParams(RequestOptions):
22
+ name: str
23
+ contentId: str
24
+
25
+ class UpdateParams(RequestOptions):
26
+ contentId: str
27
+ name: NotRequired[str | None]
28
+
29
+ @classmethod
30
+ def create(
31
+ cls,
32
+ user_id: str,
33
+ company_id: str,
34
+ **params: Unpack["DynamicFrontend.CreateParams"],
35
+ ) -> "DynamicFrontend":
36
+ return cast(
37
+ DynamicFrontend,
38
+ cls._static_request(
39
+ "post",
40
+ "/dynamic-frontend",
41
+ user_id,
42
+ company_id,
43
+ params=params,
44
+ ),
45
+ )
46
+
47
+ @classmethod
48
+ def modify(
49
+ cls,
50
+ space_id: str,
51
+ user_id: str,
52
+ company_id: str,
53
+ **params: Unpack["DynamicFrontend.UpdateParams"],
54
+ ) -> "DynamicFrontend":
55
+ return cast(
56
+ DynamicFrontend,
57
+ cls._static_request(
58
+ "patch",
59
+ f"/dynamic-frontend/{space_id}",
60
+ user_id,
61
+ company_id,
62
+ params=params,
63
+ ),
64
+ )
65
+
66
+ @classmethod
67
+ def list(
68
+ cls,
69
+ user_id: str,
70
+ company_id: str,
71
+ ) -> list["DynamicFrontend"]:
72
+ response = cls._static_request(
73
+ "get",
74
+ "/dynamic-frontend",
75
+ user_id,
76
+ company_id,
77
+ )
78
+ if isinstance(response, list):
79
+ return cast(list[DynamicFrontend], response)
80
+ data = response.get("data", []) if isinstance(response, dict) else []
81
+ return cast(list[DynamicFrontend], data)
@@ -8,6 +8,10 @@ import click
8
8
 
9
9
  from unique_sdk.cli import __version__
10
10
  from unique_sdk.cli.commands.cite_file import cmd_cite_file
11
+ from unique_sdk.cli.commands.dynamic_frontend import (
12
+ cmd_dynamic_frontend_deploy,
13
+ cmd_dynamic_frontend_list,
14
+ )
11
15
  from unique_sdk.cli.commands.elicitation import (
12
16
  cmd_elicit_ask,
13
17
  cmd_elicit_create,
@@ -53,6 +57,8 @@ from unique_sdk.cli.config import load_config
53
57
  from unique_sdk.cli.shell import UniqueShell
54
58
  from unique_sdk.cli.state import ShellState
55
59
 
60
+ _DYNAMIC_FRONTEND_ERROR_PREFIX = "dynamic-frontend "
61
+
56
62
  MAIN_HELP = """\
57
63
  Unique CLI -- Linux-like file explorer for the Unique AI Platform.
58
64
 
@@ -99,6 +105,7 @@ Examples:
99
105
  unique-cli subagent Legal "Review" Invoke a connected space/subagent
100
106
  unique-cli web-search search "x" Search the web via the public API
101
107
  unique-cli web-search crawl URL Crawl a URL via the public API
108
+ unique-cli dynamic-frontend list List manageable Dynamic Frontend spaces
102
109
  """
103
110
 
104
111
 
@@ -359,6 +366,78 @@ def read_cmd(ctx: click.Context, cont_id: str) -> None:
359
366
  click.echo(output)
360
367
 
361
368
 
369
+ @main.group(name="dynamic-frontend")
370
+ def dynamic_frontend() -> None:
371
+ """Deploy and list Dynamic Frontend spaces."""
372
+
373
+
374
+ @dynamic_frontend.command(name="deploy")
375
+ @click.option(
376
+ "--file",
377
+ "file_path",
378
+ default=None,
379
+ type=click.Path(exists=True),
380
+ help="Path to an upload-ready Dynamic Frontend ZIP bundle.",
381
+ )
382
+ @click.option(
383
+ "--content-id",
384
+ default=None,
385
+ help="Existing Knowledge Base content id for the ZIP bundle.",
386
+ )
387
+ @click.option(
388
+ "--name",
389
+ default=None,
390
+ help="Space display name. Required when creating; optional rename when updating.",
391
+ )
392
+ @click.option(
393
+ "--space-id", default=None, help="Existing Dynamic Frontend space id to update."
394
+ )
395
+ @click.option(
396
+ "--json", "output_json", is_flag=True, default=False, help="Print raw JSON."
397
+ )
398
+ @click.pass_context
399
+ def dynamic_frontend_deploy(
400
+ ctx: click.Context,
401
+ file_path: str | None,
402
+ content_id: str | None,
403
+ name: str | None,
404
+ space_id: str | None,
405
+ output_json: bool,
406
+ ) -> None:
407
+ """Create or update a Dynamic Frontend space.
408
+
409
+ \b
410
+ Examples:
411
+ unique-cli dynamic-frontend deploy --file ./app.zip --name "Revenue Dashboard"
412
+ unique-cli dynamic-frontend deploy --content-id content_123 --name "Revenue Dashboard"
413
+ unique-cli dynamic-frontend deploy --space-id assistant_123 --file ./app.zip
414
+ """
415
+ output = cmd_dynamic_frontend_deploy(
416
+ LazyState.get(ctx),
417
+ file=file_path,
418
+ content_id=content_id,
419
+ name=name,
420
+ space_id=space_id,
421
+ output_json=output_json,
422
+ )
423
+ click.echo(output)
424
+ if output.startswith(_DYNAMIC_FRONTEND_ERROR_PREFIX):
425
+ ctx.exit(1)
426
+
427
+
428
+ @dynamic_frontend.command(name="list")
429
+ @click.option(
430
+ "--json", "output_json", is_flag=True, default=False, help="Print raw JSON."
431
+ )
432
+ @click.pass_context
433
+ def dynamic_frontend_list(ctx: click.Context, output_json: bool) -> None:
434
+ """List Dynamic Frontend spaces the current user can manage."""
435
+ output = cmd_dynamic_frontend_list(LazyState.get(ctx), output_json=output_json)
436
+ click.echo(output)
437
+ if output.startswith(_DYNAMIC_FRONTEND_ERROR_PREFIX):
438
+ ctx.exit(1)
439
+
440
+
362
441
  @main.command()
363
442
  @click.argument("name_or_id")
364
443
  @click.pass_context
@@ -0,0 +1,116 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ import unique_sdk
7
+ from unique_sdk.cli.state import ShellState
8
+ from unique_sdk.utils.file_io import upload_file
9
+
10
+
11
+ def _upload_bundle(state: ShellState, file_path: str) -> str:
12
+ path = Path(file_path).expanduser().resolve()
13
+ if not path.is_file():
14
+ raise ValueError(f"local file not found: {file_path}")
15
+
16
+ if not state.scope_id:
17
+ raise ValueError("cannot upload bundle to root. cd into a folder first.")
18
+
19
+ result = upload_file(
20
+ userId=state.config.user_id,
21
+ companyId=state.config.company_id,
22
+ path_to_file=str(path),
23
+ displayed_filename=path.name,
24
+ mime_type="application/zip",
25
+ scope_or_unique_path=state.scope_id,
26
+ )
27
+ content_id = getattr(result, "id", None)
28
+ if not isinstance(content_id, str) or not content_id:
29
+ raise ValueError("upload did not return a content id")
30
+ return content_id
31
+
32
+
33
+ def _format_space(space: object) -> str:
34
+ space_id = getattr(space, "spaceId", None) or getattr(space, "id", "")
35
+ name = getattr(space, "name", "")
36
+ content_id = getattr(space, "contentId", "")
37
+ status = getattr(space, "status", None)
38
+ phase = ""
39
+ url = ""
40
+ if isinstance(status, dict):
41
+ phase = str(status.get("phase") or "")
42
+ url = str(status.get("url") or "")
43
+ return "\t".join(
44
+ part for part in [str(space_id), str(name), str(content_id), phase, url] if part
45
+ )
46
+
47
+
48
+ def cmd_dynamic_frontend_deploy(
49
+ state: ShellState,
50
+ *,
51
+ file: str | None = None,
52
+ content_id: str | None = None,
53
+ name: str | None = None,
54
+ space_id: str | None = None,
55
+ output_json: bool = False,
56
+ ) -> str:
57
+ try:
58
+ if not file and not content_id:
59
+ return "dynamic-frontend deploy: provide either --file or --content-id."
60
+ if file and content_id:
61
+ return "dynamic-frontend deploy: --file and --content-id are mutually exclusive."
62
+ if not space_id and not name:
63
+ return "dynamic-frontend deploy: provide --name when creating a Dynamic Frontend space."
64
+
65
+ resolved_content_id = _upload_bundle(state, file) if file else content_id
66
+ if not resolved_content_id:
67
+ return "dynamic-frontend deploy: expected a content id after resolving deploy input."
68
+
69
+ if space_id:
70
+ update_params: unique_sdk.DynamicFrontend.UpdateParams = {
71
+ "contentId": resolved_content_id,
72
+ }
73
+ if name is not None:
74
+ update_params["name"] = name
75
+ space = unique_sdk.DynamicFrontend.modify(
76
+ space_id,
77
+ user_id=state.config.user_id,
78
+ company_id=state.config.company_id,
79
+ **update_params,
80
+ )
81
+ action = "Updated"
82
+ else:
83
+ space = unique_sdk.DynamicFrontend.create(
84
+ user_id=state.config.user_id,
85
+ company_id=state.config.company_id,
86
+ name=name or "",
87
+ contentId=resolved_content_id,
88
+ )
89
+ action = "Created"
90
+
91
+ if output_json:
92
+ return json.dumps(dict(space), indent=2, default=str)
93
+
94
+ space_id_value = getattr(space, "spaceId", None) or getattr(space, "id", "")
95
+ name_value = getattr(space, "name", name or "")
96
+ return (
97
+ f'{action} Dynamic Frontend space "{name_value}" ({space_id_value})\n'
98
+ f"Content: {resolved_content_id}"
99
+ )
100
+ except (ValueError, unique_sdk.APIError, OSError) as e:
101
+ return f"dynamic-frontend deploy: {e}"
102
+
103
+
104
+ def cmd_dynamic_frontend_list(state: ShellState, *, output_json: bool = False) -> str:
105
+ try:
106
+ spaces = unique_sdk.DynamicFrontend.list(
107
+ user_id=state.config.user_id,
108
+ company_id=state.config.company_id,
109
+ )
110
+ if output_json:
111
+ return json.dumps(spaces, indent=2, default=str)
112
+ if not spaces:
113
+ return "No manageable Dynamic Frontend spaces found."
114
+ return "\n".join(_format_space(space) for space in spaces)
115
+ except unique_sdk.APIError as e:
116
+ return f"dynamic-frontend list: {e}"