claude-smart 0.2.42 → 0.2.44

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.
Files changed (402) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/README.md +1 -1
  3. package/bin/claude-smart.js +2 -2
  4. package/package.json +9 -3
  5. package/plugin/.claude-plugin/plugin.json +9 -3
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +23 -3
  8. package/plugin/pyproject.toml +3 -3
  9. package/plugin/scripts/_lib.sh +91 -0
  10. package/plugin/scripts/backend-service.sh +51 -4
  11. package/plugin/scripts/cli.sh +3 -1
  12. package/plugin/scripts/codex-hook.js +72 -4
  13. package/plugin/scripts/dashboard-build.sh +1 -0
  14. package/plugin/scripts/dashboard-service.sh +1 -0
  15. package/plugin/scripts/ensure-plugin-root.sh +1 -0
  16. package/plugin/scripts/hook_entry.sh +6 -3
  17. package/plugin/scripts/smart-install.sh +3 -2
  18. package/plugin/src/README.md +57 -0
  19. package/plugin/src/claude_smart/context_format.py +11 -12
  20. package/plugin/src/claude_smart/cs_cite.py +26 -12
  21. package/plugin/src/claude_smart/ids.py +13 -5
  22. package/plugin/uv.lock +126 -5
  23. package/plugin/vendor/reflexio/.env.example +62 -0
  24. package/plugin/vendor/reflexio/LICENSE +201 -0
  25. package/plugin/vendor/reflexio/README.md +338 -0
  26. package/plugin/vendor/reflexio/pyproject.toml +274 -0
  27. package/plugin/vendor/reflexio/reflexio/README.md +184 -0
  28. package/plugin/vendor/reflexio/reflexio/__init__.py +166 -0
  29. package/plugin/vendor/reflexio/reflexio/benchmarks/__init__.py +1 -0
  30. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/README.md +109 -0
  31. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/__init__.py +1 -0
  32. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/backends.py +175 -0
  33. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/bench.py +642 -0
  34. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/embed_cache.py +330 -0
  35. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/report.py +317 -0
  36. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/report.md +43 -0
  37. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/results.json +4478 -0
  38. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/scenarios.py +134 -0
  39. package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/seed.py +255 -0
  40. package/plugin/vendor/reflexio/reflexio/cli/README.md +287 -0
  41. package/plugin/vendor/reflexio/reflexio/cli/__init__.py +0 -0
  42. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +56 -0
  43. package/plugin/vendor/reflexio/reflexio/cli/_client.py +86 -0
  44. package/plugin/vendor/reflexio/reflexio/cli/app.py +127 -0
  45. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +265 -0
  46. package/plugin/vendor/reflexio/reflexio/cli/codex_auth.py +503 -0
  47. package/plugin/vendor/reflexio/reflexio/cli/commands/__init__.py +0 -0
  48. package/plugin/vendor/reflexio/reflexio/cli/commands/admin_cmd.py +65 -0
  49. package/plugin/vendor/reflexio/reflexio/cli/commands/agent_playbooks.py +503 -0
  50. package/plugin/vendor/reflexio/reflexio/cli/commands/api.py +114 -0
  51. package/plugin/vendor/reflexio/reflexio/cli/commands/auth.py +109 -0
  52. package/plugin/vendor/reflexio/reflexio/cli/commands/config_cmd.py +511 -0
  53. package/plugin/vendor/reflexio/reflexio/cli/commands/doctor.py +127 -0
  54. package/plugin/vendor/reflexio/reflexio/cli/commands/embeddings.py +53 -0
  55. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +478 -0
  56. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +303 -0
  57. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +289 -0
  58. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +964 -0
  59. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +285 -0
  60. package/plugin/vendor/reflexio/reflexio/cli/commands/status_cmd.py +143 -0
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/user_playbooks.py +373 -0
  62. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +284 -0
  63. package/plugin/vendor/reflexio/reflexio/cli/errors.py +217 -0
  64. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +247 -0
  65. package/plugin/vendor/reflexio/reflexio/cli/output.py +867 -0
  66. package/plugin/vendor/reflexio/reflexio/cli/paths.py +41 -0
  67. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +391 -0
  68. package/plugin/vendor/reflexio/reflexio/cli/state.py +204 -0
  69. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +96 -0
  70. package/plugin/vendor/reflexio/reflexio/cli/utils.py +329 -0
  71. package/plugin/vendor/reflexio/reflexio/client/__init__.py +3 -0
  72. package/plugin/vendor/reflexio/reflexio/client/cache.py +150 -0
  73. package/plugin/vendor/reflexio/reflexio/client/client.py +2613 -0
  74. package/plugin/vendor/reflexio/reflexio/defaults.py +23 -0
  75. package/plugin/vendor/reflexio/reflexio/integrations/__init__.py +0 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/.clawhubignore +7 -0
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +274 -0
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +517 -0
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +473 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +2156 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +18 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +241 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +140 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +130 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +113 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +52 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +103 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +156 -0
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +134 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +41 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +17 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +24 -0
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +48 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +278 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +164 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +36 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +35 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +27 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +31 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +42 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +49 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +91 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +50 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tsconfig.json +16 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/types/openclaw.d.ts +230 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +13 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/README.md +120 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/TESTING.md +168 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package-lock.json +1657 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package.json +16 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/HEARTBEAT.md +6 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/README.md +84 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/SKILL.md +194 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/_meta.json +6 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/agents/reflexio-extractor.md +45 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/handler.ts +214 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/setup.ts +55 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/index.ts +327 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/consolidate.ts +233 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/dedup.ts +80 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/io.ts +155 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/openclaw-cli.ts +67 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/search.ts +33 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-playbook.ts +76 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-profile.ts +79 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/openclaw.plugin.json +46 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/package.json +18 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/README.md +36 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/full_consolidation.md +56 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/playbook_extraction.md +217 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/profile_extraction.md +132 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-consolidate/SKILL.md +33 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-embedded/SKILL.md +194 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/HOOK.md +18 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/architecture.md +49 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/comparison.md +31 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/future-work.md +47 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/porting-notes.md +52 -0
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/install.sh +52 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/uninstall.sh +36 -0
  141. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/consolidate.test.ts +135 -0
  142. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/dedup.test.ts +104 -0
  143. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/io.test.ts +175 -0
  144. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/search.test.ts +66 -0
  145. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/smoke-test.ts +140 -0
  146. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-playbook.test.ts +93 -0
  147. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-profile.test.ts +174 -0
  148. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tsconfig.json +16 -0
  149. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/types/openclaw.d.ts +230 -0
  150. package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/vitest.config.ts +7 -0
  151. package/plugin/vendor/reflexio/reflexio/lib/__init__.py +23 -0
  152. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +310 -0
  153. package/plugin/vendor/reflexio/reflexio/lib/_base.py +225 -0
  154. package/plugin/vendor/reflexio/reflexio/lib/_config.py +83 -0
  155. package/plugin/vendor/reflexio/reflexio/lib/_dashboard.py +266 -0
  156. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +176 -0
  157. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +334 -0
  158. package/plugin/vendor/reflexio/reflexio/lib/_operations.py +153 -0
  159. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +545 -0
  160. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +52 -0
  161. package/plugin/vendor/reflexio/reflexio/lib/_search.py +167 -0
  162. package/plugin/vendor/reflexio/reflexio/lib/_storage_labels.py +103 -0
  163. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +288 -0
  164. package/plugin/vendor/reflexio/reflexio/lib/reflexio_lib.py +27 -0
  165. package/plugin/vendor/reflexio/reflexio/models/__init__.py +0 -0
  166. package/plugin/vendor/reflexio/reflexio/models/api_schema/__init__.py +0 -0
  167. package/plugin/vendor/reflexio/reflexio/models/api_schema/braintrust_schema.py +141 -0
  168. package/plugin/vendor/reflexio/reflexio/models/api_schema/common.py +41 -0
  169. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +3 -0
  170. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +1112 -0
  171. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +63 -0
  172. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +487 -0
  173. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +28 -0
  174. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +83 -0
  175. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +768 -0
  176. package/plugin/vendor/reflexio/reflexio/models/api_schema/service_schemas.py +9 -0
  177. package/plugin/vendor/reflexio/reflexio/models/api_schema/stall_state_schema.py +32 -0
  178. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/__init__.py +3 -0
  179. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +177 -0
  180. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +129 -0
  181. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/enums.py +25 -0
  182. package/plugin/vendor/reflexio/reflexio/models/api_schema/validators.py +333 -0
  183. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +908 -0
  184. package/plugin/vendor/reflexio/reflexio/models/py.typed +0 -0
  185. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +90 -0
  186. package/plugin/vendor/reflexio/reflexio/server/README.md +622 -0
  187. package/plugin/vendor/reflexio/reflexio/server/__init__.py +210 -0
  188. package/plugin/vendor/reflexio/reflexio/server/__main__.py +132 -0
  189. package/plugin/vendor/reflexio/reflexio/server/_auth.py +25 -0
  190. package/plugin/vendor/reflexio/reflexio/server/api.py +2868 -0
  191. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +34 -0
  192. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/account_api.py +143 -0
  193. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/health_api.py +91 -0
  194. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/pending_tool_call_api.py +572 -0
  195. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +66 -0
  196. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +562 -0
  197. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +50 -0
  198. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/stall_state_api.py +100 -0
  199. package/plugin/vendor/reflexio/reflexio/server/cache/__init__.py +15 -0
  200. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +208 -0
  201. package/plugin/vendor/reflexio/reflexio/server/correlation.py +46 -0
  202. package/plugin/vendor/reflexio/reflexio/server/llm/__init__.py +30 -0
  203. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +359 -0
  204. package/plugin/vendor/reflexio/reflexio/server/llm/image_utils.py +55 -0
  205. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1871 -0
  206. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +140 -0
  207. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +479 -0
  208. package/plugin/vendor/reflexio/reflexio/server/llm/providers/__init__.py +1 -0
  209. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1122 -0
  210. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_stream_parser.py +197 -0
  211. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +338 -0
  212. package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +213 -0
  213. package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +288 -0
  214. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/__init__.py +6 -0
  215. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +187 -0
  216. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/llm_reranker.py +148 -0
  217. package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +716 -0
  218. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +179 -0
  219. package/plugin/vendor/reflexio/reflexio/server/prompt/__init__.py +0 -0
  220. package/plugin/vendor/reflexio/reflexio/server/prompt/_dispatchers.py +54 -0
  221. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/README.md +121 -0
  222. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation/v1.0.0.prompt.md +58 -0
  223. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation_with_comparison/v1.0.0.prompt.md +76 -0
  224. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/answer_synthesis/v1.5.2.prompt.md +88 -0
  225. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/compress_session_for_query/v1.3.0.prompt.md +31 -0
  226. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +20 -0
  227. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.0.0.prompt.md +53 -0
  228. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.1.0.prompt.md +57 -0
  229. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.2.0.prompt.md +68 -0
  230. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.3.0.prompt.md +70 -0
  231. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.4.0.prompt.md +77 -0
  232. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.5.0.prompt.md +82 -0
  233. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +83 -0
  234. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.1.0.prompt.md +193 -0
  235. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +206 -0
  236. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0-deprecated.prompt.md +66 -0
  237. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0.prompt.md +43 -0
  238. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.1.0.prompt.md +46 -0
  239. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0-deprecated.prompt.md +64 -0
  240. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0.prompt.md +39 -0
  241. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.1.0.prompt.md +39 -0
  242. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.2.0.prompt.md +47 -0
  243. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.0.prompt.md +58 -0
  244. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.1.prompt.md +69 -0
  245. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +71 -0
  246. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.0.2.prompt.md +254 -0
  247. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.1.0.prompt.md +274 -0
  248. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.0.prompt.md +283 -0
  249. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.2.prompt.md +234 -0
  250. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +244 -0
  251. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v1.0.0.prompt.md +73 -0
  252. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v2.0.0.prompt.md +86 -0
  253. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.0.0.prompt.md +97 -0
  254. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.1.0.prompt.md +119 -0
  255. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.2.0.prompt.md +123 -0
  256. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +137 -0
  257. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.0.0.prompt.md +14 -0
  258. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.1.0.prompt.md +24 -0
  259. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +29 -0
  260. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.0.0.prompt.md +11 -0
  261. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.1.0.prompt.md +21 -0
  262. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.2.0.prompt.md +25 -0
  263. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.0.0.prompt.md +37 -0
  264. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.1.0.prompt.md +40 -0
  265. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.2.0.prompt.md +36 -0
  266. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v1.0.0.prompt.md +45 -0
  267. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v2.0.0.prompt.md +81 -0
  268. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v3.0.0.prompt.md +80 -0
  269. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate_expert/v1.0.0.prompt.md +34 -0
  270. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_deduplication/v1.0.0.prompt.md +116 -0
  271. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate/v1.0.0.prompt.md +33 -0
  272. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate_override/v1.0.0.prompt.md +16 -0
  273. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +140 -0
  274. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +160 -0
  275. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_main/v1.0.0.prompt.md +14 -0
  276. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +19 -0
  277. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/rerank_relevance/v1.1.0.prompt.md +44 -0
  278. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +43 -0
  279. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_content_evaluation/v1.0.0.prompt.md +33 -0
  280. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/feedback_extraction_main_v1.jsonl +10 -0
  281. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/profile_update_main_v1.jsonl +10 -0
  282. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +280 -0
  283. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_schema.py +11 -0
  284. package/plugin/vendor/reflexio/reflexio/server/services/README.md +58 -0
  285. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +131 -0
  286. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +60 -0
  287. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_service.py +228 -0
  288. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +87 -0
  289. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluator.py +372 -0
  290. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/delayed_group_evaluator.py +156 -0
  291. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/group_evaluation_runner.py +336 -0
  292. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +471 -0
  293. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +1668 -0
  294. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/__init__.py +0 -0
  295. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_cron.py +196 -0
  296. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_encryption.py +101 -0
  297. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/client.py +167 -0
  298. package/plugin/vendor/reflexio/reflexio/server/services/braintrust/service.py +281 -0
  299. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +179 -0
  300. package/plugin/vendor/reflexio/reflexio/server/services/configurator/config_storage.py +62 -0
  301. package/plugin/vendor/reflexio/reflexio/server/services/configurator/configurator.py +87 -0
  302. package/plugin/vendor/reflexio/reflexio/server/services/configurator/local_file_config_storage.py +187 -0
  303. package/plugin/vendor/reflexio/reflexio/server/services/configurator/test_config_storage.py +162 -0
  304. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +112 -0
  305. package/plugin/vendor/reflexio/reflexio/server/services/embedding_text.py +62 -0
  306. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/__init__.py +0 -0
  307. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/distribution.py +33 -0
  308. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +126 -0
  309. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +192 -0
  310. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/hero_state.py +75 -0
  311. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/rule_attribution.py +97 -0
  312. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +515 -0
  313. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/shadow_aggregation.py +90 -0
  314. package/plugin/vendor/reflexio/reflexio/server/services/extraction/__init__.py +0 -0
  315. package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +91 -0
  316. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +303 -0
  317. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +25 -0
  318. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +358 -0
  319. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +138 -0
  320. package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +217 -0
  321. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +535 -0
  322. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +171 -0
  323. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +779 -0
  324. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
  325. package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +94 -0
  326. package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
  327. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +702 -0
  328. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +835 -0
  329. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +89 -0
  330. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_aggregator.py +1388 -0
  331. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_consolidator.py +1045 -0
  332. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_extractor.py +436 -0
  333. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_generation_service.py +808 -0
  334. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_constants.py +28 -0
  335. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +362 -0
  336. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/__init__.py +24 -0
  337. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/assistant_webhook.py +246 -0
  338. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/gepa_adapter.py +291 -0
  339. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/judge.py +97 -0
  340. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +96 -0
  341. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +645 -0
  342. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/rollout.py +35 -0
  343. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scenario_resolver.py +93 -0
  344. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +174 -0
  345. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/__init__.py +26 -0
  346. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_document_expander.py +179 -0
  347. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +297 -0
  348. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_deduplicator.py +772 -0
  349. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_extractor.py +462 -0
  350. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service.py +737 -0
  351. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +290 -0
  352. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +17 -0
  353. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_extractor.py +247 -0
  354. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service.py +803 -0
  355. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +146 -0
  356. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/__init__.py +0 -0
  357. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +80 -0
  358. package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
  359. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +756 -0
  360. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/__init__.py +1 -0
  361. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +184 -0
  362. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +81 -0
  363. package/plugin/vendor/reflexio/reflexio/server/services/storage/constants.py +2 -0
  364. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +11 -0
  365. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +154 -0
  366. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +155 -0
  367. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +59 -0
  368. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +1298 -0
  369. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +1945 -0
  370. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +600 -0
  371. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +346 -0
  372. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1378 -0
  373. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +747 -0
  374. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +263 -0
  375. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +193 -0
  376. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +166 -0
  377. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +217 -0
  378. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +153 -0
  379. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +384 -0
  380. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +71 -0
  381. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +235 -0
  382. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +170 -0
  383. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +677 -0
  384. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +250 -0
  385. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +154 -0
  386. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +130 -0
  387. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +93 -0
  388. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_stall_state.py +76 -0
  389. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +572 -0
  390. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +77 -0
  391. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +116 -0
  392. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_manager.py +263 -0
  393. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +13 -0
  394. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/llm_model_setting.json +7 -0
  395. package/plugin/vendor/reflexio/reflexio/server/tracing.py +158 -0
  396. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +113 -0
  397. package/plugin/vendor/reflexio/reflexio/server/uvicorn_logging.py +76 -0
  398. package/plugin/vendor/reflexio/reflexio/test_support/__init__.py +1 -0
  399. package/plugin/vendor/reflexio/reflexio/test_support/llm_fixtures.py +62 -0
  400. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +242 -0
  401. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +129 -0
  402. 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
@@ -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}")