runops 2.1.0__tar.gz → 2.1.2__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.
Files changed (131) hide show
  1. {runops-2.1.0 → runops-2.1.2}/PKG-INFO +1 -1
  2. {runops-2.1.0 → runops-2.1.2}/pyproject.toml +1 -1
  3. {runops-2.1.0 → runops-2.1.2}/src/runops/__init__.py +1 -1
  4. {runops-2.1.0 → runops-2.1.2}/src/runops/cli.py +42 -18
  5. {runops-2.1.0 → runops-2.1.2}/tests/test_setup_misc.py +68 -0
  6. {runops-2.1.0 → runops-2.1.2}/uv.lock +5 -17
  7. {runops-2.1.0 → runops-2.1.2}/.agents/skills/check/SKILL.md +0 -0
  8. {runops-2.1.0 → runops-2.1.2}/.agents/skills/improve-harness/SKILL.md +0 -0
  9. {runops-2.1.0 → runops-2.1.2}/.agents/skills/python-package-refactor/SKILL.md +0 -0
  10. {runops-2.1.0 → runops-2.1.2}/.agents/skills/python-package-refactor/agents/openai.yaml +0 -0
  11. {runops-2.1.0 → runops-2.1.2}/.agents/skills/python-package-refactor/examples/refactor-request.md +0 -0
  12. {runops-2.1.0 → runops-2.1.2}/.agents/skills/python-package-refactor/references/anti-patterns.md +0 -0
  13. {runops-2.1.0 → runops-2.1.2}/.agents/skills/python-package-refactor/references/api-compatibility.md +0 -0
  14. {runops-2.1.0 → runops-2.1.2}/.agents/skills/python-package-refactor/references/packaging-modernization.md +0 -0
  15. {runops-2.1.0 → runops-2.1.2}/.agents/skills/python-package-refactor/references/refactor-playbook.md +0 -0
  16. {runops-2.1.0 → runops-2.1.2}/.agents/skills/python-package-refactor/references/verification.md +0 -0
  17. {runops-2.1.0 → runops-2.1.2}/.agents/skills/python-package-refactor/scripts/api_surface_snapshot.py +0 -0
  18. {runops-2.1.0 → runops-2.1.2}/.agents/skills/python-package-refactor/scripts/import_smoke.py +0 -0
  19. {runops-2.1.0 → runops-2.1.2}/.agents/skills/python-package-refactor/scripts/inspect_python_package.py +0 -0
  20. {runops-2.1.0 → runops-2.1.2}/.agents/skills/python-package-refactor/scripts/refactor_quality_gate.py +0 -0
  21. {runops-2.1.0 → runops-2.1.2}/.agents/skills/release/SKILL.md +0 -0
  22. {runops-2.1.0 → runops-2.1.2}/.agents/skills/simulator-adapter/SKILL.md +0 -0
  23. {runops-2.1.0 → runops-2.1.2}/.agents/skills/spec-reviewer/SKILL.md +0 -0
  24. {runops-2.1.0 → runops-2.1.2}/.agents/skills/sync-docs/SKILL.md +0 -0
  25. {runops-2.1.0 → runops-2.1.2}/.claude/agents/spec-reviewer.md +0 -0
  26. {runops-2.1.0 → runops-2.1.2}/.claude/rules/architecture.md +0 -0
  27. {runops-2.1.0 → runops-2.1.2}/.claude/rules/dev-workflow.md +0 -0
  28. {runops-2.1.0 → runops-2.1.2}/.claude/rules/gotchas.md +0 -0
  29. {runops-2.1.0 → runops-2.1.2}/.claude/settings.json +0 -0
  30. {runops-2.1.0 → runops-2.1.2}/.claude/skills/check/SKILL.md +0 -0
  31. {runops-2.1.0 → runops-2.1.2}/.claude/skills/improve-harness/SKILL.md +0 -0
  32. {runops-2.1.0 → runops-2.1.2}/.claude/skills/python-package-refactor/SKILL.md +0 -0
  33. {runops-2.1.0 → runops-2.1.2}/.claude/skills/release/SKILL.md +0 -0
  34. {runops-2.1.0 → runops-2.1.2}/.claude/skills/simulator-adapter/SKILL.md +0 -0
  35. {runops-2.1.0 → runops-2.1.2}/.claude/skills/spec-reviewer/SKILL.md +0 -0
  36. {runops-2.1.0 → runops-2.1.2}/.claude/skills/sync-docs/SKILL.md +0 -0
  37. {runops-2.1.0 → runops-2.1.2}/.codex/README.md +0 -0
  38. {runops-2.1.0 → runops-2.1.2}/.codex/agents/spec-reviewer.toml +0 -0
  39. {runops-2.1.0 → runops-2.1.2}/.codex/config.toml +0 -0
  40. {runops-2.1.0 → runops-2.1.2}/.codex/rules/runops.rules +0 -0
  41. {runops-2.1.0 → runops-2.1.2}/.dockerignore +0 -0
  42. {runops-2.1.0 → runops-2.1.2}/.gitattributes +0 -0
  43. {runops-2.1.0 → runops-2.1.2}/.github/assets/conversation-dark.svg +0 -0
  44. {runops-2.1.0 → runops-2.1.2}/.github/assets/conversation-light.svg +0 -0
  45. {runops-2.1.0 → runops-2.1.2}/.github/assets/hero-dark.svg +0 -0
  46. {runops-2.1.0 → runops-2.1.2}/.github/assets/hero-light.svg +0 -0
  47. {runops-2.1.0 → runops-2.1.2}/.github/assets/make_readme_art.py +0 -0
  48. {runops-2.1.0 → runops-2.1.2}/.github/workflows/ci.yml +0 -0
  49. {runops-2.1.0 → runops-2.1.2}/.github/workflows/publish.yml +0 -0
  50. {runops-2.1.0 → runops-2.1.2}/.gitignore +0 -0
  51. {runops-2.1.0 → runops-2.1.2}/.harness/manifest.toml +0 -0
  52. {runops-2.1.0 → runops-2.1.2}/.pre-commit-config.yaml +0 -0
  53. {runops-2.1.0 → runops-2.1.2}/.vscode/settings.json +0 -0
  54. {runops-2.1.0 → runops-2.1.2}/AGENTS.md +0 -0
  55. {runops-2.1.0 → runops-2.1.2}/CLAUDE.md +0 -0
  56. {runops-2.1.0 → runops-2.1.2}/LICENSE +0 -0
  57. {runops-2.1.0 → runops-2.1.2}/README.md +0 -0
  58. {runops-2.1.0 → runops-2.1.2}/SPEC.md +0 -0
  59. {runops-2.1.0 → runops-2.1.2}/docs/adapter.md +0 -0
  60. {runops-2.1.0 → runops-2.1.2}/docs/reference.md +0 -0
  61. {runops-2.1.0 → runops-2.1.2}/src/runops/adapters/__init__.py +0 -0
  62. {runops-2.1.0 → runops-2.1.2}/src/runops/adapters/base.py +0 -0
  63. {runops-2.1.0 → runops-2.1.2}/src/runops/adapters/generic.py +0 -0
  64. {runops-2.1.0 → runops-2.1.2}/src/runops/adapters/registry.py +0 -0
  65. {runops-2.1.0 → runops-2.1.2}/src/runops/case.py +0 -0
  66. {runops-2.1.0 → runops-2.1.2}/src/runops/config.py +0 -0
  67. {runops-2.1.0 → runops-2.1.2}/src/runops/errors.py +0 -0
  68. {runops-2.1.0 → runops-2.1.2}/src/runops/harness.py +0 -0
  69. {runops-2.1.0 → runops-2.1.2}/src/runops/jobgen.py +0 -0
  70. {runops-2.1.0 → runops-2.1.2}/src/runops/launchers.py +0 -0
  71. {runops-2.1.0 → runops-2.1.2}/src/runops/legacy_adapters/__init__.py +0 -0
  72. {runops-2.1.0 → runops-2.1.2}/src/runops/legacy_adapters/beach.py +0 -0
  73. {runops-2.1.0 → runops-2.1.2}/src/runops/legacy_adapters/emses.py +0 -0
  74. {runops-2.1.0 → runops-2.1.2}/src/runops/manifest.py +0 -0
  75. {runops-2.1.0 → runops-2.1.2}/src/runops/ops/__init__.py +0 -0
  76. {runops-2.1.0 → runops-2.1.2}/src/runops/ops/common.py +0 -0
  77. {runops-2.1.0 → runops-2.1.2}/src/runops/ops/create.py +0 -0
  78. {runops-2.1.0 → runops-2.1.2}/src/runops/ops/listing.py +0 -0
  79. {runops-2.1.0 → runops-2.1.2}/src/runops/ops/migrate.py +0 -0
  80. {runops-2.1.0 → runops-2.1.2}/src/runops/ops/notes.py +0 -0
  81. {runops-2.1.0 → runops-2.1.2}/src/runops/ops/setup.py +0 -0
  82. {runops-2.1.0 → runops-2.1.2}/src/runops/ops/skills.py +0 -0
  83. {runops-2.1.0 → runops-2.1.2}/src/runops/ops/submit.py +0 -0
  84. {runops-2.1.0 → runops-2.1.2}/src/runops/ops/sync.py +0 -0
  85. {runops-2.1.0 → runops-2.1.2}/src/runops/ops/tidy.py +0 -0
  86. {runops-2.1.0 → runops-2.1.2}/src/runops/project.py +0 -0
  87. {runops-2.1.0 → runops-2.1.2}/src/runops/runtime.py +0 -0
  88. {runops-2.1.0 → runops-2.1.2}/src/runops/skills/runops-check-status/SKILL.md +0 -0
  89. {runops-2.1.0 → runops-2.1.2}/src/runops/skills/runops-create-run/SKILL.md +0 -0
  90. {runops-2.1.0 → runops-2.1.2}/src/runops/skills/runops-maintain/SKILL.md +0 -0
  91. {runops-2.1.0 → runops-2.1.2}/src/runops/skills/runops-reference/SKILL.md +0 -0
  92. {runops-2.1.0 → runops-2.1.2}/src/runops/skills/runops-tidy/SKILL.md +0 -0
  93. {runops-2.1.0 → runops-2.1.2}/src/runops/slurm.py +0 -0
  94. {runops-2.1.0 → runops-2.1.2}/src/runops/util.py +0 -0
  95. {runops-2.1.0 → runops-2.1.2}/tests/__init__.py +0 -0
  96. {runops-2.1.0 → runops-2.1.2}/tests/conftest.py +0 -0
  97. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/.codex/rules/a.md +0 -0
  98. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/.runops/test-runs/T20260701-0001/receipt.toml +0 -0
  99. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/.runops/work/beach-x-abc/bin/beach +0 -0
  100. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/AGENTS.md +0 -0
  101. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/CLAUDE.md +0 -0
  102. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/cases/beach/plane/beach.toml +0 -0
  103. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/cases/beach/plane/case.toml +0 -0
  104. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/cases/old/beach.toml +0 -0
  105. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/cases/old/case.toml +0 -0
  106. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/launchers.toml +0 -0
  107. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/research/CURRENT.md +0 -0
  108. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/runops.toml +0 -0
  109. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/runs/AGENTS.md +0 -0
  110. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/runs/_archive/plane/R20260601-0001/manifest.toml +0 -0
  111. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/runs/plane/R20260701-0001--a/input/beach.toml +0 -0
  112. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/runs/plane/R20260701-0001--a/manifest.toml +0 -0
  113. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/runs/plane/R20260702-0001/manifest.toml +0 -0
  114. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/runs/scan/R20260701-0002/manifest.toml +0 -0
  115. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/runs/scan/survey.toml +0 -0
  116. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/simulators.toml +0 -0
  117. {runops-2.1.0 → runops-2.1.2}/tests/fixtures/v1-project/site.toml +0 -0
  118. {runops-2.1.0 → runops-2.1.2}/tests/golden/mpi_standard.sh +0 -0
  119. {runops-2.1.0 → runops-2.1.2}/tests/golden/openmp_rsc.sh +0 -0
  120. {runops-2.1.0 → runops-2.1.2}/tests/test_adapters.py +0 -0
  121. {runops-2.1.0 → runops-2.1.2}/tests/test_build.py +0 -0
  122. {runops-2.1.0 → runops-2.1.2}/tests/test_dev_harness.py +0 -0
  123. {runops-2.1.0 → runops-2.1.2}/tests/test_jobgen.py +0 -0
  124. {runops-2.1.0 → runops-2.1.2}/tests/test_legacy_adapters.py +0 -0
  125. {runops-2.1.0 → runops-2.1.2}/tests/test_lifecycle.py +0 -0
  126. {runops-2.1.0 → runops-2.1.2}/tests/test_manifest_config.py +0 -0
  127. {runops-2.1.0 → runops-2.1.2}/tests/test_migrate.py +0 -0
  128. {runops-2.1.0 → runops-2.1.2}/tests/test_scratch_tidy.py +0 -0
  129. {runops-2.1.0 → runops-2.1.2}/tests/test_skills.py +0 -0
  130. {runops-2.1.0 → runops-2.1.2}/tests/test_sweep.py +0 -0
  131. {runops-2.1.0 → runops-2.1.2}/tests/test_sync_evidence.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: runops
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Run ledger and Slurm job submission for HPC simulation projects
5
5
  Project-URL: Homepage, https://github.com/Nkzono99/runops
6
6
  Project-URL: Documentation, https://github.com/Nkzono99/runops/blob/main/docs/reference.md
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "runops"
7
- version = "2.1.0"
7
+ version = "2.1.2"
8
8
  description = "Run ledger and Slurm job submission for HPC simulation projects"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -1,3 +1,3 @@
1
1
  """runops: run ledger and Slurm job submission for HPC simulation projects."""
2
2
 
3
- __version__ = "2.1.0"
3
+ __version__ = "2.1.2"
@@ -91,6 +91,19 @@ def _confirm(message: str, yes: bool) -> None:
91
91
  raise typer.Exit(1)
92
92
 
93
93
 
94
+ def _ask_install(root: Path, yes: bool) -> bool:
95
+ """Ask before creating .venv; default yes. Off a terminal, require -y."""
96
+ message = (
97
+ f"Create .venv in {root} and install simulator packages "
98
+ "(may build simulator binaries and take several minutes)?"
99
+ )
100
+ if yes:
101
+ return True
102
+ if not sys.stdin.isatty():
103
+ raise UsageError(f"{message} -- confirmation required; rerun with -y/--yes or --no-install")
104
+ return bool(typer.confirm(message, default=True))
105
+
106
+
94
107
  def _resolve(project: Project, refs: list[str]) -> list[Manifest]:
95
108
  if not refs:
96
109
  raise UsageError("give at least one RUN")
@@ -121,30 +134,26 @@ def init(
121
134
  Run from a terminal without arguments, it asks for the site preset and the
122
135
  simulators; the site default is guessed from the host name.
123
136
  """
124
- from runops.ops.setup import SITE_PRESETS, detect_site, hostname, init_project
137
+ from runops.ops.setup import detect_site, hostname, init_project
125
138
 
126
139
  sims = sims or []
127
140
  root = Path.cwd()
128
141
  if not sims and not site and not yes and sys.stdin.isatty():
129
- import click
130
-
131
- guessed = detect_site()
132
- site = typer.prompt(
133
- f"Site preset (guessed from host {hostname()})",
134
- default=guessed,
135
- type=click.Choice(list(SITE_PRESETS)),
136
- )
142
+ site = _prompt_site(detect_site(), hostname())
137
143
  answer = typer.prompt(
138
144
  "Simulators, comma-separated (emses, beach, generic; empty for none)",
139
145
  default="",
140
146
  show_default=False,
141
147
  )
142
148
  sims = [s.strip() for s in answer.split(",") if s.strip()]
143
- if sims and not no_install and any(s.split("=")[0] != "generic" for s in sims):
144
- _confirm(f"Create .venv in {root} and install simulator packages?", yes)
145
- report = init_project(root, sims, site=site, name=name, install=not no_install)
149
+ install = not no_install
150
+ if sims and install and any(s.split("=")[0] != "generic" for s in sims):
151
+ install = _ask_install(root, yes)
152
+ report = init_project(root, sims, site=site, name=name, install=install)
146
153
  if report.site_detected:
147
154
  out.print(f"site: {report.site} (guessed from host {hostname()}; use --site to change)")
155
+ if not install and not no_install:
156
+ out.print("packages not installed; ask your agent or run `runo update` later")
148
157
  for item in report.created:
149
158
  out.print(f"created {item}")
150
159
  for spec in report.installed:
@@ -156,6 +165,21 @@ def init(
156
165
  )
157
166
 
158
167
 
168
+ def _prompt_site(guessed: str, host: str) -> str:
169
+ """Ask for a site preset until a known one (or alias) is given."""
170
+ from runops.ops.setup import SITE_ALIASES, SITE_PRESETS
171
+
172
+ choices = " / ".join(SITE_PRESETS)
173
+ while True:
174
+ answer = str(
175
+ typer.prompt(f"Site preset ({choices}; guessed from host {host})", default=guessed)
176
+ ).strip()
177
+ site = SITE_ALIASES.get(answer, answer)
178
+ if site in SITE_PRESETS:
179
+ return site
180
+ err.print(f"[yellow]unknown site preset {answer!r}; choose {choices}[/]")
181
+
182
+
159
183
  @app.command()
160
184
  def doctor(as_json: bool = typer.Option(False, "--json")) -> None:
161
185
  """Check configuration, adapters, executables, Slurm and skills."""
@@ -882,12 +906,12 @@ def main(argv: list[str] | None = None) -> None:
882
906
  except typer.Abort:
883
907
  err.print("aborted")
884
908
  sys.exit(1)
885
- except Exception as exc: # click usage errors
886
- import click
887
-
888
- if isinstance(exc, click.ClickException):
889
- exc.show()
890
- sys.exit(2)
909
+ except Exception as exc:
910
+ # Usage errors from typer's click (vendored in newer typer, so match by shape).
911
+ show = getattr(exc, "show", None)
912
+ if callable(show) and isinstance(getattr(exc, "exit_code", None), int):
913
+ show()
914
+ sys.exit(int(exc.exit_code) or 2) # type: ignore[attr-defined]
891
915
  raise
892
916
  if isinstance(code, int) and code:
893
917
  sys.exit(code)
@@ -143,3 +143,71 @@ def test_init_not_interactive_with_yes(
143
143
  res = cli("init", "-y", "--no-install")
144
144
  assert res.code == 0, res.err
145
145
  assert load_toml(tmp_path / "runops.toml")["simulators"] == {}
146
+
147
+
148
+ def test_init_interactive_reasks_unknown_site(
149
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, cli
150
+ ) -> None: # type: ignore[no-untyped-def]
151
+ monkeypatch.chdir(tmp_path)
152
+ monkeypatch.setattr("sys.stdin", _Tty("nowhere\ncamphor\n\n"))
153
+ res = cli("init", "--no-install")
154
+ assert res.code == 0, res.err
155
+ assert "unknown site preset 'nowhere'" in res.err
156
+ assert load_toml(tmp_path / "runops.toml")["site"]["name"] == "kudpc"
157
+
158
+
159
+ def test_usage_error_exit_code(cli) -> None: # type: ignore[no-untyped-def]
160
+ res = cli("list", "--no-such-option")
161
+ assert res.code == 2
162
+ assert "no-such-option" in res.err
163
+
164
+
165
+ def test_cli_does_not_import_click_directly() -> None:
166
+ source = (Path(__file__).parents[1] / "src/runops/cli.py").read_text()
167
+ assert "import click" not in source # newer typer vendors click
168
+
169
+
170
+ def test_init_install_prompt_defaults_to_yes(
171
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, cli
172
+ ) -> None: # type: ignore[no-untyped-def]
173
+ calls: list[bool] = []
174
+
175
+ def fake_install(project, *, upgrade): # type: ignore[no-untyped-def]
176
+ calls.append(upgrade)
177
+ return ["MPIEMSES3D"], []
178
+
179
+ monkeypatch.setattr("runops.ops.setup.install_packages", fake_install)
180
+ monkeypatch.chdir(tmp_path)
181
+ monkeypatch.setattr(
182
+ "sys.stdin", _Tty("\nemses\n\n")
183
+ ) # site: Enter, sims: emses, install: Enter
184
+ res = cli("init")
185
+ assert res.code == 0, res.err
186
+ assert "may build simulator binaries" in res.out
187
+ assert "[Y/n]" in res.out
188
+ assert calls == [False]
189
+ assert "installed MPIEMSES3D" in res.out
190
+
191
+
192
+ def test_init_install_declined_keeps_project(
193
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, cli
194
+ ) -> None: # type: ignore[no-untyped-def]
195
+ monkeypatch.setattr(
196
+ "runops.ops.setup.install_packages",
197
+ lambda project, *, upgrade: (_ for _ in ()).throw(AssertionError("must not install")),
198
+ )
199
+ monkeypatch.chdir(tmp_path)
200
+ monkeypatch.setattr("sys.stdin", _Tty("\nemses\nn\n"))
201
+ res = cli("init")
202
+ assert res.code == 0, res.err
203
+ assert "runo update" in res.out
204
+ assert "emses" in load_toml(tmp_path / "runops.toml")["simulators"]
205
+
206
+
207
+ def test_init_install_needs_yes_off_terminal(
208
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, cli
209
+ ) -> None: # type: ignore[no-untyped-def]
210
+ monkeypatch.chdir(tmp_path)
211
+ res = cli("init", "emses")
212
+ assert res.code == 2 and "--yes" in res.err
213
+ assert not (tmp_path / "runops.toml").exists()
@@ -11,18 +11,6 @@ wheels = [
11
11
  { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
12
12
  ]
13
13
 
14
- [[package]]
15
- name = "click"
16
- version = "8.3.1"
17
- source = { registry = "https://pypi.org/simple" }
18
- dependencies = [
19
- { name = "colorama", marker = "sys_platform == 'win32'" },
20
- ]
21
- sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
22
- wheels = [
23
- { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
24
- ]
25
-
26
14
  [[package]]
27
15
  name = "colorama"
28
16
  version = "0.4.6"
@@ -537,7 +525,7 @@ wheels = [
537
525
 
538
526
  [[package]]
539
527
  name = "runops"
540
- version = "2.1.0"
528
+ version = "2.1.2"
541
529
  source = { editable = "." }
542
530
  dependencies = [
543
531
  { name = "jinja2" },
@@ -661,17 +649,17 @@ wheels = [
661
649
 
662
650
  [[package]]
663
651
  name = "typer"
664
- version = "0.24.1"
652
+ version = "0.27.3"
665
653
  source = { registry = "https://pypi.org/simple" }
666
654
  dependencies = [
667
655
  { name = "annotated-doc" },
668
- { name = "click" },
656
+ { name = "colorama", marker = "sys_platform == 'win32'" },
669
657
  { name = "rich" },
670
658
  { name = "shellingham" },
671
659
  ]
672
- sdist = { url = "https://files.pythonhosted.org/packages/f5/24/cb09efec5cc954f7f9b930bf8279447d24618bb6758d4f6adf2574c41780/typer-0.24.1.tar.gz", hash = "sha256:e39b4732d65fbdcde189ae76cf7cd48aeae72919dea1fdfc16593be016256b45", size = 118613, upload-time = "2026-02-21T16:54:40.609Z" }
660
+ sdist = { url = "https://files.pythonhosted.org/packages/03/51/d33db42cc72ffd8c30777547b42d01f0cbf9d95a770457698d0174b3ed71/typer-0.27.3.tar.gz", hash = "sha256:d0396f770a560ab1b0a8504e13b5f254b728cedb05c61cf0359e944e50ce8901", size = 205303, upload-time = "2026-10-06T17:24:16.61Z" }
673
661
  wheels = [
674
- { url = "https://files.pythonhosted.org/packages/4a/91/48db081e7a63bb37284f9fbcefda7c44c277b18b0e13fbc36ea2335b71e6/typer-0.24.1-py3-none-any.whl", hash = "sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e", size = 56085, upload-time = "2026-02-21T16:54:41.616Z" },
662
+ { url = "https://files.pythonhosted.org/packages/07/ea/2e31b67051e91a133189e9c000c222502ddc6969856416de0d095de4c0b0/typer-0.27.3-py3-none-any.whl", hash = "sha256:e50022f28b82a86313e54501317a1db64bf8f8d036ff8cfe5ca7e47675454aff", size = 123312, upload-time = "2026-10-06T17:24:15.054Z" },
675
663
  ]
676
664
 
677
665
  [[package]]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes