alysis-code 0.13.0__py3-none-any.whl
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.
- alysis_code/__init__.py +3 -0
- alysis_code/__main__.py +4 -0
- alysis_code/_build_info.py +14 -0
- alysis_code/account_login.py +468 -0
- alysis_code/agent/README.md +35 -0
- alysis_code/agent/__init__.py +11 -0
- alysis_code/agent/acceptance_contract.py +2217 -0
- alysis_code/agent/blast_radius.py +1403 -0
- alysis_code/agent/cache_keepalive.py +227 -0
- alysis_code/agent/completion_certificate.py +366 -0
- alysis_code/agent/completion_gate.py +306 -0
- alysis_code/agent/empty_response_stall.py +403 -0
- alysis_code/agent/errors.py +28 -0
- alysis_code/agent/llm_calls.py +475 -0
- alysis_code/agent/mutation_classification.py +227 -0
- alysis_code/agent/prompt_context.py +2508 -0
- alysis_code/agent/read_ledger.py +253 -0
- alysis_code/agent/regression_baseline.py +642 -0
- alysis_code/agent/reproduction_first.py +610 -0
- alysis_code/agent/sensitive_output.py +629 -0
- alysis_code/agent/session.py +3218 -0
- alysis_code/agent/steering.py +191 -0
- alysis_code/agent/subagent_execution.py +5177 -0
- alysis_code/agent/subagent_workspace.py +666 -0
- alysis_code/agent/tools_assembly.py +4728 -0
- alysis_code/agent/turn/__init__.py +101 -0
- alysis_code/agent/turn/core.py +8483 -0
- alysis_code/agent/turn/events.py +113 -0
- alysis_code/agent/turn/exploration.py +590 -0
- alysis_code/agent/turn/interventions.py +65 -0
- alysis_code/agent/turn/read_cache.py +420 -0
- alysis_code/agent/turn/snapshot.py +179 -0
- alysis_code/agent/turn_contract.py +661 -0
- alysis_code/agent/turn_path.py +129 -0
- alysis_code/agent/verification.py +2885 -0
- alysis_code/agent/verification_commands.py +512 -0
- alysis_code/agent/verification_evidence.py +738 -0
- alysis_code/agent_loop.py +693 -0
- alysis_code/agent_runtimes/__init__.py +51 -0
- alysis_code/agent_runtimes/base.py +114 -0
- alysis_code/agent_runtimes/builtins.py +129 -0
- alysis_code/agent_runtimes/codex_cli.py +664 -0
- alysis_code/agent_runtimes/host.py +263 -0
- alysis_code/agent_runtimes/registry.py +64 -0
- alysis_code/agent_runtimes/service.py +150 -0
- alysis_code/agentbox_client.py +416 -0
- alysis_code/agentbox_integration.py +310 -0
- alysis_code/alysis_cloud.py +152 -0
- alysis_code/approval_scope.py +276 -0
- alysis_code/assets/README.md +33 -0
- alysis_code/assets/__init__.py +126 -0
- alysis_code/assets/asset_read_core.py +281 -0
- alysis_code/assets/budget_allocator.py +456 -0
- alysis_code/assets/comprehender.py +759 -0
- alysis_code/assets/index.py +654 -0
- alysis_code/assets/ingestion.py +275 -0
- alysis_code/assets/legacy_migration.py +413 -0
- alysis_code/assets/models.py +263 -0
- alysis_code/assets/ocr.py +239 -0
- alysis_code/assets/owl/ascii/f-000.txt +13 -0
- alysis_code/assets/owl/ascii/f-001.txt +13 -0
- alysis_code/assets/owl/ascii/f-002.txt +13 -0
- alysis_code/assets/owl/ascii/f-003.txt +13 -0
- alysis_code/assets/owl/ascii/f-004.txt +13 -0
- alysis_code/assets/owl/ascii/f-005.txt +13 -0
- alysis_code/assets/owl/ascii/f-006.txt +13 -0
- alysis_code/assets/owl/ascii/f-007.txt +13 -0
- alysis_code/assets/owl/ascii/f-008.txt +13 -0
- alysis_code/assets/owl/ascii/f-009.txt +13 -0
- alysis_code/assets/owl/ascii/f-010.txt +13 -0
- alysis_code/assets/owl/ascii/f-011.txt +13 -0
- alysis_code/assets/owl/ascii/f-012.txt +13 -0
- alysis_code/assets/owl/ascii/f-013.txt +13 -0
- alysis_code/assets/owl/ascii/f-014.txt +13 -0
- alysis_code/assets/owl/ascii/f-015.txt +13 -0
- alysis_code/assets/owl/ascii/f-016.txt +13 -0
- alysis_code/assets/owl/ascii/f-017.txt +13 -0
- alysis_code/assets/owl/ascii/f-018.txt +13 -0
- alysis_code/assets/owl/ascii/f-019.txt +13 -0
- alysis_code/assets/owl/ascii/f-020.txt +13 -0
- alysis_code/assets/owl/index.html +98 -0
- alysis_code/assets/owl/show-owl.sh +761 -0
- alysis_code/assets/paths.py +49 -0
- alysis_code/assets/plan_binding.py +326 -0
- alysis_code/assets/planner_context.py +466 -0
- alysis_code/assets/planner_tools.py +184 -0
- alysis_code/assets/prompts.py +101 -0
- alysis_code/assets/replanner_context.py +239 -0
- alysis_code/assets/surface.py +521 -0
- alysis_code/assets/untrusted_content.py +48 -0
- alysis_code/assets/usage_logger.py +94 -0
- alysis_code/assets/worker_mirror.py +428 -0
- alysis_code/assets/worker_section.py +303 -0
- alysis_code/assets/worker_tools.py +468 -0
- alysis_code/atomic_io.py +83 -0
- alysis_code/auth_diagnostics.py +272 -0
- alysis_code/background_runner.py +366 -0
- alysis_code/branding.py +270 -0
- alysis_code/budget_policy.py +390 -0
- alysis_code/build_identity.py +465 -0
- alysis_code/builtin_hooks/__init__.py +7 -0
- alysis_code/builtin_hooks/notify_done_windows.py +65 -0
- alysis_code/bwrap_etc.py +76 -0
- alysis_code/cancellation.py +41 -0
- alysis_code/capabilities.py +137 -0
- alysis_code/chatgpt_codex_static_provider.py +133 -0
- alysis_code/cli.py +51 -0
- alysis_code/cli_impl/__init__.py +1 -0
- alysis_code/cli_impl/assets_cli.py +537 -0
- alysis_code/cli_impl/assets_modal.py +412 -0
- alysis_code/cli_impl/chat/__init__.py +156 -0
- alysis_code/cli_impl/chat/commands.py +2616 -0
- alysis_code/cli_impl/chat/loop.py +4508 -0
- alysis_code/cli_impl/chat/mid_turn_policy.py +125 -0
- alysis_code/cli_impl/chat/rendering.py +444 -0
- alysis_code/cli_impl/chat/state.py +124 -0
- alysis_code/cli_impl/chat_resume.py +830 -0
- alysis_code/cli_impl/chat_slash_completer.py +258 -0
- alysis_code/cli_impl/commands/__init__.py +11 -0
- alysis_code/cli_impl/commands/_shared.py +89 -0
- alysis_code/cli_impl/commands/auth.py +623 -0
- alysis_code/cli_impl/commands/chat_resume_helpers.py +1531 -0
- alysis_code/cli_impl/commands/chat_state.py +158 -0
- alysis_code/cli_impl/commands/chat_status.py +1248 -0
- alysis_code/cli_impl/commands/chat_terminal.py +942 -0
- alysis_code/cli_impl/commands/chat_tui_panels.py +1018 -0
- alysis_code/cli_impl/commands/cli_common.py +1223 -0
- alysis_code/cli_impl/commands/cli_surface.py +77 -0
- alysis_code/cli_impl/commands/config.py +131 -0
- alysis_code/cli_impl/commands/conventions.py +85 -0
- alysis_code/cli_impl/commands/execution_helpers.py +350 -0
- alysis_code/cli_impl/commands/extensions.py +401 -0
- alysis_code/cli_impl/commands/forge.py +1282 -0
- alysis_code/cli_impl/commands/forge_asset_view.py +121 -0
- alysis_code/cli_impl/commands/forge_helpers.py +1215 -0
- alysis_code/cli_impl/commands/hooks.py +737 -0
- alysis_code/cli_impl/commands/ide_bridge.py +31 -0
- alysis_code/cli_impl/commands/mcp.py +700 -0
- alysis_code/cli_impl/commands/profile.py +453 -0
- alysis_code/cli_impl/commands/prompt_helpers.py +307 -0
- alysis_code/cli_impl/commands/report.py +88 -0
- alysis_code/cli_impl/commands/root.py +1118 -0
- alysis_code/cli_impl/commands/sandbox.py +184 -0
- alysis_code/cli_impl/commands/server.py +54 -0
- alysis_code/cli_impl/commands/sessions.py +252 -0
- alysis_code/cli_impl/commands/skills.py +404 -0
- alysis_code/cli_impl/commands/startup.py +946 -0
- alysis_code/cli_impl/commands/tools.py +335 -0
- alysis_code/cli_impl/commands/update.py +364 -0
- alysis_code/cli_impl/commands/welcome.py +972 -0
- alysis_code/cli_impl/config_menu.py +3882 -0
- alysis_code/cli_impl/forge.py +4509 -0
- alysis_code/cli_impl/forge_recovery.py +485 -0
- alysis_code/cli_impl/setup_wizard.py +2409 -0
- alysis_code/cli_impl/tui/__init__.py +58 -0
- alysis_code/cli_impl/tui/app.py +4551 -0
- alysis_code/cli_impl/tui/config.py +32 -0
- alysis_code/cli_impl/tui/config_flow.py +2754 -0
- alysis_code/cli_impl/tui/config_overlay.py +566 -0
- alysis_code/cli_impl/tui/content.py +78 -0
- alysis_code/cli_impl/tui/footer.py +218 -0
- alysis_code/cli_impl/tui/forge_status.py +136 -0
- alysis_code/cli_impl/tui/markdown.py +244 -0
- alysis_code/cli_impl/tui/owl.py +109 -0
- alysis_code/cli_impl/tui/plan_meta.py +477 -0
- alysis_code/cli_impl/tui/setup_app.py +519 -0
- alysis_code/cli_impl/tui/setup_flow.py +1622 -0
- alysis_code/cli_impl/tui/state.py +101 -0
- alysis_code/cli_impl/tui/subagent_identity.py +66 -0
- alysis_code/cli_impl/tui/subagent_panel.py +186 -0
- alysis_code/cli_impl/tui/surface.py +796 -0
- alysis_code/cli_impl/tui/transcript.py +514 -0
- alysis_code/cli_impl/tui/update_prompt.py +79 -0
- alysis_code/cli_impl/tui/workspace_guard.py +384 -0
- alysis_code/clipboard.py +172 -0
- alysis_code/code_review.py +1211 -0
- alysis_code/compaction/__init__.py +28 -0
- alysis_code/compaction/conversation_compactor.py +2932 -0
- alysis_code/compaction/importance.py +177 -0
- alysis_code/compaction/settings.py +297 -0
- alysis_code/compaction/tool_output_offload.py +447 -0
- alysis_code/config.py +3509 -0
- alysis_code/conflict_auto_resolver.py +895 -0
- alysis_code/context/__init__.py +1 -0
- alysis_code/context/tool_schema_budgeter.py +220 -0
- alysis_code/crash_diagnostics.py +282 -0
- alysis_code/custom_tools/README.md +34 -0
- alysis_code/custom_tools/__init__.py +43 -0
- alysis_code/custom_tools/discovery.py +903 -0
- alysis_code/custom_tools/runtime.py +1516 -0
- alysis_code/custom_tools/session.py +227 -0
- alysis_code/custom_tools/trust.py +232 -0
- alysis_code/diff_paths.py +113 -0
- alysis_code/direction_change.py +293 -0
- alysis_code/dispatch_timing.py +306 -0
- alysis_code/durable_service_manager.py +1236 -0
- alysis_code/edit_discipline.py +659 -0
- alysis_code/error_text.py +73 -0
- alysis_code/execution_budget.py +411 -0
- alysis_code/execution_context.py +915 -0
- alysis_code/execution_deadline.py +1065 -0
- alysis_code/execution_shared.py +1904 -0
- alysis_code/extensions/README.md +30 -0
- alysis_code/extensions/__init__.py +93 -0
- alysis_code/extensions/activation.py +138 -0
- alysis_code/extensions/install.py +1436 -0
- alysis_code/extensions/manifest.py +487 -0
- alysis_code/extensions/models.py +74 -0
- alysis_code/extensions/paths.py +56 -0
- alysis_code/extensions/registry.json +4 -0
- alysis_code/extensions/registry.py +52 -0
- alysis_code/extensions/state.py +83 -0
- alysis_code/extensions/workspace_trust.py +101 -0
- alysis_code/failed_task_evidence.py +369 -0
- alysis_code/failure_category.py +315 -0
- alysis_code/feedback_report.py +1647 -0
- alysis_code/file_classification.py +485 -0
- alysis_code/forge.py +2064 -0
- alysis_code/forge_completion.py +362 -0
- alysis_code/forge_events.py +475 -0
- alysis_code/frontmatter_utils.py +95 -0
- alysis_code/git_evidence.py +1181 -0
- alysis_code/git_ops.py +560 -0
- alysis_code/git_safe.py +62 -0
- alysis_code/git_worktrees.py +190 -0
- alysis_code/hooks/README.md +33 -0
- alysis_code/hooks/__init__.py +67 -0
- alysis_code/hooks/audit.py +171 -0
- alysis_code/hooks/config.py +225 -0
- alysis_code/hooks/dispatcher.py +1110 -0
- alysis_code/hooks/models.py +447 -0
- alysis_code/hooks/trust.py +202 -0
- alysis_code/host_actions.py +543 -0
- alysis_code/host_browser.py +103 -0
- alysis_code/ide/__init__.py +5 -0
- alysis_code/ide/activity_events.py +399 -0
- alysis_code/ide/approvals.py +337 -0
- alysis_code/ide/artifacts.py +153 -0
- alysis_code/ide/browser_egress_proxy.py +1076 -0
- alysis_code/ide/cdp_websocket_transport.py +1192 -0
- alysis_code/ide/change_ledger.py +1721 -0
- alysis_code/ide/context_blocks.py +979 -0
- alysis_code/ide/event_stream.py +531 -0
- alysis_code/ide/forge_protocol.py +3112 -0
- alysis_code/ide/forge_request_ledger.py +737 -0
- alysis_code/ide/health.py +965 -0
- alysis_code/ide/managed_browser.py +2251 -0
- alysis_code/ide/management_protocol.py +3414 -0
- alysis_code/ide/mcp_oauth_coordinator.py +744 -0
- alysis_code/ide/mcp_oauth_lifecycle.py +1504 -0
- alysis_code/ide/prompt_queue.py +1070 -0
- alysis_code/ide/protocol.py +191 -0
- alysis_code/ide/resumable_swarm.py +1543 -0
- alysis_code/ide/session_search.py +295 -0
- alysis_code/ide/stdio_bridge.py +9935 -0
- alysis_code/ide/structured_state.py +1579 -0
- alysis_code/ide/swarm_protocol.py +816 -0
- alysis_code/integration_gate.py +506 -0
- alysis_code/interactive_input_guard.py +39 -0
- alysis_code/interactive_plan_mode.py +26 -0
- alysis_code/internal_artifacts.py +179 -0
- alysis_code/knowledge_base.py +1409 -0
- alysis_code/knowledge_capture.py +1190 -0
- alysis_code/knowledge_librarian.py +605 -0
- alysis_code/language_policy.py +34 -0
- alysis_code/litellm_static_provider.py +535 -0
- alysis_code/llm/__init__.py +1 -0
- alysis_code/llm/anthropic_messages.py +2288 -0
- alysis_code/llm/base.py +71 -0
- alysis_code/llm/cache_capabilities.py +985 -0
- alysis_code/llm/cache_control_blocks.py +244 -0
- alysis_code/llm/cache_policy.py +388 -0
- alysis_code/llm/factory.py +373 -0
- alysis_code/llm/gemini_generate_content.py +2652 -0
- alysis_code/llm/gemini_interactions.py +739 -0
- alysis_code/llm/metadata.py +450 -0
- alysis_code/llm/openai_compat.py +2947 -0
- alysis_code/llm/openai_responses.py +2604 -0
- alysis_code/llm/protocols.py +609 -0
- alysis_code/llm/provider_limits.py +525 -0
- alysis_code/llm/request_plan.py +389 -0
- alysis_code/llm/request_shape.py +238 -0
- alysis_code/llm/streaming.py +108 -0
- alysis_code/llm/temperature_compat.py +78 -0
- alysis_code/llm/types.py +195 -0
- alysis_code/llm/usage_normalization.py +222 -0
- alysis_code/llm_error_display.py +315 -0
- alysis_code/logging_redaction.py +326 -0
- alysis_code/managed_host_deadline.py +196 -0
- alysis_code/mcp/README.md +33 -0
- alysis_code/mcp/__init__.py +24 -0
- alysis_code/mcp/client.py +1137 -0
- alysis_code/mcp/config.py +597 -0
- alysis_code/mcp/errors.py +113 -0
- alysis_code/mcp/forge_scope.py +154 -0
- alysis_code/mcp/jsonrpc.py +214 -0
- alysis_code/mcp/manager.py +2308 -0
- alysis_code/mcp/models.py +666 -0
- alysis_code/mcp/oauth.py +972 -0
- alysis_code/mcp/oauth_runtime.py +310 -0
- alysis_code/mcp/oauth_store.py +276 -0
- alysis_code/mcp/prompts.py +329 -0
- alysis_code/mcp/resources.py +295 -0
- alysis_code/mcp/roots.py +106 -0
- alysis_code/mcp/server_requests.py +75 -0
- alysis_code/mcp/token_store.py +859 -0
- alysis_code/mcp/transport_http.py +1338 -0
- alysis_code/mcp/transport_stdio.py +1267 -0
- alysis_code/mcp/untrusted_content.py +119 -0
- alysis_code/merge_conflict_reviewer.py +729 -0
- alysis_code/model_catalog/__init__.py +1 -0
- alysis_code/model_catalog/chatgpt_codex_subscription_snapshot.json +186 -0
- alysis_code/model_catalog/litellm_model_prices_snapshot.json +44715 -0
- alysis_code/model_catalog/litellm_model_prices_snapshot.meta.json +17 -0
- alysis_code/model_metadata_policy.py +223 -0
- alysis_code/model_metadata_utils.py +103 -0
- alysis_code/model_registry.py +1420 -0
- alysis_code/model_router.py +147 -0
- alysis_code/permission_policy.py +1016 -0
- alysis_code/personas.py +451 -0
- alysis_code/pipeline_facts.py +233 -0
- alysis_code/plan_assistant.py +4763 -0
- alysis_code/plan_mode.py +393 -0
- alysis_code/plan_reconciliation.py +1228 -0
- alysis_code/plan_repair.py +652 -0
- alysis_code/plan_validation.py +1099 -0
- alysis_code/planning_constraints.py +904 -0
- alysis_code/policy.py +95 -0
- alysis_code/preview_server.py +457 -0
- alysis_code/process_reaping.py +566 -0
- alysis_code/profile_presets.py +1834 -0
- alysis_code/profiles.py +666 -0
- alysis_code/provider_auth/__init__.py +29 -0
- alysis_code/provider_auth/base.py +99 -0
- alysis_code/provider_auth/openai_codex.py +951 -0
- alysis_code/provider_auth/registry.py +76 -0
- alysis_code/provider_auth/store.py +125 -0
- alysis_code/provider_diagnostics.py +1209 -0
- alysis_code/provider_model_catalog.py +685 -0
- alysis_code/provider_telemetry.py +1699 -0
- alysis_code/provider_url.py +75 -0
- alysis_code/reasoning_contracts.py +911 -0
- alysis_code/remote_sync.py +350 -0
- alysis_code/replanning.py +1195 -0
- alysis_code/repo_scan.py +1152 -0
- alysis_code/request_estimation.py +296 -0
- alysis_code/review_gate.py +617 -0
- alysis_code/run_lock.py +1141 -0
- alysis_code/run_outcome.py +58 -0
- alysis_code/run_provenance.py +774 -0
- alysis_code/run_state.py +445 -0
- alysis_code/runtime_artifacts.py +116 -0
- alysis_code/runtime_context_features.py +78 -0
- alysis_code/runtime_kind.py +52 -0
- alysis_code/safety/__init__.py +11 -0
- alysis_code/safety/mcp_sanitize.py +29 -0
- alysis_code/safety/safe_http.py +297 -0
- alysis_code/safety/subagent_report.py +184 -0
- alysis_code/sandbox_doctor.py +682 -0
- alysis_code/sandbox_runner.py +1025 -0
- alysis_code/sandbox_settings.py +423 -0
- alysis_code/serialized_paths.py +355 -0
- alysis_code/server/__init__.py +3 -0
- alysis_code/server/app.py +367 -0
- alysis_code/server/auth.py +34 -0
- alysis_code/server/job_config.py +30 -0
- alysis_code/server/settings.py +215 -0
- alysis_code/server/store.py +193 -0
- alysis_code/server/worker_runner.py +657 -0
- alysis_code/service_persistence.py +355 -0
- alysis_code/session_artifacts.py +108 -0
- alysis_code/session_metrics.py +331 -0
- alysis_code/session_store.py +624 -0
- alysis_code/skills/README.md +34 -0
- alysis_code/skills/__init__.py +104 -0
- alysis_code/skills/conventions.py +84 -0
- alysis_code/skills/discovery.py +176 -0
- alysis_code/skills/eval_models.py +232 -0
- alysis_code/skills/eval_runner.py +372 -0
- alysis_code/skills/evals.py +1344 -0
- alysis_code/skills/install.py +293 -0
- alysis_code/skills/loader.py +118 -0
- alysis_code/skills/matching.py +103 -0
- alysis_code/skills/models.py +71 -0
- alysis_code/skills/paths.py +56 -0
- alysis_code/skills/prompting.py +500 -0
- alysis_code/skills/scaffold.py +142 -0
- alysis_code/skills/state.py +441 -0
- alysis_code/skills/transactions.py +125 -0
- alysis_code/skills/validation.py +304 -0
- alysis_code/step_budget.py +238 -0
- alysis_code/subagent_labels.py +49 -0
- alysis_code/subagents.py +1072 -0
- alysis_code/surface/__init__.py +80 -0
- alysis_code/surface/base.py +305 -0
- alysis_code/surface/console.py +387 -0
- alysis_code/surface/events.py +372 -0
- alysis_code/surface/hidden_surface.py +529 -0
- alysis_code/surface/noop_surface.py +219 -0
- alysis_code/surface/rich_surface.py +1555 -0
- alysis_code/surface/styles.py +67 -0
- alysis_code/surface/theme.py +455 -0
- alysis_code/surface/types.py +100 -0
- alysis_code/swarm_backend.py +926 -0
- alysis_code/swarm_orchestrator.py +4020 -0
- alysis_code/swarm_scheduler.py +441 -0
- alysis_code/swarm_trace.py +429 -0
- alysis_code/swarm_worker.py +2119 -0
- alysis_code/swarm_write_guard.py +348 -0
- alysis_code/task_dependencies.py +170 -0
- alysis_code/task_readiness.py +992 -0
- alysis_code/task_scope.py +2148 -0
- alysis_code/terminal_manager.py +762 -0
- alysis_code/terminal_ownership.py +460 -0
- alysis_code/text_normalization.py +30 -0
- alysis_code/token_budget.py +97 -0
- alysis_code/tools/README.md +34 -0
- alysis_code/tools/__init__.py +1 -0
- alysis_code/tools/artifacts.py +127 -0
- alysis_code/tools/availability.py +188 -0
- alysis_code/tools/fs.py +1456 -0
- alysis_code/tools/git.py +461 -0
- alysis_code/tools/history.py +229 -0
- alysis_code/tools/http_timeout.py +78 -0
- alysis_code/tools/image_generation.py +552 -0
- alysis_code/tools/registry.py +2936 -0
- alysis_code/tools/repo_map.py +476 -0
- alysis_code/tools/search.py +563 -0
- alysis_code/tools/shell.py +135 -0
- alysis_code/tools/symbols.py +1350 -0
- alysis_code/tools/test_discovery.py +643 -0
- alysis_code/tools/web.py +482 -0
- alysis_code/tools/web_search.py +2012 -0
- alysis_code/tools/web_search_dashscope.py +557 -0
- alysis_code/tools/web_search_ddgs.py +221 -0
- alysis_code/tools/web_search_provider_adapters.py +1429 -0
- alysis_code/tools/web_search_tavily.py +194 -0
- alysis_code/updates.py +933 -0
- alysis_code/usage_tracker.py +1990 -0
- alysis_code/verification_command_analysis.py +1004 -0
- alysis_code/verification_contract.py +574 -0
- alysis_code/verification_failure_summary.py +273 -0
- alysis_code/verification_repair.py +385 -0
- alysis_code/verify_gate.py +3129 -0
- alysis_code/web_research.py +1872 -0
- alysis_code/web_search_adapters.py +66 -0
- alysis_code/web_search_policy.py +27 -0
- alysis_code/workspace_binding.py +389 -0
- alysis_code/workspace_binding_ui.py +408 -0
- alysis_code/workspace_context.py +273 -0
- alysis_code/workspace_isolation.py +138 -0
- alysis_code/workspace_provisioning.py +455 -0
- alysis_code-0.13.0.dist-info/METADATA +507 -0
- alysis_code-0.13.0.dist-info/RECORD +458 -0
- alysis_code-0.13.0.dist-info/WHEEL +4 -0
- alysis_code-0.13.0.dist-info/entry_points.txt +3 -0
- alysis_code-0.13.0.dist-info/licenses/LICENSE +176 -0
- alysis_code-0.13.0.dist-info/licenses/NOTICE +4 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import tomllib
|
|
6
|
+
from contextlib import nullcontext
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Literal
|
|
10
|
+
|
|
11
|
+
from .agentbox_client import AgentBoxClient
|
|
12
|
+
from .tools.registry import iter_builtin_tool_metadata
|
|
13
|
+
from .usage_tracker import UsageRecord
|
|
14
|
+
|
|
15
|
+
AgentBoxToolCategory = Literal["edit", "read", "exec", "net", "other"]
|
|
16
|
+
|
|
17
|
+
_TRUE_VALUES = {"1", "true", "yes", "on"}
|
|
18
|
+
_PATH_RE = re.compile(r"(?:~|\.{1,2}|/)?(?:[\w.-]+/)+[\w.-]+")
|
|
19
|
+
_INLINE_CODE_RE = re.compile(r"`[^`]*`")
|
|
20
|
+
_FENCED_CODE_RE = re.compile(r"```[\s\S]*?```")
|
|
21
|
+
_WORD_RE = re.compile(r"\s+")
|
|
22
|
+
_TOOL_CATEGORY_BY_NAME: dict[str, AgentBoxToolCategory] | None = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class _AgentBoxSettings:
|
|
27
|
+
plane_url: str
|
|
28
|
+
token: str
|
|
29
|
+
org_id: str | None
|
|
30
|
+
person_id: str | None
|
|
31
|
+
machine_id: str | None
|
|
32
|
+
agent_id: str | None
|
|
33
|
+
queue_dir: str | None
|
|
34
|
+
task_detail: Literal["topic", "category"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _load_agentbox_settings() -> _AgentBoxSettings | None:
|
|
38
|
+
config = _read_agentbox_config()
|
|
39
|
+
runtime = config.get("runtime", {})
|
|
40
|
+
alysis = runtime.get("alysis", {}) if isinstance(runtime, dict) else {}
|
|
41
|
+
if not isinstance(alysis, dict):
|
|
42
|
+
alysis = {}
|
|
43
|
+
|
|
44
|
+
plane_url = _env_or_config("AGENTBOX_PLANE_URL", config, "plane_url")
|
|
45
|
+
token = _env_or_config("AGENTBOX_TOKEN", config, "machine_token")
|
|
46
|
+
if plane_url is None or token is None:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
machine_id = _env_or_config("AGENTBOX_MACHINE_ID", config, "machine_id")
|
|
50
|
+
agent_id = _env_or_config("AGENTBOX_AGENT_ID", alysis, "agent_id")
|
|
51
|
+
if agent_id is None and machine_id is not None:
|
|
52
|
+
agent_id = f"{machine_id}_alysis"
|
|
53
|
+
|
|
54
|
+
return _AgentBoxSettings(
|
|
55
|
+
plane_url=plane_url,
|
|
56
|
+
token=token,
|
|
57
|
+
org_id=_env_or_config("AGENTBOX_ORG_ID", config, "org_id"),
|
|
58
|
+
person_id=_env_or_config("AGENTBOX_PERSON_ID", config, "person_id"),
|
|
59
|
+
machine_id=machine_id,
|
|
60
|
+
agent_id=agent_id,
|
|
61
|
+
queue_dir=_sdk_queue_dir(config),
|
|
62
|
+
task_detail=(
|
|
63
|
+
"category"
|
|
64
|
+
if _env_or_config("AGENTBOX_TASK_DETAIL", config, "task_detail") == "category"
|
|
65
|
+
else "topic"
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _read_agentbox_config() -> dict[str, Any]:
|
|
71
|
+
configured_path = _text(os.environ.get("AGENTBOX_CONFIG"))
|
|
72
|
+
if configured_path is not None:
|
|
73
|
+
path = Path(configured_path).expanduser()
|
|
74
|
+
else:
|
|
75
|
+
home = Path(os.environ.get("AGENTBOX_HOME", Path.home() / ".agentbox"))
|
|
76
|
+
path = home.expanduser() / "config.toml"
|
|
77
|
+
try:
|
|
78
|
+
with path.open("rb") as handle:
|
|
79
|
+
parsed = tomllib.load(handle)
|
|
80
|
+
return parsed if isinstance(parsed, dict) else {}
|
|
81
|
+
except (OSError, tomllib.TOMLDecodeError, TypeError):
|
|
82
|
+
return {}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _env_or_config(env_name: str, config: dict[str, Any], config_name: str) -> str | None:
|
|
86
|
+
return _text(os.environ.get(env_name)) or _text(config.get(config_name))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _sdk_queue_dir(config: dict[str, Any]) -> str | None:
|
|
90
|
+
explicit = _text(os.environ.get("AGENTBOX_QUEUE_DIR"))
|
|
91
|
+
if explicit is not None:
|
|
92
|
+
return str(Path(explicit).expanduser())
|
|
93
|
+
connector_queue = _text(config.get("queue_dir"))
|
|
94
|
+
if connector_queue is None:
|
|
95
|
+
return None
|
|
96
|
+
return str(Path(connector_queue).expanduser().parent / "sdk-queue")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _text(value: Any) -> str | None:
|
|
100
|
+
if not isinstance(value, str):
|
|
101
|
+
return None
|
|
102
|
+
stripped = value.strip()
|
|
103
|
+
return stripped or None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class AgentBoxTelemetry:
|
|
107
|
+
def __init__(self, *, client: Any, root: Path) -> None:
|
|
108
|
+
self._client = client
|
|
109
|
+
self._root = root
|
|
110
|
+
self._session_cm: Any | None = None
|
|
111
|
+
self._session: Any | None = None
|
|
112
|
+
self._turn_cm: Any | None = None
|
|
113
|
+
self._turn: Any | None = None
|
|
114
|
+
self._turn_input_tokens = 0
|
|
115
|
+
self._turn_output_tokens = 0
|
|
116
|
+
self._turn_usd: float | None = None
|
|
117
|
+
self._last_task = ""
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def from_env(cls, *, root: Path, runtime_version: str) -> AgentBoxTelemetry | None:
|
|
121
|
+
if os.environ.get("AGENTBOX_ENABLED", "").strip().lower() not in _TRUE_VALUES:
|
|
122
|
+
return None
|
|
123
|
+
settings = _load_agentbox_settings()
|
|
124
|
+
if settings is None:
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
client = AgentBoxClient(
|
|
129
|
+
token=settings.token,
|
|
130
|
+
plane_url=settings.plane_url,
|
|
131
|
+
org_id=settings.org_id,
|
|
132
|
+
person_id=settings.person_id,
|
|
133
|
+
machine_id=settings.machine_id,
|
|
134
|
+
agent_id=settings.agent_id,
|
|
135
|
+
runtime_version=runtime_version,
|
|
136
|
+
queue_dir=settings.queue_dir,
|
|
137
|
+
task_detail=settings.task_detail,
|
|
138
|
+
)
|
|
139
|
+
telemetry = cls(client=client, root=root)
|
|
140
|
+
telemetry.start_session()
|
|
141
|
+
return telemetry
|
|
142
|
+
except Exception:
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
def start_session(self) -> None:
|
|
146
|
+
if self._session is not None:
|
|
147
|
+
return
|
|
148
|
+
try:
|
|
149
|
+
self._session_cm = self._client.session(workspace=self._root.name)
|
|
150
|
+
self._session = self._session_cm.__enter__()
|
|
151
|
+
except Exception:
|
|
152
|
+
self._session_cm = None
|
|
153
|
+
self._session = None
|
|
154
|
+
|
|
155
|
+
def close(self, *, error: bool = False) -> None:
|
|
156
|
+
try:
|
|
157
|
+
if self._turn_cm is not None:
|
|
158
|
+
self._turn_cm.__exit__(None, None, None)
|
|
159
|
+
except Exception:
|
|
160
|
+
pass
|
|
161
|
+
finally:
|
|
162
|
+
self._turn_cm = None
|
|
163
|
+
self._turn = None
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
if self._session_cm is not None:
|
|
167
|
+
exc_type = RuntimeError if error else None
|
|
168
|
+
exc = RuntimeError("session ended") if error else None
|
|
169
|
+
self._session_cm.__exit__(exc_type, exc, None)
|
|
170
|
+
except Exception:
|
|
171
|
+
pass
|
|
172
|
+
finally:
|
|
173
|
+
self._session_cm = None
|
|
174
|
+
self._session = None
|
|
175
|
+
|
|
176
|
+
close = getattr(self._client, "close", None)
|
|
177
|
+
if callable(close):
|
|
178
|
+
try:
|
|
179
|
+
close()
|
|
180
|
+
except Exception:
|
|
181
|
+
pass
|
|
182
|
+
|
|
183
|
+
def turn(self) -> Any:
|
|
184
|
+
if self._session is None:
|
|
185
|
+
return nullcontext()
|
|
186
|
+
return _AgentBoxTurnContext(self)
|
|
187
|
+
|
|
188
|
+
def task(self, objective: str) -> None:
|
|
189
|
+
if self._session is None:
|
|
190
|
+
return
|
|
191
|
+
hint = sanitize_task_hint(objective)
|
|
192
|
+
if not hint or hint == self._last_task:
|
|
193
|
+
return
|
|
194
|
+
self._last_task = hint
|
|
195
|
+
try:
|
|
196
|
+
self._session.task(hint)
|
|
197
|
+
except Exception:
|
|
198
|
+
pass
|
|
199
|
+
|
|
200
|
+
def tool(self, name: str) -> None:
|
|
201
|
+
if self._session is None:
|
|
202
|
+
return
|
|
203
|
+
clean_name = safe_tool_name(name)
|
|
204
|
+
try:
|
|
205
|
+
self._session.tool(clean_name, category=tool_category(clean_name))
|
|
206
|
+
except Exception:
|
|
207
|
+
pass
|
|
208
|
+
|
|
209
|
+
def record_usage(self, record: UsageRecord) -> None:
|
|
210
|
+
if self._turn_cm is None or self._session is None:
|
|
211
|
+
return
|
|
212
|
+
self._turn_input_tokens += max(0, int(record.prompt_tokens or 0))
|
|
213
|
+
self._turn_output_tokens += max(0, int(record.completion_tokens or 0))
|
|
214
|
+
if record.cost_usd is not None:
|
|
215
|
+
self._turn_usd = (self._turn_usd or 0.0) + max(0.0, float(record.cost_usd))
|
|
216
|
+
tokens = getattr(self._session, "tokens", None)
|
|
217
|
+
if not callable(tokens):
|
|
218
|
+
tokens = getattr(self._turn, "tokens", None)
|
|
219
|
+
if not callable(tokens):
|
|
220
|
+
return
|
|
221
|
+
try:
|
|
222
|
+
tokens(
|
|
223
|
+
in_=self._turn_input_tokens,
|
|
224
|
+
out=self._turn_output_tokens,
|
|
225
|
+
usd=self._turn_usd,
|
|
226
|
+
)
|
|
227
|
+
except Exception:
|
|
228
|
+
pass
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
class _AgentBoxTurnContext:
|
|
232
|
+
def __init__(self, telemetry: AgentBoxTelemetry) -> None:
|
|
233
|
+
self._telemetry = telemetry
|
|
234
|
+
|
|
235
|
+
def __enter__(self) -> AgentBoxTelemetry:
|
|
236
|
+
self._telemetry._turn_input_tokens = 0
|
|
237
|
+
self._telemetry._turn_output_tokens = 0
|
|
238
|
+
self._telemetry._turn_usd = None
|
|
239
|
+
try:
|
|
240
|
+
self._telemetry._turn_cm = self._telemetry._session.turn()
|
|
241
|
+
self._telemetry._turn = self._telemetry._turn_cm.__enter__()
|
|
242
|
+
except Exception:
|
|
243
|
+
self._telemetry._turn_cm = None
|
|
244
|
+
self._telemetry._turn = None
|
|
245
|
+
return self._telemetry
|
|
246
|
+
|
|
247
|
+
def __exit__(self, exc_type: object, exc: object, tb: object) -> bool:
|
|
248
|
+
try:
|
|
249
|
+
if self._telemetry._turn_cm is not None:
|
|
250
|
+
self._telemetry._turn_cm.__exit__(exc_type, exc, tb)
|
|
251
|
+
except Exception:
|
|
252
|
+
pass
|
|
253
|
+
finally:
|
|
254
|
+
self._telemetry._turn_cm = None
|
|
255
|
+
self._telemetry._turn = None
|
|
256
|
+
return False
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def sanitize_task_hint(value: str) -> str:
|
|
260
|
+
text = str(value or "")
|
|
261
|
+
text = _FENCED_CODE_RE.sub(" ", text)
|
|
262
|
+
text = _INLINE_CODE_RE.sub(" ", text)
|
|
263
|
+
text = _PATH_RE.sub(" file ", text)
|
|
264
|
+
text = re.sub(r"\bfile(?:\s+file\b)+", "file", text)
|
|
265
|
+
text = _WORD_RE.sub(" ", text).strip()
|
|
266
|
+
text = re.sub(r"\s+([.,;:!?])", r"\1", text)
|
|
267
|
+
if not text:
|
|
268
|
+
return "working"
|
|
269
|
+
return text[:140].rstrip()
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def safe_tool_name(value: str) -> str:
|
|
273
|
+
return re.sub(r"[^\w:.-]", "_", str(value or "unknown"))[:80] or "unknown"
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def tool_category(tool_name: str) -> AgentBoxToolCategory:
|
|
277
|
+
global _TOOL_CATEGORY_BY_NAME
|
|
278
|
+
if _TOOL_CATEGORY_BY_NAME is None:
|
|
279
|
+
_TOOL_CATEGORY_BY_NAME = _build_tool_category_map()
|
|
280
|
+
return _TOOL_CATEGORY_BY_NAME.get(tool_name, _heuristic_tool_category(tool_name))
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _build_tool_category_map() -> dict[str, AgentBoxToolCategory]:
|
|
284
|
+
mapped: dict[str, AgentBoxToolCategory] = {}
|
|
285
|
+
for metadata in iter_builtin_tool_metadata():
|
|
286
|
+
tags = {tag.strip().lower() for tag in metadata.categories}
|
|
287
|
+
if "write" in tags:
|
|
288
|
+
mapped[metadata.name] = "edit"
|
|
289
|
+
elif "shell" in tags or "verify" in tags:
|
|
290
|
+
mapped[metadata.name] = "exec"
|
|
291
|
+
elif "web" in tags:
|
|
292
|
+
mapped[metadata.name] = "net"
|
|
293
|
+
elif "read" in tags or "search" in tags or "history" in tags:
|
|
294
|
+
mapped[metadata.name] = "read"
|
|
295
|
+
else:
|
|
296
|
+
mapped[metadata.name] = "other"
|
|
297
|
+
return mapped
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _heuristic_tool_category(tool_name: str) -> AgentBoxToolCategory:
|
|
301
|
+
normalized = tool_name.strip().lower()
|
|
302
|
+
if any(token in normalized for token in ("write", "edit", "patch", "delete", "move", "mkdir")):
|
|
303
|
+
return "edit"
|
|
304
|
+
if any(token in normalized for token in ("shell", "exec", "run", "verify", "test")):
|
|
305
|
+
return "exec"
|
|
306
|
+
if any(token in normalized for token in ("web", "http", "fetch", "search")):
|
|
307
|
+
return "net"
|
|
308
|
+
if any(token in normalized for token in ("read", "list", "history", "symbol", "grep", "rg")):
|
|
309
|
+
return "read"
|
|
310
|
+
return "other"
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Endpoints and identifiers for the hosted Alysis Code Pro service.
|
|
2
|
+
|
|
3
|
+
`alysis login` runs an RFC 8628-style device flow against Supabase Edge
|
|
4
|
+
Functions (`device-code` / `device-token`); the user approves the code on the
|
|
5
|
+
account website's /activate page. The CLI receives a gateway key (``slk_…``)
|
|
6
|
+
and talks to the Alysis Code LLM gateway — an OpenAI-compatible proxy that meters
|
|
7
|
+
the subscription's credits server-side. The CLI never holds upstream provider
|
|
8
|
+
keys for Pro; BYOK profiles are configured separately and take precedence.
|
|
9
|
+
|
|
10
|
+
Values can be overridden via environment variables to point at a different
|
|
11
|
+
deployment (e.g. a staging project or local stubs) during testing.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from urllib.parse import urlsplit
|
|
17
|
+
|
|
18
|
+
from .branding import env_get
|
|
19
|
+
|
|
20
|
+
# Supabase project hosting the device-login edge functions.
|
|
21
|
+
_DEFAULT_SUPABASE_URL = "https://vzigujbcjjmpntxhmyvr.supabase.co"
|
|
22
|
+
|
|
23
|
+
# Product site: serves the /activate device-approval page and /account.
|
|
24
|
+
#
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# Everything user-visible that names the site derives from _DEFAULT_SITE_URL —
|
|
27
|
+
# help text, proxy error messages, the /activate and /account links, and the
|
|
28
|
+
# tests asserting on them. Keep it that way: re-hardcoding the host anywhere
|
|
29
|
+
# else is caught by test_rebrand_compatibility.py.
|
|
30
|
+
#
|
|
31
|
+
# The device-login flow depends on this host actually serving /activate against
|
|
32
|
+
# the Supabase project below. The CLI sends Pro users there to approve a login
|
|
33
|
+
# code, so the two must move together — a client pointed at a host that does
|
|
34
|
+
# not serve the page breaks sign-in with no client-side workaround.
|
|
35
|
+
#
|
|
36
|
+
# _LEGACY_SITE_URL is the pre-rebrand host. It is kept only so a 301 from it
|
|
37
|
+
# can be verified and so configs that still name it are recognisable; nothing
|
|
38
|
+
# reads it at runtime.
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
PRODUCT_SITE_URL = "https://alysiscode.com"
|
|
41
|
+
_LEGACY_SITE_URL = "https://sylliptor.alysisai.com"
|
|
42
|
+
_DEFAULT_SITE_URL = PRODUCT_SITE_URL
|
|
43
|
+
|
|
44
|
+
# Supabase "anon" key: a PUBLIC client identifier (shipped in the website's
|
|
45
|
+
# browser bundle too). It grants nothing by itself — the edge functions are
|
|
46
|
+
# either public-by-design (device flow) or JWT/secret-guarded server-side.
|
|
47
|
+
ANON_KEY = (
|
|
48
|
+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."
|
|
49
|
+
"eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZ6aWd1amJjamptcG50eGhteXZyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODA5Mzc0NTIsImV4cCI6MjA5NjUxMzQ1Mn0."
|
|
50
|
+
"vLH9q-BNO8IWIZrVlvCw8pZWXdLgmKG4Tl9toTTD3pg"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# The profile/preset key used for the hosted Pro provider.
|
|
54
|
+
PROFILE_KEY = "alysis"
|
|
55
|
+
|
|
56
|
+
# LEGACY: base URL of the retired MiMo-trial proxy (an OpenRouter-forwarding
|
|
57
|
+
# Supabase Edge Function). The service is gone, but URL classifiers (web
|
|
58
|
+
# search / provider limits) still recognize it so configs from that era keep
|
|
59
|
+
# loading with sensible behavior instead of misclassifying.
|
|
60
|
+
DEFAULT_PROXY_BASE_URL = f"{_DEFAULT_SUPABASE_URL}/functions/v1/llm/v1"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# Loopback hosts may use http:// (local stubs / tests); every other host must
|
|
64
|
+
# be https so device codes and the gateway key never travel in cleartext.
|
|
65
|
+
_LOOPBACK_HOSTS = frozenset({"127.0.0.1", "localhost", "::1"})
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class AlysisCloudConfigError(ValueError):
|
|
69
|
+
"""Raised when a configured Alysis Code cloud URL is unsafe (e.g. cleartext http)."""
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _clean(url: str) -> str:
|
|
73
|
+
return str(url or "").strip().rstrip("/")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _checked(url: str) -> str:
|
|
77
|
+
"""Clean a URL and reject cleartext http:// for non-loopback hosts.
|
|
78
|
+
|
|
79
|
+
Device codes and the long-lived gateway key travel to these endpoints, so a
|
|
80
|
+
downgraded (http://) origin from an env override would leak them. https is
|
|
81
|
+
required unless the host is loopback (local stubs / tests) or
|
|
82
|
+
ALYSIS_ALLOW_INSECURE_URLS is explicitly set.
|
|
83
|
+
"""
|
|
84
|
+
cleaned = _clean(url)
|
|
85
|
+
if not cleaned:
|
|
86
|
+
return cleaned
|
|
87
|
+
parts = urlsplit(cleaned)
|
|
88
|
+
if parts.scheme.lower() == "https":
|
|
89
|
+
return cleaned
|
|
90
|
+
host = (parts.hostname or "").lower()
|
|
91
|
+
if host in _LOOPBACK_HOSTS or env_get("ALYSIS_ALLOW_INSECURE_URLS"):
|
|
92
|
+
return cleaned
|
|
93
|
+
raise AlysisCloudConfigError(
|
|
94
|
+
f"Refusing to use insecure Alysis Code URL {cleaned!r}: https is required "
|
|
95
|
+
"(set ALYSIS_ALLOW_INSECURE_URLS=1 only for trusted local testing)."
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def supabase_url() -> str:
|
|
100
|
+
return _checked(env_get("ALYSIS_SUPABASE_URL") or _DEFAULT_SUPABASE_URL)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def site_url() -> str:
|
|
104
|
+
return _checked(env_get("ALYSIS_SITE_URL") or _DEFAULT_SITE_URL)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def site_host() -> str:
|
|
108
|
+
"""Bare hostname of the product site, for prose that reads better without a scheme."""
|
|
109
|
+
return urlsplit(site_url()).netloc
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def account_url() -> str:
|
|
113
|
+
"""Where a Pro user manages their plan and credits.
|
|
114
|
+
|
|
115
|
+
Everything user-visible that names the account page goes through here, so
|
|
116
|
+
moving the site is the single constant above and not a search-and-replace
|
|
117
|
+
across help text, error messages, and docs.
|
|
118
|
+
"""
|
|
119
|
+
return f"{site_url()}/account"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def gateway_base_url() -> str:
|
|
123
|
+
"""OpenAI-compatible base URL; the LLM client appends ``/chat/completions``.
|
|
124
|
+
|
|
125
|
+
The hosted proxy runs as the `llm` Supabase Edge Function (the same shape
|
|
126
|
+
the MiMo-trial proxy used), holding the upstream DeepSeek key server-side
|
|
127
|
+
and metering each account's allowance/credits.
|
|
128
|
+
"""
|
|
129
|
+
override = env_get("ALYSIS_GATEWAY_URL")
|
|
130
|
+
if override:
|
|
131
|
+
return _checked(override)
|
|
132
|
+
return f"{supabase_url()}/functions/v1/llm/v1"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def device_code_url() -> str:
|
|
136
|
+
"""POST here to start a device login (returns user_code + device_code)."""
|
|
137
|
+
return f"{supabase_url()}/functions/v1/device-code"
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def device_token_url() -> str:
|
|
141
|
+
"""POST device_code here until the user approves (returns the slk_ key)."""
|
|
142
|
+
return f"{supabase_url()}/functions/v1/device-token"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def activate_url() -> str:
|
|
146
|
+
"""The website page where a signed-in user approves a device code."""
|
|
147
|
+
return f"{site_url()}/activate"
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def models_url() -> str:
|
|
151
|
+
"""The gateway's OpenAI-shaped model listing."""
|
|
152
|
+
return f"{gateway_base_url()}/models"
|