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.
- flash/__init__.py +157 -0
- flash/_bootstrap.py +35 -0
- flash/_core/__init__.py +39 -0
- flash/_core/credentials/__init__.py +44 -0
- flash/_core/credentials/_config.py +191 -0
- flash/_core/credentials/_core.py +277 -0
- flash/_core/credentials/_flash_entries.py +83 -0
- flash/_core/credentials/_minimal.py +73 -0
- flash/_core/credentials/api_keys.py +225 -0
- flash/_core/credentials/deepseek.py +187 -0
- flash/_core/credentials/flash_ssh.py +472 -0
- flash/_core/credentials/gitee.py +220 -0
- flash/_core/credentials/manage.py +359 -0
- flash/_core/credentials/pypi.py +144 -0
- flash/_core/credentials.py +1103 -0
- flash/_core/interface.py +90 -0
- flash/_core/registry.py +35 -0
- flash/_core/schema.py +118 -0
- flash/config/__init__.py +105 -0
- flash/flash_run/PROCESS.md +435 -0
- flash/flash_run/README.md +205 -0
- flash/flash_run/__init__.py +25 -0
- flash/flash_run/env/README.md +272 -0
- flash/flash_run/env/__init__.py +12 -0
- flash/flash_run/env/env_manager.py +535 -0
- flash/flash_run/env/resource_config.py +650 -0
- flash/flash_run/remote/README.md +313 -0
- flash/flash_run/remote/__init__.py +20 -0
- flash/flash_run/remote/remote_check.py +70 -0
- flash/flash_run/remote/remote_deploy.py +792 -0
- flash/flash_run/remote/remote_workflow.py +142 -0
- flash/flash_run/remote/route_tester.py +465 -0
- flash/flash_run/remote/ssh_check.py +107 -0
- flash/input_gen/README.md +535 -0
- flash/input_gen/__init__.py +170 -0
- flash/input_gen/gen_Grid_markRefineDerefine/GEN_GRID_GUIDE.md +68 -0
- flash/input_gen/gen_Grid_markRefineDerefine/README.md +327 -0
- flash/input_gen/gen_Grid_markRefineDerefine/__init__.py +0 -0
- flash/input_gen/gen_Grid_markRefineDerefine/generator.py +567 -0
- flash/input_gen/gen_checker/GEN_CHECKER_GUIDE.md +148 -0
- flash/input_gen/gen_checker/__init__.py +6 -0
- flash/input_gen/gen_checker/checker.py +274 -0
- flash/input_gen/gen_checker/ploter/__init__.py +13 -0
- flash/input_gen/gen_checker/ploter/density_plotter.py +195 -0
- flash/input_gen/gen_checker/ploter/pulse_plotter.py +158 -0
- flash/input_gen/gen_checker/ploter/ray_plotter.py +277 -0
- flash/input_gen/gen_config/GEN_CONFIG_GUIDE.md +534 -0
- flash/input_gen/gen_config/__init__.py +9 -0
- flash/input_gen/gen_config/generator.py +205 -0
- flash/input_gen/gen_eos_op/GEN_EOS_OP_GUIDE.md +224 -0
- flash/input_gen/gen_eos_op/__init__.py +10 -0
- flash/input_gen/gen_eos_op/generator.py +481 -0
- flash/input_gen/gen_f90/__init__.py +0 -0
- flash/input_gen/gen_makefile/GEN_MAKEFILE_GUIDE.md +56 -0
- flash/input_gen/gen_makefile/__init__.py +9 -0
- flash/input_gen/gen_makefile/generator.py +32 -0
- flash/input_gen/gen_newpara/README.md +400 -0
- flash/input_gen/gen_newpara/RP_Reference.md +639 -0
- flash/input_gen/gen_newpara/__init__.py +19 -0
- flash/input_gen/gen_newpara/generator.py +643 -0
- flash/input_gen/gen_otherf90s/GEN_OTHER_F90S_GUIDE.md +231 -0
- flash/input_gen/gen_otherf90s/__init__.py +0 -0
- flash/input_gen/gen_par/GEN_PAR_GUIDE.md +657 -0
- flash/input_gen/gen_par/__init__.py +18 -0
- flash/input_gen/gen_par/defaults.py +510 -0
- flash/input_gen/gen_par/generator.py +445 -0
- flash/input_gen/gen_par/materials.py +141 -0
- flash/input_gen/gen_shell_script/GEN_SHELL_SCRIPT_GUIDE.md +120 -0
- flash/input_gen/gen_shell_script/__init__.py +12 -0
- flash/input_gen/gen_shell_script/generator.py +850 -0
- flash/input_gen/gen_sim_data/GEN_SIM_DATA_GUIDE.md +306 -0
- flash/input_gen/gen_sim_data/__init__.py +10 -0
- flash/input_gen/gen_sim_data/generator.py +87 -0
- flash/input_gen/gen_sim_init/GEN_SIM_INIT_GUIDE.md +216 -0
- flash/input_gen/gen_sim_init/__init__.py +9 -0
- flash/input_gen/gen_sim_init/generator.py +104 -0
- flash/input_gen/gen_sim_initblock/GEN_SIM_INITBLOCK_GUIDE.md +330 -0
- flash/input_gen/gen_sim_initblock/__init__.py +17 -0
- flash/input_gen/gen_sim_initblock/generator.py +432 -0
- flash/input_gen/gen_sim_initblock/grid.py +278 -0
- flash/input_gen/gen_sim_initblock/visualizer.py +250 -0
- flash/interface.py +398 -0
- flash/output_processors/__init__.py +50 -0
- flash/output_processors/conftest.py +41 -0
- flash/output_processors/docs/api_reference.md +597 -0
- flash/output_processors/docs/how_to_add_custom_variables.md +417 -0
- flash/output_processors/docs/output_processors_usage.md +596 -0
- flash/output_processors/docs/performance_tuning.md +365 -0
- flash/output_processors/docs/workflow.md +101 -0
- flash/output_processors/hdf5processor/__init__.py +21 -0
- flash/output_processors/hdf5processor/flash_hdf5.py +1224 -0
- flash/output_processors/loader/__init__.py +7 -0
- flash/output_processors/loader/data_loader.py +392 -0
- flash/output_processors/parallel.py +580 -0
- flash/output_processors/plotter/__init__.py +8 -0
- flash/output_processors/plotter/plot_generator.py +471 -0
- flash/output_processors/plotter/plot_style.py +240 -0
- flash/physics/ele_nonlocal/Knudsen_Nonlocal_Summary.md +576 -0
- flash/projects/__init__.py +0 -0
- flash/projects/projects_demo/README.md +94 -0
- flash/projects/projects_demo/__init__.py +17 -0
- flash/projects/projects_demo/ch_center_demo/__init__.py +16 -0
- flash/projects/projects_demo/ch_center_demo/plot_compare.py +169 -0
- flash/projects/projects_demo/ch_center_demo/run_compare.py +148 -0
- flash/scenarios/README.md +669 -0
- flash/scenarios/SNBtest/__init__.py +0 -0
- flash/scenarios/__init__.py +18 -0
- flash/scenarios/base.py +50 -0
- flash/scenarios/center_evolution/__init__.py +2 -0
- flash/scenarios/center_evolution/ch_center/__init__.py +361 -0
- flash/scenarios/center_evolution/ch_center/laserslab1d_local_custom.py +804 -0
- flash/scenarios/collision_compression/__init__.py +38 -0
- flash/scenarios/flash_demo/__init__.py +0 -0
- flash/scenarios/flash_demo/demo_hpc/__init__.py +0 -0
- flash/scenarios/flash_demo/demo_hpc/_plot_utils.py +128 -0
- flash/scenarios/flash_demo/demo_hpc/laserslab1d_hpc_demo_batch.py +1035 -0
- flash/scenarios/flash_demo/demo_hpc/laserslab1d_supercomputer_demo.py +778 -0
- flash/scenarios/flash_demo/demo_hpc/remote_analysis.py +428 -0
- flash/scenarios/flash_demo/demo_hpc/remote_plot_script.py +444 -0
- flash/scenarios/flash_demo/demo_hpc/remote_ssh_helper.py +457 -0
- flash/scenarios/flash_demo/demo_local/__init__.py +0 -0
- flash/scenarios/flash_demo/demo_local/laserslab1d_local_demo.py +343 -0
- flash/scenarios/flash_demo/hello_flash/README.md +447 -0
- flash/scenarios/flash_demo/hello_flash/analyze_density.py +416 -0
- flash/scenarios/flash_demo/hello_flash/deploy_flash.py +828 -0
- flash/scenarios/interpolator.py +286 -0
- flash/scenarios/plasma_preparation/__init__.py +1 -0
- flash/scenarios/registry.py +61 -0
- flash/scenarios/simulator.py +1045 -0
- flash/test/__init__.py +1 -0
- flash/test/flash_mathtest_custom01.py +413 -0
- flash/test/math_test.py +596 -0
- flash/utils/__init__.py +9 -0
- flash/utils/check_hdf5_time.py +22 -0
- flash/utils/convert_pdf_to_md.py +134 -0
- flash/utils/convert_text_to_md.py +102 -0
- flash/utils/extract_knowledge.py +200 -0
- flash_sim-0.0.1.dist-info/METADATA +811 -0
- flash_sim-0.0.1.dist-info/RECORD +143 -0
- flash_sim-0.0.1.dist-info/WHEEL +4 -0
- flash_sim-0.0.1.dist-info/entry_points.txt +3 -0
- flash_sim-0.0.1.dist-info/licenses/LICENSE +386 -0
- flash_sim-0.0.1.dist-info/licenses/NOTICE +138 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Flash 凭据管理 -- 核心加密/解密
|
|
3
|
+
==============================
|
|
4
|
+
提供 CredentialManager 封装及公共辅助函数。
|
|
5
|
+
供其他 cred_* 模块导入使用。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
from ._config import DEFAULT_USER_NAME, ENTRIES, ENTRIES_BY_NAME
|
|
13
|
+
|
|
14
|
+
# ── 凭据管理器缓存 (确保同一会话内单例) ──
|
|
15
|
+
_CREDENTIAL_MANAGER_CACHE = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ── 敏感字段名: 列表展示时自动掩码 ──
|
|
19
|
+
_SECRET_FIELDS = {"password", "api_key", "secret", "token", "access_key", "private_key"}
|
|
20
|
+
|
|
21
|
+
_DEFAULT_PASSWORD = "123"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_default_password() -> str:
|
|
25
|
+
"""获取默认密码 (用于 SSH 账户设置提示)。"""
|
|
26
|
+
return _DEFAULT_PASSWORD
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_credential_manager():
|
|
30
|
+
"""获取 CredentialManager 实例 (Flash 专属存储),会话内缓存。
|
|
31
|
+
|
|
32
|
+
优先使用 physimx_core(加密存储),回退到内存最小实现。
|
|
33
|
+
使用模块级缓存确保同一会话内返回同一实例。
|
|
34
|
+
"""
|
|
35
|
+
global _CREDENTIAL_MANAGER_CACHE
|
|
36
|
+
if _CREDENTIAL_MANAGER_CACHE is not None:
|
|
37
|
+
return _CREDENTIAL_MANAGER_CACHE
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
from physimx_core.credentials import CredentialManager
|
|
41
|
+
# 使用 Flash 专属子目录: ~/.physimx/flash/
|
|
42
|
+
_CREDENTIAL_MANAGER_CACHE = CredentialManager(subdir="flash")
|
|
43
|
+
except ImportError:
|
|
44
|
+
# physimx_core 未安装时,使用内存最小实现
|
|
45
|
+
from ._minimal import MinimalCredentialManager
|
|
46
|
+
_CREDENTIAL_MANAGER_CACHE = MinimalCredentialManager(subdir="flash")
|
|
47
|
+
return _CREDENTIAL_MANAGER_CACHE
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ── 用户名字段 ──────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
def get_user_name() -> str:
|
|
53
|
+
"""获取用户名 (优先继承 physimx_core, fallback Flash 自己的 __meta__)。"""
|
|
54
|
+
try:
|
|
55
|
+
from physimx_core.credentials import get_user_name as _gun
|
|
56
|
+
return _gun()
|
|
57
|
+
except ImportError:
|
|
58
|
+
# fallback: 从 __meta__ 读取
|
|
59
|
+
try:
|
|
60
|
+
cm = get_credential_manager()
|
|
61
|
+
meta = cm.get("__meta__") or {}
|
|
62
|
+
return meta.get("default_user_name", DEFAULT_USER_NAME)
|
|
63
|
+
except Exception:
|
|
64
|
+
return DEFAULT_USER_NAME
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_user_name_source() -> str:
|
|
68
|
+
"""返回用户名来源: 'physimx_core' 或 'flash'。"""
|
|
69
|
+
try:
|
|
70
|
+
from physimx_core.credentials import get_user_name as _gun
|
|
71
|
+
_gun()
|
|
72
|
+
return "physimx_core"
|
|
73
|
+
except ImportError:
|
|
74
|
+
return "flash"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def set_user_name(name: str) -> None:
|
|
78
|
+
"""设置用户名。"""
|
|
79
|
+
try:
|
|
80
|
+
from physimx_core.credentials import set_user_name as _sun
|
|
81
|
+
_sun(name)
|
|
82
|
+
except ImportError:
|
|
83
|
+
# fallback: 写入 __meta__
|
|
84
|
+
try:
|
|
85
|
+
cm = get_credential_manager()
|
|
86
|
+
meta = cm.get("__meta__") or {}
|
|
87
|
+
meta["default_user_name"] = name
|
|
88
|
+
cm.set("__meta__", meta)
|
|
89
|
+
except Exception:
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def ask_one(label: str, default) -> str:
|
|
94
|
+
"""询问一个字段,回车返回默认值。"""
|
|
95
|
+
val = input(f" {label:12s} [{default}]: ").strip()
|
|
96
|
+
return val if val else str(default)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def mask_secret(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
100
|
+
"""对敏感字段进行掩码处理,返回安全可展示的副本。"""
|
|
101
|
+
masked = dict(data)
|
|
102
|
+
for k, v in masked.items():
|
|
103
|
+
if k in _SECRET_FIELDS:
|
|
104
|
+
if isinstance(v, str) and len(v) > 4:
|
|
105
|
+
masked[k] = v[:2] + "*" * (len(v) - 4) + v[-2:]
|
|
106
|
+
elif isinstance(v, str):
|
|
107
|
+
masked[k] = "****"
|
|
108
|
+
return masked
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# ── SSH 账户辅助 ──
|
|
112
|
+
|
|
113
|
+
def collect_ssh_accounts(cm) -> List[str]:
|
|
114
|
+
"""收集所有 flash_ssh 开头的凭据名,排序返回。"""
|
|
115
|
+
all_creds = cm._load_raw()
|
|
116
|
+
return sorted([k for k in all_creds if k.startswith("flash_ssh")])
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_primary_ssh(cm) -> str:
|
|
120
|
+
"""获取主 SSH 账户名。"""
|
|
121
|
+
meta = cm.get("__meta__") or {}
|
|
122
|
+
return meta.get("primary_ssh", "flash_ssh")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def set_primary_ssh(cm, account_name: str) -> None:
|
|
126
|
+
"""设置主 SSH 账户。"""
|
|
127
|
+
meta = cm.get("__meta__") or {}
|
|
128
|
+
meta["primary_ssh"] = account_name
|
|
129
|
+
cm.set("__meta__", meta)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# ── SSH 编号工具 ──
|
|
133
|
+
|
|
134
|
+
def next_ssh_number(cm) -> int:
|
|
135
|
+
"""计算下一个可用的 SSH 账户编号。"""
|
|
136
|
+
existing = collect_ssh_accounts(cm)
|
|
137
|
+
used = set()
|
|
138
|
+
for name in existing:
|
|
139
|
+
if name == "flash_ssh":
|
|
140
|
+
used.add(1)
|
|
141
|
+
elif name.startswith("flash_ssh_"):
|
|
142
|
+
try:
|
|
143
|
+
used.add(int(name.split("_")[-1]))
|
|
144
|
+
except ValueError:
|
|
145
|
+
pass
|
|
146
|
+
n = 1
|
|
147
|
+
while n in used:
|
|
148
|
+
n += 1
|
|
149
|
+
return n
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def ssh_account_name(num: int) -> str:
|
|
153
|
+
"""根据编号生成 SSH 账户名 (1 -> flash_ssh, 2+ -> flash_ssh_N)。"""
|
|
154
|
+
return "flash_ssh" if num == 1 else f"flash_ssh_{num}"
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# ── 通用凭据辅助 ──
|
|
158
|
+
|
|
159
|
+
def get_entry_def(name: str) -> Optional[Dict[str, Any]]:
|
|
160
|
+
"""根据名称获取凭据条目定义。"""
|
|
161
|
+
return ENTRIES_BY_NAME.get(name)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def list_all_entries() -> List[Dict[str, Any]]:
|
|
165
|
+
"""列出所有凭据条目定义。"""
|
|
166
|
+
return ENTRIES
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
# ── 旧接口兼容桥接 ──
|
|
170
|
+
# 以下函数是旧 _core/credentials.py (单文件) 中的 API,
|
|
171
|
+
# 供 flash_run/ 和 scenarios/flash_demo/ 等模块直接使用。
|
|
172
|
+
# 使用 Flash 专属存储 (get_credential_manager)。
|
|
173
|
+
# =================================================
|
|
174
|
+
|
|
175
|
+
def load_ssh_credentials(name: Optional[str] = None) -> Optional[Dict[str, Any]]:
|
|
176
|
+
"""加载 FLASH SSH 凭据(自动解析路由)。
|
|
177
|
+
|
|
178
|
+
返回的凭据包含 host/port/username/password 等连接所需字段。
|
|
179
|
+
优先使用已缓存的 active_route,否则从预配置路由自动选择第一条。
|
|
180
|
+
|
|
181
|
+
参数:
|
|
182
|
+
name: 凭据名 (如 "flash_ssh"),为 None 时返回主账户凭据。
|
|
183
|
+
|
|
184
|
+
返回:
|
|
185
|
+
凭据字典 (含 host/port/username/password),未找到返回 None。
|
|
186
|
+
"""
|
|
187
|
+
try:
|
|
188
|
+
cm = get_credential_manager()
|
|
189
|
+
except SystemExit:
|
|
190
|
+
return None
|
|
191
|
+
if name is not None:
|
|
192
|
+
raw = cm.get(name)
|
|
193
|
+
_effective_name = name
|
|
194
|
+
else:
|
|
195
|
+
primary = get_primary_ssh(cm)
|
|
196
|
+
raw = cm.get(primary)
|
|
197
|
+
_effective_name = primary
|
|
198
|
+
if raw is None:
|
|
199
|
+
raw = cm.get("flash_ssh")
|
|
200
|
+
_effective_name = "flash_ssh"
|
|
201
|
+
if not raw:
|
|
202
|
+
return None
|
|
203
|
+
|
|
204
|
+
# 如果已有完整字段,直接返回
|
|
205
|
+
if raw.get("host") and raw.get("username"):
|
|
206
|
+
return dict(raw)
|
|
207
|
+
|
|
208
|
+
# ── 路由解析 ──
|
|
209
|
+
from ._config import get_ssh_routes, ENTRIES_BY_NAME
|
|
210
|
+
result = dict(raw)
|
|
211
|
+
|
|
212
|
+
# route_key: 从预配置条目中补齐
|
|
213
|
+
entry = ENTRIES_BY_NAME.get(_effective_name)
|
|
214
|
+
if entry and "route_key" not in result:
|
|
215
|
+
rk = entry.get("route_key")
|
|
216
|
+
if rk:
|
|
217
|
+
result["route_key"] = rk
|
|
218
|
+
|
|
219
|
+
# 用户名: 优先使用 raw 中的,否则从预配置获取
|
|
220
|
+
if "username" not in result or not result["username"]:
|
|
221
|
+
ssh_user = raw.get("ssh_username", "")
|
|
222
|
+
if ssh_user:
|
|
223
|
+
result["username"] = ssh_user
|
|
224
|
+
|
|
225
|
+
# 路由: 按 active_route → 第一条 → None
|
|
226
|
+
routes = get_ssh_routes(_effective_name)
|
|
227
|
+
route = None
|
|
228
|
+
active_idx = raw.get("active_route")
|
|
229
|
+
if active_idx is not None and isinstance(active_idx, int) and 0 <= active_idx < len(routes):
|
|
230
|
+
route = routes[active_idx]
|
|
231
|
+
elif routes:
|
|
232
|
+
route = routes[0] # 默认第一条
|
|
233
|
+
|
|
234
|
+
if route:
|
|
235
|
+
result.setdefault("host", route["host"])
|
|
236
|
+
result.setdefault("port", route["port"])
|
|
237
|
+
|
|
238
|
+
return result
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def load_all_ssh_credentials() -> Dict[str, Dict[str, Any]]:
|
|
242
|
+
"""加载所有超算 SSH 凭据(自动解析路由)。
|
|
243
|
+
|
|
244
|
+
返回:
|
|
245
|
+
{凭据名: 凭据字典, ...},每个字典包含 host/port/username/password。
|
|
246
|
+
仅包含可解析出 host 的有效凭据。
|
|
247
|
+
"""
|
|
248
|
+
try:
|
|
249
|
+
cm = get_credential_manager()
|
|
250
|
+
except SystemExit:
|
|
251
|
+
return {}
|
|
252
|
+
names = collect_ssh_accounts(cm)
|
|
253
|
+
result = {}
|
|
254
|
+
for name in names:
|
|
255
|
+
resolved = load_ssh_credentials(name)
|
|
256
|
+
if resolved and resolved.get("host"):
|
|
257
|
+
result[name] = resolved
|
|
258
|
+
return result
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def get_best_route(cred_name: str) -> Optional[Dict[str, Any]]:
|
|
262
|
+
"""获取凭据已缓存的最佳路由。
|
|
263
|
+
|
|
264
|
+
参数:
|
|
265
|
+
cred_name: 凭据名 (如 "flash_ssh")
|
|
266
|
+
|
|
267
|
+
返回:
|
|
268
|
+
路由字典 {"host": ..., "port": ..., "label": ...},未缓存返回 None。
|
|
269
|
+
"""
|
|
270
|
+
try:
|
|
271
|
+
cm = get_credential_manager()
|
|
272
|
+
except SystemExit:
|
|
273
|
+
return None
|
|
274
|
+
cred = cm.get(cred_name)
|
|
275
|
+
if cred is None:
|
|
276
|
+
return None
|
|
277
|
+
return cred.get("best_route")
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Flash 包 -- 凭据条目定义
|
|
3
|
+
========================
|
|
4
|
+
|
|
5
|
+
这是 flash 包自己的凭据配置。
|
|
6
|
+
在 flash/_core/credentials/_config.py 中注册。
|
|
7
|
+
|
|
8
|
+
注意: SSH 线路定义在 _config.py 中 (共享基础设施)。
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# ── 导入 register 函数 (兼容直接运行和模块运行) ─────────────────────
|
|
12
|
+
try:
|
|
13
|
+
# 相对导入 (作为包的一部分运行时)
|
|
14
|
+
from ._config import register_entries, register_ssh_accounts
|
|
15
|
+
except (ImportError, ValueError):
|
|
16
|
+
# 绝对导入 (直接运行时)
|
|
17
|
+
from flash._core.credentials._config import register_entries, register_ssh_accounts
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# ── 预配置 SSH 账户 ───────────────────────────────
|
|
21
|
+
|
|
22
|
+
register_ssh_accounts([
|
|
23
|
+
{
|
|
24
|
+
"name": "flash_ssh",
|
|
25
|
+
"title": "FLASH 超算 SSH #1 (ParaCloud 中卫 NC-E)",
|
|
26
|
+
"ssh_username": "scfa2696@NC-E",
|
|
27
|
+
"routes": [
|
|
28
|
+
{"host": "ssh.cn-zhongwei-1.paracloud.com", "port": 8443, "label": "中卫-1 (8443)"},
|
|
29
|
+
{"host": "ssh.cn-hongkong-1.paracloud.com", "port": 22, "label": "香港-1 (22)"},
|
|
30
|
+
{"host": "ssh.cn-zhongwei-1.paracloud.com", "port": 22, "label": "中卫-1 (22)"},
|
|
31
|
+
{"host": "ssh.cn-zhongwei-1-v6.paracloud.com", "port": 22, "label": "中卫-1-v6 (22)"},
|
|
32
|
+
{"host": "ssh.cn-zhongwei-1.paracloud.com", "port": 2222, "label": "中卫-1 (2222)"},
|
|
33
|
+
{"host": "ssh.cn-zhongwei-1-v6.paracloud.com", "port": 2222, "label": "中卫-1-v6 (2222)"},
|
|
34
|
+
{"host": "ssh.cn-zhongwei-cstnet.paracloud.com", "port": 22, "label": "中卫-cstnet (22)"},
|
|
35
|
+
{"host": "ssh.cn-zhongwei-cstnet-v6.paracloud.com","port": 22, "label": "中卫-cstnet-v6 (22)"},
|
|
36
|
+
{"host": "ssh.paracloud.com", "port": 2222, "label": "ParaCloud (2222)"},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"name": "flash_ssh_2",
|
|
41
|
+
"title": "FLASH 超算 SSH #2 (ParaCloud 中卫 BSCC-T6)",
|
|
42
|
+
"ssh_username": "sch0348@BSCC-T6",
|
|
43
|
+
"routes": [
|
|
44
|
+
{"host": "ssh.cn-zhongwei-1.paracloud.com", "port": 8443, "label": "中卫-1 (8443)"},
|
|
45
|
+
{"host": "ssh.cn-hongkong-1.paracloud.com", "port": 22, "label": "香港-1 (22)"},
|
|
46
|
+
{"host": "ssh.cn-zhongwei-1.paracloud.com", "port": 22, "label": "中卫-1 (22)"},
|
|
47
|
+
{"host": "ssh.cn-zhongwei-1-v6.paracloud.com", "port": 22, "label": "中卫-1-v6 (22)"},
|
|
48
|
+
{"host": "ssh.cn-zhongwei-1.paracloud.com", "port": 2222, "label": "中卫-1 (2222)"},
|
|
49
|
+
{"host": "ssh.cn-zhongwei-1-v6.paracloud.com", "port": 2222, "label": "中卫-1-v6 (2222)"},
|
|
50
|
+
{"host": "ssh.cn-zhongwei-cstnet.paracloud.com", "port": 22, "label": "中卫-cstnet (22)"},
|
|
51
|
+
{"host": "ssh.cn-zhongwei-cstnet-v6.paracloud.com","port": 22, "label": "中卫-cstnet-v6 (22)"},
|
|
52
|
+
{"host": "ssh.paracloud.com", "port": 2222, "label": "ParaCloud (2222)"},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# ── 普通凭据条目 ──────────────────────────────────
|
|
59
|
+
|
|
60
|
+
register_entries([
|
|
61
|
+
# Gitee 凭据
|
|
62
|
+
{
|
|
63
|
+
"name": "gitee",
|
|
64
|
+
"type": "gitee",
|
|
65
|
+
"title": "Gitee 访问令牌",
|
|
66
|
+
"fields": [
|
|
67
|
+
("token", "访问令牌 (Personal Access Token)", "123"),
|
|
68
|
+
("username", "用户名", "hello"),
|
|
69
|
+
("repo_url", "仓库 URL (可选)", "https://gitee.com/physimx/flash.git"),
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
# DeepSeek API
|
|
74
|
+
{
|
|
75
|
+
"name": "deepseek_api",
|
|
76
|
+
"type": "api",
|
|
77
|
+
"title": "DeepSeek API (太极网关)",
|
|
78
|
+
"fields": [
|
|
79
|
+
("base_url", "API 地址", "https://gateway.taichuai.cn/modelhub/api/v1"),
|
|
80
|
+
("api_key", "API Key", "123"),
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
])
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""MinimalCredentialManager — 最小化凭据管理器 (physimx_core 不可用时的回退)
|
|
2
|
+
|
|
3
|
+
- 存储位置: ~/.physimx/flash/credentials.json (与 physimx_core 的 subdir="flash" 对齐)
|
|
4
|
+
- 简单 JSON 持久化,无加密 (仅作为 physimx_core 缺失时的回退)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MinimalCredentialManager:
|
|
14
|
+
"""最小化凭据管理器 — JSON 文件持久化,无加密。
|
|
15
|
+
|
|
16
|
+
存储路径: {HOME}/.physimx/{subdir}/credentials.json
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, subdir: str = "flash"):
|
|
20
|
+
self._subdir = subdir
|
|
21
|
+
self._store: Dict[str, Any] = {}
|
|
22
|
+
self._file = self._resolve_file()
|
|
23
|
+
self._load()
|
|
24
|
+
|
|
25
|
+
# ── 文件路径 ──
|
|
26
|
+
def _resolve_file(self) -> Path:
|
|
27
|
+
home = Path(os.path.expanduser("~"))
|
|
28
|
+
return home / ".physimx" / self._subdir / "credentials.json"
|
|
29
|
+
|
|
30
|
+
def _load(self) -> None:
|
|
31
|
+
"""从磁盘加载凭据。文件不存在时使用空存储 + 默认 __meta__。"""
|
|
32
|
+
try:
|
|
33
|
+
if self._file.exists():
|
|
34
|
+
data = json.loads(self._file.read_text(encoding="utf-8"))
|
|
35
|
+
self._store = data if isinstance(data, dict) else {}
|
|
36
|
+
else:
|
|
37
|
+
self._store = {}
|
|
38
|
+
except (json.JSONDecodeError, OSError):
|
|
39
|
+
self._store = {}
|
|
40
|
+
# 确保 __meta__ 存在
|
|
41
|
+
self._store.setdefault("__meta__", {"default_user_name": "hello"})
|
|
42
|
+
|
|
43
|
+
def _save(self) -> None:
|
|
44
|
+
"""持久化到磁盘 (原子写入)。"""
|
|
45
|
+
try:
|
|
46
|
+
self._file.parent.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
tmp = self._file.with_suffix(".json.tmp")
|
|
48
|
+
tmp.write_text(
|
|
49
|
+
json.dumps(self._store, ensure_ascii=False, indent=2),
|
|
50
|
+
encoding="utf-8",
|
|
51
|
+
)
|
|
52
|
+
os.replace(tmp, self._file)
|
|
53
|
+
except OSError:
|
|
54
|
+
# 磁盘写入失败时静默降级为内存存储 (不崩溃)
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
# ── 公开 API ──
|
|
58
|
+
def get(self, key: str, default=None) -> Optional[Any]:
|
|
59
|
+
return self._store.get(key, default)
|
|
60
|
+
|
|
61
|
+
def set(self, key: str, value: Any) -> None:
|
|
62
|
+
self._store[key] = value
|
|
63
|
+
self._save()
|
|
64
|
+
|
|
65
|
+
def delete(self, key: str) -> None:
|
|
66
|
+
self._store.pop(key, None)
|
|
67
|
+
self._save()
|
|
68
|
+
|
|
69
|
+
def list_all(self) -> Dict[str, Any]:
|
|
70
|
+
return dict(self._store)
|
|
71
|
+
|
|
72
|
+
def _load_raw(self) -> Dict[str, Any]:
|
|
73
|
+
return dict(self._store)
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Flash 凭据管理 -- API 密钥管理
|
|
3
|
+
====================================
|
|
4
|
+
管理各种 AI API 密钥 (DeepSeek、OpenAI、Claude、Gemini 等)。
|
|
5
|
+
|
|
6
|
+
用法:
|
|
7
|
+
python -m flash._core.credentials.api_keys # 交互菜单
|
|
8
|
+
python -m flash._core.credentials.api_keys setup # 设置 API 密钥
|
|
9
|
+
python -m flash._core.credentials.api_keys list # 列出所有 API 凭据
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
from typing import Dict, Any, Optional, List
|
|
14
|
+
|
|
15
|
+
from ._core import (
|
|
16
|
+
ask_one,
|
|
17
|
+
get_credential_manager,
|
|
18
|
+
mask_secret,
|
|
19
|
+
)
|
|
20
|
+
from ._config import ENTRIES, ENTRIES_BY_NAME, DEFAULT_PASSWORD
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# API 类型的凭据名识别: 名称以 _api 结尾
|
|
24
|
+
def _is_api_entry(entry: Dict[str, Any]) -> bool:
|
|
25
|
+
"""判断是否为 API 类型的凭据。"""
|
|
26
|
+
return entry["name"].endswith("_api")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _get_api_entries() -> List[Dict[str, Any]]:
|
|
30
|
+
"""获取所有 API 类型的凭据定义。"""
|
|
31
|
+
return [e for e in ENTRIES if _is_api_entry(e)]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def setup_api(name: str) -> bool:
|
|
35
|
+
"""设置指定 API 的凭据。"""
|
|
36
|
+
cm = get_credential_manager()
|
|
37
|
+
|
|
38
|
+
entry_def = ENTRIES_BY_NAME.get(name)
|
|
39
|
+
if entry_def is None:
|
|
40
|
+
print(f"\n [错误] 未找到 API 定义: {name}")
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
print(f"\n 📝 设置 {entry_def['title']}")
|
|
44
|
+
print(f" {'─' * 50}")
|
|
45
|
+
|
|
46
|
+
data = {}
|
|
47
|
+
for key, label, default in entry_def["fields"]:
|
|
48
|
+
# 使用全局默认值
|
|
49
|
+
if default == "123":
|
|
50
|
+
default = DEFAULT_PASSWORD
|
|
51
|
+
data[key] = ask_one(label, default)
|
|
52
|
+
|
|
53
|
+
cm.set(name, data)
|
|
54
|
+
print(f"\n ✅ {entry_def['title']} 凭据已保存。")
|
|
55
|
+
return True
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def show_api(name: str, raw: bool = False) -> None:
|
|
59
|
+
"""查看指定 API 的凭据。"""
|
|
60
|
+
cm = get_credential_manager()
|
|
61
|
+
data = cm.get(name)
|
|
62
|
+
|
|
63
|
+
if not data:
|
|
64
|
+
entry_def = ENTRIES_BY_NAME.get(name)
|
|
65
|
+
title = entry_def["title"] if entry_def else name
|
|
66
|
+
print(f"\n [提示] 尚未设置: {title}")
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
entry_def = ENTRIES_BY_NAME.get(name)
|
|
70
|
+
title = entry_def["title"] if entry_def else name
|
|
71
|
+
|
|
72
|
+
print(f"\n {'─' * 50}")
|
|
73
|
+
print(f" {title}")
|
|
74
|
+
print(f" {'─' * 50}")
|
|
75
|
+
|
|
76
|
+
display = data if raw else mask_secret(data)
|
|
77
|
+
for k, v in display.items():
|
|
78
|
+
print(f" {k:12s}: {v}")
|
|
79
|
+
print(f" {'─' * 50}")
|
|
80
|
+
if not raw:
|
|
81
|
+
print(f" 💡 使用 --raw 查看原始密钥")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def list_all_api() -> None:
|
|
85
|
+
"""列出所有 API 凭据。"""
|
|
86
|
+
cm = get_credential_manager()
|
|
87
|
+
api_entries = _get_api_entries()
|
|
88
|
+
|
|
89
|
+
print(f"\n {'─' * 50}")
|
|
90
|
+
print(f" API 凭据列表")
|
|
91
|
+
print(f" {'─' * 50}")
|
|
92
|
+
|
|
93
|
+
found = False
|
|
94
|
+
for entry_def in api_entries:
|
|
95
|
+
name = entry_def["name"]
|
|
96
|
+
data = cm.get(name)
|
|
97
|
+
if data:
|
|
98
|
+
found = True
|
|
99
|
+
title = entry_def["title"]
|
|
100
|
+
print(f"\n [{name}]")
|
|
101
|
+
print(f" 名称: {title}")
|
|
102
|
+
masked = mask_secret(data)
|
|
103
|
+
for k, v in masked.items():
|
|
104
|
+
print(f" {k:12s}: {v}")
|
|
105
|
+
|
|
106
|
+
if not found:
|
|
107
|
+
print("\n [提示] 尚未设置任何 API 凭据。")
|
|
108
|
+
print(f"\n {'─' * 50}")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def delete_api(name: str) -> bool:
|
|
112
|
+
"""删除指定 API 的凭据。"""
|
|
113
|
+
cm = get_credential_manager()
|
|
114
|
+
|
|
115
|
+
if not cm.get(name):
|
|
116
|
+
entry_def = ENTRIES_BY_NAME.get(name)
|
|
117
|
+
title = entry_def["title"] if entry_def else name
|
|
118
|
+
print(f"\n [提示] 尚未设置: {title}")
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
confirm = input(f"\n 确认删除 {name} 凭据? [y/N]: ").strip().lower()
|
|
122
|
+
if confirm != "y":
|
|
123
|
+
print("\n [已取消]")
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
cm.delete(name)
|
|
128
|
+
print(f"\n ✅ {name} 凭据已删除。")
|
|
129
|
+
return True
|
|
130
|
+
except Exception as e:
|
|
131
|
+
print(f"\n ❌ 删除失败: {e}")
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def interactive_menu() -> None:
|
|
136
|
+
"""交互式菜单。"""
|
|
137
|
+
while True:
|
|
138
|
+
api_entries = _get_api_entries()
|
|
139
|
+
|
|
140
|
+
print(f"\n {'=' * 50}")
|
|
141
|
+
print(f" API 密钥管理")
|
|
142
|
+
print(f" {'=' * 50}")
|
|
143
|
+
print(" [1] 设置 API 密钥")
|
|
144
|
+
print(" [2] 查看所有 API 凭据")
|
|
145
|
+
print(" [3] 删除 API 凭据")
|
|
146
|
+
print(" [0] 返回")
|
|
147
|
+
|
|
148
|
+
choice = input("\n 请选择 [0-3]: ").strip()
|
|
149
|
+
|
|
150
|
+
if choice == "1":
|
|
151
|
+
_setup_menu(api_entries)
|
|
152
|
+
elif choice == "2":
|
|
153
|
+
list_all_api()
|
|
154
|
+
elif choice == "3":
|
|
155
|
+
_delete_menu()
|
|
156
|
+
elif choice == "0":
|
|
157
|
+
break
|
|
158
|
+
else:
|
|
159
|
+
print(" [无效选择]")
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _setup_menu(api_entries: List[Dict[str, Any]]) -> None:
|
|
163
|
+
"""选择要设置的 API。"""
|
|
164
|
+
if not api_entries:
|
|
165
|
+
print("\n [提示] 尚未定义任何 API 类型。")
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
print("\n 可选 API:")
|
|
169
|
+
for i, entry in enumerate(api_entries, 1):
|
|
170
|
+
print(f" [{i}] {entry['title']}")
|
|
171
|
+
|
|
172
|
+
choice = input(f"\n 选择要设置的 API [1-{len(api_entries)}, 0=取消]: ").strip()
|
|
173
|
+
try:
|
|
174
|
+
idx = int(choice) - 1
|
|
175
|
+
if 0 <= idx < len(api_entries):
|
|
176
|
+
setup_api(api_entries[idx]["name"])
|
|
177
|
+
except ValueError:
|
|
178
|
+
pass
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _delete_menu() -> None:
|
|
182
|
+
"""删除菜单(选择要删除的 API)。"""
|
|
183
|
+
cm = get_credential_manager()
|
|
184
|
+
api_entries = _get_api_entries()
|
|
185
|
+
|
|
186
|
+
# 只显示已设置的
|
|
187
|
+
existing = [(e["name"], e["title"]) for e in api_entries if cm.get(e["name"])]
|
|
188
|
+
|
|
189
|
+
if not existing:
|
|
190
|
+
print("\n [提示] 尚未设置任何 API 凭据。")
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
print("\n 可选 API 凭据:")
|
|
194
|
+
for i, (name, title) in enumerate(existing, 1):
|
|
195
|
+
print(f" [{i}] {title}")
|
|
196
|
+
|
|
197
|
+
choice = input(f"\n 选择要删除的编号 [1-{len(existing)}, 0=取消]: ").strip()
|
|
198
|
+
try:
|
|
199
|
+
idx = int(choice) - 1
|
|
200
|
+
if 0 <= idx < len(existing):
|
|
201
|
+
delete_api(existing[idx][0])
|
|
202
|
+
except ValueError:
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
if __name__ == "__main__":
|
|
207
|
+
main_arg = sys.argv[1] if len(sys.argv) > 1 else None
|
|
208
|
+
|
|
209
|
+
if main_arg == "setup":
|
|
210
|
+
name = sys.argv[2] if len(sys.argv) > 2 else "deepseek_api"
|
|
211
|
+
setup_api(name)
|
|
212
|
+
elif main_arg == "show":
|
|
213
|
+
name = sys.argv[2] if len(sys.argv) > 2 else "deepseek_api"
|
|
214
|
+
raw = "--raw" in sys.argv
|
|
215
|
+
show_api(name, raw)
|
|
216
|
+
elif main_arg == "list":
|
|
217
|
+
list_all_api()
|
|
218
|
+
elif main_arg == "del":
|
|
219
|
+
name = sys.argv[2] if len(sys.argv) > 2 else None
|
|
220
|
+
if name:
|
|
221
|
+
delete_api(name)
|
|
222
|
+
else:
|
|
223
|
+
_delete_menu()
|
|
224
|
+
else:
|
|
225
|
+
interactive_menu()
|