code-context-control 2.124.0__tar.gz → 2.125.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {code_context_control-2.124.0/code_context_control.egg-info → code_context_control-2.125.0}/PKG-INFO +2 -2
- {code_context_control-2.124.0 → code_context_control-2.125.0}/README.md +1 -1
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/c3.py +1 -1
- {code_context_control-2.124.0 → code_context_control-2.125.0/code_context_control.egg-info}/PKG-INFO +2 -2
- {code_context_control-2.124.0 → code_context_control-2.125.0}/code_context_control.egg-info/SOURCES.txt +4 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/config.py +5 -0
- code_context_control-2.125.0/oracle/listeners.py +210 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/oracle_server.py +88 -2
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/oracle_ui.html +9 -3
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/chat_poll.py +8 -3
- code_context_control-2.125.0/oracle/services/client_tokens.py +239 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/mobile_api.py +203 -17
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/app.js +1 -1
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/settings.js +75 -19
- {code_context_control-2.124.0 → code_context_control-2.125.0}/pyproject.toml +1 -1
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mobile_api.py +85 -1
- code_context_control-2.125.0/tests/test_mobile_clients.py +543 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mobile_extras.py +4 -2
- code_context_control-2.125.0/tests/test_oracle_loopback.py +269 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/LICENSE +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/__init__.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/_hook_utils.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/_shell_writes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/commands/__init__.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/commands/common.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/commands/parser.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/docs.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/edits.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/access.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/bitbucket.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/credentials.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/getting-started.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/index.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/masking.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/oracle.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/shared.css +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/tools.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/guide/workflow.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_access_guard.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_artifact.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_auto_snapshot.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_c3_signal.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_c3read.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_codex_lifecycle.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_dispatch.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_edit_ledger.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_edit_unlock.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_filter.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_ghost_files.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_pretool_enforce.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_prompt_recall.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_read.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_session_stats.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hook_terse_advisor.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_server.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/app.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/add_project.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/config_editor.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/drill_artifacts.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/drill_health.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/drill_panel.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/drill_subprojects.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/drill_tasks.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/drill_views.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/global_search.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_access.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_ci.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_cred_audit.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_credentials.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_enforcement.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_locks.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/hub_tokens.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/mcp_manager.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/modals.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/project_card.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/project_tree.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/session_drawer.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/settings_modal.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/sidebar.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/summary_bar.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/task_board.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/toasts.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/components/topbar.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui/state.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/hub_ui.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/mcp_proxy.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/mcp_server.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/progress.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/server.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/__init__.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/_grants.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/_helpers.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/agent.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/artifacts.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/bitbucket.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/ci.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/compress.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/credentials.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/delegate.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/edit.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/edit_verify.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/edits.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/federate.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/filter.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/impact.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/jira.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/locks.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/memory.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/override.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/project.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/read.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/search.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/session.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/shell.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/shell_job.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/shell_nudge.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/shell_parsers.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/shell_render.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/status.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/tasks.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/tools/validate.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/api.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/app.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/access.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/bitbucket.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/chat.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/credentials.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/dashboard.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/edits.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/enforcement.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/instructions.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/jira.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/memory.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/sessions.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/settings.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/sidebar.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/components/tasks.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/icons.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/pm_shared.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/shared.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui/theme.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui_legacy.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/cli/ui_nano.html +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/code_context_control.egg-info/dependency_links.txt +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/code_context_control.egg-info/entry_points.txt +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/code_context_control.egg-info/requires.txt +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/code_context_control.egg-info/top_level.txt +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/core/__init__.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/core/codex_patch.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/core/config.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/core/host.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/core/ide.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/core/mcp_toml.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/core/web_security.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/__init__.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/mcp_oracle.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/__init__.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/activity_reporter.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/api_auth.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/c3_bridge.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/chat_engine.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/chat_store.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/cross_memory.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/discovery_audit.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/federated_graph.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/health_checker.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/insight_engine.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/local_session.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/memory_reader.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/memory_writer.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/project_scanner.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/review_agent.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/tool_executor.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/services/tool_registry.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/activity.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/agents.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/busy.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/chat/conversations.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/chat/input.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/chat/markdown.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/chat/send.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/chat/stream_renderer.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/chat/toolbar.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/core.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/crossgraph.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/header.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/insights.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/projects.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/qrcode.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/suggestions.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/oracle/ui/theme_tabs.js +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/__init__.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/access_guard.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/access_telemetry.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/activity_log.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/agent_base.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/agent_locks.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/agents.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/artifact_defs.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/artifact_store.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/atomic_json.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/auto_memory.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/background_service.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/__init__.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/external/__init__.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/external/aider_polyglot.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/external/swe_bench.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/map_eval.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/search_eval.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bench/shell_eval.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/benchmark_dashboard.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bitbucket_client.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/bitbucket_credentials.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_act.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_cache.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_expr.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_failures.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_github.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_impact.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_intel.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_runner.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ci_workflow.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/circuit_breaker.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/claude_md.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/codex_integration.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/codex_transcript.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/compressor.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/context_snapshot.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/conversation_store.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/cred_audit.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/cred_telemetry.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/credential_store.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/dir_map.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/doc_index.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/e2e_benchmark.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/e2e_evaluator.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/e2e_tasks.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/edit_ledger.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/embedding_index.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/enforcement_policy.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/error_reporting.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/file_map.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/file_memory.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/git_context.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/hub_service.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/index_store.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/indexer.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/jira_client.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/jira_cloud.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/jira_credentials.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/jira_data_center.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/jira_links.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/lexical_index.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/mask_activation.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/mask_mirror.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/mask_presets.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory_consolidator.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory_distiller.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory_graph.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory_grounder.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory_queue.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/memory_scorer.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/metrics.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/notifications.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ollama_bridge.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ollama_client.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/ollama_credentials.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/oracle_service.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/output_filter.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/override_grants.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/override_policy.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/override_requests.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/override_wake.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/parser.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/project_manager.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/project_runtime.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/protocol.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/proxy_state.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/repo_map.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/repo_shape.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/reranker.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/retention.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/retrieval.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/retrieval_broker.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/router.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/runtime.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/scanner.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/session_benchmark.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/session_manager.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/session_preloader.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/shell_jobs.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/shell_output.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/subprojects.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/task_store.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/telemetry.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/text_index.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/time_tracker.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/tool_classifier.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/transcript_index.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/validation_cache.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/vector_store.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/watcher.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/services/win_subprocess.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/setup.cfg +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_access_builtin_optout.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_access_guard.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_access_guard_meta.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_access_guard_shell_project.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_access_guard_surfaces.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_access_guard_wiring.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_activity_reporter.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_agent_locks.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_aider_polyglot.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_antigravity_transition.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_artifact_store.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_artifact_tool.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_atomic_json.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_background_service.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_bitbucket_cli_smoke.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_bitbucket_client.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_bitbucket_config_fallback.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_bitbucket_credentials.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_bitbucket_tool.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_builtin_modes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_c3_bridge_project_artifacts.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_c3_shell.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_chat_poll.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_act.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_cache.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_expr.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_impact.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_intel.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_runner.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ci_workflow.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_circuit_breaker.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_claude_md_merge.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_claude_md_updater.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_cli_smoke.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_codex_mcp_transport.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_codex_native.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_compressor_large_file.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_confirm_guard.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_confirm_guard_wiring.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_cooccurrence_bounds.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_cred_audit.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_cred_telemetry.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_credential_login_kind.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_credential_store.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_credential_ui_parity.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_credentials_routes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_credentials_tool.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_creds_cli_smoke.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_delegate_cascade.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_delegate_version_probe.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_dir_map_c3.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_e2e_benchmark.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_edit_ledger_deadline.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_edit_ledger_hook.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_edit_locking.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_edit_normalization.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_edit_verify.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_embedding_index_deadlock.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_enforcement_flip.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_enforcement_policy.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_enforcement_routes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_env_parser.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_failed_calls_do_not_unlock.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_federated_graph.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_file_map_c1.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_filter_backoff.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ghost_files.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ghost_generation.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_git_branch_awareness.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_access_guard.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_codex_compat.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_dispatch.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_pretool_enforce.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_prompt_recall.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_smoke.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_state.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hook_utf8_stdio.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_credentials_route.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_credentials_routes_write.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_enforcement_routes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_inspect_api.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_locks_routes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_oracle_service_routes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_override_routes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_server_smoke.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_hub_subproject_links.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ide_workflow_adaptation.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_index_cap_not_hardcoded.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_init_clear_preserves_user_content.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_init_scan.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_install_mcp_entrypoint.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_cli_smoke.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_client.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_config_fallback.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_credentials.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_links.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_routes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_jira_tool.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_large_files_c4.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_lazy_store_init.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_map_eval.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_map_telemetry_c0.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mask_guard.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mask_routes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mask_surfaces.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mcp_host_guard.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mcp_memory_timeout.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mcp_server_smoke.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mcp_toml.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_memory_distiller.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_memory_graph_api.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_memory_queue.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_memory_system.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mobile_override_routes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_mobile_security_api.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_no_stale_model_pins.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_notification_dedup.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_notification_discipline.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ollama_credentials.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_api_auth.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_apikey_api.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_c3_bridge.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_chat_engine.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_chat_store.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_discovery_api.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_discovery_audit.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_health_probe.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_local_auth.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_ollama_bridge.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_scanner_cache.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_security_fixes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_session_bootstrap.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_subproject_awareness.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_oracle_ui_bundle.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_output_filter.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_override_grants.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_override_grants_mcp.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_override_requests.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_override_wake.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_permissions.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_pm_api.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_project_manager.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_project_manager_merge.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_project_tool.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_read_coercion.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_read_edit_parity.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_related_facts.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_repo_map.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_repo_shape.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_response_boilerplate.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_retention.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_review_digest.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_eval.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_p1.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_p2.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_p2b.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_p3.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_p4.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_search_p5.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_service_durability.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_session_benchmark.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_session_budget.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_budget_s1.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_creds.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_eval.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_jobs_s3.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_nudge_s4.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_output.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_parsers_s2.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_robustness.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_telemetry_s0.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_write_ledger.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_shell_writes.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_subproject_clear_scope.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_subproject_depth_consumers.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_subproject_exclusion.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_subproject_federation.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_subproject_linkage_api.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_subprojects.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_swe_bench.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_task_store.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_task_tool.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_time_tracker.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_token_telemetry.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_token_tracking.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_tool_registry.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_ui_jsx_syntax.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_upgrade_and_version.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_validate.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_vault_write_guard.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_version_sync.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_web_security.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tests/test_windows_reliability.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/__init__.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/backend.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/main.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/__init__.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/benchmark_view.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/claudemd_view.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/compress_view.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/index_view.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/init_view.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/mcp_view.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/optimize_view.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/pipe_view.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/projects_view.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/search_view.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/session_view.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/stats.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/screens/ui_view.py +0 -0
- {code_context_control-2.124.0 → code_context_control-2.125.0}/tui/theme.tcss +0 -0
{code_context_control-2.124.0/code_context_control.egg-info → code_context_control-2.125.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-context-control
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.125.0
|
|
4
4
|
Summary: Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub.
|
|
5
5
|
Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -380,7 +380,7 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
|
|
|
380
380
|
## Security & privacy
|
|
381
381
|
|
|
382
382
|
- **All web servers bind `127.0.0.1` by default** and are guarded against browser-based attacks even on loopback — a Host-header allowlist (defeats DNS rebinding) plus an Origin/Referer check on every request (defeats cross-origin CSRF), with scoped, non-wildcard CORS. There is still **no user authentication**, so do not expose these servers to an untrusted network without auth/TLS in front. Binding to a non-loopback interface is opt-in and warned at startup. *(Hardening added in v2.33.0.)*
|
|
383
|
-
- **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires
|
|
383
|
+
- **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires a **Bearer token on every method, GETs included** — the Discovery token, or (v2.125.0+) a **per-client token**: one credential per paired device, stored hashed in `~/.c3/oracle/clients.json`, revocable one at a time, and carrying a kind (`mobile` or `desk`) that override decisions are audited under. It validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (the QR carries the URL and a freshly minted per-device token; a legacy Discovery-token code is one button away). A same-machine client such as C3 Desk pairs itself with `POST /api/mobile/clients` and the owner-only `~/.c3/oracle/bootstrap.key`. Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`; with such a bind the Oracle also listens on `127.0.0.1` (`loopback_listener`, default on) so local clients are not refused. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
|
|
384
384
|
- **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing, `set_builtin_disabled`, and the raw-content access preview.
|
|
385
385
|
- **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`; even then request bodies, local variables, and prompts are stripped.
|
|
386
386
|
- **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
|
|
@@ -314,7 +314,7 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
|
|
|
314
314
|
## Security & privacy
|
|
315
315
|
|
|
316
316
|
- **All web servers bind `127.0.0.1` by default** and are guarded against browser-based attacks even on loopback — a Host-header allowlist (defeats DNS rebinding) plus an Origin/Referer check on every request (defeats cross-origin CSRF), with scoped, non-wildcard CORS. There is still **no user authentication**, so do not expose these servers to an untrusted network without auth/TLS in front. Binding to a non-loopback interface is opt-in and warned at startup. *(Hardening added in v2.33.0.)*
|
|
317
|
-
- **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires
|
|
317
|
+
- **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires a **Bearer token on every method, GETs included** — the Discovery token, or (v2.125.0+) a **per-client token**: one credential per paired device, stored hashed in `~/.c3/oracle/clients.json`, revocable one at a time, and carrying a kind (`mobile` or `desk`) that override decisions are audited under. It validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (the QR carries the URL and a freshly minted per-device token; a legacy Discovery-token code is one button away). A same-machine client such as C3 Desk pairs itself with `POST /api/mobile/clients` and the owner-only `~/.c3/oracle/bootstrap.key`. Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`; with such a bind the Oracle also listens on `127.0.0.1` (`loopback_listener`, default on) so local clients are not refused. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
|
|
318
318
|
- **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing, `set_builtin_disabled`, and the raw-content access preview.
|
|
319
319
|
- **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`; even then request bodies, local variables, and prompts are stripped.
|
|
320
320
|
- **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
|
{code_context_control-2.124.0 → code_context_control-2.125.0/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.
|
|
3
|
+
Version: 2.125.0
|
|
4
4
|
Summary: Local MCP code-intelligence for AI coding tools: surgical search/read/edit, agent-config version history, path-level access + masking guards, and a multi-project hub.
|
|
5
5
|
Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -380,7 +380,7 @@ C3 also records real per-tool usage to `.c3/tool_telemetry.jsonl`, so estimates
|
|
|
380
380
|
## Security & privacy
|
|
381
381
|
|
|
382
382
|
- **All web servers bind `127.0.0.1` by default** and are guarded against browser-based attacks even on loopback — a Host-header allowlist (defeats DNS rebinding) plus an Origin/Referer check on every request (defeats cross-origin CSRF), with scoped, non-wildcard CORS. There is still **no user authentication**, so do not expose these servers to an untrusted network without auth/TLS in front. Binding to a non-loopback interface is opt-in and warned at startup. *(Hardening added in v2.33.0.)*
|
|
383
|
-
- **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires
|
|
383
|
+
- **Mobile gateway (`/api/mobile/*`)** — the Oracle exposes a companion-app surface for the C3 mobile app: merged cross-project feed, project overview, PM board read/write, digest, notification ack, plus the security surface (credential vault, Access Guard rules and masks, tool discipline). Unlike the legacy Oracle GETs it requires a **Bearer token on every method, GETs included** — the Discovery token, or (v2.125.0+) a **per-client token**: one credential per paired device, stored hashed in `~/.c3/oracle/clients.json`, revocable one at a time, and carrying a kind (`mobile` or `desk`) that override decisions are audited under. It validates every project path against the registry, and audits every write to the project's activity log and edit ledger. Pair the app from Oracle Settings → *Mobile app* (the QR carries the URL and a freshly minted per-device token; a legacy Discovery-token code is one button away). A same-machine client such as C3 Desk pairs itself with `POST /api/mobile/clients` and the owner-only `~/.c3/oracle/bootstrap.key`. Remote reachability is the same recipe as the Discovery API: set `bind_host` to the Tailscale/LAN interface IP (avoid `0.0.0.0`) and list that address in `allowed_hosts`, both in `~/.c3/oracle/config.json`; with such a bind the Oracle also listens on `127.0.0.1` (`loopback_listener`, default on) so local clients are not refused. Disable the whole surface with `"mobile_api_enabled": false`, or any subsystem with `mobile_credentials_enabled` / `mobile_access_enabled` / their `_write` twins.
|
|
384
384
|
- **The mobile gateway never returns a credential value.** It has no reveal route by design: entries cross the wire through an allowlist serializer, and a value can only be identified by its length and an on-demand fingerprint. Two further levers ship **off** — `mobile_creds_agent_readable_raise` (raising `agent_readable` from the phone) and `mobile_access_global_scope` (machine-wide access rules) — because each exceeds what a leaked Bearer token could otherwise reach. Deliberately absent from the surface: bulk `.env` import, denial-counter clearing, `set_builtin_disabled`, and the raw-content access preview.
|
|
385
385
|
- **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`; even then request bodies, local variables, and prompts are stripped.
|
|
386
386
|
- **LLM memory distillation is local-first.** Cloud distillation (v2.51.0) is off by default and opt-in per project.
|
|
@@ -141,6 +141,7 @@ core/mcp_toml.py
|
|
|
141
141
|
core/web_security.py
|
|
142
142
|
oracle/__init__.py
|
|
143
143
|
oracle/config.py
|
|
144
|
+
oracle/listeners.py
|
|
144
145
|
oracle/mcp_oracle.py
|
|
145
146
|
oracle/oracle_server.py
|
|
146
147
|
oracle/oracle_ui.html
|
|
@@ -151,6 +152,7 @@ oracle/services/c3_bridge.py
|
|
|
151
152
|
oracle/services/chat_engine.py
|
|
152
153
|
oracle/services/chat_poll.py
|
|
153
154
|
oracle/services/chat_store.py
|
|
155
|
+
oracle/services/client_tokens.py
|
|
154
156
|
oracle/services/cross_memory.py
|
|
155
157
|
oracle/services/discovery_audit.py
|
|
156
158
|
oracle/services/federated_graph.py
|
|
@@ -410,6 +412,7 @@ tests/test_memory_graph_api.py
|
|
|
410
412
|
tests/test_memory_queue.py
|
|
411
413
|
tests/test_memory_system.py
|
|
412
414
|
tests/test_mobile_api.py
|
|
415
|
+
tests/test_mobile_clients.py
|
|
413
416
|
tests/test_mobile_extras.py
|
|
414
417
|
tests/test_mobile_override_routes.py
|
|
415
418
|
tests/test_mobile_security_api.py
|
|
@@ -426,6 +429,7 @@ tests/test_oracle_discovery_api.py
|
|
|
426
429
|
tests/test_oracle_discovery_audit.py
|
|
427
430
|
tests/test_oracle_health_probe.py
|
|
428
431
|
tests/test_oracle_local_auth.py
|
|
432
|
+
tests/test_oracle_loopback.py
|
|
429
433
|
tests/test_oracle_ollama_bridge.py
|
|
430
434
|
tests/test_oracle_scanner_cache.py
|
|
431
435
|
tests/test_oracle_security_fixes.py
|
|
@@ -11,6 +11,11 @@ DEFAULTS = {
|
|
|
11
11
|
"port": 3331,
|
|
12
12
|
# ── Discovery API (external LLM tool surface, v2.32.0) ──
|
|
13
13
|
"bind_host": "127.0.0.1", # loopback only by default (was 0.0.0.0)
|
|
14
|
+
# When bind_host is a specific non-loopback address (a Tailscale / LAN
|
|
15
|
+
# IP), ALSO serve the same app on 127.0.0.1 so same-machine clients (the
|
|
16
|
+
# desk tray client, `c3 oracle open`, curl) are not refused. No effect on
|
|
17
|
+
# a loopback or wildcard bind. See oracle/listeners.py.
|
|
18
|
+
"loopback_listener": True,
|
|
14
19
|
"api_enabled": True, # expose /api/discovery/* REST surface
|
|
15
20
|
"api_require_auth": True, # require Bearer token on /api/discovery/*
|
|
16
21
|
"api_max_tier": "action", # cap exposed tools: "read" | "action"
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""Serve the Oracle on its configured bind host AND on loopback.
|
|
2
|
+
|
|
3
|
+
Why
|
|
4
|
+
---
|
|
5
|
+
``bind_host`` exists so a phone on the tailnet can reach the Oracle: set it
|
|
6
|
+
to the Tailscale address and the server listens there — and ONLY there. A
|
|
7
|
+
process on the same machine dialling ``http://127.0.0.1:3331`` was refused,
|
|
8
|
+
which broke every same-box client that reasonably assumes loopback: the
|
|
9
|
+
desk tray client, ``c3 oracle open``'s health probe, the single-instance
|
|
10
|
+
check in ``run_oracle``, curl in a terminal.
|
|
11
|
+
|
|
12
|
+
So when the bind host is a specific non-loopback address, the same Flask app
|
|
13
|
+
is served a second time on ``127.0.0.1``, same port. Same app object means
|
|
14
|
+
the same guards: the Host allowlist (``core.web_security``) always admits
|
|
15
|
+
``localhost`` / ``127.0.0.1``, the Bearer gates do not care which socket a
|
|
16
|
+
request arrived on, and ``local_session.is_local`` already treats loopback
|
|
17
|
+
as same-machine. A wildcard bind (``0.0.0.0`` / ``::``) already answers on
|
|
18
|
+
loopback, and a loopback bind needs no second socket, so neither gets one.
|
|
19
|
+
|
|
20
|
+
Config: ``loopback_listener`` in ``~/.c3/oracle/config.json`` (default
|
|
21
|
+
true). Off = the pre-v2.125.0 behaviour, one socket on ``bind_host``.
|
|
22
|
+
|
|
23
|
+
Shape
|
|
24
|
+
-----
|
|
25
|
+
``werkzeug.serving.make_server`` builds one threaded WSGI server per host;
|
|
26
|
+
every listener but the first runs ``serve_forever`` in a daemon thread, the
|
|
27
|
+
first blocks the calling thread exactly as ``app.run`` did. ``shutdown()``
|
|
28
|
+
stops every listener, and ``serve_forever`` calls it on the way out
|
|
29
|
+
(Ctrl-C included) so a secondary socket never outlives the primary.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import ipaddress
|
|
35
|
+
import logging
|
|
36
|
+
import threading
|
|
37
|
+
|
|
38
|
+
LOOPBACK_HOST = "127.0.0.1"
|
|
39
|
+
|
|
40
|
+
# Binds that already answer on loopback, so a second socket would only
|
|
41
|
+
# collide with the first.
|
|
42
|
+
_WILDCARD_HOSTS = frozenset({"", "0.0.0.0", "::", "*"})
|
|
43
|
+
|
|
44
|
+
_log = logging.getLogger("oracle.listeners")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def is_loopback_host(host: str | None) -> bool:
|
|
48
|
+
"""True for ``localhost`` and any loopback IP literal (v4 or v6)."""
|
|
49
|
+
h = (host or "").strip().lower()
|
|
50
|
+
if h == "localhost":
|
|
51
|
+
return True
|
|
52
|
+
try:
|
|
53
|
+
return ipaddress.ip_address(h.strip("[]")).is_loopback
|
|
54
|
+
except ValueError:
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def listener_hosts(bind_host: str | None, loopback_listener: bool = True) -> list[str]:
|
|
59
|
+
"""The hosts to listen on, primary first.
|
|
60
|
+
|
|
61
|
+
>>> listener_hosts("100.77.40.101")
|
|
62
|
+
['100.77.40.101', '127.0.0.1']
|
|
63
|
+
>>> listener_hosts("127.0.0.1")
|
|
64
|
+
['127.0.0.1']
|
|
65
|
+
>>> listener_hosts("0.0.0.0")
|
|
66
|
+
['0.0.0.0']
|
|
67
|
+
>>> listener_hosts("100.77.40.101", loopback_listener=False)
|
|
68
|
+
['100.77.40.101']
|
|
69
|
+
"""
|
|
70
|
+
primary = (bind_host or "").strip() or LOOPBACK_HOST
|
|
71
|
+
hosts = [primary]
|
|
72
|
+
if not loopback_listener:
|
|
73
|
+
return hosts
|
|
74
|
+
if primary.lower() in _WILDCARD_HOSTS or is_loopback_host(primary):
|
|
75
|
+
return hosts
|
|
76
|
+
hosts.append(LOOPBACK_HOST)
|
|
77
|
+
return hosts
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class OracleListeners:
|
|
81
|
+
"""One Flask app, N sockets. See the module docstring."""
|
|
82
|
+
|
|
83
|
+
def __init__(self, app, hosts: list[str], port: int, *, threaded: bool = True):
|
|
84
|
+
if not hosts:
|
|
85
|
+
raise ValueError("at least one host is required")
|
|
86
|
+
self.app = app
|
|
87
|
+
self.hosts = list(hosts)
|
|
88
|
+
self.port = int(port)
|
|
89
|
+
self.threaded = threaded
|
|
90
|
+
self.servers: list = []
|
|
91
|
+
# Servers whose serve_forever loop has been entered. socketserver's
|
|
92
|
+
# shutdown() waits on an Event that only serve_forever sets, so
|
|
93
|
+
# calling it on a server that was never served blocks forever.
|
|
94
|
+
self._serving: list = []
|
|
95
|
+
self._threads: list[threading.Thread] = []
|
|
96
|
+
self._lock = threading.Lock()
|
|
97
|
+
self._started = False
|
|
98
|
+
self._stopped = False
|
|
99
|
+
|
|
100
|
+
# ── lifecycle ──────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
def start(self) -> OracleListeners:
|
|
103
|
+
"""Bind every socket. The primary is NOT served yet — call
|
|
104
|
+
:meth:`serve_forever` (blocking) or :meth:`serve_in_background`.
|
|
105
|
+
|
|
106
|
+
A secondary bind that fails (something else already holds the port on
|
|
107
|
+
loopback, say) is logged and skipped; the primary failing raises, as
|
|
108
|
+
``app.run`` would have.
|
|
109
|
+
"""
|
|
110
|
+
from werkzeug.serving import make_server
|
|
111
|
+
|
|
112
|
+
with self._lock:
|
|
113
|
+
if self._started:
|
|
114
|
+
return self
|
|
115
|
+
self._started = True
|
|
116
|
+
primary_host = self.hosts[0]
|
|
117
|
+
primary = make_server(primary_host, self.port, self.app, threaded=self.threaded)
|
|
118
|
+
self.servers.append(primary)
|
|
119
|
+
# A port of 0 asks the OS for one; every secondary must then share it.
|
|
120
|
+
if self.port == 0:
|
|
121
|
+
self.port = int(primary.socket.getsockname()[1])
|
|
122
|
+
for host in self.hosts[1:]:
|
|
123
|
+
try:
|
|
124
|
+
srv = make_server(host, self.port, self.app, threaded=self.threaded)
|
|
125
|
+
except (OSError, SystemExit) as exc:
|
|
126
|
+
# werkzeug turns a failed bind into sys.exit(1) after printing
|
|
127
|
+
# the reason; for a SECONDARY socket that is a warning, not
|
|
128
|
+
# the end of the server.
|
|
129
|
+
_log.warning("loopback listener on %s:%s not started: %s",
|
|
130
|
+
host, self.port, exc)
|
|
131
|
+
continue
|
|
132
|
+
self.servers.append(srv)
|
|
133
|
+
self._serving.append(srv)
|
|
134
|
+
t = threading.Thread(target=srv.serve_forever, daemon=True,
|
|
135
|
+
name=f"oracle-listener-{host}")
|
|
136
|
+
t.start()
|
|
137
|
+
self._threads.append(t)
|
|
138
|
+
return self
|
|
139
|
+
|
|
140
|
+
def serve_forever(self) -> None:
|
|
141
|
+
"""Serve the primary listener on the calling thread until shutdown.
|
|
142
|
+
|
|
143
|
+
Every other listener is stopped on the way out, whichever way the
|
|
144
|
+
primary exits (``shutdown()`` from another thread, Ctrl-C, an error).
|
|
145
|
+
"""
|
|
146
|
+
if not self._started:
|
|
147
|
+
self.start()
|
|
148
|
+
primary = self.servers[0]
|
|
149
|
+
self._serving.append(primary)
|
|
150
|
+
try:
|
|
151
|
+
primary.serve_forever()
|
|
152
|
+
finally:
|
|
153
|
+
self.shutdown()
|
|
154
|
+
|
|
155
|
+
def serve_in_background(self) -> OracleListeners:
|
|
156
|
+
"""Serve the primary on a daemon thread too (tests, embedders)."""
|
|
157
|
+
if not self._started:
|
|
158
|
+
self.start()
|
|
159
|
+
primary = self.servers[0]
|
|
160
|
+
self._serving.append(primary)
|
|
161
|
+
t = threading.Thread(target=primary.serve_forever, daemon=True,
|
|
162
|
+
name=f"oracle-listener-{self.hosts[0]}")
|
|
163
|
+
t.start()
|
|
164
|
+
self._threads.insert(0, t)
|
|
165
|
+
return self
|
|
166
|
+
|
|
167
|
+
def shutdown(self) -> None:
|
|
168
|
+
"""Stop every listener and close its socket. Idempotent, and safe to
|
|
169
|
+
call from any thread that is not itself inside ``serve_forever``."""
|
|
170
|
+
with self._lock:
|
|
171
|
+
if self._stopped:
|
|
172
|
+
return
|
|
173
|
+
self._stopped = True
|
|
174
|
+
for srv in self.servers:
|
|
175
|
+
if srv in self._serving:
|
|
176
|
+
try:
|
|
177
|
+
srv.shutdown()
|
|
178
|
+
except Exception:
|
|
179
|
+
pass
|
|
180
|
+
try:
|
|
181
|
+
srv.server_close()
|
|
182
|
+
except Exception:
|
|
183
|
+
pass
|
|
184
|
+
for t in self._threads:
|
|
185
|
+
if t is not threading.current_thread():
|
|
186
|
+
t.join(timeout=5)
|
|
187
|
+
|
|
188
|
+
# ── introspection ──────────────────────────────────────
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def addresses(self) -> list[tuple[str, int]]:
|
|
192
|
+
"""``(host, port)`` per live listener, primary first."""
|
|
193
|
+
out = []
|
|
194
|
+
for srv in self.servers:
|
|
195
|
+
try:
|
|
196
|
+
name = srv.socket.getsockname()
|
|
197
|
+
out.append((str(name[0]), int(name[1])))
|
|
198
|
+
except OSError:
|
|
199
|
+
continue
|
|
200
|
+
return out
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def urls(self) -> list[str]:
|
|
204
|
+
out = []
|
|
205
|
+
for host, port in self.addresses:
|
|
206
|
+
shown = "127.0.0.1" if host in ("0.0.0.0", "") else host
|
|
207
|
+
if ":" in shown and not shown.startswith("["):
|
|
208
|
+
shown = f"[{shown}]"
|
|
209
|
+
out.append(f"http://{shown}:{port}")
|
|
210
|
+
return out
|
|
@@ -19,8 +19,9 @@ if str(_PROJECT_ROOT) not in sys.path:
|
|
|
19
19
|
from flask import Flask, Response, jsonify, redirect, request, url_for # noqa: E402
|
|
20
20
|
|
|
21
21
|
from oracle.config import ORACLE_DIR, load_config, save_config # noqa: E402
|
|
22
|
+
from oracle.listeners import OracleListeners, listener_hosts # noqa: E402
|
|
22
23
|
from oracle.mcp_oracle import mcp_url, start_mcp_thread # noqa: E402
|
|
23
|
-
from oracle.services import api_auth, local_session # noqa: E402
|
|
24
|
+
from oracle.services import api_auth, client_tokens, local_session # noqa: E402
|
|
24
25
|
from oracle.services.activity_reporter import ActivityReporter # noqa: E402
|
|
25
26
|
from oracle.services.api_auth import extract_bearer # noqa: E402
|
|
26
27
|
from oracle.services.api_auth import verify as verify_api_key # noqa: E402
|
|
@@ -233,6 +234,14 @@ def _local_write_guard():
|
|
|
233
234
|
path = request.path or ""
|
|
234
235
|
if not path.startswith("/api/") or path.startswith("/api/discovery"):
|
|
235
236
|
return None
|
|
237
|
+
# The mobile gateway authenticates itself: both of its blueprints gate
|
|
238
|
+
# EVERY method (GETs included) on a Bearer — the Discovery token or a
|
|
239
|
+
# per-client token (v2.125.0) — and the one route that takes no Bearer,
|
|
240
|
+
# ``POST /api/mobile/clients``, runs its own local-address + bootstrap-key
|
|
241
|
+
# check. Applying this cookie-or-Discovery gate on top would refuse every
|
|
242
|
+
# per-client token before the blueprint ever saw it.
|
|
243
|
+
if path.startswith("/api/mobile/"):
|
|
244
|
+
return None
|
|
236
245
|
if request.method in ("GET", "HEAD", "OPTIONS"):
|
|
237
246
|
return None
|
|
238
247
|
# The bootstrap mint is how a browser ACQUIRES the cookie, so it cannot
|
|
@@ -1086,6 +1095,63 @@ def api_apikey_clear():
|
|
|
1086
1095
|
return jsonify({"error": str(e)}), 500
|
|
1087
1096
|
|
|
1088
1097
|
|
|
1098
|
+
# ── Paired clients (dashboard side of oracle/services/client_tokens.py) ──
|
|
1099
|
+
# Cookie-authenticated like the apikey routes: the mutations sit behind
|
|
1100
|
+
# _local_write_guard, the list is a GET and returns no secret. The QR the
|
|
1101
|
+
# Settings tab renders now carries a per-device token minted here, so
|
|
1102
|
+
# rotating the Discovery token no longer un-pairs every phone.
|
|
1103
|
+
|
|
1104
|
+
@app.route("/api/pair/clients", methods=["GET"])
|
|
1105
|
+
def api_pair_clients():
|
|
1106
|
+
"""Every paired client (never a hash or token). Signed-in dashboard only
|
|
1107
|
+
— the list names devices, which is more than an anonymous local process
|
|
1108
|
+
should learn."""
|
|
1109
|
+
if not (verify_api_key(extract_bearer(request.headers.get("Authorization")))
|
|
1110
|
+
or local_session.verify(request.cookies.get(local_session.COOKIE_NAME))):
|
|
1111
|
+
return jsonify({"error": "unauthorized"}), 401
|
|
1112
|
+
try:
|
|
1113
|
+
return jsonify({"clients": client_tokens.list_clients()})
|
|
1114
|
+
except Exception as e:
|
|
1115
|
+
return jsonify({"error": str(e)}), 500
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
@app.route("/api/pair/mobile", methods=["POST"])
|
|
1119
|
+
def api_pair_mobile():
|
|
1120
|
+
"""Mint a per-device ``mobile`` client token for the pairing QR.
|
|
1121
|
+
|
|
1122
|
+
Body ``{label?}``. The token is revealed exactly once, here, for the QR;
|
|
1123
|
+
the store keeps only its hash. ``url`` is what the phone should dial —
|
|
1124
|
+
the dashboard overrides it with the Tailscale/LAN address it entered.
|
|
1125
|
+
"""
|
|
1126
|
+
data = request.get_json(silent=True) or {}
|
|
1127
|
+
try:
|
|
1128
|
+
row, token = client_tokens.mint("mobile", str(data.get("label") or "phone"))
|
|
1129
|
+
except ValueError as e:
|
|
1130
|
+
return jsonify({"error": str(e)}), 400
|
|
1131
|
+
except Exception as e:
|
|
1132
|
+
return jsonify({"error": str(e)}), 500
|
|
1133
|
+
return jsonify({
|
|
1134
|
+
"client_id": row["client_id"],
|
|
1135
|
+
"kind": row["kind"],
|
|
1136
|
+
"label": row["label"],
|
|
1137
|
+
"created": row["created"],
|
|
1138
|
+
"token": token,
|
|
1139
|
+
"url": request.host_url.rstrip("/"),
|
|
1140
|
+
}), 201
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
@app.route("/api/pair/clients/<client_id>", methods=["DELETE"])
|
|
1144
|
+
def api_pair_revoke(client_id):
|
|
1145
|
+
"""Un-pair one device. Its token fails on the next request."""
|
|
1146
|
+
try:
|
|
1147
|
+
row = client_tokens.revoke(str(client_id))
|
|
1148
|
+
except Exception as e:
|
|
1149
|
+
return jsonify({"error": str(e)}), 500
|
|
1150
|
+
if row is None:
|
|
1151
|
+
return jsonify({"error": "unknown client"}), 404
|
|
1152
|
+
return jsonify({"client": row, "revoked": True})
|
|
1153
|
+
|
|
1154
|
+
|
|
1089
1155
|
# ── Error handlers ────────────────────────────────────────
|
|
1090
1156
|
@app.errorhandler(404)
|
|
1091
1157
|
def not_found(e):
|
|
@@ -1215,7 +1281,27 @@ def run_oracle(port: int = None, open_browser: bool = None):
|
|
|
1215
1281
|
signed_in = f"{url}/?{local_session.BOOTSTRAP_PARAM}={local_session.mint_code()}"
|
|
1216
1282
|
threading.Timer(0.8, lambda: webbrowser.open(signed_in)).start()
|
|
1217
1283
|
|
|
1218
|
-
|
|
1284
|
+
_serve(bind_host, actual_port,
|
|
1285
|
+
loopback_listener=bool(cfg.get("loopback_listener", True)))
|
|
1286
|
+
|
|
1287
|
+
|
|
1288
|
+
def _serve(bind_host: str, port: int, *, loopback_listener: bool = True) -> None:
|
|
1289
|
+
"""Block serving ``app`` on ``bind_host`` — and on loopback too when the
|
|
1290
|
+
bind is a specific non-loopback address (``oracle/listeners.py``).
|
|
1291
|
+
|
|
1292
|
+
Replaces ``app.run``: the same threaded werkzeug server, minus the
|
|
1293
|
+
dev-server banner, plus the second socket. Every listener is stopped on
|
|
1294
|
+
the way out (Ctrl-C, service stop, an error) and at interpreter exit.
|
|
1295
|
+
"""
|
|
1296
|
+
hosts = listener_hosts(bind_host, loopback_listener)
|
|
1297
|
+
listeners = OracleListeners(app, hosts, port).start()
|
|
1298
|
+
atexit.register(listeners.shutdown)
|
|
1299
|
+
if len(listeners.servers) > 1:
|
|
1300
|
+
print(f"Loopback listener → {listeners.urls[-1]} (same app, same guards)")
|
|
1301
|
+
elif len(hosts) > 1:
|
|
1302
|
+
print(f"Warning: loopback listener on 127.0.0.1:{port} could not start "
|
|
1303
|
+
f"(port in use?) — same-machine clients must dial {bind_host}.")
|
|
1304
|
+
listeners.serve_forever()
|
|
1219
1305
|
|
|
1220
1306
|
|
|
1221
1307
|
if __name__ == "__main__":
|
|
@@ -1198,19 +1198,25 @@
|
|
|
1198
1198
|
<!-- ── Mobile pairing (companion app) ── -->
|
|
1199
1199
|
<h2 style="font-size:16px;font-weight:600;margin:28px 0 16px">Mobile app <span style="font-size:11px;color:var(--text2);font-weight:400">— pair the C3 companion app</span></h2>
|
|
1200
1200
|
<div class="card">
|
|
1201
|
-
<p style="font-size:12px;color:var(--text2);margin:0 0 14px;line-height:1.5">Scan a QR code from the C3 mobile companion to pair it with this Oracle. The code carries the server URL and
|
|
1201
|
+
<p style="font-size:12px;color:var(--text2);margin:0 0 14px;line-height:1.5">Scan a QR code from the C3 mobile companion to pair it with this Oracle. The code carries the server URL and a <b>per-device token</b> minted for that phone — treat it like a password. Each device gets its own token, so one can be revoked below without touching the others, and rotating the Discovery API token above no longer un-pairs phones.</p>
|
|
1202
1202
|
<div class="field">
|
|
1203
1203
|
<label>Server URL (as reachable from the phone)</label>
|
|
1204
1204
|
<input id="mpUrl" type="text" placeholder="e.g. http://100.x.y.z:3331 (Tailscale IP)">
|
|
1205
1205
|
<div style="font-size:11px;color:var(--text2);margin-top:6px">Defaults to this page's origin — replace it with the Tailscale/LAN address the phone can reach. Remote access needs <code>bind_host</code> and <code>allowed_hosts</code> set in <code>~/.c3/oracle/config.json</code>.</div>
|
|
1206
1206
|
</div>
|
|
1207
|
-
<div style="display:flex;gap:8px;margin-top:4px">
|
|
1207
|
+
<div style="display:flex;gap:8px;margin-top:4px;flex-wrap:wrap">
|
|
1208
1208
|
<button class="btn btn-primary" onclick="showMobilePairQr()">Show pairing code</button>
|
|
1209
|
+
<button class="btn btn-ghost" onclick="showDiscoveryPairQr()" title="Legacy: the shared Discovery token, for an app build that predates per-device tokens">Discovery-token code (legacy)</button>
|
|
1209
1210
|
<button class="btn btn-ghost" id="mpHideBtn" onclick="hideMobilePairQr()" style="display:none">Hide</button>
|
|
1210
1211
|
</div>
|
|
1211
1212
|
<div id="mpQrWrap" style="display:none;margin-top:14px">
|
|
1212
1213
|
<div id="mpQr" style="display:inline-block;background:#fff;padding:14px;border-radius:8px;line-height:0"></div>
|
|
1213
|
-
<div style="font-size:11px;color:var(--text2);margin-top:8px"
|
|
1214
|
+
<div id="mpQrNote" style="font-size:11px;color:var(--text2);margin-top:8px"></div>
|
|
1215
|
+
<div style="font-size:11px;color:var(--text2);margin-top:4px">Contains a raw Bearer token — don't screenshot or share it.</div>
|
|
1216
|
+
</div>
|
|
1217
|
+
<div style="margin-top:16px">
|
|
1218
|
+
<label style="font-size:12px;font-weight:600">Paired devices</label>
|
|
1219
|
+
<div id="mpClients" style="margin-top:6px"></div>
|
|
1214
1220
|
</div>
|
|
1215
1221
|
</div>
|
|
1216
1222
|
</div>
|
|
@@ -33,9 +33,8 @@ import threading
|
|
|
33
33
|
import time
|
|
34
34
|
import uuid
|
|
35
35
|
|
|
36
|
-
from flask import Blueprint, jsonify, request
|
|
36
|
+
from flask import Blueprint, g, jsonify, request
|
|
37
37
|
|
|
38
|
-
from oracle.services import api_auth
|
|
39
38
|
from oracle.services.api_auth import extract_bearer
|
|
40
39
|
|
|
41
40
|
#: Capability advertised on ``/api/mobile/info``. Wired into
|
|
@@ -115,8 +114,14 @@ def _chat_auth_guard():
|
|
|
115
114
|
return None # CORS preflight
|
|
116
115
|
if not _cfg().get("mobile_api_enabled", True):
|
|
117
116
|
return jsonify({"error": "mobile API disabled"}), 404
|
|
118
|
-
|
|
117
|
+
# Same credential set as the gateway proper (Discovery token OR a live
|
|
118
|
+
# per-client token, v2.125.0), resolved by the one shared function so a
|
|
119
|
+
# device paired with a client token can chat too.
|
|
120
|
+
from oracle.services import mobile_api
|
|
121
|
+
who = mobile_api.authenticate(extract_bearer(request.headers.get("Authorization")))
|
|
122
|
+
if who is None:
|
|
119
123
|
return jsonify({"error": "unauthorized"}), 401
|
|
124
|
+
g.c3_client = who
|
|
120
125
|
return None
|
|
121
126
|
|
|
122
127
|
|