code-context-control 2.61.0__tar.gz → 2.61.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.
- {code_context_control-2.61.0/code_context_control.egg-info → code_context_control-2.61.2}/PKG-INFO +1 -1
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/c3.py +1 -1
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_pretool_enforce.py +22 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/project_card.js +57 -9
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/credentials.py +8 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/edit.py +8 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2/code_context_control.egg-info}/PKG-INFO +1 -1
- {code_context_control-2.61.0 → code_context_control-2.61.2}/code_context_control.egg-info/SOURCES.txt +1 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/pyproject.toml +1 -1
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/credential_store.py +58 -0
- code_context_control-2.61.2/tests/test_vault_write_guard.py +284 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/LICENSE +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/README.md +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/__init__.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/_hook_utils.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/commands/__init__.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/commands/common.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/commands/parser.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/docs.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/edits.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/guide/bitbucket.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/guide/credentials.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/guide/getting-started.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/guide/index.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/guide/oracle.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/guide/shared.css +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/guide/tools.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/guide/workflow.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_artifact.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_auto_snapshot.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_c3_signal.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_c3read.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_dispatch.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_edit_ledger.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_edit_unlock.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_filter.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_ghost_files.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_prompt_recall.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_read.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_session_stats.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hook_terse_advisor.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_server.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/app.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/add_project.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/config_editor.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/drill_artifacts.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/drill_health.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/drill_panel.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/drill_subprojects.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/drill_tasks.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/drill_views.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/global_search.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/hub_credentials.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/mcp_manager.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/modals.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/project_tree.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/session_drawer.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/settings_modal.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/sidebar.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/summary_bar.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/task_board.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/toasts.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/topbar.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/state.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/mcp_proxy.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/mcp_server.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/progress.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/server.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/__init__.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/_helpers.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/agent.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/artifacts.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/bitbucket.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/compress.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/delegate.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/edits.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/federate.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/filter.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/impact.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/jira.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/memory.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/project.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/read.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/search.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/session.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/shell.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/status.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/tasks.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/tools/validate.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/api.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/app.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/components/bitbucket.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/components/chat.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/components/credentials.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/components/dashboard.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/components/edits.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/components/instructions.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/components/jira.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/components/memory.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/components/sessions.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/components/settings.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/components/sidebar.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/components/tasks.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/icons.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/pm_shared.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/shared.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui/theme.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui_legacy.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/cli/ui_nano.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/code_context_control.egg-info/dependency_links.txt +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/code_context_control.egg-info/entry_points.txt +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/code_context_control.egg-info/requires.txt +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/code_context_control.egg-info/top_level.txt +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/core/__init__.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/core/config.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/core/ide.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/core/mcp_toml.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/core/web_security.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/__init__.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/config.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/mcp_oracle.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/oracle_server.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/oracle_ui.html +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/__init__.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/activity_reporter.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/api_auth.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/c3_bridge.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/chat_engine.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/chat_store.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/cross_memory.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/federated_graph.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/health_checker.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/insight_engine.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/local_session.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/memory_reader.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/memory_writer.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/project_scanner.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/review_agent.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/tool_executor.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/services/tool_registry.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/activity.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/agents.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/app.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/busy.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/chat/conversations.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/chat/input.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/chat/markdown.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/chat/send.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/chat/stream_renderer.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/chat/toolbar.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/core.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/crossgraph.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/header.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/insights.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/projects.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/settings.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/suggestions.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/oracle/ui/theme_tabs.js +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/__init__.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/activity_log.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/agent_base.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/agents.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/artifact_defs.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/artifact_store.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/auto_memory.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/bench/__init__.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/bench/external/__init__.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/bench/external/aider_polyglot.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/bench/external/swe_bench.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/benchmark_dashboard.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/bitbucket_client.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/bitbucket_credentials.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/circuit_breaker.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/claude_md.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/compressor.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/context_snapshot.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/conversation_store.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/doc_index.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/e2e_benchmark.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/e2e_evaluator.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/e2e_tasks.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/edit_ledger.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/embedding_index.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/error_reporting.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/file_memory.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/git_context.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/hub_service.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/indexer.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/jira_client.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/jira_cloud.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/jira_credentials.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/jira_data_center.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/jira_links.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/memory.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/memory_consolidator.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/memory_distiller.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/memory_graph.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/memory_grounder.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/memory_queue.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/memory_scorer.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/metrics.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/notifications.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/ollama_bridge.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/ollama_client.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/ollama_credentials.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/output_filter.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/parser.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/project_manager.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/project_runtime.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/protocol.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/proxy_state.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/repo_map.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/retention.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/retrieval_broker.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/router.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/runtime.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/scanner.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/session_benchmark.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/session_manager.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/session_preloader.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/subprojects.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/task_store.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/telemetry.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/text_index.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/time_tracker.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/tool_classifier.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/transcript_index.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/validation_cache.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/vector_store.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/watcher.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/services/win_subprocess.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/setup.cfg +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_activity_reporter.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_aider_polyglot.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_antigravity_transition.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_artifact_store.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_artifact_tool.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_bitbucket_cli_smoke.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_bitbucket_client.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_bitbucket_config_fallback.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_bitbucket_credentials.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_bitbucket_tool.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_c3_bridge_project_artifacts.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_c3_shell.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_circuit_breaker.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_claude_md_merge.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_cli_smoke.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_compressor_large_file.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_credential_store.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_credentials_routes.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_credentials_tool.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_creds_cli_smoke.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_delegate_cascade.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_e2e_benchmark.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_edit_ledger_hook.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_edit_normalization.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_enforcement_flip.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_federated_graph.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_filter_backoff.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_ghost_files.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_ghost_generation.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_git_branch_awareness.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_hook_dispatch.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_hook_pretool_enforce.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_hook_prompt_recall.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_hook_smoke.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_hook_state.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_hook_utf8_stdio.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_hub_credentials_route.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_hub_credentials_routes_write.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_hub_inspect_api.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_hub_server_smoke.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_hub_subproject_links.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_init_scan.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_install_mcp_entrypoint.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_jira_cli_smoke.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_jira_client.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_jira_config_fallback.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_jira_credentials.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_jira_links.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_jira_routes.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_jira_tool.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_lazy_store_init.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_mcp_host_guard.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_mcp_memory_timeout.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_mcp_server_smoke.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_mcp_toml.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_memory_distiller.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_memory_graph_api.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_memory_queue.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_memory_system.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_no_stale_model_pins.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_notification_dedup.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_notification_discipline.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_ollama_credentials.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_oracle_api_auth.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_oracle_apikey_api.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_oracle_c3_bridge.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_oracle_chat_engine.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_oracle_discovery_api.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_oracle_local_auth.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_oracle_ollama_bridge.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_oracle_scanner_cache.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_oracle_security_fixes.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_oracle_subproject_awareness.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_oracle_ui_bundle.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_output_filter.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_permissions.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_pm_api.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_project_manager.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_project_manager_merge.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_project_tool.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_read_coercion.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_read_edit_parity.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_related_facts.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_repo_map.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_response_boilerplate.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_retention.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_review_digest.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_service_durability.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_session_benchmark.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_session_budget.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_shell_creds.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_shell_robustness.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_subproject_exclusion.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_subproject_federation.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_subprojects.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_swe_bench.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_task_store.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_task_tool.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_time_tracker.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_token_telemetry.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_tool_registry.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_upgrade_and_version.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_validate.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_version_sync.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_web_security.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tests/test_windows_reliability.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/__init__.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/backend.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/main.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/__init__.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/benchmark_view.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/claudemd_view.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/compress_view.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/index_view.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/init_view.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/mcp_view.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/optimize_view.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/pipe_view.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/projects_view.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/search_view.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/session_view.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/stats.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/screens/ui_view.py +0 -0
- {code_context_control-2.61.0 → code_context_control-2.61.2}/tui/theme.tcss +0 -0
{code_context_control-2.61.0/code_context_control.egg-info → code_context_control-2.61.2}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-context-control
|
|
3
|
-
Version: 2.61.
|
|
3
|
+
Version: 2.61.2
|
|
4
4
|
Summary: Local code-intelligence layer for AI coding tools (Claude Code, Codex, Copilot, Cursor, Antigravity). Retrieve less, read less, edit safer — version the configs that shape your agent (CLAUDE.md, skills, hooks, MCP), and manage multi-project + sub-project hierarchies from a local hub.
|
|
5
5
|
Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -78,6 +78,11 @@ _PREREQS = {
|
|
|
78
78
|
_ADVISORY_TOOLS = {"Read", "Grep", "Glob", "FindFiles", "SearchText"}
|
|
79
79
|
_BLOCKED_TOOLS = {"Edit", "Write", "MultiEdit"}
|
|
80
80
|
|
|
81
|
+
# Vault files no native write may touch, regardless of unlock state.
|
|
82
|
+
# Mirrors services.credential_store.VAULT_PROTECTED_FILES (parity-tested);
|
|
83
|
+
# duplicated because hooks must stay import-light.
|
|
84
|
+
_VAULT_FILES = frozenset({"config.json", "secrets.enc", "cred_state.json"})
|
|
85
|
+
|
|
81
86
|
# Redirect messages per native tool
|
|
82
87
|
_REDIRECTS = {
|
|
83
88
|
"Read": (
|
|
@@ -283,6 +288,23 @@ def run(payload: dict, project_path: Path | None = None) -> dict | None:
|
|
|
283
288
|
session_id = str(payload.get("session_id") or "")
|
|
284
289
|
base = project_path if project_path is not None else Path.cwd()
|
|
285
290
|
|
|
291
|
+
# Vault write-guard: the credential registry/state may never be modified
|
|
292
|
+
# by the agent — even with a warm c3 signal or sticky unlock (v2.61.2).
|
|
293
|
+
if tool_name in _BLOCKED_TOOLS:
|
|
294
|
+
target = Path(str(tool_input.get("file_path") or ""))
|
|
295
|
+
if target.name.lower() in _VAULT_FILES and target.parent.name.lower() == ".c3":
|
|
296
|
+
return {
|
|
297
|
+
"hookSpecificOutput": {
|
|
298
|
+
"hookEventName": "PreToolUse",
|
|
299
|
+
"permissionDecision": "deny",
|
|
300
|
+
"permissionDecisionReason": (
|
|
301
|
+
f"[c3:vault-protected] {target.name} belongs to the "
|
|
302
|
+
"credential vault and cannot be modified by the agent. "
|
|
303
|
+
"Ask the user to use the Credentials UI or `c3 creds` CLI."
|
|
304
|
+
),
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
286
308
|
# Session-scoped load: state written by a different session comes back
|
|
287
309
|
# empty, so stale unlocks degrade to the advisory path below.
|
|
288
310
|
state = load_enforcement_state(base, session_id=session_id)
|
{code_context_control-2.61.0 → code_context_control-2.61.2}/cli/hub_ui/components/project_card.js
RENAMED
|
@@ -377,24 +377,63 @@ function ProjectCard({ p, isChild, rollup, expanded, onToggleExpand, onChanged,
|
|
|
377
377
|
// Launch the UI server, then open its tab once the port shows up in the
|
|
378
378
|
// registry. launch_session cannot return the port (the detached child
|
|
379
379
|
// picks it), so open a placeholder tab synchronously — still inside the
|
|
380
|
-
// click gesture, so popup blockers allow it
|
|
381
|
-
//
|
|
380
|
+
// click gesture, so popup blockers allow it. The placeholder carries its
|
|
381
|
+
// own poll/redirect script (about:blank inherits this tab's origin, so
|
|
382
|
+
// its fetches are same-origin) and resolves even if this hub tab is
|
|
383
|
+
// closed, reloaded, or hung mid-launch — it must never depend on the
|
|
384
|
+
// opener surviving the launch window.
|
|
382
385
|
const start = async (e) => {
|
|
383
386
|
e.stopPropagation();
|
|
384
387
|
if (starting) return;
|
|
385
388
|
setStarting(true);
|
|
386
389
|
const win = window.open('', '_blank');
|
|
390
|
+
// armed = the placeholder polls and navigates itself; when false
|
|
391
|
+
// (write failed / popup blocked) the hub-side poll navigates instead.
|
|
392
|
+
let armed = false;
|
|
387
393
|
if (win) {
|
|
388
394
|
try {
|
|
395
|
+
const escHtml = (s) => String(s).replace(/&/g, '&').replace(/</g, '<');
|
|
396
|
+
const jsStr = (s) => JSON.stringify(String(s)).replace(/</g, '\\u003c');
|
|
389
397
|
win.document.write(
|
|
390
|
-
`<title>Starting ${p.name}…</title>` +
|
|
398
|
+
`<title>Starting ${escHtml(p.name)}…</title>` +
|
|
391
399
|
'<body style="background:#0d1117;color:#8b949e;font-family:sans-serif;' +
|
|
392
|
-
'display:flex;align-items:center;justify-content:center;height:100vh">' +
|
|
393
|
-
|
|
400
|
+
'display:flex;align-items:center;justify-content:center;height:100vh;margin:0">' +
|
|
401
|
+
'<div style="text-align:center;max-width:560px;padding:0 20px">' +
|
|
402
|
+
'<div id="spin" style="margin:0 auto 14px;width:22px;height:22px;' +
|
|
403
|
+
'border:2px solid #21262d;border-top-color:#58a6ff;border-radius:50%;' +
|
|
404
|
+
'animation:s .8s linear infinite"></div>' +
|
|
405
|
+
'<style>@keyframes s{to{transform:rotate(360deg)}}</style>' +
|
|
406
|
+
`<div id="msg" style="color:#c9d1d9">Starting ${escHtml(p.name)}…</div>` +
|
|
407
|
+
'<div id="sub" style="margin-top:6px;font-size:12px">waiting for the UI server to report a port</div>' +
|
|
408
|
+
'</div><script>' +
|
|
409
|
+
`var ORIGIN=${jsStr(location.origin)},TARGET=${jsStr((p.path || '').toLowerCase())},NAME=${jsStr(p.name)},tries=0,dead=0;` +
|
|
410
|
+
'function fail(why){dead=1;document.getElementById("spin").style.display="none";' +
|
|
411
|
+
'document.getElementById("msg").textContent=NAME+": "+why;' +
|
|
412
|
+
'document.getElementById("sub").textContent=' +
|
|
413
|
+
'"Check the project\'s .c3/ui.log — you can close this tab.";}' +
|
|
414
|
+
'window.__c3fail=fail;' +
|
|
415
|
+
'function poll(){if(dead)return;' +
|
|
416
|
+
'fetch(ORIGIN+"/api/projects").then(function(r){return r.json()})' +
|
|
417
|
+
'.then(function(rows){if(dead)return;' +
|
|
418
|
+
'var row=(Array.isArray(rows)?rows:[]).find(function(r){' +
|
|
419
|
+
'return (r.path||"").toLowerCase()===TARGET});' +
|
|
420
|
+
'if(row&&row.port){location.replace("http://127.0.0.1:"+row.port);return}' +
|
|
421
|
+
'if(++tries>=20){fail("UI server did not report a port after 30s");return}' +
|
|
422
|
+
'setTimeout(poll,1500)}).catch(function(){if(dead)return;' +
|
|
423
|
+
'if(++tries>=20){fail("hub unreachable");return}setTimeout(poll,1500)})}' +
|
|
424
|
+
'setTimeout(poll,1200);<\/script>');
|
|
425
|
+
win.document.close();
|
|
426
|
+
armed = true;
|
|
394
427
|
} catch { }
|
|
395
428
|
}
|
|
396
429
|
const fail = (msg) => {
|
|
397
|
-
if (win)
|
|
430
|
+
if (win) {
|
|
431
|
+
// Surface the error inside the tab the user is looking at; close
|
|
432
|
+
// it only when the placeholder script isn't there to show it.
|
|
433
|
+
let shown = false;
|
|
434
|
+
try { if (armed && win.__c3fail) { win.__c3fail(msg); shown = true; } } catch { }
|
|
435
|
+
if (!shown) { try { win.close(); } catch { } }
|
|
436
|
+
}
|
|
398
437
|
notify(msg, 'err');
|
|
399
438
|
setStarting(false);
|
|
400
439
|
};
|
|
@@ -403,19 +442,28 @@ function ProjectCard({ p, isChild, rollup, expanded, onToggleExpand, onChanged,
|
|
|
403
442
|
if (!d.launched) { fail('Launch failed'); return; }
|
|
404
443
|
notify(`Starting ${p.name}…`);
|
|
405
444
|
setTimeout(onChanged, 1500);
|
|
445
|
+
// Bookkeeping poll: refresh the card and clear the busy state. Only
|
|
446
|
+
// navigates or closes the tab when the placeholder isn't armed.
|
|
406
447
|
const poll = async (tries) => {
|
|
407
448
|
let rows = [];
|
|
408
449
|
try { rows = await api.get('/api/projects'); } catch { }
|
|
409
450
|
const row = (Array.isArray(rows) ? rows : []).find(r =>
|
|
410
451
|
(r.path || '').toLowerCase() === (p.path || '').toLowerCase());
|
|
411
452
|
if (row && row.port) {
|
|
412
|
-
|
|
413
|
-
|
|
453
|
+
if (!armed) {
|
|
454
|
+
const url = 'http://127.0.0.1:' + row.port;
|
|
455
|
+
if (win) { win.location = url; } else { window.open(url, '_blank'); }
|
|
456
|
+
}
|
|
414
457
|
onChanged();
|
|
415
458
|
setStarting(false);
|
|
416
459
|
return;
|
|
417
460
|
}
|
|
418
|
-
if (tries >= 20) {
|
|
461
|
+
if (tries >= 20) {
|
|
462
|
+
if (!armed && win) { try { win.close(); } catch { } }
|
|
463
|
+
notify(`${p.name}: UI server did not report a port — check .c3/ui.log`, 'err');
|
|
464
|
+
setStarting(false);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
419
467
|
setTimeout(() => poll(tries + 1), 1500);
|
|
420
468
|
};
|
|
421
469
|
setTimeout(() => poll(0), 1200);
|
|
@@ -130,6 +130,14 @@ def _act_reveal(name: str, svc, project_path: str) -> str:
|
|
|
130
130
|
"or ask the user to enable agent_readable in the Credentials UI / "
|
|
131
131
|
f"`c3 creds set {name} --agent-readable`."
|
|
132
132
|
)
|
|
133
|
+
if not cs.verify_agent_readable(name, scope=entry["scope"], project_path=project_path):
|
|
134
|
+
return (
|
|
135
|
+
f"[creds:integrity] {name!r} is marked agent_readable in the registry, "
|
|
136
|
+
"but the flag's keyring attestation is missing or disagrees — the "
|
|
137
|
+
"registry may have been modified outside the credentials API. Ask the "
|
|
138
|
+
"user to re-confirm the flag (toggle it in the Credentials UI or "
|
|
139
|
+
"`c3 creds`); re-saving it restores the attestation."
|
|
140
|
+
)
|
|
133
141
|
value = cs.get_value(name, project_path=project_path, scope=entry["scope"])
|
|
134
142
|
if value is None:
|
|
135
143
|
return (
|
|
@@ -13,6 +13,8 @@ import json
|
|
|
13
13
|
import threading
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
|
+
from services import credential_store as _cs
|
|
17
|
+
|
|
16
18
|
# Per-file locks — keyed by resolved absolute path string.
|
|
17
19
|
_file_locks: dict[str, threading.Lock] = {}
|
|
18
20
|
_locks_lock = threading.Lock()
|
|
@@ -237,6 +239,12 @@ def handle_edit(file_path: str, old_string: str, new_string: str,
|
|
|
237
239
|
path = Path(svc.project_path) / path
|
|
238
240
|
path = path.resolve()
|
|
239
241
|
|
|
242
|
+
# Vault write-guard: the credential registry/state is never agent-writable
|
|
243
|
+
# (covers create, edit, and batch modes — and c3_project's edit proxy).
|
|
244
|
+
guard = _cs.vault_guard_reason(path)
|
|
245
|
+
if guard:
|
|
246
|
+
return finalize("c3_edit", {"file": file_path}, guard, "vault-protected")
|
|
247
|
+
|
|
240
248
|
# Relative path for ledger + display (computed even for new files)
|
|
241
249
|
try:
|
|
242
250
|
rel = str(path.relative_to(Path(svc.project_path).resolve())).replace("\\", "/")
|
{code_context_control-2.61.0 → code_context_control-2.61.2/code_context_control.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-context-control
|
|
3
|
-
Version: 2.61.
|
|
3
|
+
Version: 2.61.2
|
|
4
4
|
Summary: Local code-intelligence layer for AI coding tools (Claude Code, Codex, Copilot, Cursor, Antigravity). Retrieve less, read less, edit safer — version the configs that shape your agent (CLAUDE.md, skills, hooks, MCP), and manage multi-project + sub-project hierarchies from a local hub.
|
|
5
5
|
Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -333,6 +333,7 @@ tests/test_token_telemetry.py
|
|
|
333
333
|
tests/test_tool_registry.py
|
|
334
334
|
tests/test_upgrade_and_version.py
|
|
335
335
|
tests/test_validate.py
|
|
336
|
+
tests/test_vault_write_guard.py
|
|
336
337
|
tests/test_version_sync.py
|
|
337
338
|
tests/test_web_security.py
|
|
338
339
|
tests/test_windows_reliability.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "code-context-control"
|
|
7
|
-
version = "2.61.
|
|
7
|
+
version = "2.61.2"
|
|
8
8
|
description = "Local code-intelligence layer for AI coding tools (Claude Code, Codex, Copilot, Cursor, Antigravity). Retrieve less, read less, edit safer — version the configs that shape your agent (CLAUDE.md, skills, hooks, MCP), and manage multi-project + sub-project hierarchies from a local hub."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -135,6 +135,56 @@ def _account(realm_s: str, name: str) -> str:
|
|
|
135
135
|
return f"{realm_s}|{name}"
|
|
136
136
|
|
|
137
137
|
|
|
138
|
+
def _flag_account(realm_s: str, name: str) -> str:
|
|
139
|
+
return f"{realm_s}|{name}::agent_readable"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# Files the agent must never write through c3 tool surfaces: the vault
|
|
143
|
+
# registry (config.json) and its sidecar state. Editing these outside the
|
|
144
|
+
# credentials API is how a prompt-injected agent would grant itself reveal
|
|
145
|
+
# access. Mirrored in cli/hook_pretool_enforce.py (parity-tested).
|
|
146
|
+
VAULT_PROTECTED_FILES = frozenset({"config.json", "secrets.enc", "cred_state.json"})
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def vault_guard_reason(path) -> str:
|
|
150
|
+
"""Non-empty refusal message when ``path`` is a vault file under .c3/."""
|
|
151
|
+
p = Path(path)
|
|
152
|
+
if p.name.lower() in VAULT_PROTECTED_FILES and p.parent.name.lower() == ".c3":
|
|
153
|
+
return (
|
|
154
|
+
f"[c3:vault-protected] {p.name} belongs to the credential vault "
|
|
155
|
+
"(registry/state) and cannot be modified by the agent. Changes go "
|
|
156
|
+
"through the Credentials UI or the `c3 creds` CLI — ask the user."
|
|
157
|
+
)
|
|
158
|
+
return ""
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _write_flag_attestation(realm_s: str, name: str, readable: bool) -> None:
|
|
162
|
+
"""Keyring copy of agent_readable — reveal trusts registry AND keyring.
|
|
163
|
+
|
|
164
|
+
A registry flag flipped by editing config.json directly (outside this
|
|
165
|
+
API) disagrees with the attestation, and reveal fails closed.
|
|
166
|
+
"""
|
|
167
|
+
try:
|
|
168
|
+
_keyring_module().set_password(
|
|
169
|
+
KEYRING_SERVICE, _flag_account(realm_s, name), "1" if readable else "0")
|
|
170
|
+
except Exception:
|
|
171
|
+
pass # keyring-less env: verify_agent_readable fails closed anyway
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def verify_agent_readable(name: str, *, scope: str, project_path: str = ".") -> bool:
|
|
175
|
+
"""True only when the keyring attestation agrees the flag is enabled.
|
|
176
|
+
|
|
177
|
+
Fails closed: a missing attestation (pre-v2.61.2 entry, keyring failure,
|
|
178
|
+
or a registry edited outside this API) reads as not-readable.
|
|
179
|
+
"""
|
|
180
|
+
try:
|
|
181
|
+
realm_s = realm(_norm_scope(scope, project_path), project_path)
|
|
182
|
+
return _keyring_module().get_password(
|
|
183
|
+
KEYRING_SERVICE, _flag_account(realm_s, name)) == "1"
|
|
184
|
+
except Exception:
|
|
185
|
+
return False
|
|
186
|
+
|
|
187
|
+
|
|
138
188
|
def _scope_dir(scope: str, project_path: str) -> Optional[Path]:
|
|
139
189
|
"""Base directory holding .c3/ for the scope; None when global is unresolvable."""
|
|
140
190
|
if scope == "global":
|
|
@@ -376,6 +426,7 @@ def set_credential(
|
|
|
376
426
|
section["entries"][name] = entry
|
|
377
427
|
config["credentials"] = section
|
|
378
428
|
_save_config(base, config)
|
|
429
|
+
_write_flag_attestation(realm_s, name, bool(agent_readable))
|
|
379
430
|
return dict(entry)
|
|
380
431
|
|
|
381
432
|
|
|
@@ -407,6 +458,9 @@ def update_metadata(name: str, *, scope: str, project_path: str = ".", **fields)
|
|
|
407
458
|
entry["updated"] = _utcnow()
|
|
408
459
|
config["credentials"] = section
|
|
409
460
|
_save_config(base, config)
|
|
461
|
+
if "agent_readable" in fields:
|
|
462
|
+
_write_flag_attestation(
|
|
463
|
+
realm(scope, project_path), name, bool(fields["agent_readable"]))
|
|
410
464
|
return dict(entry)
|
|
411
465
|
|
|
412
466
|
|
|
@@ -467,6 +521,10 @@ def delete_credential(name: str, *, scope: str, project_path: str = ".") -> bool
|
|
|
467
521
|
removed_value = True
|
|
468
522
|
except Exception:
|
|
469
523
|
pass
|
|
524
|
+
try:
|
|
525
|
+
_keyring_module().delete_password(KEYRING_SERVICE, _flag_account(realm_s, name))
|
|
526
|
+
except Exception:
|
|
527
|
+
pass
|
|
470
528
|
config = _load_config(base)
|
|
471
529
|
section = _creds_section(config)
|
|
472
530
|
removed_entry = name in section["entries"]
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""Vault write-guard hotfix (v2.61.2) — closes the agent_readable escalation.
|
|
2
|
+
|
|
3
|
+
Attack: the agent edits .c3/config.json (c3_edit, native Write, or shell),
|
|
4
|
+
flips a credential's agent_readable flag, then calls reveal. Three guards:
|
|
5
|
+
1. c3_edit refuses vault files (registry + sidecar state), all modes.
|
|
6
|
+
2. The PreToolUse hook denies native Edit/Write/MultiEdit on vault files
|
|
7
|
+
even when a warm c3 signal or sticky unlock would otherwise allow them.
|
|
8
|
+
3. reveal requires a keyring attestation of agent_readable written only by
|
|
9
|
+
the credentials API — a registry edited outside the API fails closed.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import base64
|
|
14
|
+
import json
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
import unittest
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from unittest import mock
|
|
20
|
+
|
|
21
|
+
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
22
|
+
sys.path.insert(0, str(REPO_ROOT))
|
|
23
|
+
|
|
24
|
+
from cli import _hook_utils # noqa: E402
|
|
25
|
+
|
|
26
|
+
# hook_pretool_enforce imports plain `_hook_utils`; alias so both spellings
|
|
27
|
+
# resolve to the same module instance (mirrors cli/hook_dispatch.py).
|
|
28
|
+
sys.modules.setdefault("_hook_utils", _hook_utils)
|
|
29
|
+
|
|
30
|
+
import cli.hook_pretool_enforce as hook # noqa: E402
|
|
31
|
+
from cli.tools.credentials import handle_credentials # noqa: E402
|
|
32
|
+
from cli.tools.edit import handle_edit # noqa: E402
|
|
33
|
+
from services import credential_store as cs # noqa: E402
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class _StubKeyring:
|
|
37
|
+
def __init__(self):
|
|
38
|
+
self.store: dict[tuple[str, str], str] = {}
|
|
39
|
+
|
|
40
|
+
def set_password(self, service, account, password):
|
|
41
|
+
self.store[(service, account)] = password
|
|
42
|
+
|
|
43
|
+
def get_password(self, service, account):
|
|
44
|
+
return self.store.get((service, account))
|
|
45
|
+
|
|
46
|
+
def delete_password(self, service, account):
|
|
47
|
+
if (service, account) not in self.store:
|
|
48
|
+
raise KeyError("not found")
|
|
49
|
+
del self.store[(service, account)]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class _StubFernet:
|
|
53
|
+
def __init__(self, key):
|
|
54
|
+
self._key = key
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def generate_key():
|
|
58
|
+
return base64.urlsafe_b64encode(b"0" * 32)
|
|
59
|
+
|
|
60
|
+
def encrypt(self, data):
|
|
61
|
+
return base64.urlsafe_b64encode(self._key + b"|" + data)
|
|
62
|
+
|
|
63
|
+
def decrypt(self, token):
|
|
64
|
+
raw = base64.urlsafe_b64decode(token)
|
|
65
|
+
key, _, data = raw.partition(b"|")
|
|
66
|
+
if key != self._key:
|
|
67
|
+
raise ValueError("bad key")
|
|
68
|
+
return data
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class _StubLedger:
|
|
72
|
+
def __init__(self):
|
|
73
|
+
self.edits: list[dict] = []
|
|
74
|
+
|
|
75
|
+
def log_edit(self, **kw):
|
|
76
|
+
self.edits.append(kw)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class _StubActivity:
|
|
80
|
+
def __init__(self):
|
|
81
|
+
self.events: list[tuple] = []
|
|
82
|
+
|
|
83
|
+
def log(self, kind, data):
|
|
84
|
+
self.events.append((kind, data))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class _Svc:
|
|
88
|
+
def __init__(self, project_path):
|
|
89
|
+
self.project_path = project_path
|
|
90
|
+
self.edit_ledger = _StubLedger()
|
|
91
|
+
self.activity_log = _StubActivity()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _finalize(tool, args, resp, summary, **kw):
|
|
95
|
+
return resp
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class TestParity(unittest.TestCase):
|
|
99
|
+
def test_hook_set_matches_store_set(self):
|
|
100
|
+
self.assertEqual(hook._VAULT_FILES, cs.VAULT_PROTECTED_FILES)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class TestEditVaultGuard(unittest.TestCase):
|
|
104
|
+
def setUp(self):
|
|
105
|
+
self._tmp = tempfile.TemporaryDirectory()
|
|
106
|
+
self.proj = Path(self._tmp.name)
|
|
107
|
+
(self.proj / ".c3").mkdir()
|
|
108
|
+
(self.proj / ".c3" / "config.json").write_text("{}", encoding="utf-8")
|
|
109
|
+
self.svc = _Svc(str(self.proj))
|
|
110
|
+
|
|
111
|
+
def tearDown(self):
|
|
112
|
+
self._tmp.cleanup()
|
|
113
|
+
|
|
114
|
+
def _edit(self, rel, old, new, edits=""):
|
|
115
|
+
return handle_edit(rel, old, new, summary="", tags="",
|
|
116
|
+
replace_all=False, svc=self.svc,
|
|
117
|
+
finalize=_finalize, edits=edits)
|
|
118
|
+
|
|
119
|
+
def test_edit_config_json_refused(self):
|
|
120
|
+
resp = self._edit(".c3/config.json", "{}", '{"x": 1}')
|
|
121
|
+
self.assertIn("[c3:vault-protected]", resp)
|
|
122
|
+
text = (self.proj / ".c3" / "config.json").read_text(encoding="utf-8")
|
|
123
|
+
self.assertEqual(text, "{}")
|
|
124
|
+
|
|
125
|
+
def test_create_mode_refused_for_missing_vault_file(self):
|
|
126
|
+
resp = self._edit(".c3/cred_state.json", "", '{"pwned": true}')
|
|
127
|
+
self.assertIn("[c3:vault-protected]", resp)
|
|
128
|
+
self.assertFalse((self.proj / ".c3" / "cred_state.json").exists())
|
|
129
|
+
|
|
130
|
+
def test_batch_mode_refused(self):
|
|
131
|
+
edits = json.dumps([{"old_string": "{}", "new_string": "{}"}])
|
|
132
|
+
resp = self._edit(".c3/config.json", "", "", edits=edits)
|
|
133
|
+
self.assertIn("[c3:vault-protected]", resp)
|
|
134
|
+
|
|
135
|
+
def test_secrets_enc_refused(self):
|
|
136
|
+
resp = self._edit(".c3/secrets.enc", "", "AAAA")
|
|
137
|
+
self.assertIn("[c3:vault-protected]", resp)
|
|
138
|
+
|
|
139
|
+
def test_absolute_path_refused(self):
|
|
140
|
+
resp = self._edit(str(self.proj / ".c3" / "config.json"), "{}", "{ }")
|
|
141
|
+
self.assertIn("[c3:vault-protected]", resp)
|
|
142
|
+
|
|
143
|
+
def test_case_insensitive_match(self):
|
|
144
|
+
resp = self._edit(".C3/Config.JSON", "{}", "{ }")
|
|
145
|
+
self.assertIn("[c3:vault-protected]", resp)
|
|
146
|
+
|
|
147
|
+
def test_normal_file_still_editable(self):
|
|
148
|
+
(self.proj / "a.txt").write_text("hello", encoding="utf-8")
|
|
149
|
+
resp = self._edit("a.txt", "hello", "world")
|
|
150
|
+
self.assertIn("✓", resp)
|
|
151
|
+
self.assertEqual((self.proj / "a.txt").read_text(encoding="utf-8"), "world")
|
|
152
|
+
|
|
153
|
+
def test_config_json_outside_c3_dir_not_guarded(self):
|
|
154
|
+
(self.proj / "config.json").write_text("{}", encoding="utf-8")
|
|
155
|
+
resp = self._edit("config.json", "{}", '{"ok": 1}')
|
|
156
|
+
self.assertIn("✓", resp)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class TestHookVaultGuard(unittest.TestCase):
|
|
160
|
+
def setUp(self):
|
|
161
|
+
self._tmp = tempfile.TemporaryDirectory()
|
|
162
|
+
self.proj = Path(self._tmp.name)
|
|
163
|
+
(self.proj / ".c3").mkdir()
|
|
164
|
+
|
|
165
|
+
def tearDown(self):
|
|
166
|
+
self._tmp.cleanup()
|
|
167
|
+
|
|
168
|
+
def _run(self, tool_name, file_path):
|
|
169
|
+
payload = {"tool_name": tool_name, "tool_input": {"file_path": file_path}}
|
|
170
|
+
return hook.run(payload, project_path=self.proj)
|
|
171
|
+
|
|
172
|
+
def _assert_vault_deny(self, out):
|
|
173
|
+
self.assertIsNotNone(out)
|
|
174
|
+
decision = out.get("hookSpecificOutput", {})
|
|
175
|
+
self.assertEqual(decision.get("permissionDecision"), "deny")
|
|
176
|
+
self.assertIn("[c3:vault-protected]",
|
|
177
|
+
decision.get("permissionDecisionReason", ""))
|
|
178
|
+
|
|
179
|
+
def test_write_tools_denied_on_vault_files(self):
|
|
180
|
+
vault = str(self.proj / ".c3" / "config.json")
|
|
181
|
+
for tool in ("Edit", "Write", "MultiEdit"):
|
|
182
|
+
self._assert_vault_deny(self._run(tool, vault))
|
|
183
|
+
|
|
184
|
+
def test_denied_even_when_c3_prereq_satisfied(self):
|
|
185
|
+
# Force the unlock check to allow — the vault guard must fire first.
|
|
186
|
+
with mock.patch.object(hook, "_check_c3_used",
|
|
187
|
+
return_value=(True, "signal")) as checked:
|
|
188
|
+
out = self._run("Write", str(self.proj / ".c3" / "config.json"))
|
|
189
|
+
self._assert_vault_deny(out)
|
|
190
|
+
checked.assert_not_called()
|
|
191
|
+
|
|
192
|
+
def test_read_of_vault_file_not_vault_denied(self):
|
|
193
|
+
out = self._run("Read", str(self.proj / ".c3" / "config.json"))
|
|
194
|
+
reason = (out or {}).get("hookSpecificOutput", {}).get(
|
|
195
|
+
"permissionDecisionReason", "")
|
|
196
|
+
self.assertNotIn("[c3:vault-protected]", reason)
|
|
197
|
+
|
|
198
|
+
def test_non_vault_write_gets_normal_enforcement(self):
|
|
199
|
+
out = self._run("Write", str(self.proj / "notes.txt"))
|
|
200
|
+
decision = (out or {}).get("hookSpecificOutput", {})
|
|
201
|
+
self.assertEqual(decision.get("permissionDecision"), "deny")
|
|
202
|
+
self.assertIn("[c3:enforce]", decision.get("permissionDecisionReason", ""))
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class TestRevealIntegrity(unittest.TestCase):
|
|
206
|
+
def setUp(self):
|
|
207
|
+
self._stub = _StubKeyring()
|
|
208
|
+
self._tmp_proj = tempfile.TemporaryDirectory()
|
|
209
|
+
self._tmp_home = tempfile.TemporaryDirectory()
|
|
210
|
+
self._patchers = [
|
|
211
|
+
mock.patch.object(cs, "_keyring_module", return_value=self._stub),
|
|
212
|
+
mock.patch.object(cs, "_crypto_module", return_value=_StubFernet),
|
|
213
|
+
mock.patch.object(cs, "_global_base", return_value=Path(self._tmp_home.name)),
|
|
214
|
+
]
|
|
215
|
+
for p in self._patchers:
|
|
216
|
+
p.start()
|
|
217
|
+
cs._ACTIVE_SECRETS.clear()
|
|
218
|
+
self.svc = _Svc(self._tmp_proj.name)
|
|
219
|
+
|
|
220
|
+
def tearDown(self):
|
|
221
|
+
for p in self._patchers:
|
|
222
|
+
p.stop()
|
|
223
|
+
cs._ACTIVE_SECRETS.clear()
|
|
224
|
+
self._tmp_proj.cleanup()
|
|
225
|
+
self._tmp_home.cleanup()
|
|
226
|
+
|
|
227
|
+
def _creds(self, action, **kwargs):
|
|
228
|
+
return handle_credentials(action, self.svc, _finalize, **kwargs)
|
|
229
|
+
|
|
230
|
+
def _tamper_flag(self, name, value=True):
|
|
231
|
+
cfg_path = Path(self.svc.project_path) / ".c3" / "config.json"
|
|
232
|
+
config = json.loads(cfg_path.read_text(encoding="utf-8"))
|
|
233
|
+
config["credentials"]["entries"][name]["agent_readable"] = value
|
|
234
|
+
cfg_path.write_text(json.dumps(config), encoding="utf-8")
|
|
235
|
+
|
|
236
|
+
def test_tampered_registry_flag_fails_closed(self):
|
|
237
|
+
self._creds("set", name="API_KEY", value="s3cret")
|
|
238
|
+
self.assertIn("[creds:not-readable]", self._creds("reveal", name="API_KEY"))
|
|
239
|
+
self._tamper_flag("API_KEY")
|
|
240
|
+
resp = self._creds("reveal", name="API_KEY")
|
|
241
|
+
self.assertIn("[creds:integrity]", resp)
|
|
242
|
+
self.assertNotIn("s3cret", resp)
|
|
243
|
+
|
|
244
|
+
def test_creation_time_agent_readable_reveals(self):
|
|
245
|
+
self._creds("set", name="MINE", value="agent-made", agent_readable=True)
|
|
246
|
+
resp = self._creds("reveal", name="MINE")
|
|
247
|
+
self.assertIn("[creds:reveal]", resp)
|
|
248
|
+
self.assertIn("agent-made", resp)
|
|
249
|
+
|
|
250
|
+
def test_user_toggle_via_update_metadata_reveals(self):
|
|
251
|
+
self._creds("set", name="TOKEN", value="tok-1")
|
|
252
|
+
cs.update_metadata("TOKEN", scope="project",
|
|
253
|
+
project_path=self.svc.project_path,
|
|
254
|
+
agent_readable=True)
|
|
255
|
+
resp = self._creds("reveal", name="TOKEN")
|
|
256
|
+
self.assertIn("[creds:reveal]", resp)
|
|
257
|
+
self.assertIn("tok-1", resp)
|
|
258
|
+
|
|
259
|
+
def test_missing_attestation_fails_closed(self):
|
|
260
|
+
# Legacy entry: registry says readable but no keyring attestation.
|
|
261
|
+
self._creds("set", name="OLD", value="v", agent_readable=True)
|
|
262
|
+
flag_keys = [k for k in self._stub.store if k[1].endswith("::agent_readable")]
|
|
263
|
+
for k in flag_keys:
|
|
264
|
+
del self._stub.store[k]
|
|
265
|
+
self.assertIn("[creds:integrity]", self._creds("reveal", name="OLD"))
|
|
266
|
+
|
|
267
|
+
def test_delete_removes_attestation(self):
|
|
268
|
+
self._creds("set", name="GONE", value="v", agent_readable=True)
|
|
269
|
+
self.assertTrue(
|
|
270
|
+
any(k[1].endswith("::agent_readable") for k in self._stub.store))
|
|
271
|
+
self._creds("delete", name="GONE", scope="project")
|
|
272
|
+
self.assertFalse(
|
|
273
|
+
any(k[1].endswith("::agent_readable") for k in self._stub.store))
|
|
274
|
+
|
|
275
|
+
def test_verify_false_on_keyring_failure(self):
|
|
276
|
+
self._creds("set", name="KR", value="v", agent_readable=True)
|
|
277
|
+
with mock.patch.object(cs, "_keyring_module",
|
|
278
|
+
side_effect=RuntimeError("keyring down")):
|
|
279
|
+
self.assertFalse(cs.verify_agent_readable(
|
|
280
|
+
"KR", scope="project", project_path=self.svc.project_path))
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
if __name__ == "__main__":
|
|
284
|
+
unittest.main()
|
|
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
|