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.
- aisc/VERSION +1 -0
- aisc/__init__.py +49 -0
- aisc/__main__.py +6 -0
- aisc/adapters/__init__.py +1 -0
- aisc/adapters/config_reader.py +215 -0
- aisc/adapters/container_registry.py +718 -0
- aisc/adapters/data_root_store.py +296 -0
- aisc/adapters/docker_.py +1183 -0
- aisc/adapters/docker_gateway.py +1244 -0
- aisc/adapters/docker_sdk_backed.py +274 -0
- aisc/adapters/maintenance_lock.py +97 -0
- aisc/adapters/state_file.py +184 -0
- aisc/adapters/system.py +101 -0
- aisc/adapters/windows_config_reader.py +249 -0
- aisc/adapters/workspace_lease_store.py +189 -0
- aisc/application/__init__.py +1 -0
- aisc/application/artifact.py +265 -0
- aisc/application/bundle_fetch.py +836 -0
- aisc/application/cc_switch_provider.py +245 -0
- aisc/application/cc_switch_resolver.py +363 -0
- aisc/application/config_service.py +452 -0
- aisc/application/conversation.py +516 -0
- aisc/application/data_migration.py +440 -0
- aisc/application/data_root.py +275 -0
- aisc/application/docker_lifecycle.py +659 -0
- aisc/application/doctor.py +821 -0
- aisc/application/legacy_scan.py +252 -0
- aisc/application/network_subscription.py +642 -0
- aisc/application/profile_service.py +105 -0
- aisc/application/provider.py +92 -0
- aisc/application/repair.py +387 -0
- aisc/application/resources.py +270 -0
- aisc/application/runtime.py +1826 -0
- aisc/application/session.py +320 -0
- aisc/application/tool_versions.py +210 -0
- aisc/application/toolchain.py +91 -0
- aisc/application/update.py +286 -0
- aisc/application/usage.py +305 -0
- aisc/application/version.py +133 -0
- aisc/application/web_gateway.py +459 -0
- aisc/application/workspace_reconcile.py +318 -0
- aisc/applog.py +103 -0
- aisc/cli/__init__.py +1 -0
- aisc/cli/build_progress_parser.py +217 -0
- aisc/cli/commands/__init__.py +1 -0
- aisc/cli/commands/agents.py +117 -0
- aisc/cli/commands/artifact.py +210 -0
- aisc/cli/commands/build.py +566 -0
- aisc/cli/commands/bundle.py +107 -0
- aisc/cli/commands/cc_switch.py +158 -0
- aisc/cli/commands/config.py +104 -0
- aisc/cli/commands/container.py +787 -0
- aisc/cli/commands/conversation.py +72 -0
- aisc/cli/commands/data_root.py +84 -0
- aisc/cli/commands/logs.py +89 -0
- aisc/cli/commands/network.py +132 -0
- aisc/cli/commands/profile.py +52 -0
- aisc/cli/commands/provider.py +49 -0
- aisc/cli/commands/run.py +494 -0
- aisc/cli/commands/runs.py +125 -0
- aisc/cli/commands/runtime.py +450 -0
- aisc/cli/commands/serve.py +550 -0
- aisc/cli/commands/serve_fs.py +369 -0
- aisc/cli/commands/session.py +167 -0
- aisc/cli/commands/update.py +145 -0
- aisc/cli/commands/usage.py +74 -0
- aisc/cli/commands/wizard.py +130 -0
- aisc/cli/commands/workspaces.py +162 -0
- aisc/cli/help_i18n.py +385 -0
- aisc/cli/main.py +2926 -0
- aisc/cli/output.py +239 -0
- aisc/cli/tutorial.py +109 -0
- aisc/domain/__init__.py +1 -0
- aisc/domain/artifacts.py +279 -0
- aisc/domain/cc_switch_release.py +303 -0
- aisc/domain/config.py +64 -0
- aisc/domain/data_migration.py +259 -0
- aisc/domain/data_root.py +124 -0
- aisc/domain/docker_ownership.py +148 -0
- aisc/domain/gateway.py +168 -0
- aisc/domain/models.py +674 -0
- aisc/domain/toolchain.py +72 -0
- aisc/domain/web_services.py +355 -0
- aisc/domain/workspace_lease.py +139 -0
- aisc/schemas/__init__.py +1 -0
- aisc/schemas/config_schema.py +145 -0
- aisc_cli-0.1.0.dist-info/METADATA +420 -0
- aisc_cli-0.1.0.dist-info/RECORD +92 -0
- aisc_cli-0.1.0.dist-info/WHEEL +5 -0
- aisc_cli-0.1.0.dist-info/entry_points.txt +2 -0
- aisc_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- 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)
|