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
aisc/cli/help_i18n.py ADDED
@@ -0,0 +1,385 @@
1
+ """Help-text localization: follow the OS locale (manual test ask 2026-09-09).
2
+
3
+ The parser keeps English help strings as the single source of truth —
4
+ zero per-flag maintenance, zero risk to non-Chinese environments. When the
5
+ user's locale is Chinese, we translate at the OUTPUT layer by patching
6
+ argparse: builtin wording (``usage:``/``options``/error phrasing) via the
7
+ module-level ``_`` hook, and per-argument help via a wrapper around
8
+ ``HelpFormatter._expand_help`` (exact full-string match — a miss keeps
9
+ English, nothing is partially rewritten).
10
+
11
+ Encoding safety: the translated text is plain Han Chinese + ASCII, which
12
+ zh-CN Windows GBK consoles encode fine; the CLI's existing
13
+ ``sys.stdout.reconfigure(errors="replace")`` remains the last-resort guard
14
+ for anything exotic (emoji degrade to ``?`` instead of crashing).
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import os
20
+ from typing import Callable, Dict, Optional
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Locale detection
24
+ # ---------------------------------------------------------------------------
25
+
26
+ def is_zh_locale() -> bool:
27
+ """True when the OS/user locale is Chinese (zh*, e.g. zh_CN.UTF-8)."""
28
+ for var in ("LC_ALL", "LC_MESSAGES", "LANG", "LANGUAGE"):
29
+ val = os.environ.get(var)
30
+ if val and val.split(".", 1)[0].split("@", 1)[0].lower().startswith("zh"):
31
+ return True
32
+ # Windows often has none of the POSIX vars set — ask the CRT.
33
+ try:
34
+ import locale as _locale
35
+
36
+ pref = _locale.getdefaultlocale()[0]
37
+ if pref and pref.lower().startswith("zh"):
38
+ return True
39
+ except Exception:
40
+ pass
41
+ return False
42
+
43
+
44
+ # ---------------------------------------------------------------------------
45
+ # Dictionaries (exact full-string keys; miss ⇒ English passes through)
46
+ # ---------------------------------------------------------------------------
47
+
48
+ # argparse builtin wording and error templates
49
+ _BUILTIN: Dict[str, str] = {
50
+ "usage: ": "用法: ",
51
+ "options": "选项",
52
+ "optional arguments": "选项",
53
+ "positional arguments": "位置参数",
54
+ "show this help message and exit": "显示此帮助信息并退出",
55
+ "unrecognized arguments: %s": "无法识别的参数: %s",
56
+ "invalid choice: %r (choose from %s)": "无效选择: %r(可选: %s)",
57
+ "the following arguments are required: %s": "缺少必填参数: %s",
58
+ "expected one argument": "缺少参数值",
59
+ "expected at least one argument": "至少需要一个参数",
60
+ "not allowed with argument %s": "不能与参数 %s 同时使用",
61
+ "argument %s: %s": "参数 %s: %s",
62
+ }
63
+
64
+ # per-argument help strings (exact match against the expanded help text)
65
+ HELP_ZH: Dict[str, str] = {
66
+ # --- global flags ---
67
+ "Output format (default: text)": "输出格式(默认: text)",
68
+ "Disable ANSI color output": "禁用 ANSI 彩色输出",
69
+ "Path to AISC repository root": "AISC 仓库根目录路径",
70
+ "Enable JSONL event stream output (build/run commands)":
71
+ "启用 JSONL 事件流输出(build/run 命令)",
72
+ # --- version / doctor / serve / build ---
73
+ "Show version information": "显示版本信息",
74
+ "Run environment diagnostics": "运行环境诊断",
75
+ "Serve remote callers: newline-delimited JSON frames on stdio":
76
+ "服务远端调用方:stdio 换行分隔 JSON 帧",
77
+ "speak the frame protocol on stdin/stdout (the only transport in this version)":
78
+ "在 stdin/stdout 上讲帧协议(本版本唯一传输)",
79
+ "Build Docker image": "构建 Docker 镜像",
80
+ "Image tag (default: super-claude:latest)": "镜像标签(默认: super-claude:latest)",
81
+ "Disable Docker build cache": "禁用 Docker 构建缓存",
82
+ "Always pull the base image": "始终拉取基础镜像",
83
+ "Plan the build without executing": "只规划构建不执行",
84
+ "cc-switch version: 'latest' (default) or vX.Y.Z ":
85
+ "cc-switch 版本: 'latest'(默认)或 vX.Y.Z ",
86
+ "cc-switch release channel (default: stable; ":
87
+ "cc-switch 发布通道(默认: stable; ",
88
+ "Build from a resolver manifest file (offline / ":
89
+ "从解析清单文件构建(离线 / ",
90
+ "Fail the build when the cc-switch version cannot be ":
91
+ "cc-switch 版本无法确定时使构建失败 ",
92
+ # --- run ---
93
+ "Activate a workspace (detached container)": "激活工作区(detached 容器)",
94
+ "Workspace directory to activate (e.g. ./ or /home/user/proj)":
95
+ "要激活的工作区目录(如 ./ 或 /home/user/proj)",
96
+ "Re-activate from history (see `aisc runs`); explicit flags win":
97
+ "从历史恢复(见 `aisc runs`);显式参数优先",
98
+ "Docker image (default: super-claude:latest)":
99
+ "Docker 镜像(默认: super-claude:latest)",
100
+ "Workspace alias — also the container name prefix; ":
101
+ "工作区别名—兼作容器名前缀; ",
102
+ "Network mode: direct or proxy (default: direct)":
103
+ "网络模式: direct 或 proxy(默认: direct)",
104
+ "Compatibility alias for --network proxy (prefer --network proxy)":
105
+ "--network proxy 的兼容别名(建议直接用 --network proxy)",
106
+ "Plan the activation without executing": "只规划激活不执行",
107
+ "Container label for multi-container addressing (optional)":
108
+ "多容器寻址用标签(可选)",
109
+ # --- agent sugar (f-strings expand to per-agent text) ---
110
+ "Open claude in the active workspace": "在活跃工作区容器内打开 claude",
111
+ "Open codex in the active workspace": "在活跃工作区容器内打开 codex",
112
+ "Target a registered workspace instead of the active one":
113
+ "指定已注册工作区(而非当前活跃工作区)",
114
+ "Container name (overrides registry discovery)": "容器名(覆盖注册表发现)",
115
+ "Args passed to claude verbatim (use -- first: aisc claude -- -c)":
116
+ "原样透传给 claude 的参数(-- 开头: aisc claude -- -c)",
117
+ "Args passed to codex verbatim (use -- first: aisc codex -- -c)":
118
+ "原样透传给 codex 的参数(-- 开头: aisc codex -- -c)",
119
+ # --- runs / workspaces ---
120
+ "Activation history (aisc run records)": "激活历史(aisc run 记录)",
121
+ "Manage running CLI workspaces": "管理运行中的 CLI 工作区",
122
+ "Stop & remove every RUNNING CLI-owned workspace":
123
+ "停删全部运行中的 CLI 工作区",
124
+ # --- config ---
125
+ "Config management": "配置管理",
126
+ "Validate config files": "校验配置文件",
127
+ "Explicit user config file path": "显式用户配置文件路径",
128
+ "Workspace root path": "工作区根路径",
129
+ "Show effective config": "显示生效配置",
130
+ "Alias for 'config effective' (compatibility name)":
131
+ "'config effective' 的兼容别名",
132
+ # --- profile ---
133
+ "Profile management": "Profile 管理",
134
+ "List available profiles": "列出可用 profile",
135
+ "Show profile details": "显示 profile 详情",
136
+ "Profile name (default: safe)": "profile 名(默认: safe)",
137
+ # --- status / stop / restart / shell / switch ---
138
+ "Show container status": "显示容器状态",
139
+ "Target container by label": "按标签指定容器",
140
+ "Stop & remove the active workspace container": "停删当前活跃工作区容器",
141
+ "Stop & remove EVERY CLI-owned container (Workbench runtimes untouched)":
142
+ "停删全部 CLI 容器(Workbench 运行时不碰)",
143
+ "Restart the container": "重启容器",
144
+ "Open a bash shell in the container": "在容器内打开 bash shell",
145
+ "One-shot command in the container (use -- first: aisc shell -- ls -la)":
146
+ "容器内单发命令(-- 开头: aisc shell -- ls -la)",
147
+ "Switch AI provider in the container": "切换容器内 AI provider",
148
+ "Provider id or alias for quick switch (e.g. deepseek)":
149
+ "快速切换用 provider id 或别名(如 deepseek)",
150
+ # --- provider ---
151
+ "Manage AI provider configuration": "管理 AI provider 配置",
152
+ "Open the secure interactive editor for a provider":
153
+ "打开 provider 的安全交互编辑器",
154
+ "Provider ID (e.g., deepseek, zhipu, kimi)": "provider ID(如 deepseek、zhipu、kimi)",
155
+ "Target agent (default: claude)": "目标 agent(默认: claude)",
156
+ "Show the current provider status for an agent (Workbench)":
157
+ "显示 agent 当前 provider 状态(Workbench 用)",
158
+ "Runtime ID (UUID v4)": "运行时 ID(UUID v4)",
159
+ "Agent (claude|codex)": "agent(claude|codex)",
160
+ "Workspace path (default: current directory)": "工作区路径(默认: 当前目录)",
161
+ # --- cc-switch ---
162
+ "Manage cc-switch providers (list/add/edit/delete)":
163
+ "管理 cc-switch provider(增删改查)",
164
+ "List providers (secret-free snapshot)": "列出 provider(无密钥快照)",
165
+ "Add a provider (request JSON on stdin)": "添加 provider(stdin 收 JSON 请求)",
166
+ "simple = preset provider + api key; custom = full ":
167
+ "simple = 预置 provider + api key; custom = 完整 ",
168
+ "Preset provider id (simple mode, e.g. deepseek)":
169
+ "预置 provider id(simple 模式,如 deepseek)",
170
+ "Provider id to create (default: preset id / name slug)":
171
+ "要创建的 provider id(默认: 预置 id / 名字 slug)",
172
+ "Edit a provider (patch JSON on stdin)": "编辑 provider(stdin 收 patch JSON)",
173
+ "Provider ID to edit": "要编辑的 provider ID",
174
+ "Activate a provider (make it current)": "激活 provider(设为当前)",
175
+ "Provider ID to activate": "要激活的 provider ID",
176
+ "Delete a provider": "删除 provider",
177
+ "Provider ID to delete": "要删除的 provider ID",
178
+ "Required confirmation flag": "必需的确认开关",
179
+ "Fetch the remote model list for a provider": "拉取 provider 的远端模型列表",
180
+ "Provider ID to query": "要查询的 provider ID",
181
+ # --- network ---
182
+ "Network management (mihomo subscription for container TUN)":
183
+ "网络管理(容器 TUN 的 mihomo 订阅)",
184
+ "Manage the proxy subscription (IDEA-2)": "管理代理订阅(IDEA-2)",
185
+ "Import a subscription (URL on stdin — a credential, ":
186
+ "导入订阅(stdin 收 URL—凭据, ",
187
+ "Import manually supplied subscription content ":
188
+ "导入手动提供的订阅内容 ",
189
+ "Re-fetch the stored subscription URL": "重新拉取已存的订阅 URL",
190
+ "Persist a Rust-side download ({url, content_b64, userinfo} JSON ":
191
+ "持久化 Rust 侧下载({url, content_b64, userinfo} JSON ",
192
+ "Show subscription status (secret-free)": "显示订阅状态(无密钥)",
193
+ "Remove the stored subscription": "删除已存的订阅",
194
+ # --- usage ---
195
+ "Provider token usage statistics (all workspaces)":
196
+ "provider token 用量统计(全部工作区)",
197
+ "Subscription status + per-provider token usage":
198
+ "订阅状态 + 各 provider token 用量",
199
+ "Time window (default: 7d)": "时间窗(默认: 7d)",
200
+ "Limit to one workspace path (default: all)": "限定单个工作区(默认: 全部)",
201
+ # --- logs ---
202
+ "Lifecycle event log (full-flow debugging timeline)":
203
+ "生命周期事件日志(全流程调试时间线)",
204
+ "Show recent lifecycle events (secret-free by construction)":
205
+ "显示近期生命周期事件(构造上无密钥)",
206
+ "Number of recent events (default: 200; current file only)":
207
+ "近期事件条数(默认: 200; 仅当前文件)",
208
+ "Filter by writer (default: all)": "按写入方过滤(默认: 全部)",
209
+ "Print the log file path (for scripts and the Workbench)":
210
+ "打印日志文件路径(脚本与 Workbench 用)",
211
+ # --- ps / maintenance ---
212
+ "List all registered containers": "列出全部已注册容器",
213
+ "Installer-facing Docker lifecycle ops": "面向安装器的 Docker 生命周期操作",
214
+ "Read-only ownership classification": "只读所有权分类",
215
+ "Install context drives legacy-image evidence rules":
216
+ "安装上下文决定旧镜像证据规则",
217
+ "Upgrade-captured old image ID (temporary evidence; repeatable)":
218
+ "升级时捕获的旧镜像 ID(临时证据; 可重复)",
219
+ "Remove owned/legacy containers+images": "清除自有/旧版容器与镜像",
220
+ "No-cache rebuild with old-ID handoff": "无缓存重建(旧 ID 交接)",
221
+ "Bundle root (contains Dockerfile)": "bundle 根目录(含 Dockerfile)",
222
+ "Read-only docker system df summary": "只读 docker system df 摘要",
223
+ "Prune builder cache + dangling images (until-filtered)":
224
+ "清理构建缓存与悬空镜像(until 过滤)",
225
+ "Only clear cache entries older than this (default 24)":
226
+ "仅清理超过此时长的缓存(默认 24)",
227
+ # --- runtime ---
228
+ "Runtime control plane (Workbench Phase 0)": "运行时控制面(Workbench Phase 0)",
229
+ "Preflight checks for runtime start": "运行时启动前置检查",
230
+ "Runtime ID (UUID v4, provided by Workbench)":
231
+ "运行时 ID(UUID v4, Workbench 提供)",
232
+ "Network mode (default: direct)": "网络模式(默认: direct)",
233
+ "Runtime scope (default: project)": "运行时作用域(默认: project)",
234
+ "Owner identifier (default: workbench)": "所有者标识(默认: workbench)",
235
+ "Start a Workbench runtime": "启动 Workbench 运行时",
236
+ "Host path to mihomo config.yaml to mount for --network proxy ":
237
+ "--network proxy 挂载的宿主 mihomo config.yaml 路径 ",
238
+ "F2 (D-10): full host-tools MCP URL incl. token query; forwarded ":
239
+ "F2 (D-10): 宿主工具 MCP 完整 URL(含 token query); 转发 ",
240
+ "PERF P8 (D-13): container memory limit (docker --memory, e.g. 3g)":
241
+ "PERF P8 (D-13): 容器内存上限(docker --memory, 如 3g)",
242
+ "PERF P8 (D-13): container CPU limit (docker --cpus, e.g. 1.5)":
243
+ "PERF P8 (D-13): 容器 CPU 上限(docker --cpus, 如 1.5)",
244
+ "List runtimes with Docker reconciliation": "列出运行时(含 Docker 对账)",
245
+ "Filter by owner (e.g. workbench)": "按所有者过滤(如 workbench)",
246
+ "Show a single runtime": "显示单个运行时",
247
+ "Snapshot + services in one call (poll path)": "快照 + 服务一次拿(轮询路径)",
248
+ "Stop a runtime (keep container + metadata)": "停止运行时(保留容器与元数据)",
249
+ "docker stop grace period in seconds (1..600; default 10)":
250
+ "docker stop 宽限秒数(1..600; 默认 10)",
251
+ "Restart a runtime with original config": "按原配置重启运行时",
252
+ "Remove a runtime (container + registry)": "移除运行时(容器 + 注册表)",
253
+ "Remove even if the runtime is running": "运行中亦强制移除",
254
+ "Web service gateway info + registered services": "Web 服务网关信息 + 已注册服务",
255
+ "List registered services (default)": "列出已注册服务(默认)",
256
+ "Register a service port (ops/test entry)": "注册服务端口(运维/测试入口)",
257
+ "Container service port (1024..65535)": "容器内服务端口(1024..65535)",
258
+ "Display label (safe short text)": "显示名(安全短文本)",
259
+ "Unregister a service port": "注销服务端口",
260
+ "Classify + auto-recycle stale runtimes for a workspace":
261
+ "分类并自动回收工作区的陈旧运行时",
262
+ "This Workbench instance's UUID v4": "本 Workbench 实例的 UUID v4",
263
+ "Optional cross-check: expected sha256 workspace key":
264
+ "可选交叉校验: 期望的 sha256 工作区键",
265
+ "Workspace lease claim/heartbeat/release/inspect":
266
+ "工作区租约(领取/心跳/释放/查看)",
267
+ "Lease ID (heartbeat/release match guard)": "租约 ID(心跳/释放匹配保护)",
268
+ # --- session ---
269
+ "Session data plane (Workbench Phase 0)": "会话数据面(Workbench Phase 0)",
270
+ "Open an interactive agent session": "打开交互 agent 会话",
271
+ "Session ID (UUID v4)": "会话 ID(UUID v4)",
272
+ "Agent type (claude|codex|bash|cc-switch)": "agent 类型(claude|codex|bash|cc-switch)",
273
+ "Provider conversation ID to resume (claude|codex only, v2.1.8 T4)":
274
+ "要续接的 provider 会话 ID(仅 claude|codex, v2.1.8 T4)",
275
+ "List sessions in a runtime": "列出运行时内会话",
276
+ "Terminate a session": "终止会话",
277
+ "Grace period in seconds before SIGKILL (default: 5.0)":
278
+ "SIGKILL 前宽限秒数(默认: 5.0)",
279
+ # --- conversation ---
280
+ "Agent conversation discovery (Workbench v2.1.8)":
281
+ "agent 会话发现(Workbench v2.1.8)",
282
+ "List agent history conversations in a workspace":
283
+ "列出工作区 agent 历史会话",
284
+ "Validate a conversation is resumable (captured, JSON)":
285
+ "校验会话可续接(captured, JSON)",
286
+ "Provider-native conversation ID (UUID)": "provider 原生会话 ID(UUID)",
287
+ "Agent type (claude|codex)": "agent 类型(claude|codex)",
288
+ "Delete a conversation's session file": "删除会话的 session 文件",
289
+ "Set a conversation's display title": "设置会话显示标题",
290
+ "New display title (sanitized, ≤80 chars)":
291
+ "新显示标题(净化, ≤80 字符)",
292
+ # --- artifact ---
293
+ "Agent Artifact fact protocol (Stage 3)": "agent 产物事实协议(Stage 3)",
294
+ "Record an agent artifact fact": "记录 agent 产物事实",
295
+ "Runtime ID (UUID v4; default: env AISC_RUNTIME_ID)":
296
+ "运行时 ID(UUID v4; 默认: 环境变量 AISC_RUNTIME_ID)",
297
+ "Session ID (UUID v4; default: env AISC_TERMINAL_SESSION_ID)":
298
+ "会话 ID(UUID v4; 默认: 环境变量 AISC_TERMINAL_SESSION_ID)",
299
+ "Producer agent (default: env AISC_AGENT)":
300
+ "产出 agent(默认: 环境变量 AISC_AGENT)",
301
+ "Workspace-relative path of the artifact": "产物的 workspace 相对路径",
302
+ "media type, e.g. text/markdown": "媒体类型, 如 text/markdown",
303
+ "Human label (<=256 chars)": "人类可读标签(<=256 字符)",
304
+ "previous relative path (required for renamed)": "原相对路径(renamed 时必填)",
305
+ "List artifact records": "列出产物记录",
306
+ "Filter by session id": "按会话 id 过滤",
307
+ "Filter by kind": "按 kind 过滤",
308
+ "Inspect one artifact by id": "按 id 查看单个产物",
309
+ "Artifact ID (UUID)": "产物 ID(UUID)",
310
+ "Remove a session's registry": "清除某会话的注册数据",
311
+ # --- data-root ---
312
+ "Data root diagnostics and legacy migration": "数据根诊断与旧布局迁移",
313
+ "Resolve + legacy findings + manifest state": "解析 + 旧布局发现 + 清单状态",
314
+ "Migrate legacy layout into the data root": "旧布局迁入数据根",
315
+ "Report the plan without touching anything": "只报告计划不做改动",
316
+ "Execute the migration (default when --dry-run is absent)":
317
+ "执行迁移(无 --dry-run 时默认)",
318
+ "Move unknown files to the migration quarantine ":
319
+ "未知文件移入迁移隔离区 ",
320
+ "Undo one migration via its manifest": "按清单回滚一次迁移",
321
+ "Manifest path (default: this workspace's manifest)":
322
+ "清单路径(默认: 本工作区清单)",
323
+ }
324
+
325
+
326
+ # ---------------------------------------------------------------------------
327
+ # Installation (output-layer patches; restore returned for tests)
328
+ # ---------------------------------------------------------------------------
329
+
330
+ _active_restore: Optional[Callable[[], None]] = None
331
+
332
+
333
+ def install(force: bool = False) -> Optional[Callable[[], None]]:
334
+ """Patch argparse for Chinese help output when the locale says so.
335
+
336
+ Returns a restore callable (tests), or None when nothing was installed
337
+ (non-Chinese locale, or already active — the patch is process-global,
338
+ e.g. a prior main() on a zh locale). Idempotent.
339
+ """
340
+ if not force and not is_zh_locale():
341
+ return None
342
+
343
+ import argparse
344
+
345
+ if getattr(argparse.HelpFormatter._expand_help, "_aisc_zh", False):
346
+ return None # already installed
347
+
348
+ saved_gettext = argparse._
349
+ saved_expand = argparse.HelpFormatter._expand_help
350
+
351
+ def _zh_builtin(s: str) -> str:
352
+ return _BUILTIN.get(s, saved_gettext(s))
353
+
354
+ def _expand_zh(self, action): # type: ignore[no-untyped-def]
355
+ text = saved_expand(self, action)
356
+ return HELP_ZH.get(text) or _BUILTIN.get(text, text)
357
+
358
+ _expand_zh._aisc_zh = True # type: ignore[attr-defined]
359
+
360
+ argparse._ = _zh_builtin
361
+ argparse.HelpFormatter._expand_help = _expand_zh
362
+
363
+ def restore() -> None:
364
+ argparse._ = saved_gettext
365
+ argparse.HelpFormatter._expand_help = saved_expand
366
+
367
+ global _active_restore
368
+ _active_restore = restore
369
+ return restore
370
+
371
+
372
+ def uninstall() -> None:
373
+ """Tear down an active install (idempotent; test hygiene)."""
374
+ global _active_restore
375
+ if _active_restore is not None:
376
+ _active_restore()
377
+ _active_restore = None
378
+
379
+
380
+ def maybe_install() -> None:
381
+ """Convenience entry for main(): locale check inside, never raises."""
382
+ try:
383
+ install()
384
+ except Exception:
385
+ pass # localization is cosmetic — never take the CLI down