inspire-skill 2.0.0__tar.gz
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.
- inspire_skill-2.0.0/PKG-INFO +30 -0
- inspire_skill-2.0.0/inspire/__init__.py +3 -0
- inspire_skill-2.0.0/inspire/accounts/__init__.py +44 -0
- inspire_skill-2.0.0/inspire/accounts/storage.py +141 -0
- inspire_skill-2.0.0/inspire/bridge/__init__.py +1 -0
- inspire_skill-2.0.0/inspire/bridge/forge.py +713 -0
- inspire_skill-2.0.0/inspire/bridge/tunnel/__init__.py +66 -0
- inspire_skill-2.0.0/inspire/bridge/tunnel/config.py +115 -0
- inspire_skill-2.0.0/inspire/bridge/tunnel/models.py +197 -0
- inspire_skill-2.0.0/inspire/bridge/tunnel/rtunnel.py +126 -0
- inspire_skill-2.0.0/inspire/bridge/tunnel/scp.py +110 -0
- inspire_skill-2.0.0/inspire/bridge/tunnel/ssh.py +230 -0
- inspire_skill-2.0.0/inspire/bridge/tunnel/ssh_exec.py +312 -0
- inspire_skill-2.0.0/inspire/cli/__init__.py +5 -0
- inspire_skill-2.0.0/inspire/cli/commands/__init__.py +35 -0
- inspire_skill-2.0.0/inspire/cli/commands/account/__init__.py +32 -0
- inspire_skill-2.0.0/inspire/cli/commands/account/add.py +206 -0
- inspire_skill-2.0.0/inspire/cli/commands/account/current.py +26 -0
- inspire_skill-2.0.0/inspire/cli/commands/account/list_cmd.py +22 -0
- inspire_skill-2.0.0/inspire/cli/commands/account/remove.py +30 -0
- inspire_skill-2.0.0/inspire/cli/commands/account/use.py +23 -0
- inspire_skill-2.0.0/inspire/cli/commands/config/__init__.py +23 -0
- inspire_skill-2.0.0/inspire/cli/commands/config/check.py +324 -0
- inspire_skill-2.0.0/inspire/cli/commands/config/context.py +230 -0
- inspire_skill-2.0.0/inspire/cli/commands/config/env_cmd.py +95 -0
- inspire_skill-2.0.0/inspire/cli/commands/config/show.py +309 -0
- inspire_skill-2.0.0/inspire/cli/commands/hpc/__init__.py +26 -0
- inspire_skill-2.0.0/inspire/cli/commands/hpc/hpc_commands.py +580 -0
- inspire_skill-2.0.0/inspire/cli/commands/hpc/hpc_events.py +83 -0
- inspire_skill-2.0.0/inspire/cli/commands/hpc/hpc_metrics.py +57 -0
- inspire_skill-2.0.0/inspire/cli/commands/image/__init__.py +51 -0
- inspire_skill-2.0.0/inspire/cli/commands/image/image_commands.py +857 -0
- inspire_skill-2.0.0/inspire/cli/commands/init/__init__.py +20 -0
- inspire_skill-2.0.0/inspire/cli/commands/init/discover.py +2722 -0
- inspire_skill-2.0.0/inspire/cli/commands/init/env_detect.py +162 -0
- inspire_skill-2.0.0/inspire/cli/commands/init/errors.py +35 -0
- inspire_skill-2.0.0/inspire/cli/commands/init/init_cmd.py +386 -0
- inspire_skill-2.0.0/inspire/cli/commands/init/json_report.py +95 -0
- inspire_skill-2.0.0/inspire/cli/commands/init/templates.py +274 -0
- inspire_skill-2.0.0/inspire/cli/commands/init/toml_helpers.py +96 -0
- inspire_skill-2.0.0/inspire/cli/commands/job/__init__.py +32 -0
- inspire_skill-2.0.0/inspire/cli/commands/job/job_commands.py +809 -0
- inspire_skill-2.0.0/inspire/cli/commands/job/job_create.py +309 -0
- inspire_skill-2.0.0/inspire/cli/commands/job/job_deps.py +23 -0
- inspire_skill-2.0.0/inspire/cli/commands/job/job_events.py +114 -0
- inspire_skill-2.0.0/inspire/cli/commands/job/job_logs.py +1171 -0
- inspire_skill-2.0.0/inspire/cli/commands/job/job_metrics.py +56 -0
- inspire_skill-2.0.0/inspire/cli/commands/model/__init__.py +25 -0
- inspire_skill-2.0.0/inspire/cli/commands/model/model_commands.py +192 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/__init__.py +87 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/connection_test_cmd.py +110 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/connections_cmd.py +122 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/forget_cmd.py +60 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/notebook_commands.py +966 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/notebook_create_flow.py +1352 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/notebook_events.py +98 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/notebook_lifecycle.py +116 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/notebook_lookup.py +520 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/notebook_metrics.py +76 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/notebook_presenters.py +106 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/notebook_ssh_flow.py +961 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/refresh_cmd.py +144 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/remote_exec.py +783 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/remote_scp.py +172 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/remote_shell.py +264 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/set_default_cmd.py +54 -0
- inspire_skill-2.0.0/inspire/cli/commands/notebook/top.py +408 -0
- inspire_skill-2.0.0/inspire/cli/commands/project/__init__.py +32 -0
- inspire_skill-2.0.0/inspire/cli/commands/project/project_commands.py +645 -0
- inspire_skill-2.0.0/inspire/cli/commands/ray/__init__.py +39 -0
- inspire_skill-2.0.0/inspire/cli/commands/ray/ray_commands.py +865 -0
- inspire_skill-2.0.0/inspire/cli/commands/resources/__init__.py +20 -0
- inspire_skill-2.0.0/inspire/cli/commands/resources/resources_list.py +858 -0
- inspire_skill-2.0.0/inspire/cli/commands/resources/resources_nodes.py +220 -0
- inspire_skill-2.0.0/inspire/cli/commands/resources/resources_specs.py +333 -0
- inspire_skill-2.0.0/inspire/cli/commands/run.py +391 -0
- inspire_skill-2.0.0/inspire/cli/commands/serving/__init__.py +32 -0
- inspire_skill-2.0.0/inspire/cli/commands/serving/serving_commands.py +291 -0
- inspire_skill-2.0.0/inspire/cli/commands/serving/serving_metrics.py +48 -0
- inspire_skill-2.0.0/inspire/cli/commands/update.py +317 -0
- inspire_skill-2.0.0/inspire/cli/commands/user/__init__.py +21 -0
- inspire_skill-2.0.0/inspire/cli/commands/user/user_commands.py +174 -0
- inspire_skill-2.0.0/inspire/cli/context.py +34 -0
- inspire_skill-2.0.0/inspire/cli/formatters/__init__.py +24 -0
- inspire_skill-2.0.0/inspire/cli/formatters/human_formatter.py +405 -0
- inspire_skill-2.0.0/inspire/cli/formatters/json_formatter.py +47 -0
- inspire_skill-2.0.0/inspire/cli/logging_setup.py +204 -0
- inspire_skill-2.0.0/inspire/cli/main.py +143 -0
- inspire_skill-2.0.0/inspire/cli/utils/__init__.py +7 -0
- inspire_skill-2.0.0/inspire/cli/utils/auth.py +81 -0
- inspire_skill-2.0.0/inspire/cli/utils/compute_group_autoselect.py +44 -0
- inspire_skill-2.0.0/inspire/cli/utils/errors.py +55 -0
- inspire_skill-2.0.0/inspire/cli/utils/events.py +238 -0
- inspire_skill-2.0.0/inspire/cli/utils/id_resolver.py +259 -0
- inspire_skill-2.0.0/inspire/cli/utils/job_cache.py +11 -0
- inspire_skill-2.0.0/inspire/cli/utils/job_cache_api.py +175 -0
- inspire_skill-2.0.0/inspire/cli/utils/job_cli.py +150 -0
- inspire_skill-2.0.0/inspire/cli/utils/job_submit.py +224 -0
- inspire_skill-2.0.0/inspire/cli/utils/metrics_plot.py +286 -0
- inspire_skill-2.0.0/inspire/cli/utils/metrics_shared.py +614 -0
- inspire_skill-2.0.0/inspire/cli/utils/notebook_cli.py +45 -0
- inspire_skill-2.0.0/inspire/cli/utils/notebook_post_start.py +183 -0
- inspire_skill-2.0.0/inspire/cli/utils/output.py +57 -0
- inspire_skill-2.0.0/inspire/cli/utils/profile.py +40 -0
- inspire_skill-2.0.0/inspire/cli/utils/spec_resolver.py +108 -0
- inspire_skill-2.0.0/inspire/cli/utils/tunnel_reconnect.py +237 -0
- inspire_skill-2.0.0/inspire/cli/utils/update_notice.py +197 -0
- inspire_skill-2.0.0/inspire/compute_groups.py +64 -0
- inspire_skill-2.0.0/inspire/config/__init__.py +77 -0
- inspire_skill-2.0.0/inspire/config/env.py +75 -0
- inspire_skill-2.0.0/inspire/config/load.py +95 -0
- inspire_skill-2.0.0/inspire/config/load_account_layer.py +166 -0
- inspire_skill-2.0.0/inspire/config/load_accounts.py +58 -0
- inspire_skill-2.0.0/inspire/config/load_common.py +237 -0
- inspire_skill-2.0.0/inspire/config/load_env.py +152 -0
- inspire_skill-2.0.0/inspire/config/load_layers.py +104 -0
- inspire_skill-2.0.0/inspire/config/load_runtime.py +106 -0
- inspire_skill-2.0.0/inspire/config/models.py +209 -0
- inspire_skill-2.0.0/inspire/config/options/__init__.py +1 -0
- inspire_skill-2.0.0/inspire/config/options/api.py +169 -0
- inspire_skill-2.0.0/inspire/config/options/forge.py +73 -0
- inspire_skill-2.0.0/inspire/config/options/infra.py +145 -0
- inspire_skill-2.0.0/inspire/config/options/project.py +99 -0
- inspire_skill-2.0.0/inspire/config/rtunnel_defaults.py +63 -0
- inspire_skill-2.0.0/inspire/config/schema.py +92 -0
- inspire_skill-2.0.0/inspire/config/schema_categories.py +19 -0
- inspire_skill-2.0.0/inspire/config/schema_models.py +76 -0
- inspire_skill-2.0.0/inspire/config/ssh_runtime.py +66 -0
- inspire_skill-2.0.0/inspire/config/toml.py +45 -0
- inspire_skill-2.0.0/inspire/config/workspaces.py +91 -0
- inspire_skill-2.0.0/inspire/platform/__init__.py +1 -0
- inspire_skill-2.0.0/inspire/platform/openapi/__init__.py +40 -0
- inspire_skill-2.0.0/inspire/platform/openapi/auth.py +39 -0
- inspire_skill-2.0.0/inspire/platform/openapi/client.py +358 -0
- inspire_skill-2.0.0/inspire/platform/openapi/endpoints.py +62 -0
- inspire_skill-2.0.0/inspire/platform/openapi/errors.py +73 -0
- inspire_skill-2.0.0/inspire/platform/openapi/hpc_jobs.py +193 -0
- inspire_skill-2.0.0/inspire/platform/openapi/http.py +167 -0
- inspire_skill-2.0.0/inspire/platform/openapi/inference_servings.py +146 -0
- inspire_skill-2.0.0/inspire/platform/openapi/jobs.py +161 -0
- inspire_skill-2.0.0/inspire/platform/openapi/models.py +57 -0
- inspire_skill-2.0.0/inspire/platform/openapi/nodes.py +48 -0
- inspire_skill-2.0.0/inspire/platform/openapi/resources.py +222 -0
- inspire_skill-2.0.0/inspire/platform/web/__init__.py +1 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/__init__.py +201 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/availability/__init__.py +22 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/availability/api.py +278 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/availability/models.py +45 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/availability/select.py +142 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/core.py +173 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/hpc_jobs.py +183 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/images.py +392 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/jobs.py +318 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/metrics.py +235 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/models.py +163 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/notebooks.py +772 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/playwright_notebooks.py +342 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/projects.py +392 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/ray_jobs.py +479 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/rtunnel.py +2209 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/servings.py +201 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/users.py +96 -0
- inspire_skill-2.0.0/inspire/platform/web/browser_api/workspaces.py +64 -0
- inspire_skill-2.0.0/inspire/platform/web/resources.py +230 -0
- inspire_skill-2.0.0/inspire/platform/web/session/__init__.py +238 -0
- inspire_skill-2.0.0/inspire/platform/web/session/auth.py +366 -0
- inspire_skill-2.0.0/inspire/platform/web/session/browser_client.py +173 -0
- inspire_skill-2.0.0/inspire/platform/web/session/models.py +155 -0
- inspire_skill-2.0.0/inspire/platform/web/session/proxy.py +126 -0
- inspire_skill-2.0.0/inspire/platform/web/session/requests.py +61 -0
- inspire_skill-2.0.0/inspire/platform/web/session/workspace.py +167 -0
- inspire_skill-2.0.0/inspire_skill.egg-info/PKG-INFO +30 -0
- inspire_skill-2.0.0/inspire_skill.egg-info/SOURCES.txt +229 -0
- inspire_skill-2.0.0/inspire_skill.egg-info/dependency_links.txt +1 -0
- inspire_skill-2.0.0/inspire_skill.egg-info/entry_points.txt +2 -0
- inspire_skill-2.0.0/inspire_skill.egg-info/requires.txt +13 -0
- inspire_skill-2.0.0/inspire_skill.egg-info/top_level.txt +1 -0
- inspire_skill-2.0.0/pyproject.toml +88 -0
- inspire_skill-2.0.0/setup.cfg +4 -0
- inspire_skill-2.0.0/tests/test_account_commands.py +375 -0
- inspire_skill-2.0.0/tests/test_bridge_exec.py +1634 -0
- inspire_skill-2.0.0/tests/test_bridge_scp.py +389 -0
- inspire_skill-2.0.0/tests/test_browser_api_metrics.py +320 -0
- inspire_skill-2.0.0/tests/test_browser_api_ray_jobs.py +511 -0
- inspire_skill-2.0.0/tests/test_browser_api_servings.py +205 -0
- inspire_skill-2.0.0/tests/test_cli_commands.py +2585 -0
- inspire_skill-2.0.0/tests/test_cli_smoke.py +83 -0
- inspire_skill-2.0.0/tests/test_compute_group_autoselect.py +236 -0
- inspire_skill-2.0.0/tests/test_config_files.py +1528 -0
- inspire_skill-2.0.0/tests/test_cpu_compute_group_fixes.py +198 -0
- inspire_skill-2.0.0/tests/test_debug_logging.py +121 -0
- inspire_skill-2.0.0/tests/test_error_utils.py +125 -0
- inspire_skill-2.0.0/tests/test_forge_utils.py +256 -0
- inspire_skill-2.0.0/tests/test_hpc_commands.py +322 -0
- inspire_skill-2.0.0/tests/test_id_resolver.py +170 -0
- inspire_skill-2.0.0/tests/test_image_commands.py +1111 -0
- inspire_skill-2.0.0/tests/test_main_help.py +31 -0
- inspire_skill-2.0.0/tests/test_notebook_alias_defaults.py +220 -0
- inspire_skill-2.0.0/tests/test_notebook_commands.py +1499 -0
- inspire_skill-2.0.0/tests/test_notebook_create_flow.py +645 -0
- inspire_skill-2.0.0/tests/test_notebook_jupyter.py +240 -0
- inspire_skill-2.0.0/tests/test_notebook_metrics.py +391 -0
- inspire_skill-2.0.0/tests/test_notebook_post_start.py +91 -0
- inspire_skill-2.0.0/tests/test_notebook_rtunnel_commands.py +255 -0
- inspire_skill-2.0.0/tests/test_notebook_rtunnel_flow.py +195 -0
- inspire_skill-2.0.0/tests/test_notebook_rtunnel_helpers.py +1040 -0
- inspire_skill-2.0.0/tests/test_notebook_rtunnel_probe.py +196 -0
- inspire_skill-2.0.0/tests/test_notebook_rtunnel_verify.py +28 -0
- inspire_skill-2.0.0/tests/test_notebook_top.py +197 -0
- inspire_skill-2.0.0/tests/test_notebook_wait.py +114 -0
- inspire_skill-2.0.0/tests/test_openapi_client_config.py +86 -0
- inspire_skill-2.0.0/tests/test_openapi_hpc_jobs.py +133 -0
- inspire_skill-2.0.0/tests/test_openapi_inference_servings.py +225 -0
- inspire_skill-2.0.0/tests/test_openapi_jobs.py +111 -0
- inspire_skill-2.0.0/tests/test_openapi_proxy_config.py +118 -0
- inspire_skill-2.0.0/tests/test_openapi_resource_manager.py +86 -0
- inspire_skill-2.0.0/tests/test_project_list_commands.py +252 -0
- inspire_skill-2.0.0/tests/test_project_selection.py +481 -0
- inspire_skill-2.0.0/tests/test_resource_metrics_variants.py +293 -0
- inspire_skill-2.0.0/tests/test_resources_specs_command.py +352 -0
- inspire_skill-2.0.0/tests/test_rtunnel_binary.py +75 -0
- inspire_skill-2.0.0/tests/test_rtunnel_defaults.py +117 -0
- inspire_skill-2.0.0/tests/test_serving_commands.py +51 -0
- inspire_skill-2.0.0/tests/test_ssh_exec.py +243 -0
- inspire_skill-2.0.0/tests/test_ssh_runtime_config.py +195 -0
- inspire_skill-2.0.0/tests/test_tunnel_reconnect.py +130 -0
- inspire_skill-2.0.0/tests/test_utils.py +894 -0
- inspire_skill-2.0.0/tests/test_web_config_resolution.py +53 -0
- inspire_skill-2.0.0/tests/test_web_session.py +767 -0
- inspire_skill-2.0.0/tests/test_web_session_proxy.py +112 -0
- inspire_skill-2.0.0/tests/test_workspace_selection.py +83 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: inspire-skill
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: CLI for the Inspire training platform: notebook lifecycle, distributed/HPC job submission, SSH tunneling, image and resource ops
|
|
5
|
+
Author: Inspire Platform Team
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Documentation, https://github.com/realZillionX/InspireSkill
|
|
8
|
+
Project-URL: Repository, https://github.com/realZillionX/InspireSkill
|
|
9
|
+
Keywords: inspire,hpc,training,cli,machine-learning
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: requests>=2.28.0
|
|
22
|
+
Requires-Dist: click>=8.0.0
|
|
23
|
+
Requires-Dist: playwright>=1.57.0
|
|
24
|
+
Requires-Dist: matplotlib>=3.8.0
|
|
25
|
+
Requires-Dist: tomli>=1.2.0; python_version < "3.11"
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.0.280; extra == "dev"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Account storage — one account = one isolated directory on disk.
|
|
2
|
+
|
|
3
|
+
See :mod:`inspire.accounts.storage` for the rationale and layout. This
|
|
4
|
+
package re-exports the storage helpers so callers can do
|
|
5
|
+
``from inspire.accounts import current_account, account_config_path``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from inspire.accounts.storage import (
|
|
9
|
+
CONFIG_FILENAME,
|
|
10
|
+
AccountError,
|
|
11
|
+
account_config_path,
|
|
12
|
+
account_dir,
|
|
13
|
+
account_exists,
|
|
14
|
+
accounts_dir,
|
|
15
|
+
clear_current_account,
|
|
16
|
+
create_account,
|
|
17
|
+
current_account,
|
|
18
|
+
current_file,
|
|
19
|
+
ensure_inspire_home,
|
|
20
|
+
inspire_home,
|
|
21
|
+
list_accounts,
|
|
22
|
+
remove_account,
|
|
23
|
+
set_current_account,
|
|
24
|
+
validate_name,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"CONFIG_FILENAME",
|
|
29
|
+
"AccountError",
|
|
30
|
+
"account_config_path",
|
|
31
|
+
"account_dir",
|
|
32
|
+
"account_exists",
|
|
33
|
+
"accounts_dir",
|
|
34
|
+
"clear_current_account",
|
|
35
|
+
"create_account",
|
|
36
|
+
"current_account",
|
|
37
|
+
"current_file",
|
|
38
|
+
"ensure_inspire_home",
|
|
39
|
+
"inspire_home",
|
|
40
|
+
"list_accounts",
|
|
41
|
+
"remove_account",
|
|
42
|
+
"set_current_account",
|
|
43
|
+
"validate_name",
|
|
44
|
+
]
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""File-based account storage.
|
|
2
|
+
|
|
3
|
+
One account = one isolated directory under ``~/.inspire/accounts/<name>/``.
|
|
4
|
+
The active account is named in a single line at ``~/.inspire/current``. No
|
|
5
|
+
layered merge, no ``[accounts."<name>"]`` sections, no env-var precedence
|
|
6
|
+
chains — every account's state (config.toml, bridges.json, web_session.json,
|
|
7
|
+
rtunnel cache) lives inside its own directory and never leaks into another.
|
|
8
|
+
|
|
9
|
+
All callers must resolve per-account paths through helpers here rather than
|
|
10
|
+
hard-coding ``~/.inspire/accounts/<name>/...`` strings, so there is only one
|
|
11
|
+
place to change when the on-disk layout evolves.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import os
|
|
17
|
+
import re
|
|
18
|
+
import shutil
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
CONFIG_FILENAME = "config.toml"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _atomic_write_text(target: Path, content: str) -> None:
|
|
25
|
+
"""Write *content* to *target* atomically (temp file + ``os.replace``).
|
|
26
|
+
|
|
27
|
+
Matches the pattern already used by
|
|
28
|
+
``inspire.platform.web.session.models.WebSession.save`` — keep partial
|
|
29
|
+
writes out of the target path so concurrent ``account use`` or a crash
|
|
30
|
+
mid-write never leaves a half-written ``current`` / ``config.toml``.
|
|
31
|
+
"""
|
|
32
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
tmp = target.with_name(target.name + ".tmp")
|
|
34
|
+
with open(tmp, "w", encoding="utf-8") as f:
|
|
35
|
+
f.write(content)
|
|
36
|
+
f.flush()
|
|
37
|
+
os.fsync(f.fileno())
|
|
38
|
+
os.replace(tmp, target)
|
|
39
|
+
|
|
40
|
+
_NAME_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class AccountError(Exception):
|
|
44
|
+
"""Raised for account-related failures (not found, already exists, bad name)."""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def validate_name(name: str) -> str:
|
|
48
|
+
candidate = (name or "").strip()
|
|
49
|
+
if not _NAME_PATTERN.match(candidate):
|
|
50
|
+
raise AccountError(
|
|
51
|
+
f"Invalid account name: {name!r}. Allowed: letters, digits, '.', '_', '-'; "
|
|
52
|
+
"must start with a letter or digit; 1-64 chars."
|
|
53
|
+
)
|
|
54
|
+
return candidate
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def inspire_home() -> Path:
|
|
58
|
+
return Path.home() / ".inspire"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def accounts_dir() -> Path:
|
|
62
|
+
return inspire_home() / "accounts"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def current_file() -> Path:
|
|
66
|
+
return inspire_home() / "current"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def account_dir(name: str) -> Path:
|
|
70
|
+
return accounts_dir() / validate_name(name)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def account_config_path(name: str) -> Path:
|
|
74
|
+
return account_dir(name) / CONFIG_FILENAME
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def ensure_inspire_home() -> None:
|
|
78
|
+
inspire_home().mkdir(parents=True, exist_ok=True)
|
|
79
|
+
accounts_dir().mkdir(parents=True, exist_ok=True)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def list_accounts() -> list[str]:
|
|
83
|
+
root = accounts_dir()
|
|
84
|
+
if not root.exists():
|
|
85
|
+
return []
|
|
86
|
+
return sorted(
|
|
87
|
+
p.name
|
|
88
|
+
for p in root.iterdir()
|
|
89
|
+
if p.is_dir() and (p / CONFIG_FILENAME).exists()
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def account_exists(name: str) -> bool:
|
|
94
|
+
try:
|
|
95
|
+
return validate_name(name) in list_accounts()
|
|
96
|
+
except AccountError:
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def current_account() -> str | None:
|
|
101
|
+
try:
|
|
102
|
+
raw = current_file().read_text(encoding="utf-8").strip()
|
|
103
|
+
except FileNotFoundError:
|
|
104
|
+
return None
|
|
105
|
+
return raw or None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def set_current_account(name: str) -> None:
|
|
109
|
+
validated = validate_name(name)
|
|
110
|
+
if not account_exists(validated):
|
|
111
|
+
raise AccountError(f"Account not found: {validated}")
|
|
112
|
+
ensure_inspire_home()
|
|
113
|
+
_atomic_write_text(current_file(), validated + "\n")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def clear_current_account() -> None:
|
|
117
|
+
try:
|
|
118
|
+
current_file().unlink()
|
|
119
|
+
except FileNotFoundError:
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def create_account(name: str, config_content: str, *, overwrite: bool = False) -> Path:
|
|
124
|
+
validated = validate_name(name)
|
|
125
|
+
target = accounts_dir() / validated
|
|
126
|
+
if target.exists() and not overwrite:
|
|
127
|
+
raise AccountError(f"Account already exists: {validated}")
|
|
128
|
+
ensure_inspire_home()
|
|
129
|
+
target.mkdir(parents=True, exist_ok=overwrite)
|
|
130
|
+
_atomic_write_text(target / CONFIG_FILENAME, config_content)
|
|
131
|
+
return target
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def remove_account(name: str) -> None:
|
|
135
|
+
validated = validate_name(name)
|
|
136
|
+
target = accounts_dir() / validated
|
|
137
|
+
if not target.exists():
|
|
138
|
+
raise AccountError(f"Account not found: {validated}")
|
|
139
|
+
shutil.rmtree(target)
|
|
140
|
+
if current_account() == validated:
|
|
141
|
+
clear_current_account()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Bridge execution integrations (forge workflows, tunnels, SSH)."""
|