netbox-super-cli 1.6.2__tar.gz → 1.6.3__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.
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/CHANGELOG.md +19 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/PKG-INFO +1 -1
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/_version.py +1 -1
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cache/store.py +11 -3
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schema/source.py +22 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/pyproject.toml +1 -1
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cache/test_store.py +14 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/schema/test_source.py +72 -1
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/uv.lock +1 -1
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/ISSUE_TEMPLATE/docs.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/dependabot.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/pull_request_template.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/bench.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/claude.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/docs.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/e2e.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/lint.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/release.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/skill-convention-review.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/test.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.gitignore +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.pre-commit-config.yaml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.python-version +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/AGENTS.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/CLAUDE.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/LICENSE +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/README.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/architecture/caching.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/architecture/command-generation.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/architecture/http-client.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/architecture/overview.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/architecture/schema-loading.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/assets/tui/detail.svg +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/assets/tui/filter.svg +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/assets/tui/list.svg +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/assets/tui/picker.svg +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/assets/tui/search.svg +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/contributing/adding-bundled-schemas.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/contributing/branching.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/contributing/development.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/contributing/release-process.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/getting-started/concepts.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/getting-started/first-run.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/getting-started/install.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/audit-log.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/ci-and-automation.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/interactive-tui.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/managing-profiles.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/output-formats.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/using-with-ai-agents.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/working-with-plugins.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/guides/writes-and-safety.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/index.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/reference/cli.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/reference/config.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/reference/exit-codes.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/reference/schemas.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/justfile +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/mkdocs.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/__main__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/aliases/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/aliases/resolver.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/auth/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/auth/verify.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/builder/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/builder/build.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cache/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/aliases_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/app.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/cache_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/commands_dump.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/config_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/globals.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/handlers.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/init_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/login_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/profiles_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/registration.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/runtime.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/skill_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/tui_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/apply.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/bulk.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/coercion.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/confirmation.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/input.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/cli/writes/preflight.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/completion/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/completion/cache_probe.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/completion/callbacks.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/completion/providers.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/config/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/config/loader.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/config/models.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/config/saved_searches.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/config/settings.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/config/writer.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/http/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/http/audit.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/http/client.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/http/errors.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/http/retry.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/model/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/model/command_model.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/_console.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/colors.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/csv_.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/errors.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/explain.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/flatten.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/headers.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/json_.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/jsonl.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/render.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/table.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/output/yaml_.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/savedfilters/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/savedfilters/custom_fields.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/savedfilters/objecttypes.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/savedfilters/params.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/savedfilters/store.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/savedfilters/tags.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schema/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schema/hashing.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schema/loader.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schema/models.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schemas/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schemas/bundled/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schemas/bundled/manifest.yaml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schemas/bundled/netbox-4.5.10.json.gz +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/schemas/bundled/netbox-4.6.0.json.gz +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/skill/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/_bindings.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/app.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/bulk.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/catalog.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/columns.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/errors.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/filters.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/fk.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/forms.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/keymap.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/nav.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/relations.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/bulk_edit_form.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/columns.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/detail.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/edit_form.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/filter.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/global_search.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/list.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/picker.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/record_picker.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/screens/saved_search_picker.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/search.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/selection.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/styles.tcss +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/view.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/_modal.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/bulk_diff.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/bulk_summary.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/confirm.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/diff.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/help.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/nsc/tui/widgets/nav_tree.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/scripts/gen_docs.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/scripts/sync_agents_md.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/skills/netbox-super-cli/SKILL.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/aliases/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/aliases/test_resolver.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/audit/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/audit/test_redaction_full.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/auth/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/auth/test_verify.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/benchmarks/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/benchmarks/test_startup.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/builder/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/builder/test_build.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/builder/test_default_columns.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/builder/test_fk_metadata.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/builder/test_redaction_marking.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/builder/test_request_body_shape.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cache/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cache/test_prune.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_aliases_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_app_smoke.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_cache_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_commands_dump.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_completion_protocol_smoke.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_completion_smoke.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_config_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_dynamic_completion.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_explain.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_globals_color.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_handlers.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_init_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_lazy_textual_import.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_login_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_meta_subcommands_under_broken_config.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_ndjson_input.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_profiles_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_registration.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_respx_integration.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_runtime.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_runtime_color.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_skill_commands.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_stdin_sniffer.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_tui_command.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/test_writes_respx.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_apply.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_bulk.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_confirmation.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_handler_helpers.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_handlers_audit.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_input.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/cli/writes/test_preflight.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_loader.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_models.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_saved_search_fallback.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_saved_searches.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_settings_private_dir.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_writer_atomicity.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_writer_dotted_paths.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/config/test_writer_roundtrip.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/conftest.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/README.md +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/conftest.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/docker-compose.yml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_audit_redaction.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_auth_error_envelope.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_bulk_create_with_loop_fallback.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_full_cycle.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_login.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_ndjson.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_preflight_blocks_apply.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/test_validation_error_envelope_from_netbox.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/e2e/wait_for_netbox.sh +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/fixtures/profiles/single_profile.yaml +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/fixtures/responses/auth_401.json +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/fixtures/responses/circuits_providers_list.json +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/fixtures/responses/dcim_devices_get.json +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/fixtures/responses/dcim_devices_list_p1.json +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/fixtures/responses/dcim_devices_list_p2.json +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_audit.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_audit_permissions.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_audit_redaction.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_client.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_client_redaction_threading.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_errors.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/http/test_retry.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/model/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/model/test_command_model.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/model/test_request_body_shape.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_colors.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_console.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_csv.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_errors.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_errors_aliases.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_errors_color.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_explain.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_explain_color.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_flatten.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_input_error_envelope.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_json.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_jsonl.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_render_dispatch.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_table.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_table_color.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/output/test_yaml.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/savedfilters/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/savedfilters/test_custom_fields.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/savedfilters/test_objecttypes.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/savedfilters/test_params.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/savedfilters/test_store.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/savedfilters/test_tags.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/schema/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/schema/test_hashing.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/schema/test_loader.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/schema/test_models.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/schema/test_source_ttl.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/scripts/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/scripts/test_gen_docs.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/scripts/test_sync_agents_md.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/skill/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/skill/test_bundle.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/test_packaging.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/__init__.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_app.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bindings.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bulk_apply.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bulk_diff.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bulk_diff_modal.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bulk_edit_form.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bulk_import_isolation.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_bulk_summary.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_catalog.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_column_chooser.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_columns.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_detail_screen.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_diff.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_diff_modal.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_edit_form.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_errors.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_filter_screen.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_filters.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_fk_resolve.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_forms.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_global_search.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_help_overlay.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_keymap.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_list_screen.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_picker.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_record_picker.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_relations.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_saved_filter_integration.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_search.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_selection.py +0 -0
- {netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/tests/tui/test_view.py +0 -0
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to netbox-super-cli are tracked here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loosely. From v1.0.0 onward, releases follow [Semantic Versioning](https://semver.org/) and the version in `pyproject.toml` matches the git tag. Pre-1.0 milestones (Phase 1-5) were pinned by tag while `pyproject.toml` stayed at `0.0.1`.
|
|
4
4
|
|
|
5
|
+
## v1.6.3 — 2026-07-03
|
|
6
|
+
|
|
7
|
+
Patch release. Stops an offline instance from rebuilding the command model from
|
|
8
|
+
the bundled schema on every invocation after a format-version bump.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Offline invocations rebuilt from the bundled schema every time when the only
|
|
13
|
+
cached model was format-stale** ([#140]). After the v1.6.2 format-version bump
|
|
14
|
+
(#139), a cache entry built by an older `nsc` is rejected so it rebuilds with
|
|
15
|
+
the new metadata. Online this self-heals on the first call, but while NetBox
|
|
16
|
+
was unreachable nothing re-saved: each invocation re-parsed the bundled schema
|
|
17
|
+
(~180ms) and tab-completion went briefly blank for the profile. The offline
|
|
18
|
+
bundled fallback is now persisted under the profile — without a fetch
|
|
19
|
+
timestamp, so the TTL fast-path still refetches once NetBox is reachable — so
|
|
20
|
+
the next invocation hits the cache and completion recovers immediately.
|
|
21
|
+
|
|
22
|
+
[#140]: https://github.com/thomaschristory/netbox-super-cli/issues/140
|
|
23
|
+
|
|
5
24
|
## v1.6.2 — 2026-06-30
|
|
6
25
|
|
|
7
26
|
Patch release. Fixes TUI foreign-key pickers resolving to the wrong resource.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: netbox-super-cli
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.3
|
|
4
4
|
Summary: Dynamic NetBox CLI generated from the live OpenAPI schema.
|
|
5
5
|
Project-URL: Homepage, https://github.com/thomaschristory/netbox-super-cli
|
|
6
6
|
Project-URL: Documentation, https://thomaschristory.github.io/netbox-super-cli/
|
|
@@ -82,13 +82,21 @@ class CacheStore:
|
|
|
82
82
|
return None
|
|
83
83
|
return model
|
|
84
84
|
|
|
85
|
-
def save(self, profile: str, model: CommandModel) -> Path:
|
|
85
|
+
def save(self, profile: str, model: CommandModel, *, record_fetch: bool = True) -> Path:
|
|
86
|
+
"""Persist `model` under the profile. `record_fetch=True` also stamps
|
|
87
|
+
the `fetched_at` sidecar (a live fetch just produced this model, so
|
|
88
|
+
the TTL fast-path may trust it). Pass `record_fetch=False` for models
|
|
89
|
+
that were *not* fetched from this NetBox — e.g. the offline bundled
|
|
90
|
+
fallback (issue #140): we want the persisted copy so future offline
|
|
91
|
+
invocations skip the rebuild, but the TTL fast-path must keep
|
|
92
|
+
distrusting it so a real fetch is attempted once NetBox is reachable."""
|
|
86
93
|
self._validate_profile(profile)
|
|
87
94
|
target = self._path_for(profile, model.schema_hash)
|
|
88
95
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
89
96
|
_atomic_write(target, model.model_dump_json(indent=2))
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
if record_fetch:
|
|
98
|
+
meta = self._meta_path_for(profile, model.schema_hash)
|
|
99
|
+
_atomic_write(meta, json.dumps({"fetched_at": time.time()}))
|
|
92
100
|
return target
|
|
93
101
|
|
|
94
102
|
def touch_fetched_at(self, profile: str, schema_hash: str) -> None:
|
|
@@ -15,6 +15,7 @@ access logs as a `GET /api/schema/` between every PATCH.
|
|
|
15
15
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
|
+
import contextlib
|
|
18
19
|
import sys
|
|
19
20
|
import time
|
|
20
21
|
from pathlib import Path
|
|
@@ -97,6 +98,7 @@ def resolve_command_model(
|
|
|
97
98
|
return cached
|
|
98
99
|
bundled = _load_bundled_command_model()
|
|
99
100
|
if bundled is not None:
|
|
101
|
+
_persist_offline_fallback(paths, profile.name, bundled)
|
|
100
102
|
print(
|
|
101
103
|
f"Could not reach NetBox ({exc}) and no cache; using bundled schema.",
|
|
102
104
|
file=sys.stderr,
|
|
@@ -197,6 +199,26 @@ def _find_fresh_cached(
|
|
|
197
199
|
return None
|
|
198
200
|
|
|
199
201
|
|
|
202
|
+
def _persist_offline_fallback(paths: Paths, profile_name: str, model: CommandModel) -> None:
|
|
203
|
+
"""Cache a bundled-schema model under the profile so subsequent offline
|
|
204
|
+
invocations hit `_find_any_cached` (a cheap JSON load) instead of
|
|
205
|
+
re-building from the bundled schema on every call (issue #140). This also
|
|
206
|
+
ends the completion blackout: `cache_probe` can now surface a
|
|
207
|
+
format-current entry.
|
|
208
|
+
|
|
209
|
+
No fetch timestamp is recorded (`record_fetch=False`): the model did not
|
|
210
|
+
come from this NetBox, so the TTL fast-path must keep distrusting it and
|
|
211
|
+
attempt a real fetch on the next invocation — self-healing once NetBox is
|
|
212
|
+
reachable again. A write failure here is non-fatal; we still return the
|
|
213
|
+
in-memory model to the caller."""
|
|
214
|
+
store = CacheStore(root=paths.cache_dir)
|
|
215
|
+
# OSError: disk/permission failure. ValueError: CacheStore rejects a
|
|
216
|
+
# profile name outside `_PROFILE_RE` (names aren't validated at config
|
|
217
|
+
# time). Both are non-fatal here — the in-memory model is still returned.
|
|
218
|
+
with contextlib.suppress(OSError, ValueError):
|
|
219
|
+
store.save(profile_name, model, record_fetch=False)
|
|
220
|
+
|
|
221
|
+
|
|
200
222
|
def _load_bundled_command_model() -> CommandModel | None:
|
|
201
223
|
pkg_dir = Path(_bundled_pkg.__file__).resolve().parent
|
|
202
224
|
manifest_path = pkg_dir / "manifest.yaml"
|
|
@@ -148,6 +148,20 @@ def test_save_writes_fetched_at_sidecar(tmp_path: Path) -> None:
|
|
|
148
148
|
assert before <= fetched_at <= after
|
|
149
149
|
|
|
150
150
|
|
|
151
|
+
def test_save_without_record_fetch_writes_no_sidecar(tmp_path: Path) -> None:
|
|
152
|
+
"""`save(record_fetch=False)` persists the model but leaves no fetch
|
|
153
|
+
timestamp, so the TTL fast-path keeps distrusting the entry. Used by the
|
|
154
|
+
offline bundled-fallback path (issue #140): we cache the rebuilt model to
|
|
155
|
+
skip per-invocation rebuilds, but a real fetch must still be attempted
|
|
156
|
+
once NetBox is reachable again."""
|
|
157
|
+
store = CacheStore(root=tmp_path)
|
|
158
|
+
store.save("prod", _model("a" * 64), record_fetch=False)
|
|
159
|
+
|
|
160
|
+
assert store.load("prod", "a" * 64) is not None
|
|
161
|
+
assert not (tmp_path / "prod" / ("a" * 64 + ".meta.json")).exists()
|
|
162
|
+
assert store.load_fetched_at("prod", "a" * 64) is None
|
|
163
|
+
|
|
164
|
+
|
|
151
165
|
def test_load_fetched_at_returns_none_when_missing(tmp_path: Path) -> None:
|
|
152
166
|
"""No sidecar means we can't prove freshness — distrust the entry."""
|
|
153
167
|
store = CacheStore(root=tmp_path)
|
|
@@ -10,9 +10,11 @@ import httpx
|
|
|
10
10
|
import pytest
|
|
11
11
|
import respx
|
|
12
12
|
|
|
13
|
+
from nsc.cache.store import CacheStore
|
|
13
14
|
from nsc.cli.runtime import ResolvedProfile
|
|
15
|
+
from nsc.completion.cache_probe import load_cached_model_for_profile
|
|
14
16
|
from nsc.config.settings import Paths
|
|
15
|
-
from nsc.model.command_model import CommandModel
|
|
17
|
+
from nsc.model.command_model import MODEL_FORMAT_VERSION, CommandModel
|
|
16
18
|
from nsc.schema.source import (
|
|
17
19
|
SchemaSourceError,
|
|
18
20
|
resolve_command_model,
|
|
@@ -143,6 +145,75 @@ def test_offline_no_cache_falls_back_to_bundled(
|
|
|
143
145
|
assert "bundled" in err.lower()
|
|
144
146
|
|
|
145
147
|
|
|
148
|
+
def _stale_the_only_cache_entry(paths: Paths, profile_name: str) -> str:
|
|
149
|
+
"""Downgrade the single cached entry to a pre-versioning format so
|
|
150
|
+
`CacheStore.load` rejects it purely on `format_version`. Returns its hash."""
|
|
151
|
+
profile_dir = paths.cache_dir / profile_name
|
|
152
|
+
cache_files = [p for p in profile_dir.glob("*.json") if not p.name.endswith(".meta.json")]
|
|
153
|
+
assert len(cache_files) == 1
|
|
154
|
+
stale_file = cache_files[0]
|
|
155
|
+
data = json.loads(stale_file.read_text())
|
|
156
|
+
data["format_version"] = 0
|
|
157
|
+
stale_file.write_text(json.dumps(data))
|
|
158
|
+
return stale_file.stem
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@respx.mock
|
|
162
|
+
def test_offline_format_stale_cache_persists_bundled_fallback(
|
|
163
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
164
|
+
) -> None:
|
|
165
|
+
"""Issue #140: when the only cached entry is format-stale (hash-fresh) and
|
|
166
|
+
NetBox is unreachable, the bundled fallback is persisted under the profile
|
|
167
|
+
so the next invocation skips the per-call rebuild — without a fetch
|
|
168
|
+
timestamp, so the TTL fast-path still refetches once NetBox returns."""
|
|
169
|
+
route = respx.get("https://nb.example/api/schema/?format=json").mock(
|
|
170
|
+
return_value=httpx.Response(200, json=_minimal_schema_doc())
|
|
171
|
+
)
|
|
172
|
+
paths = _paths(tmp_path)
|
|
173
|
+
profile = _profile()
|
|
174
|
+
resolve_command_model(paths=paths, profile=profile, schema_override=None)
|
|
175
|
+
stale_hash = _stale_the_only_cache_entry(paths, "prod")
|
|
176
|
+
capsys.readouterr() # drain
|
|
177
|
+
|
|
178
|
+
route.mock(side_effect=httpx.ConnectError("offline"))
|
|
179
|
+
first = resolve_command_model(paths=paths, profile=profile, schema_override=None)
|
|
180
|
+
assert first.format_version == MODEL_FORMAT_VERSION
|
|
181
|
+
assert "bundled" in capsys.readouterr().err.lower()
|
|
182
|
+
|
|
183
|
+
# The bundled model is now persisted under the profile (a distinct hash),
|
|
184
|
+
# carries no fetch timestamp, and is visible to completion — no blackout.
|
|
185
|
+
store = CacheStore(root=paths.cache_dir)
|
|
186
|
+
assert first.schema_hash != stale_hash
|
|
187
|
+
assert store.load("prod", first.schema_hash) is not None
|
|
188
|
+
assert store.load_fetched_at("prod", first.schema_hash) is None
|
|
189
|
+
probed = load_cached_model_for_profile(paths, "prod")
|
|
190
|
+
assert probed is not None
|
|
191
|
+
assert probed.format_version == MODEL_FORMAT_VERSION
|
|
192
|
+
|
|
193
|
+
# Second offline invocation hits `_find_any_cached` instead of rebuilding.
|
|
194
|
+
second = resolve_command_model(paths=paths, profile=profile, schema_override=None)
|
|
195
|
+
assert second.schema_hash == first.schema_hash
|
|
196
|
+
assert "cached" in capsys.readouterr().err.lower()
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@respx.mock
|
|
200
|
+
def test_offline_bundled_fallback_survives_unpersistable_profile_name(
|
|
201
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
202
|
+
) -> None:
|
|
203
|
+
"""A profile name outside `_PROFILE_RE` (names aren't validated at config
|
|
204
|
+
time) makes `CacheStore.save` raise `ValueError`; the offline bundled
|
|
205
|
+
fallback must swallow it and still return the in-memory model."""
|
|
206
|
+
respx.get("https://nb.example/api/schema/?format=json").mock(
|
|
207
|
+
side_effect=httpx.ConnectError("offline")
|
|
208
|
+
)
|
|
209
|
+
paths = _paths(tmp_path)
|
|
210
|
+
model = resolve_command_model(
|
|
211
|
+
paths=paths, profile=_profile(name="has space"), schema_override=None
|
|
212
|
+
)
|
|
213
|
+
assert isinstance(model, CommandModel)
|
|
214
|
+
assert "bundled" in capsys.readouterr().err.lower()
|
|
215
|
+
|
|
216
|
+
|
|
146
217
|
@respx.mock
|
|
147
218
|
def test_offline_no_cache_no_bundled_raises(
|
|
148
219
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/ISSUE_TEMPLATE/feature_request.yml
RENAMED
|
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
|
{netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/.github/workflows/skill-convention-review.yml
RENAMED
|
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
|
{netbox_super_cli-1.6.2 → netbox_super_cli-1.6.3}/docs/contributing/adding-bundled-schemas.md
RENAMED
|
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
|
|
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
|
|
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
|