aisc-cli 0.1.0__py3-none-any.whl

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 (92) hide show
  1. aisc/VERSION +1 -0
  2. aisc/__init__.py +49 -0
  3. aisc/__main__.py +6 -0
  4. aisc/adapters/__init__.py +1 -0
  5. aisc/adapters/config_reader.py +215 -0
  6. aisc/adapters/container_registry.py +718 -0
  7. aisc/adapters/data_root_store.py +296 -0
  8. aisc/adapters/docker_.py +1183 -0
  9. aisc/adapters/docker_gateway.py +1244 -0
  10. aisc/adapters/docker_sdk_backed.py +274 -0
  11. aisc/adapters/maintenance_lock.py +97 -0
  12. aisc/adapters/state_file.py +184 -0
  13. aisc/adapters/system.py +101 -0
  14. aisc/adapters/windows_config_reader.py +249 -0
  15. aisc/adapters/workspace_lease_store.py +189 -0
  16. aisc/application/__init__.py +1 -0
  17. aisc/application/artifact.py +265 -0
  18. aisc/application/bundle_fetch.py +836 -0
  19. aisc/application/cc_switch_provider.py +245 -0
  20. aisc/application/cc_switch_resolver.py +363 -0
  21. aisc/application/config_service.py +452 -0
  22. aisc/application/conversation.py +516 -0
  23. aisc/application/data_migration.py +440 -0
  24. aisc/application/data_root.py +275 -0
  25. aisc/application/docker_lifecycle.py +659 -0
  26. aisc/application/doctor.py +821 -0
  27. aisc/application/legacy_scan.py +252 -0
  28. aisc/application/network_subscription.py +642 -0
  29. aisc/application/profile_service.py +105 -0
  30. aisc/application/provider.py +92 -0
  31. aisc/application/repair.py +387 -0
  32. aisc/application/resources.py +270 -0
  33. aisc/application/runtime.py +1826 -0
  34. aisc/application/session.py +320 -0
  35. aisc/application/tool_versions.py +210 -0
  36. aisc/application/toolchain.py +91 -0
  37. aisc/application/update.py +286 -0
  38. aisc/application/usage.py +305 -0
  39. aisc/application/version.py +133 -0
  40. aisc/application/web_gateway.py +459 -0
  41. aisc/application/workspace_reconcile.py +318 -0
  42. aisc/applog.py +103 -0
  43. aisc/cli/__init__.py +1 -0
  44. aisc/cli/build_progress_parser.py +217 -0
  45. aisc/cli/commands/__init__.py +1 -0
  46. aisc/cli/commands/agents.py +117 -0
  47. aisc/cli/commands/artifact.py +210 -0
  48. aisc/cli/commands/build.py +566 -0
  49. aisc/cli/commands/bundle.py +107 -0
  50. aisc/cli/commands/cc_switch.py +158 -0
  51. aisc/cli/commands/config.py +104 -0
  52. aisc/cli/commands/container.py +787 -0
  53. aisc/cli/commands/conversation.py +72 -0
  54. aisc/cli/commands/data_root.py +84 -0
  55. aisc/cli/commands/logs.py +89 -0
  56. aisc/cli/commands/network.py +132 -0
  57. aisc/cli/commands/profile.py +52 -0
  58. aisc/cli/commands/provider.py +49 -0
  59. aisc/cli/commands/run.py +494 -0
  60. aisc/cli/commands/runs.py +125 -0
  61. aisc/cli/commands/runtime.py +450 -0
  62. aisc/cli/commands/serve.py +550 -0
  63. aisc/cli/commands/serve_fs.py +369 -0
  64. aisc/cli/commands/session.py +167 -0
  65. aisc/cli/commands/update.py +145 -0
  66. aisc/cli/commands/usage.py +74 -0
  67. aisc/cli/commands/wizard.py +130 -0
  68. aisc/cli/commands/workspaces.py +162 -0
  69. aisc/cli/help_i18n.py +385 -0
  70. aisc/cli/main.py +2926 -0
  71. aisc/cli/output.py +239 -0
  72. aisc/cli/tutorial.py +109 -0
  73. aisc/domain/__init__.py +1 -0
  74. aisc/domain/artifacts.py +279 -0
  75. aisc/domain/cc_switch_release.py +303 -0
  76. aisc/domain/config.py +64 -0
  77. aisc/domain/data_migration.py +259 -0
  78. aisc/domain/data_root.py +124 -0
  79. aisc/domain/docker_ownership.py +148 -0
  80. aisc/domain/gateway.py +168 -0
  81. aisc/domain/models.py +674 -0
  82. aisc/domain/toolchain.py +72 -0
  83. aisc/domain/web_services.py +355 -0
  84. aisc/domain/workspace_lease.py +139 -0
  85. aisc/schemas/__init__.py +1 -0
  86. aisc/schemas/config_schema.py +145 -0
  87. aisc_cli-0.1.0.dist-info/METADATA +420 -0
  88. aisc_cli-0.1.0.dist-info/RECORD +92 -0
  89. aisc_cli-0.1.0.dist-info/WHEEL +5 -0
  90. aisc_cli-0.1.0.dist-info/entry_points.txt +2 -0
  91. aisc_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
  92. aisc_cli-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,270 @@
1
+ """Resource location — find the AISC root directory.
2
+
3
+ Priority:
4
+ 1. Explicit ``--aisc-root`` / *explicit_root* parameter
5
+ 2. Environment variable ``AISC_ROOT``
6
+ 3. Frozen executable: adjacent ``aisc-bundle/`` directory
7
+ (bundle missing → continue to repo discovery; bundle corrupt → raise)
8
+ 4. Walk up from *cwd* discovering a repo (``.git`` + structure markers)
9
+ 5. Data-root bundles: ``<data-root>/bundles/<ver>/aisc-bundle`` installed
10
+ by ``aisc bundle fetch`` (0.1.0 A3). Inserted AFTER cwd-repo so a
11
+ developer running in the repo keeps hitting the working tree, and
12
+ BEFORE package ancestors so pip installs resolve fetched bundles.
13
+ Incompatible manifests are skipped, not errors (guide 3.3.1).
14
+ 6. Installed package ancestor fallback: walk ancestors of the aisc package
15
+ source (``Path(__file__).resolve()``) looking for structure markers.
16
+ Supports editable installs; ordinary site-packages wheels return ``None``.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import os
22
+ import sys
23
+ from pathlib import Path
24
+ from typing import Callable, Optional, List, Tuple
25
+
26
+
27
+ # ---------------------------------------------------------------------------
28
+ # Structure markers
29
+ # ---------------------------------------------------------------------------
30
+
31
+ # A2 (guide 3.2): the VERSION marker is dual-shape — repo checkouts carry
32
+ # src/aisc/VERSION (package-data since the migration), while staged bundles
33
+ # and frozen builds keep VERSION at their root (the bundle layout is an
34
+ # installed-artifact contract that must not move). The other two markers
35
+ # are single-shape.
36
+ _VERSION_MARKERS: List[str] = ["VERSION", "src/aisc/VERSION"]
37
+ _STRUCTURE_MARKERS: List[str] = ["container/Dockerfile", "config/versions.env"]
38
+
39
+
40
+ def _is_root(path: Path) -> bool:
41
+ if not any((path / m).is_file() for m in _VERSION_MARKERS):
42
+ return False
43
+ return all((path / m).is_file() for m in _STRUCTURE_MARKERS)
44
+
45
+
46
+ def _has_git(path: Path) -> bool:
47
+ return (path / ".git").exists()
48
+
49
+
50
+ def _find_repo_root(start: Path) -> Optional[Path]:
51
+ current = start.resolve()
52
+ while True:
53
+ if _has_git(current) and _is_root(current):
54
+ return current
55
+ parent = current.parent
56
+ if parent == current:
57
+ break
58
+ current = parent
59
+ return None
60
+
61
+
62
+ # ---------------------------------------------------------------------------
63
+ # Data-root bundle layer (0.1.0 A3, guide 3.3.1)
64
+ # ---------------------------------------------------------------------------
65
+
66
+ def _bundle_manifest_compatible(bundle_root: Path) -> bool:
67
+ """Runtime manifest gate at RESOLUTION time: an incompatible bundle is
68
+ skipped (not an error) — the chain continues. Imported lazily so this
69
+ module stays importable without the fetch machinery (resources.py is a
70
+ zero-dependency leaf by design)."""
71
+ from aisc import __version__
72
+ from aisc.application.bundle_fetch import bundle_compatible
73
+
74
+ ok, _reason = bundle_compatible(bundle_root, __version__)
75
+ return ok
76
+
77
+
78
+ def find_data_root_bundles(data_root: Path) -> Tuple[List[Path], List[str]]:
79
+ """Compatible bundle roots under ``<data_root>/bundles/<ver>/aisc-bundle``.
80
+
81
+ Returns (compatible_roots, skipped_notes) — incompatible/corrupt entries
82
+ are skipped with a note for the failure-path error copy (guide 3.3.1:
83
+ 全链失败时错误信息列出「发现 bundle X 但要求 CLI 版本 Y」).
84
+ """
85
+ bundles = data_root / "bundles"
86
+ if not bundles.is_dir():
87
+ return [], []
88
+ roots: List[Path] = []
89
+ skipped: List[str] = []
90
+ for child in sorted(bundles.iterdir(), reverse=True):
91
+ bundle = child / "aisc-bundle"
92
+ if not bundle.is_dir():
93
+ continue
94
+ if not _is_root(bundle):
95
+ skipped.append(f"{bundle}: missing structure markers")
96
+ continue
97
+ if not _bundle_manifest_compatible(bundle):
98
+ skipped.append(f"{bundle}: manifest does not allow this CLI version")
99
+ continue
100
+ roots.append(bundle)
101
+ return roots, skipped
102
+
103
+
104
+ def _find_installed_root(package_start: Optional[Path] = None) -> Optional[Path]:
105
+ """Walk ancestors of *package_start* looking for a valid AISC root.
106
+
107
+ Used as the final fallback when no explicit/env/frozen/repo root is found.
108
+ Supports editable installs where ``Path(__file__).resolve()`` points into
109
+ the actual repo tree. For ordinary wheel installs the walk will reach the
110
+ filesystem root without matching structure markers and return ``None``.
111
+ """
112
+ start = (package_start or Path(__file__).resolve()).parent
113
+ current = start
114
+ while True:
115
+ if _is_root(current):
116
+ return current
117
+ parent = current.parent
118
+ if parent == current:
119
+ break
120
+ current = parent
121
+ return None
122
+
123
+
124
+ # ---------------------------------------------------------------------------
125
+ # Lightweight error-info helper
126
+ # ---------------------------------------------------------------------------
127
+
128
+ class _RootSourceError(Exception):
129
+ """Indicates a root source is invalid. Carries the source label."""
130
+
131
+ def __init__(self, message: str, source: str) -> None:
132
+ super().__init__(message)
133
+ self.source = source # "--aisc-root", "AISC_ROOT", "frozen-bundle"
134
+ self.message = message # duplicate for clarity
135
+
136
+
137
+ # ---------------------------------------------------------------------------
138
+ # Frozen helper (reads nothing — fully parametric)
139
+ # ---------------------------------------------------------------------------
140
+
141
+ def _resolve_frozen_bundle(exe_path: str) -> Optional[Path]:
142
+ """Look for an adjacent ``aisc-bundle/`` relative to *exe_path*.
143
+
144
+ Returns the bundle path if valid, ``None`` when absent,
145
+ raises ``_RootSourceError`` when the bundle exists but is corrupt.
146
+ """
147
+ exe_dir = Path(exe_path).resolve().parent
148
+ bundle = exe_dir / "aisc-bundle"
149
+ if not bundle.is_dir():
150
+ return None
151
+ if not _is_root(bundle):
152
+ raise _RootSourceError(
153
+ f"Frozen bundle at {bundle} is corrupt: missing structure markers",
154
+ source="frozen-bundle",
155
+ )
156
+ return bundle
157
+
158
+
159
+ # ---------------------------------------------------------------------------
160
+ # Public API
161
+ # ---------------------------------------------------------------------------
162
+
163
+ def locate_aisc_root(
164
+ explicit_root: Optional[str] = None,
165
+ cwd: Optional[Path] = None,
166
+ is_frozen: Optional[Callable[[], bool]] = None,
167
+ executable_path: Optional[str] = None,
168
+ package_start: Optional[Path] = None,
169
+ data_root: Optional[Path] = None,
170
+ ) -> Optional[Path]:
171
+ """Find the AISC root directory.
172
+
173
+ Parameters
174
+ ----------
175
+ explicit_root:
176
+ Explicit path supplied via ``--aisc-root``.
177
+ cwd:
178
+ Starting directory for repo discovery. Defaults to ``os.getcwd()``.
179
+ is_frozen:
180
+ Callable that returns ``True`` when we are a frozen executable.
181
+ Default: ``getattr(sys, 'frozen', False)`` (lazy, at call time).
182
+ executable_path:
183
+ Path to the executable when frozen. Default: ``sys.executable``.
184
+ package_start:
185
+ Path to use as starting point for installed-package ancestor walk.
186
+ Default: ``Path(__file__).resolve()`` in this module.
187
+ Injection point for deterministic tests.
188
+ data_root:
189
+ Data root for the fetched-bundles layer (step 5). Default: lazily
190
+ ``aisc.application.data_root.shared_root()`` (local import — this
191
+ module stays a zero-dependency leaf; build.py:67 pattern).
192
+
193
+ Returns
194
+ -------
195
+ Path
196
+ The AISC root, or ``None`` if no root could be located.
197
+
198
+ Raises
199
+ ------
200
+ _RootSourceError
201
+ When an explicit/bundle/env source is provided but invalid.
202
+ The ``source`` attribute distinguishes the origin.
203
+ """
204
+ # -- 1. Explicit --
205
+ if explicit_root is not None:
206
+ p = Path(explicit_root).resolve()
207
+ if not p.is_dir():
208
+ raise _RootSourceError(
209
+ f"--aisc-root {explicit_root}: not a directory",
210
+ source="--aisc-root",
211
+ )
212
+ if not _is_root(p):
213
+ raise _RootSourceError(
214
+ f"--aisc-root {explicit_root}: missing required structure markers",
215
+ source="--aisc-root",
216
+ )
217
+ return p
218
+
219
+ # -- 2. Environment variable --
220
+ env_root = os.environ.get("AISC_ROOT")
221
+ if env_root is not None:
222
+ p = Path(env_root).resolve()
223
+ if not p.is_dir():
224
+ raise _RootSourceError(
225
+ f"AISC_ROOT={env_root}: not a directory",
226
+ source="AISC_ROOT",
227
+ )
228
+ if not _is_root(p):
229
+ raise _RootSourceError(
230
+ f"AISC_ROOT={env_root}: missing required structure markers",
231
+ source="AISC_ROOT",
232
+ )
233
+ return p
234
+
235
+ # -- 3. Frozen bundle --
236
+ # Production defaults: lazy-read sys at call time (not at import)
237
+ frozen_check = is_frozen if is_frozen is not None else (
238
+ lambda: getattr(sys, "frozen", False)
239
+ )
240
+ if frozen_check():
241
+ exe = executable_path if executable_path is not None else sys.executable
242
+ if exe:
243
+ bundle = _resolve_frozen_bundle(exe)
244
+ if bundle is not None:
245
+ return bundle
246
+ # Bundle not found → fall through to repo discovery
247
+
248
+ # -- 4. Repo discovery --
249
+ start = cwd if cwd is not None else Path.cwd()
250
+ repo = _find_repo_root(start)
251
+ if repo is not None:
252
+ return repo
253
+
254
+ # -- 5. Data-root bundles (aisc bundle fetch) --
255
+ if data_root is None:
256
+ from aisc.application.data_root import shared_root
257
+
258
+ try:
259
+ data_root = shared_root()
260
+ except Exception:
261
+ data_root = None
262
+ if data_root is not None:
263
+ candidates, _skipped = find_data_root_bundles(data_root)
264
+ if candidates:
265
+ return candidates[0]
266
+
267
+ # -- 6. Installed package ancestor fallback --
268
+ # Walks up from the aisc package source. Supports editable installs.
269
+ # Ordinary site-packages wheels will reach filesystem root and return None.
270
+ return _find_installed_root(package_start)