flash-sim 0.0.1__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 (143) hide show
  1. flash/__init__.py +157 -0
  2. flash/_bootstrap.py +35 -0
  3. flash/_core/__init__.py +39 -0
  4. flash/_core/credentials/__init__.py +44 -0
  5. flash/_core/credentials/_config.py +191 -0
  6. flash/_core/credentials/_core.py +277 -0
  7. flash/_core/credentials/_flash_entries.py +83 -0
  8. flash/_core/credentials/_minimal.py +73 -0
  9. flash/_core/credentials/api_keys.py +225 -0
  10. flash/_core/credentials/deepseek.py +187 -0
  11. flash/_core/credentials/flash_ssh.py +472 -0
  12. flash/_core/credentials/gitee.py +220 -0
  13. flash/_core/credentials/manage.py +359 -0
  14. flash/_core/credentials/pypi.py +144 -0
  15. flash/_core/credentials.py +1103 -0
  16. flash/_core/interface.py +90 -0
  17. flash/_core/registry.py +35 -0
  18. flash/_core/schema.py +118 -0
  19. flash/config/__init__.py +105 -0
  20. flash/flash_run/PROCESS.md +435 -0
  21. flash/flash_run/README.md +205 -0
  22. flash/flash_run/__init__.py +25 -0
  23. flash/flash_run/env/README.md +272 -0
  24. flash/flash_run/env/__init__.py +12 -0
  25. flash/flash_run/env/env_manager.py +535 -0
  26. flash/flash_run/env/resource_config.py +650 -0
  27. flash/flash_run/remote/README.md +313 -0
  28. flash/flash_run/remote/__init__.py +20 -0
  29. flash/flash_run/remote/remote_check.py +70 -0
  30. flash/flash_run/remote/remote_deploy.py +792 -0
  31. flash/flash_run/remote/remote_workflow.py +142 -0
  32. flash/flash_run/remote/route_tester.py +465 -0
  33. flash/flash_run/remote/ssh_check.py +107 -0
  34. flash/input_gen/README.md +535 -0
  35. flash/input_gen/__init__.py +170 -0
  36. flash/input_gen/gen_Grid_markRefineDerefine/GEN_GRID_GUIDE.md +68 -0
  37. flash/input_gen/gen_Grid_markRefineDerefine/README.md +327 -0
  38. flash/input_gen/gen_Grid_markRefineDerefine/__init__.py +0 -0
  39. flash/input_gen/gen_Grid_markRefineDerefine/generator.py +567 -0
  40. flash/input_gen/gen_checker/GEN_CHECKER_GUIDE.md +148 -0
  41. flash/input_gen/gen_checker/__init__.py +6 -0
  42. flash/input_gen/gen_checker/checker.py +274 -0
  43. flash/input_gen/gen_checker/ploter/__init__.py +13 -0
  44. flash/input_gen/gen_checker/ploter/density_plotter.py +195 -0
  45. flash/input_gen/gen_checker/ploter/pulse_plotter.py +158 -0
  46. flash/input_gen/gen_checker/ploter/ray_plotter.py +277 -0
  47. flash/input_gen/gen_config/GEN_CONFIG_GUIDE.md +534 -0
  48. flash/input_gen/gen_config/__init__.py +9 -0
  49. flash/input_gen/gen_config/generator.py +205 -0
  50. flash/input_gen/gen_eos_op/GEN_EOS_OP_GUIDE.md +224 -0
  51. flash/input_gen/gen_eos_op/__init__.py +10 -0
  52. flash/input_gen/gen_eos_op/generator.py +481 -0
  53. flash/input_gen/gen_f90/__init__.py +0 -0
  54. flash/input_gen/gen_makefile/GEN_MAKEFILE_GUIDE.md +56 -0
  55. flash/input_gen/gen_makefile/__init__.py +9 -0
  56. flash/input_gen/gen_makefile/generator.py +32 -0
  57. flash/input_gen/gen_newpara/README.md +400 -0
  58. flash/input_gen/gen_newpara/RP_Reference.md +639 -0
  59. flash/input_gen/gen_newpara/__init__.py +19 -0
  60. flash/input_gen/gen_newpara/generator.py +643 -0
  61. flash/input_gen/gen_otherf90s/GEN_OTHER_F90S_GUIDE.md +231 -0
  62. flash/input_gen/gen_otherf90s/__init__.py +0 -0
  63. flash/input_gen/gen_par/GEN_PAR_GUIDE.md +657 -0
  64. flash/input_gen/gen_par/__init__.py +18 -0
  65. flash/input_gen/gen_par/defaults.py +510 -0
  66. flash/input_gen/gen_par/generator.py +445 -0
  67. flash/input_gen/gen_par/materials.py +141 -0
  68. flash/input_gen/gen_shell_script/GEN_SHELL_SCRIPT_GUIDE.md +120 -0
  69. flash/input_gen/gen_shell_script/__init__.py +12 -0
  70. flash/input_gen/gen_shell_script/generator.py +850 -0
  71. flash/input_gen/gen_sim_data/GEN_SIM_DATA_GUIDE.md +306 -0
  72. flash/input_gen/gen_sim_data/__init__.py +10 -0
  73. flash/input_gen/gen_sim_data/generator.py +87 -0
  74. flash/input_gen/gen_sim_init/GEN_SIM_INIT_GUIDE.md +216 -0
  75. flash/input_gen/gen_sim_init/__init__.py +9 -0
  76. flash/input_gen/gen_sim_init/generator.py +104 -0
  77. flash/input_gen/gen_sim_initblock/GEN_SIM_INITBLOCK_GUIDE.md +330 -0
  78. flash/input_gen/gen_sim_initblock/__init__.py +17 -0
  79. flash/input_gen/gen_sim_initblock/generator.py +432 -0
  80. flash/input_gen/gen_sim_initblock/grid.py +278 -0
  81. flash/input_gen/gen_sim_initblock/visualizer.py +250 -0
  82. flash/interface.py +398 -0
  83. flash/output_processors/__init__.py +50 -0
  84. flash/output_processors/conftest.py +41 -0
  85. flash/output_processors/docs/api_reference.md +597 -0
  86. flash/output_processors/docs/how_to_add_custom_variables.md +417 -0
  87. flash/output_processors/docs/output_processors_usage.md +596 -0
  88. flash/output_processors/docs/performance_tuning.md +365 -0
  89. flash/output_processors/docs/workflow.md +101 -0
  90. flash/output_processors/hdf5processor/__init__.py +21 -0
  91. flash/output_processors/hdf5processor/flash_hdf5.py +1224 -0
  92. flash/output_processors/loader/__init__.py +7 -0
  93. flash/output_processors/loader/data_loader.py +392 -0
  94. flash/output_processors/parallel.py +580 -0
  95. flash/output_processors/plotter/__init__.py +8 -0
  96. flash/output_processors/plotter/plot_generator.py +471 -0
  97. flash/output_processors/plotter/plot_style.py +240 -0
  98. flash/physics/ele_nonlocal/Knudsen_Nonlocal_Summary.md +576 -0
  99. flash/projects/__init__.py +0 -0
  100. flash/projects/projects_demo/README.md +94 -0
  101. flash/projects/projects_demo/__init__.py +17 -0
  102. flash/projects/projects_demo/ch_center_demo/__init__.py +16 -0
  103. flash/projects/projects_demo/ch_center_demo/plot_compare.py +169 -0
  104. flash/projects/projects_demo/ch_center_demo/run_compare.py +148 -0
  105. flash/scenarios/README.md +669 -0
  106. flash/scenarios/SNBtest/__init__.py +0 -0
  107. flash/scenarios/__init__.py +18 -0
  108. flash/scenarios/base.py +50 -0
  109. flash/scenarios/center_evolution/__init__.py +2 -0
  110. flash/scenarios/center_evolution/ch_center/__init__.py +361 -0
  111. flash/scenarios/center_evolution/ch_center/laserslab1d_local_custom.py +804 -0
  112. flash/scenarios/collision_compression/__init__.py +38 -0
  113. flash/scenarios/flash_demo/__init__.py +0 -0
  114. flash/scenarios/flash_demo/demo_hpc/__init__.py +0 -0
  115. flash/scenarios/flash_demo/demo_hpc/_plot_utils.py +128 -0
  116. flash/scenarios/flash_demo/demo_hpc/laserslab1d_hpc_demo_batch.py +1035 -0
  117. flash/scenarios/flash_demo/demo_hpc/laserslab1d_supercomputer_demo.py +778 -0
  118. flash/scenarios/flash_demo/demo_hpc/remote_analysis.py +428 -0
  119. flash/scenarios/flash_demo/demo_hpc/remote_plot_script.py +444 -0
  120. flash/scenarios/flash_demo/demo_hpc/remote_ssh_helper.py +457 -0
  121. flash/scenarios/flash_demo/demo_local/__init__.py +0 -0
  122. flash/scenarios/flash_demo/demo_local/laserslab1d_local_demo.py +343 -0
  123. flash/scenarios/flash_demo/hello_flash/README.md +447 -0
  124. flash/scenarios/flash_demo/hello_flash/analyze_density.py +416 -0
  125. flash/scenarios/flash_demo/hello_flash/deploy_flash.py +828 -0
  126. flash/scenarios/interpolator.py +286 -0
  127. flash/scenarios/plasma_preparation/__init__.py +1 -0
  128. flash/scenarios/registry.py +61 -0
  129. flash/scenarios/simulator.py +1045 -0
  130. flash/test/__init__.py +1 -0
  131. flash/test/flash_mathtest_custom01.py +413 -0
  132. flash/test/math_test.py +596 -0
  133. flash/utils/__init__.py +9 -0
  134. flash/utils/check_hdf5_time.py +22 -0
  135. flash/utils/convert_pdf_to_md.py +134 -0
  136. flash/utils/convert_text_to_md.py +102 -0
  137. flash/utils/extract_knowledge.py +200 -0
  138. flash_sim-0.0.1.dist-info/METADATA +811 -0
  139. flash_sim-0.0.1.dist-info/RECORD +143 -0
  140. flash_sim-0.0.1.dist-info/WHEEL +4 -0
  141. flash_sim-0.0.1.dist-info/entry_points.txt +3 -0
  142. flash_sim-0.0.1.dist-info/licenses/LICENSE +386 -0
  143. flash_sim-0.0.1.dist-info/licenses/NOTICE +138 -0
@@ -0,0 +1,220 @@
1
+ """
2
+ Flash 凭据管理 -- Gitee 凭据管理
3
+ =====================================
4
+ 管理 Gitee 访问令牌。
5
+
6
+ 用法:
7
+ python -m flash._core.credentials.gitee # 交互菜单
8
+ python -m flash._core.credentials.gitee setup # 设置 Gitee 凭据
9
+ python -m flash._core.credentials.gitee show # 查看 Gitee 凭据
10
+ """
11
+
12
+ import sys
13
+ from typing import Optional
14
+
15
+ from ._core import (
16
+ ask_one,
17
+ get_credential_manager,
18
+ mask_secret,
19
+ )
20
+ from ._config import DEFAULT_USER_NAME, DEFAULT_PASSWORD
21
+
22
+
23
+ def setup_gitee() -> bool:
24
+ """设置 Gitee 凭据。"""
25
+ cm = get_credential_manager()
26
+
27
+ print(f"\n 📝 设置 Gitee 凭据")
28
+ print(f" {'─' * 50}")
29
+
30
+ # 获取默认值
31
+ entry_def = next((e for e in __import__("_config", fromlist=["ENTRIES"]).ENTRIES if e["name"] == "gitee"), None)
32
+ if entry_def is None:
33
+ print("\n ❌ 未找到 Gitee 配置定义。")
34
+ return False
35
+
36
+ data = {}
37
+ for key, label, default in entry_def["fields"]:
38
+ # 使用全局默认值
39
+ if key == "username" and default == "hello":
40
+ default = DEFAULT_USER_NAME
41
+ elif key == "token" and default == "123":
42
+ default = DEFAULT_PASSWORD
43
+ data[key] = ask_one(label, default)
44
+
45
+ cm.set("gitee", data)
46
+ print(f"\n ✅ Gitee 凭据已保存。")
47
+ return True
48
+
49
+
50
+ def show_gitee(raw: bool = False) -> None:
51
+ """查看 Gitee 凭据。"""
52
+ cm = get_credential_manager()
53
+ data = cm.get("gitee")
54
+
55
+ if not data:
56
+ print("\n [提示] 尚未设置 Gitee 凭据。")
57
+ return
58
+
59
+ print(f"\n {'─' * 50}")
60
+ print(f" Gitee 凭据")
61
+ print(f" {'─' * 50}")
62
+
63
+ display = data if raw else mask_secret(data)
64
+ for k, v in display.items():
65
+ print(f" {k:12s}: {v}")
66
+ print(f" {'─' * 50}")
67
+ if not raw:
68
+ print(f" 💡 使用 --raw 查看原始 token")
69
+
70
+
71
+ def delete_gitee() -> bool:
72
+ """删除 Gitee 凭据。"""
73
+ cm = get_credential_manager()
74
+
75
+ if not cm.get("gitee"):
76
+ print("\n [提示] 尚未设置 Gitee 凭据。")
77
+ return False
78
+
79
+ confirm = input("\n 确认删除 Gitee 凭据? [y/N]: ").strip().lower()
80
+ if confirm != "y":
81
+ print("\n [已取消]")
82
+ return False
83
+
84
+ try:
85
+ cm.delete("gitee")
86
+ print("\n ✅ Gitee 凭据已删除。")
87
+ return True
88
+ except Exception as e:
89
+ print(f"\n ❌ 删除失败: {e}")
90
+ return False
91
+
92
+
93
+ def test_gitee() -> bool:
94
+ """测试 Gitee 连接。"""
95
+ cm = get_credential_manager()
96
+ data = cm.get("gitee")
97
+
98
+ if not data:
99
+ print("\n [提示] 尚未设置 Gitee 凭据。")
100
+ return False
101
+
102
+ token = data.get("token")
103
+ username = data.get("username")
104
+ repo_url = data.get("repo_url", "")
105
+
106
+ print(f"\n 🔧 测试 Gitee 连接...")
107
+ print(f" 用户名: {username}")
108
+ print(f" 仓库: {repo_url}")
109
+
110
+ # 解析仓库 owner 和 repo name
111
+ import re
112
+ match = re.search(r"gitee\.com[:/]([^/]+)/([^/.]+)", repo_url)
113
+ if not match:
114
+ print(f"\n ⚠️ 无法解析仓库 URL: {repo_url}")
115
+ print(f" 请确保 URL 格式类似: https://gitee.com/owner/repo.git")
116
+ return False
117
+
118
+ owner, repo = match.group(1), match.group(2).rstrip(".git")
119
+
120
+ # 测试 1: 验证 token (获取用户信息)
121
+ print(f"\n [1/2] 验证访问令牌...")
122
+ import urllib.request
123
+ import urllib.error
124
+ import json
125
+
126
+ try:
127
+ # 获取用户信息
128
+ url = f"https://gitee.com/api/v5/user?access_token={token}"
129
+ req = urllib.request.Request(url)
130
+ with urllib.request.urlopen(req, timeout=10) as resp:
131
+ user_info = json.loads(resp.read().decode("utf-8"))
132
+
133
+ if "login" in user_info:
134
+ print(f" ✅ Token 有效 (用户: {user_info.get('login')})")
135
+ else:
136
+ print(f" ❌ Token 验证失败: {user_info}")
137
+ return False
138
+
139
+ except urllib.error.HTTPError as e:
140
+ error_msg = e.read().decode("utf-8")
141
+ print(f" ❌ Token 验证失败 (HTTP {e.code}): {error_msg}")
142
+ return False
143
+ except Exception as e:
144
+ print(f" ❌ 连接失败: {e}")
145
+ return False
146
+
147
+ # 测试 2: 验证仓库访问
148
+ print(f"\n [2/2] 验证仓库访问...")
149
+ try:
150
+ url = f"https://gitee.com/api/v5/repos/{owner}/{repo}?access_token={token}"
151
+ req = urllib.request.Request(url)
152
+ with urllib.request.urlopen(req, timeout=10) as resp:
153
+ repo_info = json.loads(resp.read().decode("utf-8"))
154
+
155
+ if "name" in repo_info:
156
+ print(f" ✅ 仓库可访问: {repo_info.get('name')}")
157
+ print(f" 仓库 URL: {repo_info.get('html_url', repo_url)}")
158
+ else:
159
+ print(f" ⚠️ 仓库访问异常: {repo_info}")
160
+ return False
161
+
162
+ except urllib.error.HTTPError as e:
163
+ if e.code == 404:
164
+ print(f" ⚠️ 仓库不存在或无访问权限: {owner}/{repo}")
165
+ print(f" 请检查仓库 URL 是否正确,以及 token 是否有访问权限")
166
+ else:
167
+ error_msg = e.read().decode("utf-8")
168
+ print(f" ❌ 仓库访问失败 (HTTP {e.code}): {error_msg}")
169
+ return False
170
+ except Exception as e:
171
+ print(f" ❌ 连接失败: {e}")
172
+ return False
173
+
174
+ print(f"\n ✅ Gitee 连接测试通过!")
175
+ return True
176
+
177
+
178
+ def interactive_menu() -> None:
179
+ """交互式菜单。"""
180
+ while True:
181
+ print(f"\n {'=' * 50}")
182
+ print(f" Gitee 凭据管理")
183
+ print(f" {'=' * 50}")
184
+ print(" [1] 设置/修改 Gitee 凭据")
185
+ print(" [2] 查看 Gitee 凭据")
186
+ print(" [3] 删除 Gitee 凭据")
187
+ print(" [4] 测试 Gitee 连接")
188
+ print(" [0] 返回")
189
+
190
+ choice = input("\n 请选择 [0-4]: ").strip()
191
+
192
+ if choice == "1":
193
+ setup_gitee()
194
+ elif choice == "2":
195
+ raw = input(" 显示原始 token? [y/N]: ").strip().lower() == "y"
196
+ show_gitee(raw)
197
+ elif choice == "3":
198
+ delete_gitee()
199
+ elif choice == "4":
200
+ test_gitee()
201
+ elif choice == "0":
202
+ break
203
+ else:
204
+ print(" [无效选择]")
205
+
206
+
207
+ if __name__ == "__main__":
208
+ main_arg = sys.argv[1] if len(sys.argv) > 1 else None
209
+
210
+ if main_arg == "setup":
211
+ setup_gitee()
212
+ elif main_arg == "show":
213
+ raw = "--raw" in sys.argv
214
+ show_gitee(raw)
215
+ elif main_arg == "del":
216
+ delete_gitee()
217
+ elif main_arg == "test":
218
+ test_gitee()
219
+ else:
220
+ interactive_menu()
@@ -0,0 +1,359 @@
1
+ """
2
+ Flash 凭据管理 -- 统一管理脚本
3
+ ==============================
4
+ 主入口菜单,可导航到各个凭据管理模块。2
5
+
6
+ 用法:
7
+ python -m flash._core.credentials.manage # 主菜单 (模块方式,推荐)
8
+ python -m flash._core.credentials.manage setup # 一键设置所有凭据
9
+ python -m flash._core.credentials.manage ssh # 直接进入 SSH 管理
10
+ python -m flash._core.credentials.manage gitee # 直接进入 Gitee 管理
11
+ python -m flash._core.credentials.manage api # 直接进入 API 管理
12
+ """
13
+
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ # =======================================================================
18
+ # 直接运行时的自动转换:
19
+ # 如果 __package__ 为 None,说明是直接运行 (python manage.py),
20
+ # 此时用 runpy 以模块方式重新运行,使相对导入能正常工作。
21
+ # =======================================================================
22
+ if __name__ == "__main__" and __package__ is None:
23
+ _CRED_DIR = Path(__file__).resolve().parent # .../flash/_core/credentials/
24
+ _CORE_DIR = _CRED_DIR.parent # .../flash/_core/
25
+ _FLASH_DIR = _CORE_DIR.parent # .../flash/
26
+ _PARENT = _FLASH_DIR.parent # flash/ 的父目录
27
+
28
+ # standalone 模式: 将 flash/ 的父目录加入 sys.path
29
+ if str(_PARENT) not in sys.path:
30
+ sys.path.insert(0, str(_PARENT))
31
+
32
+ # 用 runpy 以模块方式重新运行自己
33
+ import warnings
34
+ warnings.filterwarnings("ignore", category=RuntimeWarning, module="runpy")
35
+
36
+ import runpy
37
+ sys.argv.insert(0, __file__) # 保留原始 argv
38
+ runpy.run_module("flash._core.credentials.manage", run_name="__main__")
39
+ sys.exit(0)
40
+
41
+
42
+ # =======================================================================
43
+ # 所有导入使用相对导入 (flash 独立包模式)
44
+ # =======================================================================
45
+ from ._core import get_credential_manager, get_user_name, get_user_name_source, set_user_name
46
+ from ._config import DEFAULT_USER_NAME
47
+
48
+
49
+ def _print_header():
50
+ """打印标题和状态。"""
51
+ try:
52
+ cm = get_credential_manager()
53
+ existing = cm.list_all()
54
+ real_keys = [k for k in existing if k != "__meta__"]
55
+ user_name = get_user_name()
56
+ except SystemExit:
57
+ real_keys = []
58
+ try:
59
+ user_name = get_user_name()
60
+ except Exception:
61
+ user_name = "hello"
62
+
63
+ print(f"\n {'=' * 55}")
64
+ print(f" Flash 凭据管理中心")
65
+ source = get_user_name_source()
66
+ source_note = f" (继承 {source})" if source == "physimx_core" else " (独立)"
67
+ print(f" 当前用户: {user_name}{source_note}")
68
+ if real_keys:
69
+ print(f" 已保存凭据: {', '.join(real_keys)}")
70
+ else:
71
+ print(f" 当前未保存任何凭据")
72
+ print(f" {'=' * 55}")
73
+
74
+
75
+ def _set_user_name() -> None:
76
+ """设置默认用户名。"""
77
+ current = get_user_name()
78
+ print(f"\n 👤 设置默认用户名")
79
+ print(f" {'─' * 55}")
80
+ print(f" 当前用户名: {current}")
81
+ print(f" {'─' * 55}")
82
+
83
+ new_name = input(f"\n 输入新用户名 [回车保留 '{current}']: ").strip()
84
+ if not new_name:
85
+ print("\n [未修改]")
86
+ return
87
+
88
+ set_user_name(new_name)
89
+ print(f"\n ✅ 用户名已设为: {new_name}")
90
+
91
+
92
+ def _setup_ssh_presconfigured() -> None:
93
+ """设置预配置 SSH 账户(带路由测试)。"""
94
+ from ._config import ENTRIES, ENTRIES_BY_NAME
95
+ from .flash_ssh import _get_ssh_username, test_all_routes, test_route
96
+ from ._core import get_credential_manager, ask_one, set_primary_ssh, collect_ssh_accounts
97
+
98
+ cm = get_credential_manager()
99
+
100
+ # 收集预配置账号
101
+ presconfigured = []
102
+ for entry in ENTRIES:
103
+ if not entry["name"].startswith("flash_ssh"):
104
+ continue
105
+ name = entry["name"]
106
+ title = entry.get("title", name)
107
+ ssh_user = _get_ssh_username(name)
108
+ presconfigured.append((name, title, ssh_user, entry))
109
+
110
+ if not presconfigured:
111
+ print("\n [提示] 未找到预配置 SSH 账户。")
112
+ return
113
+
114
+ # 显示预配置账号
115
+ print(f"\n 📋 预配置 SSH 账户:")
116
+ for i, (name, title, ssh_user, entry) in enumerate(presconfigured, 1):
117
+ existing = cm.get(name)
118
+ status = "✅ 已配置" if existing else "⬜ 未配置"
119
+ print(f" [{i}] {title}")
120
+ print(f" 账号: {ssh_user} [{status}]")
121
+
122
+ print(f"\n 选项:")
123
+ print(f" [1-{len(presconfigured)}] 设置对应的预配置账号")
124
+ print(f" [A] 设置所有预配置账号")
125
+ print(f" [0] 跳过")
126
+
127
+ choice = input(f"\n 请选择: ").strip().lower()
128
+
129
+ if choice == "0":
130
+ print("\n [已跳过]")
131
+ return
132
+ elif choice == "a":
133
+ # 设置所有预配置账号
134
+ accounts = collect_ssh_accounts(cm)
135
+
136
+ # 先进行路由测试
137
+ print(f"\n 🔍 进行路由测试...")
138
+ try:
139
+ results = test_all_routes()
140
+ if results:
141
+ print(f"\n {'─' * 60}")
142
+ print(f" 路由测试结果 (延迟越低越好)")
143
+ print(f" {'─' * 60}")
144
+ sorted_results = sorted(results.items(), key=lambda x: x[1][1])
145
+ for name, (ok, delay, host, port, title) in sorted_results:
146
+ status = f"✅ {delay:.3f}s" if ok else f"❌ 超时"
147
+ print(f" {title}")
148
+ print(f" {host}:{port} → {status}")
149
+
150
+ # 自动选择延迟最低的作为主账户
151
+ if accounts:
152
+ best_name = sorted_results[0][0]
153
+ set_primary_ssh(cm, best_name)
154
+ print(f"\n ✅ 已自动选择延迟最低的账号作为主账户: {best_name}")
155
+ except Exception as e:
156
+ print(f"\n ⚠️ 路由测试失败: {e}")
157
+
158
+ # 依次设置每个预配置账号
159
+ for name, title, ssh_user, entry in presconfigured:
160
+ print(f"\n {'=' * 60}")
161
+ print(f" 📝 设置: {title}")
162
+ print(f" {'=' * 60}")
163
+ print(f" 账号: {ssh_user}")
164
+ print(f" {'─' * 60}")
165
+
166
+ data = {}
167
+ for key, label, default in entry.get("fields", []):
168
+ if key == "password":
169
+ print(f"\n ⚠️ 请为账号 {ssh_user} 设置密码")
170
+ data[key] = ask_one(label, default)
171
+
172
+ # 如果连接模式是 manual,询问额外字段
173
+ if data.get("connection_mode") == "manual":
174
+ print("\n [手动模式] 需要额外信息:")
175
+ for key, label, default in entry.get("manual_fields", []):
176
+ data[key] = ask_one(label, default)
177
+
178
+ cm.set(name, data)
179
+ print(f"\n ✅ {title} 已保存。")
180
+
181
+ print(f"\n ✅ 所有预配置 SSH 账号设置完成!")
182
+ elif choice.isdigit() and 1 <= int(choice) <= len(presconfigured):
183
+ idx = int(choice) - 1
184
+ name, title, ssh_user, entry = presconfigured[idx]
185
+
186
+ print(f"\n 📝 设置: {title}")
187
+ print(f" {'─' * 50}")
188
+ print(f" 账号: {ssh_user}")
189
+ print(f" {'─' * 50}")
190
+
191
+ # 先进行路由测试
192
+ try:
193
+ results = test_all_routes()
194
+ if name in results:
195
+ ok, delay, host, port, _ = results[name]
196
+ status = f"✅ {delay:.3f}s" if ok else f"❌ 超时"
197
+ print(f"\n 🔍 路由测试: {host}:{port} → {status}")
198
+ except Exception:
199
+ pass
200
+
201
+ data = {}
202
+ for key, label, default in entry.get("fields", []):
203
+ if key == "password":
204
+ print(f"\n ⚠️ 请为账号 {ssh_user} 设置密码")
205
+ data[key] = ask_one(label, default)
206
+
207
+ if data.get("connection_mode") == "manual":
208
+ print("\n [手动模式] 需要额外信息:")
209
+ for key, label, default in entry.get("manual_fields", []):
210
+ data[key] = ask_one(label, default)
211
+
212
+ cm.set(name, data)
213
+ print(f"\n ✅ {title} 已保存。")
214
+
215
+ # 如果是第一个账户,设为主账户
216
+ accounts = collect_ssh_accounts(cm)
217
+ if len(accounts) == 1:
218
+ set_primary_ssh(cm, name)
219
+ print(f" ✅ 已设为默认主账户。")
220
+ else:
221
+ print("\n [无效选择]")
222
+
223
+
224
+ def _setup_all() -> None:
225
+ """一键设置所有凭据。"""
226
+ user_name = get_user_name()
227
+ print(f"\n 🚀 一键设置所有凭据")
228
+ print(f" {'─' * 55}")
229
+ print(f" 默认值: 用户名={user_name}, 密码=123")
230
+ print(f" {'─' * 55}\n")
231
+
232
+ # 1. FLASH SSH
233
+ print(" [1/4] FLASH SSH 凭据")
234
+ print(f" {'─' * 55}")
235
+ _setup_ssh_presconfigured()
236
+
237
+ # 2. Gitee
238
+ print(f"\n [2/4] Gitee 凭据")
239
+ print(f" {'─' * 55}")
240
+ from .gitee import setup_gitee
241
+ setup_gitee()
242
+
243
+ # 3. DeepSeek API
244
+ print(f"\n [3/4] DeepSeek API 凭据")
245
+ print(f" {'─' * 55}")
246
+ from .api_keys import setup_api
247
+ setup_api("deepseek_api")
248
+
249
+ print(f"\n ✅ 所有凭据设置完成!")
250
+
251
+
252
+ def main():
253
+ """主入口。"""
254
+ args = sys.argv[1:]
255
+
256
+ # 命令行模式
257
+ if args:
258
+ if args[0] in ("setup", "all"):
259
+ force = "--force" in args or "-f" in args
260
+ _setup_all()
261
+ return
262
+ elif args[0] == "ssh":
263
+ from .flash_ssh import interactive_menu
264
+ cm = get_credential_manager()
265
+ interactive_menu(cm)
266
+ return
267
+ elif args[0] == "gitee":
268
+ from .gitee import interactive_menu
269
+ interactive_menu()
270
+ return
271
+ elif args[0] == "api":
272
+ from .api_keys import interactive_menu
273
+ interactive_menu()
274
+ return
275
+ elif args[0] in ("pypi", "py"):
276
+ from .pypi import setup_pypi, show_pypi
277
+ if "--show" in args:
278
+ show_pypi()
279
+ else:
280
+ setup_pypi(args[1] if len(args) > 1 and not args[1].startswith("-") else None)
281
+ return
282
+
283
+ # 交互模式
284
+ try:
285
+ get_credential_manager()
286
+ except SystemExit:
287
+ return
288
+
289
+ while True:
290
+ _print_header()
291
+ print(" [1] 一键设置所有凭据")
292
+ print(" [2] FLASH SSH 账户管理 (增/删/改)")
293
+ print(" [3] Gitee 访问令牌管理 (设置/查看/删除)")
294
+ print(" [4] API 密钥管理 (AI API)")
295
+ print(" [5] PyPI 发布令牌管理 (pypi.org / test.pypi.org)")
296
+ print(" [6] 查看所有已保存凭据")
297
+ print(" [7] 设置默认用户名 (当前: {}, 默认: {})".format(get_user_name(), DEFAULT_USER_NAME))
298
+ print(" [0] 退出")
299
+
300
+ choice = input("\n 请选择 [0-7]: ").strip()
301
+
302
+ if choice == "1":
303
+ _setup_all()
304
+ elif choice == "2":
305
+ from .flash_ssh import interactive_menu
306
+ cm = get_credential_manager()
307
+ interactive_menu(cm)
308
+ elif choice == "3":
309
+ from .gitee import interactive_menu
310
+ interactive_menu()
311
+ elif choice == "4":
312
+ from .api_keys import interactive_menu
313
+ interactive_menu()
314
+ elif choice == "5":
315
+ from .pypi import setup_pypi, show_pypi
316
+ print("\n [P] 设置令牌")
317
+ print(" [V] 查看状态")
318
+ sub = input(" 请选择 [P/V]: ").strip().lower()
319
+ if sub in ("v", "view"):
320
+ show_pypi()
321
+ else:
322
+ setup_pypi()
323
+ elif choice == "6":
324
+ cm = get_credential_manager()
325
+ existing = cm.list_all()
326
+ real_keys = [k for k in existing if k != "__meta__"]
327
+ if not real_keys:
328
+ print("\n 当前没有保存任何凭据。")
329
+ else:
330
+ print(f"\n {'─' * 55}")
331
+ print(f" 已保存凭据 ({len(real_keys)} 组)")
332
+ print(f" {'─' * 55}")
333
+ for name, data in existing.items():
334
+ if name == "__meta__":
335
+ continue
336
+ print(f"\n [{name}]")
337
+ for k, v in data.items():
338
+ print(f" {k:12s}: {v}")
339
+ print(f"\n {'─' * 55}")
340
+ # 显示 Flash 专属存储位置
341
+ try:
342
+ storage_dir = cm._cred_dir
343
+ except AttributeError:
344
+ # 兼容旧版(无 subdir 支持)
345
+ from pathlib import Path
346
+ storage_dir = Path.home() / ".physimx" / "flash"
347
+ print(f" 存储位置: {storage_dir}/")
348
+ input("\n 按回车返回...")
349
+ elif choice == "7":
350
+ _set_user_name()
351
+ elif choice == "0":
352
+ print("\n 再见!\n")
353
+ break
354
+ else:
355
+ print(" [无效选择]")
356
+
357
+
358
+ if __name__ == "__main__":
359
+ main()