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,158 @@
|
|
|
1
|
+
"""``aisc cc-switch`` command layer (Stage 8d).
|
|
2
|
+
|
|
3
|
+
Thin wrappers over ``aisc.application.cc_switch_provider``: assemble the
|
|
4
|
+
request document (secrets from STDIN per the contract's ``--secret-stdin`` /
|
|
5
|
+
``--patch-stdin`` semantics — one JSON document on stdin carries both the
|
|
6
|
+
patch fields and the optional ``api_key``), then print the redacted snapshot.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
from typing import Any, Dict, Optional
|
|
14
|
+
|
|
15
|
+
from aisc.domain.models import CliError
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _read_stdin_request(*, required: bool) -> Dict[str, Any]:
|
|
19
|
+
raw = sys.stdin.read()
|
|
20
|
+
if not raw.strip():
|
|
21
|
+
if required:
|
|
22
|
+
raise CliError(
|
|
23
|
+
message="a JSON request document on stdin is required "
|
|
24
|
+
"(--secret-stdin/--patch-stdin semantics)",
|
|
25
|
+
exit_code=2,
|
|
26
|
+
error_code="AISC_ERR_USAGE",
|
|
27
|
+
)
|
|
28
|
+
return {}
|
|
29
|
+
try:
|
|
30
|
+
data = json.loads(raw)
|
|
31
|
+
except json.JSONDecodeError as exc:
|
|
32
|
+
raise CliError(
|
|
33
|
+
message=f"stdin request is not valid JSON: {exc}",
|
|
34
|
+
exit_code=2,
|
|
35
|
+
error_code="AISC_ERR_USAGE",
|
|
36
|
+
) from exc
|
|
37
|
+
if not isinstance(data, dict):
|
|
38
|
+
raise CliError(
|
|
39
|
+
message="stdin request must be a JSON object",
|
|
40
|
+
exit_code=2,
|
|
41
|
+
error_code="AISC_ERR_USAGE",
|
|
42
|
+
)
|
|
43
|
+
return data
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def cmd_cc_switch_list(args: Any) -> Dict[str, Any]:
|
|
47
|
+
from aisc.application.cc_switch_provider import list_providers
|
|
48
|
+
|
|
49
|
+
return list_providers(
|
|
50
|
+
runtime_id=args.runtime_id,
|
|
51
|
+
agent=args.agent,
|
|
52
|
+
workspace=args.workspace,
|
|
53
|
+
executor=None,
|
|
54
|
+
reveal_id=getattr(args, "reveal_id", None),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def cmd_cc_switch_add(args: Any) -> Dict[str, Any]:
|
|
59
|
+
from aisc.application.cc_switch_provider import add_provider
|
|
60
|
+
|
|
61
|
+
request = _read_stdin_request(required=True)
|
|
62
|
+
request["mode"] = getattr(args, "mode", None) or request.get("mode") or "simple"
|
|
63
|
+
if getattr(args, "provider", None):
|
|
64
|
+
request["provider"] = args.provider
|
|
65
|
+
if getattr(args, "new_id", None):
|
|
66
|
+
request["id"] = args.new_id
|
|
67
|
+
return add_provider(
|
|
68
|
+
runtime_id=args.runtime_id,
|
|
69
|
+
agent=args.agent,
|
|
70
|
+
request=request,
|
|
71
|
+
workspace=args.workspace,
|
|
72
|
+
executor=None,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def cmd_cc_switch_edit(args: Any) -> Dict[str, Any]:
|
|
77
|
+
from aisc.application.cc_switch_provider import edit_provider
|
|
78
|
+
|
|
79
|
+
request = _read_stdin_request(required=True)
|
|
80
|
+
request.setdefault("patch", {})
|
|
81
|
+
if not isinstance(request["patch"], dict):
|
|
82
|
+
raise CliError(
|
|
83
|
+
message="request.patch must be a JSON object",
|
|
84
|
+
exit_code=2,
|
|
85
|
+
error_code="AISC_ERR_USAGE",
|
|
86
|
+
)
|
|
87
|
+
return edit_provider(
|
|
88
|
+
runtime_id=args.runtime_id,
|
|
89
|
+
agent=args.agent,
|
|
90
|
+
provider_id=args.provider_id,
|
|
91
|
+
request=request,
|
|
92
|
+
workspace=args.workspace,
|
|
93
|
+
executor=None,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def cmd_cc_switch_switch(args: Any) -> Dict[str, Any]:
|
|
98
|
+
from aisc.application.cc_switch_provider import switch_provider
|
|
99
|
+
|
|
100
|
+
return switch_provider(
|
|
101
|
+
runtime_id=args.runtime_id,
|
|
102
|
+
agent=args.agent,
|
|
103
|
+
provider_id=args.provider_id,
|
|
104
|
+
workspace=args.workspace,
|
|
105
|
+
executor=None,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def cmd_cc_switch_delete(args: Any) -> Dict[str, Any]:
|
|
110
|
+
from aisc.application.cc_switch_provider import delete_provider
|
|
111
|
+
|
|
112
|
+
if not getattr(args, "confirm", False):
|
|
113
|
+
raise CliError(
|
|
114
|
+
message="delete requires --confirm",
|
|
115
|
+
exit_code=2,
|
|
116
|
+
error_code="AISC_ERR_USAGE",
|
|
117
|
+
)
|
|
118
|
+
return delete_provider(
|
|
119
|
+
runtime_id=args.runtime_id,
|
|
120
|
+
agent=args.agent,
|
|
121
|
+
provider_id=args.provider_id,
|
|
122
|
+
workspace=args.workspace,
|
|
123
|
+
executor=None,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def cmd_cc_switch_fetch_models(args: Any) -> Dict[str, Any]:
|
|
128
|
+
from aisc.application.cc_switch_provider import fetch_models
|
|
129
|
+
|
|
130
|
+
# PP r3: an optional stdin document may carry the form's unsaved
|
|
131
|
+
# api_key — forwarded to the adapter inside the same stdin channel
|
|
132
|
+
# (never argv, never logged).
|
|
133
|
+
request = _read_stdin_request(required=False)
|
|
134
|
+
api_key = str(request.get("api_key") or "")
|
|
135
|
+
# 手测 r2#3: add-mode inline probe — the form's base_url rides the same
|
|
136
|
+
# stdin channel when no provider row exists yet.
|
|
137
|
+
base_url = str(request.get("base_url") or "")
|
|
138
|
+
if base_url:
|
|
139
|
+
request["base_url"] = base_url
|
|
140
|
+
return fetch_models(
|
|
141
|
+
runtime_id=args.runtime_id,
|
|
142
|
+
agent=args.agent,
|
|
143
|
+
provider_id=args.provider_id,
|
|
144
|
+
workspace=args.workspace,
|
|
145
|
+
executor=None,
|
|
146
|
+
request={"api_key": api_key} if api_key else None,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def print_cc_switch_text(data: Optional[Dict[str, Any]]) -> None:
|
|
151
|
+
"""Human-readable output: one line per provider (secret-free)."""
|
|
152
|
+
if not isinstance(data, dict):
|
|
153
|
+
return
|
|
154
|
+
for p in data.get("providers", []):
|
|
155
|
+
current = " →" if p.get("is_current") else " "
|
|
156
|
+
key = p.get("api_key_mask") or "no-key"
|
|
157
|
+
print(f"{current} {p.get('id', ''):24} {p.get('name', ''):20} "
|
|
158
|
+
f"{p.get('base_url', '')} | {p.get('model', '')} | {key}")
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""CLI command implementations for ``aisc config validate`` and ``aisc config effective``.
|
|
2
|
+
|
|
3
|
+
Thin wrapper — all classification logic lives in config_service.ServiceResult.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json as _json
|
|
9
|
+
import sys
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
|
|
12
|
+
from aisc.application.config_service import (
|
|
13
|
+
run_config_validate, run_config_effective, ServiceResult,
|
|
14
|
+
ERR_CONFIG_INVALID, ERR_CONFIG_MISSING, ERR_PERMISSION_DENIED, ERR_GENERAL,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def cmd_config_validate(*, explicit_config=None, workspace=None, home=None,
|
|
19
|
+
env=None, platform=None) -> ServiceResult:
|
|
20
|
+
return run_config_validate(explicit_config=explicit_config, workspace=workspace,
|
|
21
|
+
home=home, env=env, platform_name=platform)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def cmd_config_effective(*, explicit_config=None, workspace=None, home=None,
|
|
25
|
+
env=None, platform=None) -> ServiceResult:
|
|
26
|
+
return run_config_effective(explicit_config=explicit_config, workspace=workspace,
|
|
27
|
+
home=home, env=env, platform_name=platform)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# Text output helpers
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
def _escape_path(p: str) -> str:
|
|
35
|
+
"""Escape control characters in path for safe text output."""
|
|
36
|
+
if not p:
|
|
37
|
+
return ""
|
|
38
|
+
out = []
|
|
39
|
+
for ch in p:
|
|
40
|
+
cp = ord(ch)
|
|
41
|
+
if cp < 0x20 or cp == 0x7f:
|
|
42
|
+
out.append(f"\\x{cp:02x}")
|
|
43
|
+
elif cp == 0x5c:
|
|
44
|
+
out.append("\\\\")
|
|
45
|
+
else:
|
|
46
|
+
out.append(ch)
|
|
47
|
+
return "".join(out)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def print_validate_text(result: ServiceResult) -> None:
|
|
51
|
+
"""Print validate result (stdout). Non-zero also prints stderr summary."""
|
|
52
|
+
print("=== Config Validate ===")
|
|
53
|
+
print()
|
|
54
|
+
for s in result.data.get("sources", []):
|
|
55
|
+
label = s.get("status", "?").upper()
|
|
56
|
+
print(f" [{label}] {_escape_path(s.get('path', ''))}")
|
|
57
|
+
if s.get("error"):
|
|
58
|
+
print(f" {s['error']}")
|
|
59
|
+
print()
|
|
60
|
+
print(f"Valid: {'yes' if result.valid else 'NO'}")
|
|
61
|
+
issues = result.data.get("issues", [])
|
|
62
|
+
if issues:
|
|
63
|
+
print()
|
|
64
|
+
print("Issues:")
|
|
65
|
+
for i in issues:
|
|
66
|
+
sev = i["severity"].upper()
|
|
67
|
+
print(f" [{sev}] {i['source']}:{i['path']}: {i['message']}")
|
|
68
|
+
print()
|
|
69
|
+
|
|
70
|
+
if result.exit_code != 0:
|
|
71
|
+
print(f"Error: {result.error_message}", file=sys.stderr)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def print_effective_text(result: ServiceResult) -> None:
|
|
75
|
+
"""Print effective result (stdout). Non-zero also prints stderr summary."""
|
|
76
|
+
print("=== Config Effective ===")
|
|
77
|
+
print()
|
|
78
|
+
for s in result.data.get("sources", []):
|
|
79
|
+
label = s.get("status", "?").upper()
|
|
80
|
+
print(f" [{label}] {_escape_path(s.get('path', ''))}")
|
|
81
|
+
print()
|
|
82
|
+
eff = result.data.get("effective")
|
|
83
|
+
if eff is None:
|
|
84
|
+
print("Effective: (none)")
|
|
85
|
+
else:
|
|
86
|
+
print("Effective:")
|
|
87
|
+
print(_json.dumps(eff, indent=2, ensure_ascii=False))
|
|
88
|
+
prov = result.data.get("provenance", {})
|
|
89
|
+
if prov:
|
|
90
|
+
print()
|
|
91
|
+
print("Provenance:")
|
|
92
|
+
for k, v in sorted(prov.items()):
|
|
93
|
+
print(f" {k}: {v}")
|
|
94
|
+
issues = result.data.get("issues", [])
|
|
95
|
+
if issues:
|
|
96
|
+
print()
|
|
97
|
+
print("Issues:")
|
|
98
|
+
for i in issues:
|
|
99
|
+
sev = i["severity"].upper()
|
|
100
|
+
print(f" [{sev}] {i['source']}:{i['path']}: {i['message']}")
|
|
101
|
+
print()
|
|
102
|
+
|
|
103
|
+
if result.exit_code != 0:
|
|
104
|
+
print(f"Error: {result.error_message}", file=sys.stderr)
|