claude-smart 0.2.41 → 0.2.43
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.
- package/.claude-plugin/marketplace.json +17 -0
- package/README.md +1 -1
- package/bin/claude-smart.js +86 -48
- package/package.json +10 -3
- package/plugin/.claude-plugin/plugin.json +9 -3
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/README.md +2 -2
- package/plugin/dashboard/next.config.ts +9 -1
- package/plugin/pyproject.toml +2 -2
- package/plugin/scripts/_lib.sh +91 -0
- package/plugin/scripts/backend-service.sh +46 -15
- package/plugin/scripts/cli.sh +29 -1
- package/plugin/scripts/codex-hook.js +72 -4
- package/plugin/scripts/dashboard-build.sh +1 -0
- package/plugin/scripts/dashboard-service.sh +1 -0
- package/plugin/scripts/ensure-plugin-root.sh +7 -14
- package/plugin/scripts/hook_entry.sh +1 -0
- package/plugin/scripts/smart-install.sh +18 -2
- package/plugin/src/claude_smart/cli.py +72 -38
- package/plugin/src/claude_smart/context_format.py +11 -12
- package/plugin/src/claude_smart/cs_cite.py +26 -12
- package/plugin/src/claude_smart/ids.py +13 -5
- package/plugin/uv.lock +1 -1
- package/plugin/vendor/reflexio/.env.example +53 -0
- package/plugin/vendor/reflexio/LICENSE +201 -0
- package/plugin/vendor/reflexio/README.md +338 -0
- package/plugin/vendor/reflexio/pyproject.toml +271 -0
- package/plugin/vendor/reflexio/reflexio/README.md +184 -0
- package/plugin/vendor/reflexio/reflexio/__init__.py +166 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/README.md +109 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/backends.py +175 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/bench.py +642 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/embed_cache.py +330 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/report.py +317 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/report.md +43 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/results.json +4478 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/scenarios.py +134 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/seed.py +255 -0
- package/plugin/vendor/reflexio/reflexio/cli/README.md +287 -0
- package/plugin/vendor/reflexio/reflexio/cli/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/cli/__main__.py +56 -0
- package/plugin/vendor/reflexio/reflexio/cli/_client.py +86 -0
- package/plugin/vendor/reflexio/reflexio/cli/app.py +127 -0
- package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +266 -0
- package/plugin/vendor/reflexio/reflexio/cli/codex_auth.py +503 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/admin_cmd.py +65 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/agent_playbooks.py +503 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/api.py +114 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/auth.py +109 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/config_cmd.py +511 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/doctor.py +127 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/embeddings.py +53 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +478 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +303 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +289 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +961 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +285 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/status_cmd.py +143 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/user_playbooks.py +373 -0
- package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +284 -0
- package/plugin/vendor/reflexio/reflexio/cli/errors.py +217 -0
- package/plugin/vendor/reflexio/reflexio/cli/log_format.py +247 -0
- package/plugin/vendor/reflexio/reflexio/cli/output.py +867 -0
- package/plugin/vendor/reflexio/reflexio/cli/paths.py +41 -0
- package/plugin/vendor/reflexio/reflexio/cli/run_services.py +391 -0
- package/plugin/vendor/reflexio/reflexio/cli/state.py +204 -0
- package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +96 -0
- package/plugin/vendor/reflexio/reflexio/cli/utils.py +329 -0
- package/plugin/vendor/reflexio/reflexio/client/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/client/cache.py +150 -0
- package/plugin/vendor/reflexio/reflexio/client/client.py +2613 -0
- package/plugin/vendor/reflexio/reflexio/defaults.py +23 -0
- package/plugin/vendor/reflexio/reflexio/integrations/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/.clawhubignore +7 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +274 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +517 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +473 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +2156 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +241 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +140 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +130 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +113 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +103 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +156 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +134 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +41 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +17 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +24 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +48 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +278 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +164 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +35 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +27 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +31 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +42 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +49 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +91 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +50 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tsconfig.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/types/openclaw.d.ts +230 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +13 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/README.md +120 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/TESTING.md +168 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package-lock.json +1657 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/HEARTBEAT.md +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/README.md +84 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/SKILL.md +194 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/_meta.json +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/agents/reflexio-extractor.md +45 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/handler.ts +214 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/setup.ts +55 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/index.ts +327 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/consolidate.ts +233 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/dedup.ts +80 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/io.ts +155 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/openclaw-cli.ts +67 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/search.ts +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-playbook.ts +76 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-profile.ts +79 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/openclaw.plugin.json +46 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/package.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/README.md +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/full_consolidation.md +56 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/playbook_extraction.md +217 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/profile_extraction.md +132 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-consolidate/SKILL.md +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-embedded/SKILL.md +194 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/HOOK.md +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/architecture.md +49 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/comparison.md +31 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/future-work.md +47 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/porting-notes.md +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/install.sh +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/uninstall.sh +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/consolidate.test.ts +135 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/dedup.test.ts +104 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/io.test.ts +175 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/search.test.ts +66 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/smoke-test.ts +140 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-playbook.test.ts +93 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-profile.test.ts +174 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tsconfig.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/types/openclaw.d.ts +230 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/vitest.config.ts +7 -0
- package/plugin/vendor/reflexio/reflexio/lib/__init__.py +23 -0
- package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +310 -0
- package/plugin/vendor/reflexio/reflexio/lib/_base.py +225 -0
- package/plugin/vendor/reflexio/reflexio/lib/_config.py +83 -0
- package/plugin/vendor/reflexio/reflexio/lib/_dashboard.py +266 -0
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +176 -0
- package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +334 -0
- package/plugin/vendor/reflexio/reflexio/lib/_operations.py +153 -0
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +545 -0
- package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +52 -0
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +167 -0
- package/plugin/vendor/reflexio/reflexio/lib/_storage_labels.py +103 -0
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +288 -0
- package/plugin/vendor/reflexio/reflexio/lib/reflexio_lib.py +27 -0
- package/plugin/vendor/reflexio/reflexio/models/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/braintrust_schema.py +141 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/common.py +41 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +1103 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +63 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +487 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +28 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +83 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +766 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/service_schemas.py +9 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/stall_state_schema.py +32 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +177 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +129 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/enums.py +25 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/validators.py +280 -0
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +908 -0
- package/plugin/vendor/reflexio/reflexio/models/py.typed +0 -0
- package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +90 -0
- package/plugin/vendor/reflexio/reflexio/server/README.md +616 -0
- package/plugin/vendor/reflexio/reflexio/server/__init__.py +210 -0
- package/plugin/vendor/reflexio/reflexio/server/__main__.py +132 -0
- package/plugin/vendor/reflexio/reflexio/server/_auth.py +25 -0
- package/plugin/vendor/reflexio/reflexio/server/api.py +2714 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/account_api.py +143 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/health_api.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/pending_tool_call_api.py +572 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +66 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +540 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +50 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/stall_state_api.py +100 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/__init__.py +15 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +208 -0
- package/plugin/vendor/reflexio/reflexio/server/correlation.py +46 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/__init__.py +30 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +110 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/image_utils.py +55 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1595 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +112 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +469 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1122 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_stream_parser.py +197 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +210 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +213 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +255 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/__init__.py +6 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +177 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/llm_reranker.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +699 -0
- package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/_dispatchers.py +54 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/README.md +121 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation/v1.0.0.prompt.md +58 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation_with_comparison/v1.0.0.prompt.md +76 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/answer_synthesis/v1.5.2.prompt.md +88 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/compress_session_for_query/v1.3.0.prompt.md +31 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +20 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.0.0.prompt.md +53 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.1.0.prompt.md +57 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.2.0.prompt.md +68 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.3.0.prompt.md +70 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.4.0.prompt.md +77 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.5.0.prompt.md +82 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +83 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.1.0.prompt.md +193 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +206 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0-deprecated.prompt.md +66 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0.prompt.md +43 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.1.0.prompt.md +46 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0-deprecated.prompt.md +64 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0.prompt.md +39 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.1.0.prompt.md +39 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.2.0.prompt.md +47 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.0.prompt.md +58 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.0.2.prompt.md +254 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.1.0.prompt.md +274 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.0.prompt.md +279 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v1.0.0.prompt.md +73 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v2.0.0.prompt.md +86 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.0.0.prompt.md +97 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.1.0.prompt.md +119 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.2.0.prompt.md +123 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +127 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.0.0.prompt.md +14 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.1.0.prompt.md +24 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +29 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.0.0.prompt.md +11 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.1.0.prompt.md +21 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.2.0.prompt.md +25 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.0.0.prompt.md +37 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.1.0.prompt.md +40 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.2.0.prompt.md +36 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v1.0.0.prompt.md +45 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v2.0.0.prompt.md +81 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v3.0.0.prompt.md +80 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate_expert/v1.0.0.prompt.md +34 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_deduplication/v1.0.0.prompt.md +116 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate/v1.0.0.prompt.md +33 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate_override/v1.0.0.prompt.md +16 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +140 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +160 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_main/v1.0.0.prompt.md +14 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +19 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/rerank_relevance/v1.1.0.prompt.md +44 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +43 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_content_evaluation/v1.0.0.prompt.md +33 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/feedback_extraction_main_v1.jsonl +10 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/profile_update_main_v1.jsonl +10 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +280 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_schema.py +11 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +131 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +60 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_service.py +228 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +87 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluator.py +372 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/delayed_group_evaluator.py +156 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/group_evaluation_runner.py +340 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +471 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +1626 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_cron.py +196 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_encryption.py +101 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/client.py +167 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/service.py +281 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/config_storage.py +62 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/configurator.py +87 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/local_file_config_storage.py +187 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/test_config_storage.py +162 -0
- package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +112 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/distribution.py +33 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +126 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +192 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/hero_state.py +75 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/rule_attribution.py +97 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +515 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/shadow_aggregation.py +90 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +303 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +25 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +351 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +138 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +217 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +468 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +171 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +777 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +689 -0
- package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +835 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +89 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_aggregator.py +1388 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_consolidator.py +960 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_extractor.py +436 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_generation_service.py +808 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_constants.py +28 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +362 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/__init__.py +24 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/assistant_webhook.py +246 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/gepa_adapter.py +291 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/judge.py +97 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +96 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +645 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/rollout.py +35 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scenario_resolver.py +93 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +174 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/__init__.py +26 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_document_expander.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +297 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_deduplicator.py +741 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_extractor.py +462 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service.py +734 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +290 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_extractor.py +247 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service.py +800 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +146 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +70 -0
- package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +671 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +184 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +81 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/constants.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +11 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +154 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +155 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +59 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +1253 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +1945 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +600 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +346 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1378 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +747 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +193 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +166 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +217 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +153 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +372 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +71 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +235 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +170 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +677 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +250 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +154 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +130 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +93 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_stall_state.py +76 -0
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +568 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +77 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +116 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_manager.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +13 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/llm_model_setting.json +7 -0
- package/plugin/vendor/reflexio/reflexio/server/tracing.py +158 -0
- package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +113 -0
- package/plugin/vendor/reflexio/reflexio/server/uvicorn_logging.py +76 -0
- package/plugin/vendor/reflexio/reflexio/test_support/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_fixtures.py +62 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +242 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +129 -0
- package/plugin/vendor/reflexio/reflexio/test_support/skip_decorators.py +43 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
"""Reflexio-native OAuth tokens for OpenAI Codex / ChatGPT subscription.
|
|
2
|
+
|
|
3
|
+
This module owns reflexio's own OAuth tokens against ``auth.openai.com``,
|
|
4
|
+
independent of OpenClaw or any other CLI. Tokens are stored at
|
|
5
|
+
``~/.reflexio/auth/openai-codex.json`` and the refresh-token flow is built
|
|
6
|
+
into the loader so callers always see a fresh access token.
|
|
7
|
+
|
|
8
|
+
Why a separate module: the token store is consumed by both the CLI
|
|
9
|
+
(``reflexio setup openai-codex``) and the runtime proxy (``codex_proxy.py``
|
|
10
|
+
in the enterprise tree). Putting it in one place keeps the file shape and
|
|
11
|
+
refresh policy in sync.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import base64
|
|
17
|
+
import contextlib
|
|
18
|
+
import hashlib
|
|
19
|
+
import json
|
|
20
|
+
import logging
|
|
21
|
+
import secrets
|
|
22
|
+
import time
|
|
23
|
+
import urllib.error
|
|
24
|
+
import urllib.request
|
|
25
|
+
from dataclasses import dataclass
|
|
26
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Any
|
|
29
|
+
from urllib.parse import parse_qs, urlencode, urlparse
|
|
30
|
+
|
|
31
|
+
from .paths import reflexio_home
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
# OAuth client + endpoints used by the Codex CLI. Values verified by
|
|
36
|
+
# inspecting the JWT payload of an existing OpenClaw-issued token
|
|
37
|
+
# (`client_id`, `iss` claims) and the codex-rs source.
|
|
38
|
+
CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann"
|
|
39
|
+
CODEX_AUTH_ISSUER = "https://auth.openai.com"
|
|
40
|
+
CODEX_AUTHORIZE_URL = f"{CODEX_AUTH_ISSUER}/oauth/authorize"
|
|
41
|
+
CODEX_TOKEN_URL = f"{CODEX_AUTH_ISSUER}/oauth/token"
|
|
42
|
+
|
|
43
|
+
# Codex CLI binds its callback server to this port; OpenAI's OAuth client
|
|
44
|
+
# config has ``http://localhost:1455/auth/callback`` registered as a valid
|
|
45
|
+
# redirect URI, so we reuse it.
|
|
46
|
+
CODEX_CALLBACK_HOST = "localhost"
|
|
47
|
+
CODEX_CALLBACK_PORT = 1455
|
|
48
|
+
CODEX_CALLBACK_PATH = "/auth/callback"
|
|
49
|
+
CODEX_REDIRECT_URI = (
|
|
50
|
+
f"http://{CODEX_CALLBACK_HOST}:{CODEX_CALLBACK_PORT}{CODEX_CALLBACK_PATH}"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
CODEX_SCOPES = "openid profile email offline_access"
|
|
54
|
+
|
|
55
|
+
# Refresh slightly before the access token actually expires so a slow
|
|
56
|
+
# downstream call doesn't cross the boundary mid-flight.
|
|
57
|
+
_REFRESH_LEAD_SECONDS = 60
|
|
58
|
+
|
|
59
|
+
REFLEXIO_AUTH_DIR = reflexio_home() / "auth"
|
|
60
|
+
REFLEXIO_CODEX_TOKENS_PATH = REFLEXIO_AUTH_DIR / "openai-codex.json"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class CodexTokens:
|
|
65
|
+
"""Persisted Codex OAuth tokens.
|
|
66
|
+
|
|
67
|
+
Attributes:
|
|
68
|
+
access_token (str): Bearer token used for ``api.openai.com`` and
|
|
69
|
+
``chatgpt.com/backend-api/codex`` calls.
|
|
70
|
+
refresh_token (str): Long-lived token used to mint a new access
|
|
71
|
+
token at ``/oauth/token``.
|
|
72
|
+
account_id (str): ``ChatGPT-Account-ID`` header value (from the
|
|
73
|
+
JWT's ``chatgpt_account_id`` claim).
|
|
74
|
+
expires_at (int): Unix epoch seconds when ``access_token`` expires.
|
|
75
|
+
plan_type (str): Cached ``chatgpt_plan_type`` from the JWT (e.g.
|
|
76
|
+
``"plus"``, ``"max-x20"``) for human-facing diagnostics.
|
|
77
|
+
email (str): User email from the JWT, surfaced in CLI status.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
access_token: str
|
|
81
|
+
refresh_token: str
|
|
82
|
+
account_id: str
|
|
83
|
+
expires_at: int
|
|
84
|
+
plan_type: str
|
|
85
|
+
email: str
|
|
86
|
+
|
|
87
|
+
def is_expired(self, lead_seconds: int = _REFRESH_LEAD_SECONDS) -> bool:
|
|
88
|
+
"""Return True if the access token will expire within ``lead_seconds``.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
lead_seconds (int): Treat tokens with less than this much time
|
|
92
|
+
remaining as already expired.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
bool: ``True`` if a refresh is needed.
|
|
96
|
+
"""
|
|
97
|
+
return self.expires_at - lead_seconds <= int(time.time())
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _b64url(data: bytes) -> str:
|
|
101
|
+
"""Base64url-encode without padding (PKCE-style)."""
|
|
102
|
+
return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _make_pkce_pair() -> tuple[str, str]:
|
|
106
|
+
"""Generate a (code_verifier, code_challenge) PKCE pair.
|
|
107
|
+
|
|
108
|
+
Uses a 32-byte random verifier; SHA-256 + base64url for the challenge.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
tuple[str, str]: ``(verifier, challenge)``.
|
|
112
|
+
"""
|
|
113
|
+
verifier = _b64url(secrets.token_bytes(32))
|
|
114
|
+
challenge = _b64url(hashlib.sha256(verifier.encode("ascii")).digest())
|
|
115
|
+
return verifier, challenge
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _decode_jwt_payload(jwt: str) -> dict[str, Any]:
|
|
119
|
+
"""Decode an unsigned JWT payload (no signature verification).
|
|
120
|
+
|
|
121
|
+
Codex JWTs are issued by ``auth.openai.com`` with RS256; we don't have
|
|
122
|
+
the public key locally and don't need to: storing tokens we receive over
|
|
123
|
+
HTTPS from the issuer is sufficient. The payload is read for metadata
|
|
124
|
+
(account_id, plan_type, email, exp).
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
jwt (str): A JWT in standard ``header.payload.signature`` form.
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
dict[str, Any]: The JSON-parsed payload.
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
ValueError: If the JWT is malformed.
|
|
134
|
+
"""
|
|
135
|
+
parts = jwt.split(".")
|
|
136
|
+
if len(parts) != 3:
|
|
137
|
+
raise ValueError("not a JWT (expected three dot-separated parts)")
|
|
138
|
+
payload_b64 = parts[1] + "=" * (-len(parts[1]) % 4) # restore padding
|
|
139
|
+
return json.loads(base64.urlsafe_b64decode(payload_b64))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _tokens_from_response(payload: dict[str, Any]) -> CodexTokens:
|
|
143
|
+
"""Build a ``CodexTokens`` from an ``/oauth/token`` JSON response.
|
|
144
|
+
|
|
145
|
+
Reads the access JWT to derive ``account_id``, ``plan_type``, ``email``,
|
|
146
|
+
and ``expires_at``. Falls back to ``expires_in`` from the response if the
|
|
147
|
+
JWT lacks an ``exp`` claim.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
payload (dict): Decoded JSON body from an OAuth token endpoint.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
CodexTokens: Populated record.
|
|
154
|
+
|
|
155
|
+
Raises:
|
|
156
|
+
ValueError: Required fields missing.
|
|
157
|
+
"""
|
|
158
|
+
access = payload.get("access_token")
|
|
159
|
+
refresh = payload.get("refresh_token")
|
|
160
|
+
if not access or not refresh:
|
|
161
|
+
raise ValueError(
|
|
162
|
+
f"OAuth response missing access_token / refresh_token: {payload}"
|
|
163
|
+
)
|
|
164
|
+
claims = _decode_jwt_payload(access)
|
|
165
|
+
auth_claims = claims.get("https://api.openai.com/auth", {}) or {}
|
|
166
|
+
profile_claims = claims.get("https://api.openai.com/profile", {}) or {}
|
|
167
|
+
account_id = auth_claims.get("chatgpt_account_id", "") or ""
|
|
168
|
+
plan_type = auth_claims.get("chatgpt_plan_type", "unknown") or "unknown"
|
|
169
|
+
email = profile_claims.get("email", "") or ""
|
|
170
|
+
if (exp := claims.get("exp")) is not None:
|
|
171
|
+
expires_at = int(exp)
|
|
172
|
+
else:
|
|
173
|
+
expires_at = int(time.time()) + int(payload.get("expires_in", 0))
|
|
174
|
+
return CodexTokens(
|
|
175
|
+
access_token=access,
|
|
176
|
+
refresh_token=refresh,
|
|
177
|
+
account_id=account_id,
|
|
178
|
+
expires_at=expires_at,
|
|
179
|
+
plan_type=str(plan_type),
|
|
180
|
+
email=str(email),
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def save_tokens(tokens: CodexTokens) -> Path:
|
|
185
|
+
"""Persist tokens to ``~/.reflexio/auth/openai-codex.json``.
|
|
186
|
+
|
|
187
|
+
Creates the parent directory with restrictive permissions on first write.
|
|
188
|
+
The token file itself is written with mode 0600 — bearer tokens shouldn't
|
|
189
|
+
be world-readable.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
tokens (CodexTokens): Tokens to persist.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Path: Where the file was written.
|
|
196
|
+
"""
|
|
197
|
+
REFLEXIO_AUTH_DIR.mkdir(parents=True, exist_ok=True)
|
|
198
|
+
# Filesystems without POSIX permissions (e.g., FAT) won't honour chmod;
|
|
199
|
+
# tolerate the failure rather than aborting the login.
|
|
200
|
+
with contextlib.suppress(OSError):
|
|
201
|
+
REFLEXIO_AUTH_DIR.chmod(0o700)
|
|
202
|
+
payload = {
|
|
203
|
+
"version": 1,
|
|
204
|
+
"access_token": tokens.access_token,
|
|
205
|
+
"refresh_token": tokens.refresh_token,
|
|
206
|
+
"account_id": tokens.account_id,
|
|
207
|
+
"expires_at": tokens.expires_at,
|
|
208
|
+
"plan_type": tokens.plan_type,
|
|
209
|
+
"email": tokens.email,
|
|
210
|
+
}
|
|
211
|
+
REFLEXIO_CODEX_TOKENS_PATH.write_text(json.dumps(payload, indent=2))
|
|
212
|
+
with contextlib.suppress(OSError):
|
|
213
|
+
REFLEXIO_CODEX_TOKENS_PATH.chmod(0o600)
|
|
214
|
+
return REFLEXIO_CODEX_TOKENS_PATH
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def load_tokens_raw() -> CodexTokens | None:
|
|
218
|
+
"""Load tokens from disk without refreshing.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
CodexTokens | None: Persisted tokens, or ``None`` if the file is
|
|
222
|
+
missing or malformed.
|
|
223
|
+
"""
|
|
224
|
+
if not REFLEXIO_CODEX_TOKENS_PATH.exists():
|
|
225
|
+
return None
|
|
226
|
+
try:
|
|
227
|
+
data = json.loads(REFLEXIO_CODEX_TOKENS_PATH.read_text())
|
|
228
|
+
return CodexTokens(
|
|
229
|
+
access_token=data["access_token"],
|
|
230
|
+
refresh_token=data["refresh_token"],
|
|
231
|
+
account_id=data.get("account_id", ""),
|
|
232
|
+
expires_at=int(data.get("expires_at", 0)),
|
|
233
|
+
plan_type=data.get("plan_type", "unknown"),
|
|
234
|
+
email=data.get("email", ""),
|
|
235
|
+
)
|
|
236
|
+
except (KeyError, json.JSONDecodeError, ValueError) as e:
|
|
237
|
+
logger.warning("Bad reflexio codex tokens file: %s", e)
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def refresh_tokens(tokens: CodexTokens) -> CodexTokens:
|
|
242
|
+
"""Exchange the refresh_token for a new (access, refresh) pair.
|
|
243
|
+
|
|
244
|
+
POSTs to ``auth.openai.com/oauth/token`` with ``grant_type=refresh_token``.
|
|
245
|
+
The new tokens are persisted to disk before returning.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
tokens (CodexTokens): The current tokens; only ``refresh_token`` is read.
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
CodexTokens: A fresh, persisted token record.
|
|
252
|
+
|
|
253
|
+
Raises:
|
|
254
|
+
urllib.error.HTTPError: If the token endpoint rejects the refresh
|
|
255
|
+
(e.g., refresh_token revoked — caller should prompt re-login).
|
|
256
|
+
"""
|
|
257
|
+
body = urlencode(
|
|
258
|
+
{
|
|
259
|
+
"grant_type": "refresh_token",
|
|
260
|
+
"refresh_token": tokens.refresh_token,
|
|
261
|
+
"client_id": CODEX_CLIENT_ID,
|
|
262
|
+
"scope": CODEX_SCOPES,
|
|
263
|
+
}
|
|
264
|
+
).encode("utf-8")
|
|
265
|
+
req = urllib.request.Request( # noqa: S310 - fixed https URL
|
|
266
|
+
CODEX_TOKEN_URL,
|
|
267
|
+
data=body,
|
|
268
|
+
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
269
|
+
method="POST",
|
|
270
|
+
)
|
|
271
|
+
with urllib.request.urlopen(req, timeout=30) as resp: # noqa: S310 - fixed https URL
|
|
272
|
+
payload = json.loads(resp.read())
|
|
273
|
+
new_tokens = _tokens_from_response(payload)
|
|
274
|
+
save_tokens(new_tokens)
|
|
275
|
+
logger.info(
|
|
276
|
+
"Refreshed OpenAI Codex tokens; new access expires at %d (plan=%s)",
|
|
277
|
+
new_tokens.expires_at,
|
|
278
|
+
new_tokens.plan_type,
|
|
279
|
+
)
|
|
280
|
+
return new_tokens
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def get_fresh_tokens() -> CodexTokens | None:
|
|
284
|
+
"""Return tokens, refreshing on disk if the access token has expired.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
CodexTokens | None: Fresh tokens, or ``None`` if no tokens are saved.
|
|
288
|
+
Caller should run ``reflexio setup openai-codex`` if ``None``.
|
|
289
|
+
"""
|
|
290
|
+
tokens = load_tokens_raw()
|
|
291
|
+
if tokens is None:
|
|
292
|
+
return None
|
|
293
|
+
if tokens.is_expired():
|
|
294
|
+
try:
|
|
295
|
+
return refresh_tokens(tokens)
|
|
296
|
+
except urllib.error.HTTPError as e:
|
|
297
|
+
logger.warning(
|
|
298
|
+
"Refresh failed (HTTP %d); re-login required via "
|
|
299
|
+
"'reflexio setup openai-codex'",
|
|
300
|
+
e.code,
|
|
301
|
+
)
|
|
302
|
+
return None
|
|
303
|
+
return tokens
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
# ---------------------------------------------------------------------------
|
|
307
|
+
# Authorization-code login flow (browser + PKCE + local callback)
|
|
308
|
+
# ---------------------------------------------------------------------------
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
class _CallbackHandler(BaseHTTPRequestHandler):
|
|
312
|
+
"""One-shot HTTP handler that captures the OAuth callback.
|
|
313
|
+
|
|
314
|
+
The handler stashes the parsed query parameters on the server instance
|
|
315
|
+
(which a stricter typer would model as a custom HTTPServer subclass);
|
|
316
|
+
the orchestrating function reads them back after ``handle_request``.
|
|
317
|
+
|
|
318
|
+
Browsers expect a tidy success page; we serve a small HTML body so the
|
|
319
|
+
user knows the CLI took control.
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
# Silence default access logs; this is a 1-shot interactive flow.
|
|
323
|
+
def log_message( # noqa: ANN401, ARG002 — signature dictated by stdlib
|
|
324
|
+
self,
|
|
325
|
+
format: str, # noqa: A002, ARG002
|
|
326
|
+
*args: Any, # noqa: ARG002
|
|
327
|
+
) -> None:
|
|
328
|
+
"""No-op — suppress the default access log noise."""
|
|
329
|
+
return
|
|
330
|
+
|
|
331
|
+
def do_GET(self) -> None: # noqa: N802 - dictated by stdlib
|
|
332
|
+
"""Capture the callback query and write a success page."""
|
|
333
|
+
parsed = urlparse(self.path)
|
|
334
|
+
if parsed.path != CODEX_CALLBACK_PATH:
|
|
335
|
+
self.send_response(404)
|
|
336
|
+
self.end_headers()
|
|
337
|
+
return
|
|
338
|
+
query = parse_qs(parsed.query)
|
|
339
|
+
# Store on the server instance for the caller to read.
|
|
340
|
+
self.server._captured = { # type: ignore[attr-defined]
|
|
341
|
+
"code": (query.get("code") or [""])[0],
|
|
342
|
+
"state": (query.get("state") or [""])[0],
|
|
343
|
+
"error": (query.get("error") or [""])[0],
|
|
344
|
+
"error_description": (query.get("error_description") or [""])[0],
|
|
345
|
+
}
|
|
346
|
+
self.send_response(200)
|
|
347
|
+
self.send_header("Content-Type", "text/html; charset=utf-8")
|
|
348
|
+
self.end_headers()
|
|
349
|
+
self.wfile.write(
|
|
350
|
+
b"<html><body style='font-family:system-ui;max-width:520px;margin:48px auto'>"
|
|
351
|
+
b"<h2>Reflexio is now signed in.</h2>"
|
|
352
|
+
b"<p>You can close this tab and return to the terminal.</p>"
|
|
353
|
+
b"</body></html>"
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _capture_oauth_callback(state: str, timeout_s: int) -> dict[str, str]:
|
|
358
|
+
"""Run a one-shot HTTP server and return the OAuth callback query.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
state (str): The CSRF ``state`` value sent on the authorize call;
|
|
362
|
+
verified to match here.
|
|
363
|
+
timeout_s (int): Hard ceiling on how long to wait for the user to
|
|
364
|
+
complete the browser flow.
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
dict[str, str]: The captured query parameters
|
|
368
|
+
(``code``, ``state``, ``error``, ``error_description``).
|
|
369
|
+
|
|
370
|
+
Raises:
|
|
371
|
+
TimeoutError: If the callback isn't received in time.
|
|
372
|
+
ValueError: If the callback's state doesn't match the request's.
|
|
373
|
+
"""
|
|
374
|
+
server = HTTPServer((CODEX_CALLBACK_HOST, CODEX_CALLBACK_PORT), _CallbackHandler)
|
|
375
|
+
server._captured = None # type: ignore[attr-defined]
|
|
376
|
+
server.timeout = timeout_s
|
|
377
|
+
server.handle_request()
|
|
378
|
+
captured: dict[str, str] | None = getattr(server, "_captured", None)
|
|
379
|
+
if captured is None:
|
|
380
|
+
raise TimeoutError(
|
|
381
|
+
f"OAuth callback not received within {timeout_s}s — open the URL "
|
|
382
|
+
"yourself and complete the sign-in?"
|
|
383
|
+
)
|
|
384
|
+
if captured.get("state") != state:
|
|
385
|
+
raise ValueError("OAuth state mismatch — refusing to continue (possible CSRF).")
|
|
386
|
+
if err := captured.get("error"):
|
|
387
|
+
raise ValueError(
|
|
388
|
+
f"OAuth provider returned error '{err}': {captured.get('error_description', '')}"
|
|
389
|
+
)
|
|
390
|
+
return captured
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def build_authorize_url(verifier: str, state: str) -> tuple[str, str]:
|
|
394
|
+
"""Build the authorization URL for the browser step of the OAuth flow.
|
|
395
|
+
|
|
396
|
+
Args:
|
|
397
|
+
verifier (str): PKCE code verifier (the random secret stored locally).
|
|
398
|
+
state (str): CSRF state value to round-trip through the redirect.
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
tuple[str, str]: ``(authorize_url, code_challenge)``. The challenge
|
|
402
|
+
is returned for callers that want to display it; the URL is what
|
|
403
|
+
actually goes in the browser.
|
|
404
|
+
"""
|
|
405
|
+
challenge = _b64url(hashlib.sha256(verifier.encode("ascii")).digest())
|
|
406
|
+
qs = urlencode(
|
|
407
|
+
{
|
|
408
|
+
"client_id": CODEX_CLIENT_ID,
|
|
409
|
+
"response_type": "code",
|
|
410
|
+
"redirect_uri": CODEX_REDIRECT_URI,
|
|
411
|
+
"scope": CODEX_SCOPES,
|
|
412
|
+
"code_challenge": challenge,
|
|
413
|
+
"code_challenge_method": "S256",
|
|
414
|
+
"state": state,
|
|
415
|
+
}
|
|
416
|
+
)
|
|
417
|
+
return f"{CODEX_AUTHORIZE_URL}?{qs}", challenge
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def exchange_authorization_code(code: str, verifier: str) -> CodexTokens:
|
|
421
|
+
"""Exchange an OAuth authorization code for tokens.
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
code (str): The ``code`` query param the redirect delivered.
|
|
425
|
+
verifier (str): The PKCE code verifier (must be the one used when
|
|
426
|
+
building the authorize URL).
|
|
427
|
+
|
|
428
|
+
Returns:
|
|
429
|
+
CodexTokens: The persisted token record.
|
|
430
|
+
|
|
431
|
+
Raises:
|
|
432
|
+
urllib.error.HTTPError: If the token endpoint rejects the request.
|
|
433
|
+
"""
|
|
434
|
+
body = urlencode(
|
|
435
|
+
{
|
|
436
|
+
"grant_type": "authorization_code",
|
|
437
|
+
"code": code,
|
|
438
|
+
"redirect_uri": CODEX_REDIRECT_URI,
|
|
439
|
+
"client_id": CODEX_CLIENT_ID,
|
|
440
|
+
"code_verifier": verifier,
|
|
441
|
+
}
|
|
442
|
+
).encode("utf-8")
|
|
443
|
+
req = urllib.request.Request( # noqa: S310 - fixed https URL
|
|
444
|
+
CODEX_TOKEN_URL,
|
|
445
|
+
data=body,
|
|
446
|
+
headers={"Content-Type": "application/x-www-form-urlencoded"},
|
|
447
|
+
method="POST",
|
|
448
|
+
)
|
|
449
|
+
with urllib.request.urlopen(req, timeout=30) as resp: # noqa: S310 - fixed https URL
|
|
450
|
+
payload = json.loads(resp.read())
|
|
451
|
+
return _tokens_from_response(payload)
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def login_interactive(
|
|
455
|
+
*,
|
|
456
|
+
open_browser: bool = True,
|
|
457
|
+
timeout_s: int = 300,
|
|
458
|
+
) -> CodexTokens:
|
|
459
|
+
"""Run the full PKCE OAuth flow against ``auth.openai.com``.
|
|
460
|
+
|
|
461
|
+
Steps:
|
|
462
|
+
1. Generate a fresh PKCE pair + CSRF state.
|
|
463
|
+
2. Build the authorize URL and either open the user's browser or
|
|
464
|
+
print the URL for them to open manually.
|
|
465
|
+
3. Bind a one-shot HTTP server on ``localhost:1455`` to catch the
|
|
466
|
+
callback.
|
|
467
|
+
4. Exchange the returned auth code for tokens.
|
|
468
|
+
5. Persist tokens to disk.
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
open_browser (bool): When True (default), call ``webbrowser.open``
|
|
472
|
+
on the authorize URL. When False, just print it.
|
|
473
|
+
timeout_s (int): Maximum wall time to wait for the callback before
|
|
474
|
+
failing.
|
|
475
|
+
|
|
476
|
+
Returns:
|
|
477
|
+
CodexTokens: The persisted token record.
|
|
478
|
+
"""
|
|
479
|
+
verifier, _challenge = _make_pkce_pair()
|
|
480
|
+
state = _b64url(secrets.token_bytes(16))
|
|
481
|
+
authorize_url, _ = build_authorize_url(verifier, state)
|
|
482
|
+
|
|
483
|
+
if open_browser:
|
|
484
|
+
# Lazy import — webbrowser pulls in tkinter on some platforms.
|
|
485
|
+
import webbrowser
|
|
486
|
+
|
|
487
|
+
opened = webbrowser.open(authorize_url, new=1)
|
|
488
|
+
if not opened:
|
|
489
|
+
print("Could not open browser automatically.")
|
|
490
|
+
print()
|
|
491
|
+
print("Open this URL to sign in to ChatGPT:")
|
|
492
|
+
print(f" {authorize_url}")
|
|
493
|
+
print()
|
|
494
|
+
print(f"Listening for callback on {CODEX_REDIRECT_URI} ...")
|
|
495
|
+
|
|
496
|
+
captured = _capture_oauth_callback(state=state, timeout_s=timeout_s)
|
|
497
|
+
code = captured.get("code") or ""
|
|
498
|
+
if not code:
|
|
499
|
+
raise ValueError("OAuth callback returned no authorization code.")
|
|
500
|
+
|
|
501
|
+
tokens = exchange_authorization_code(code, verifier)
|
|
502
|
+
save_tokens(tokens)
|
|
503
|
+
return tokens
|
|
File without changes
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Administrative server-side controls: cache invalidation and friends.
|
|
2
|
+
|
|
3
|
+
Subcommands live under nested Typer groups (``admin cache invalidate``)
|
|
4
|
+
so the surface stays organised as more admin operations are added.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Annotated
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
|
|
13
|
+
from reflexio.cli.errors import handle_errors
|
|
14
|
+
from reflexio.cli.output import print_info, render
|
|
15
|
+
from reflexio.cli.state import get_client
|
|
16
|
+
|
|
17
|
+
# Top-level "admin" group.
|
|
18
|
+
app = typer.Typer(help="Administrative server-side controls.")
|
|
19
|
+
|
|
20
|
+
# Nested "admin cache" group — invalidate, plus future stats/clear hooks.
|
|
21
|
+
cache_app = typer.Typer(help="Manage the per-org Reflexio cache.")
|
|
22
|
+
app.add_typer(cache_app, name="cache")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@cache_app.command(name="invalidate")
|
|
26
|
+
@handle_errors
|
|
27
|
+
def cache_invalidate(
|
|
28
|
+
ctx: typer.Context,
|
|
29
|
+
org_id: Annotated[
|
|
30
|
+
str | None,
|
|
31
|
+
typer.Option(
|
|
32
|
+
"--org-id",
|
|
33
|
+
help=(
|
|
34
|
+
"Optional org_id verification token. When supplied, the "
|
|
35
|
+
"server will reject the request unless it matches the "
|
|
36
|
+
"caller's authenticated org. Cross-org invalidation is "
|
|
37
|
+
"not supported."
|
|
38
|
+
),
|
|
39
|
+
),
|
|
40
|
+
] = None,
|
|
41
|
+
) -> None:
|
|
42
|
+
"""Evict the caller's per-org Reflexio cache entry.
|
|
43
|
+
|
|
44
|
+
Necessary when the running config was mutated through a channel
|
|
45
|
+
the server can't observe (sibling-replica DB writes, hand-edited
|
|
46
|
+
config files on backends that don't auto-detect mtime changes).
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
ctx: Typer context with CliState in ctx.obj
|
|
50
|
+
org_id: Optional verification token; must match the caller's
|
|
51
|
+
authenticated org if supplied.
|
|
52
|
+
"""
|
|
53
|
+
client = get_client(ctx)
|
|
54
|
+
resp = client.invalidate_cache(org_id=org_id)
|
|
55
|
+
|
|
56
|
+
json_mode: bool = ctx.obj.json_mode
|
|
57
|
+
if json_mode:
|
|
58
|
+
render(resp, json_mode=True)
|
|
59
|
+
else:
|
|
60
|
+
invalidated = bool(resp.get("invalidated"))
|
|
61
|
+
target_org = resp.get("org_id", "(unknown)")
|
|
62
|
+
if invalidated:
|
|
63
|
+
print_info(f"Cache evicted for org {target_org}")
|
|
64
|
+
else:
|
|
65
|
+
print_info(f"No cache entry to evict for org {target_org}")
|