cmpnd 0.62.0__tar.gz → 0.63.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (395) hide show
  1. {cmpnd-0.62.0 → cmpnd-0.63.0}/PKG-INFO +1 -1
  2. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/org.py +71 -4
  3. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/top.py +2 -2
  4. {cmpnd-0.62.0 → cmpnd-0.63.0}/docs/cli.md +8 -3
  5. {cmpnd-0.62.0 → cmpnd-0.63.0}/pyproject.toml +1 -1
  6. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_org.py +160 -4
  7. {cmpnd-0.62.0 → cmpnd-0.63.0}/uv.lock +1 -1
  8. {cmpnd-0.62.0 → cmpnd-0.63.0}/.gitignore +0 -0
  9. {cmpnd-0.62.0 → cmpnd-0.63.0}/AGENTS.md +0 -0
  10. {cmpnd-0.62.0 → cmpnd-0.63.0}/CLAUDE.md +0 -0
  11. {cmpnd-0.62.0 → cmpnd-0.63.0}/README.md +0 -0
  12. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/__init__.py +0 -0
  13. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/_assessments.py +0 -0
  14. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/_authored_import.py +0 -0
  15. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/_deployments.py +0 -0
  16. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/_exporter_pool.py +0 -0
  17. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/_gepa_patch.py +0 -0
  18. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/_http.py +0 -0
  19. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/_program_patch.py +0 -0
  20. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/_rlm_patch.py +0 -0
  21. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/_runtime_tools.py +0 -0
  22. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/_timespec.py +0 -0
  23. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/_traces.py +0 -0
  24. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/_valsets.py +0 -0
  25. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/authored.py +0 -0
  26. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/callback.py +0 -0
  27. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/__init__.py +0 -0
  28. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/api_client.py +0 -0
  29. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/__init__.py +0 -0
  30. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/admin.py +0 -0
  31. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/assessments.py +0 -0
  32. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/auth.py +0 -0
  33. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/datasets.py +0 -0
  34. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/deployments.py +0 -0
  35. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/evals.py +0 -0
  36. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/login.py +0 -0
  37. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/optimizations.py +0 -0
  38. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/programs.py +0 -0
  39. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
  40. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/signatures.py +0 -0
  41. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/tool_servers.py +0 -0
  42. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/traces.py +0 -0
  43. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/commands/valsets.py +0 -0
  44. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/credentials.py +0 -0
  45. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/deprecation.py +0 -0
  46. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/pagination.py +0 -0
  47. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/shell.py +0 -0
  48. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/trace_render.py +0 -0
  49. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/tree_help.py +0 -0
  50. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/cli/verbs.py +0 -0
  51. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/client.py +0 -0
  52. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/configuration.py +0 -0
  53. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/context.py +0 -0
  54. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/credentials.py +0 -0
  55. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/dataset_sync.py +0 -0
  56. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/datasets.py +0 -0
  57. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/decorators.py +0 -0
  58. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/deployment.py +0 -0
  59. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/deployment_metadata.py +0 -0
  60. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/deployment_runs.py +0 -0
  61. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/eval_handler.py +0 -0
  62. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/execution.py +0 -0
  63. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/exporter.py +0 -0
  64. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/exporter_http.py +0 -0
  65. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/helpers.py +0 -0
  66. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/identity.py +0 -0
  67. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/imaging.py +0 -0
  68. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/ir/__init__.py +0 -0
  69. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/ir/decode.py +0 -0
  70. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/ir/encode.py +0 -0
  71. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/ir/hash.py +0 -0
  72. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/ir/program.py +0 -0
  73. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/ir/reflection.py +0 -0
  74. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/ir/types.py +0 -0
  75. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/ir/xxh64.py +0 -0
  76. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/lm_configuration.py +0 -0
  77. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/mcp_servers.py +0 -0
  78. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/models.py +0 -0
  79. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/named_programs.py +0 -0
  80. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/optimization.py +0 -0
  81. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/optimizers/__init__.py +0 -0
  82. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/optimizers/gepa.py +0 -0
  83. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/optimizers/gepa_callback.py +0 -0
  84. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/optimizers/resume.py +0 -0
  85. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/optimizers/tracker.py +0 -0
  86. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/packaging.py +0 -0
  87. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/programs.py +0 -0
  88. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/references.py +0 -0
  89. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/retry.py +0 -0
  90. {cmpnd-0.62.0 → cmpnd-0.63.0}/cmpnd/traces.py +0 -0
  91. {cmpnd-0.62.0 → cmpnd-0.63.0}/docs/2026-04-02-code-review.md +0 -0
  92. {cmpnd-0.62.0 → cmpnd-0.63.0}/docs/client.md +0 -0
  93. {cmpnd-0.62.0 → cmpnd-0.63.0}/docs/custom-proposer.md +0 -0
  94. {cmpnd-0.62.0 → cmpnd-0.63.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
  95. {cmpnd-0.62.0 → cmpnd-0.63.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
  96. {cmpnd-0.62.0 → cmpnd-0.63.0}/docs/reading-traces.md +0 -0
  97. {cmpnd-0.62.0 → cmpnd-0.63.0}/docs/sdk-instrumentation.md +0 -0
  98. {cmpnd-0.62.0 → cmpnd-0.63.0}/examples/generalizing_proposer.py +0 -0
  99. {cmpnd-0.62.0 → cmpnd-0.63.0}/examples/local_ollama.py +0 -0
  100. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/LINEAGE.md +0 -0
  101. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/README.md +0 -0
  102. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/__init__.py +0 -0
  103. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/bodies.py +0 -0
  104. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
  105. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
  106. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/program_names.py +0 -0
  107. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/protocols.py +0 -0
  108. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/py.typed +0 -0
  109. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/request_log.py +0 -0
  110. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/retry.py +0 -0
  111. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
  112. {cmpnd-0.62.0 → cmpnd-0.63.0}/ingest/pyproject.toml +0 -0
  113. {cmpnd-0.62.0 → cmpnd-0.63.0}/scripts/audit-sdist.sh +0 -0
  114. {cmpnd-0.62.0 → cmpnd-0.63.0}/scripts/capture_review_run.py +0 -0
  115. {cmpnd-0.62.0 → cmpnd-0.63.0}/scripts/check-pip-install.sh +0 -0
  116. {cmpnd-0.62.0 → cmpnd-0.63.0}/scripts/check_release_artifacts.py +0 -0
  117. {cmpnd-0.62.0 → cmpnd-0.63.0}/scripts/fixtures/review_run.json.gz +0 -0
  118. {cmpnd-0.62.0 → cmpnd-0.63.0}/scripts/gepa_resume_bug.py +0 -0
  119. {cmpnd-0.62.0 → cmpnd-0.63.0}/scripts/install-test-env.sh +0 -0
  120. {cmpnd-0.62.0 → cmpnd-0.63.0}/scripts/release-stress.sh +0 -0
  121. {cmpnd-0.62.0 → cmpnd-0.63.0}/scripts/require_version_bump.py +0 -0
  122. {cmpnd-0.62.0 → cmpnd-0.63.0}/scripts/seed_review_run.py +0 -0
  123. {cmpnd-0.62.0 → cmpnd-0.63.0}/scripts/selfhost_compat.py +0 -0
  124. {cmpnd-0.62.0 → cmpnd-0.63.0}/scripts/test-in-linux.sh +0 -0
  125. {cmpnd-0.62.0 → cmpnd-0.63.0}/stubs/dspy/__init__.pyi +0 -0
  126. {cmpnd-0.62.0 → cmpnd-0.63.0}/stubs/dspy/primitives/__init__.pyi +0 -0
  127. {cmpnd-0.62.0 → cmpnd-0.63.0}/stubs/dspy/primitives/prediction.pyi +0 -0
  128. {cmpnd-0.62.0 → cmpnd-0.63.0}/stubs/dspy/utils/__init__.pyi +0 -0
  129. {cmpnd-0.62.0 → cmpnd-0.63.0}/stubs/dspy/utils/callback.pyi +0 -0
  130. {cmpnd-0.62.0 → cmpnd-0.63.0}/stubs/dspy/utils/dummies.pyi +0 -0
  131. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/__init__.py +0 -0
  132. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/_credential_route.py +0 -0
  133. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/_deployment_signature_helpers.py +0 -0
  134. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/_exporter_helpers.py +0 -0
  135. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/_fake_clock.py +0 -0
  136. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/_identity_helpers.py +0 -0
  137. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/_resolve_refs.py +0 -0
  138. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/_traced_run.py +0 -0
  139. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/conftest.py +0 -0
  140. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/__init__.py +0 -0
  141. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/committee_task.py +0 -0
  142. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/conftest.py +0 -0
  143. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
  144. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
  145. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/fake_lm.py +0 -0
  146. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/helpers.py +0 -0
  147. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/target_capabilities.py +0 -0
  148. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_assessments_cli.py +0 -0
  149. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_capture_prompts.py +0 -0
  150. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_composite_module.py +0 -0
  151. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
  152. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_deploy_execute.py +0 -0
  153. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_deploy_pandas.py +0 -0
  154. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_deploy_programs.py +0 -0
  155. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_edge_cases.py +0 -0
  156. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_example_hash.py +0 -0
  157. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_gepa_optimization.py +0 -0
  158. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_interpreter_spans.py +0 -0
  159. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_lm_accuracy.py +0 -0
  160. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_module_tracing.py +0 -0
  161. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_optimization_lineage.py +0 -0
  162. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
  163. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
  164. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
  165. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_optimize_failures.py +0 -0
  166. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_optimize_loop.py +0 -0
  167. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_optimize_review_committee.py +0 -0
  168. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
  169. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_parallel_export.py +0 -0
  170. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_project_attribution.py +0 -0
  171. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_react_tool_spans.py +0 -0
  172. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_save_load.py +0 -0
  173. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_trace_structure.py +0 -0
  174. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_traces_read.py +0 -0
  175. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_user_attribution.py +0 -0
  176. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/e2e/test_valsets_cli.py +0 -0
  177. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/01_predict_qa.json +0 -0
  178. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
  179. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
  180. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
  181. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
  182. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
  183. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
  184. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/08_opaque_module.json +0 -0
  185. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
  186. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/10_react.json +0 -0
  187. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/11_refine.json +0 -0
  188. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/12_retrieve.json +0 -0
  189. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/13_knn.json +0 -0
  190. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/14_parallel.json +0 -0
  191. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/identity_vectors/README.md +0 -0
  192. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/ingest_conformance.py +0 -0
  193. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/integration/__init__.py +0 -0
  194. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/integration/authored_optimize/judge.py +0 -0
  195. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
  196. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/integration/conftest.py +0 -0
  197. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
  198. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/integration/hosted_optimize_probe.py +0 -0
  199. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/integration/optimization_run_probe.py +0 -0
  200. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
  201. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/integration/test_logs_renders_traces.py +0 -0
  202. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/integration/test_optimization_run.py +0 -0
  203. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/integration/test_optimizing_progress.py +0 -0
  204. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/integration/test_unhappy_path.py +0 -0
  205. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/load/__init__.py +0 -0
  206. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/load/__main__.py +0 -0
  207. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/load/drain.py +0 -0
  208. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/load/ingest.py +0 -0
  209. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/load/latency_transport.py +0 -0
  210. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/load/rlm_shape.py +0 -0
  211. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/load/rlm_size_report.py +0 -0
  212. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/load/sweep.py +0 -0
  213. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/load/test_rlm_shape.py +0 -0
  214. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/load/test_rlm_size_report.py +0 -0
  215. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/__init__.py +0 -0
  216. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/conftest.py +0 -0
  217. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/seed_parity.py +0 -0
  218. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/test_auth.py +0 -0
  219. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/test_datasets.py +0 -0
  220. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/test_deployments.py +0 -0
  221. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/test_evals.py +0 -0
  222. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/test_health.py +0 -0
  223. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/test_modules.py +0 -0
  224. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/test_optimizations.py +0 -0
  225. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/test_programs.py +0 -0
  226. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/test_signatures.py +0 -0
  227. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/test_trace_streaming.py +0 -0
  228. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/parity/test_traces.py +0 -0
  229. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/span_reference_fixture.py +0 -0
  230. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/__init__.py +0 -0
  231. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/_provider.py +0 -0
  232. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/_readiness.py +0 -0
  233. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/_reliability.py +0 -0
  234. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/_result.py +0 -0
  235. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/mint_bedrock_token.py +0 -0
  236. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_bedrock_converse.py +0 -0
  237. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_deploy_programs.py +0 -0
  238. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_multi_client_stress.py +0 -0
  239. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_optimize_client_committee.py +0 -0
  240. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_optimize_loop.py +0 -0
  241. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_optimize_loop_wasm.py +0 -0
  242. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_optimize_stress.py +0 -0
  243. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_provider_roles.py +0 -0
  244. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_readiness.py +0 -0
  245. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_real_provider_twins.py +0 -0
  246. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_reliability.py +0 -0
  247. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_reliability_classify.py +0 -0
  248. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_rlm_wasm.py +0 -0
  249. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/stress/test_trace_search.py +0 -0
  250. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_adapter_reentrancy.py +0 -0
  251. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_assessments_api.py +0 -0
  252. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_authored.py +0 -0
  253. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_authored_binding_credentials.py +0 -0
  254. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_authored_bundle_collisions.py +0 -0
  255. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_authored_metric_contract.py +0 -0
  256. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_authored_named_models.py +0 -0
  257. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_authored_optimize.py +0 -0
  258. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_callback.py +0 -0
  259. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_candidate_lineage.py +0 -0
  260. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/__init__.py +0 -0
  261. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/conftest.py +0 -0
  262. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_admin.py +0 -0
  263. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_api_client.py +0 -0
  264. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_assessments.py +0 -0
  265. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_auth.py +0 -0
  266. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_credentials.py +0 -0
  267. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_datasets.py +0 -0
  268. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_deployment_runs.py +0 -0
  269. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_deployments.py +0 -0
  270. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_evals.py +0 -0
  271. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_login.py +0 -0
  272. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_main.py +0 -0
  273. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_optimizations.py +0 -0
  274. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_pagination.py +0 -0
  275. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_parity.py +0 -0
  276. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_programs.py +0 -0
  277. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_provider_credentials.py +0 -0
  278. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_schema_sample.py +0 -0
  279. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_shell.py +0 -0
  280. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_signatures.py +0 -0
  281. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_tool_servers.py +0 -0
  282. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_top.py +0 -0
  283. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_traces.py +0 -0
  284. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
  285. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_traces_search_wire.py +0 -0
  286. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_tree_help.py +0 -0
  287. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cli/test_valsets.py +0 -0
  288. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_client.py +0 -0
  289. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_client_floor.py +0 -0
  290. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_client_program_names.py +0 -0
  291. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_clustering/__init__.py +0 -0
  292. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_clustering/conftest.py +0 -0
  293. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_clustering/test_edge_cases.py +0 -0
  294. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_clustering/test_hash_consistency.py +0 -0
  295. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_clustering/test_type_conversion.py +0 -0
  296. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_cmp551_grouping_identity.py +0 -0
  297. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_configuration.py +0 -0
  298. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_context.py +0 -0
  299. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_context_propagation.py +0 -0
  300. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_credential_kwargs.py +0 -0
  301. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_credential_selection.py +0 -0
  302. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_credential_values.py +0 -0
  303. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_dataset_polling.py +0 -0
  304. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_dataset_sync.py +0 -0
  305. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_datasets.py +0 -0
  306. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_decision_client_spans.py +0 -0
  307. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_decorators.py +0 -0
  308. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_deploy.py +0 -0
  309. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_deploy_mcp_tool_references.py +0 -0
  310. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_deploy_program_id.py +0 -0
  311. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_deployment_metadata.py +0 -0
  312. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_deployment_runs.py +0 -0
  313. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_deployments_read.py +0 -0
  314. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_eval.py +0 -0
  315. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_eval_example_attribution.py +0 -0
  316. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_eval_start.py +0 -0
  317. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_eval_trace_grouping.py +0 -0
  318. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_exception_paths.py +0 -0
  319. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_execute.py +0 -0
  320. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_execute_run_identity.py +0 -0
  321. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_exporter.py +0 -0
  322. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_exporter_program_names.py +0 -0
  323. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_exporter_queue.py +0 -0
  324. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_exporter_real.py +0 -0
  325. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_fake_lm.py +0 -0
  326. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_gepa_callback.py +0 -0
  327. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_gepa_integration.py +0 -0
  328. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_gepa_output_normalization.py +0 -0
  329. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_gepa_resume.py +0 -0
  330. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_identity.py +0 -0
  331. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_imaging.py +0 -0
  332. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_import_weight.py +0 -0
  333. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_ingest_conformance.py +0 -0
  334. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_ingest_second_producer.py +0 -0
  335. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_integration.py +0 -0
  336. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_interpreter_callback.py +0 -0
  337. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_ir_encoder_properties.py +0 -0
  338. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_ir_golden_vectors.py +0 -0
  339. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_ir_roundtrip.py +0 -0
  340. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_lm15_native_calls.py +0 -0
  341. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_lm_configuration.py +0 -0
  342. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_lm_usage_shapes.py +0 -0
  343. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_mcp_tools.py +0 -0
  344. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_models.py +0 -0
  345. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_multi_client_stress_readiness.py +0 -0
  346. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_optimization_progress.py +0 -0
  347. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_optimization_start.py +0 -0
  348. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_optimize.py +0 -0
  349. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_optimize_budget.py +0 -0
  350. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_optimize_lift.py +0 -0
  351. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_optimize_lms.py +0 -0
  352. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_optimize_proposer.py +0 -0
  353. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_optimize_reflection_lm.py +0 -0
  354. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_optimize_terminal_failure.py +0 -0
  355. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_packaging.py +0 -0
  356. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_packaging_callable_source.py +0 -0
  357. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_packaging_mcp_tools.py +0 -0
  358. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_packaging_program_source.py +0 -0
  359. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_poll_until.py +0 -0
  360. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_program_id_deprecation.py +0 -0
  361. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_program_identity.py +0 -0
  362. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_program_lineage_persist.py +0 -0
  363. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_program_names_in_deploy.py +0 -0
  364. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_programs_create.py +0 -0
  365. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_project_payload.py +0 -0
  366. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_real_provider_twins_readiness.py +0 -0
  367. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_reflector_properties.py +0 -0
  368. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_reflector_smoke.py +0 -0
  369. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_release_stress_script.py +0 -0
  370. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_replaced_names.py +0 -0
  371. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_repro_reactv2_bugs.py +0 -0
  372. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_require_version_bump.py +0 -0
  373. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_retry.py +0 -0
  374. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_reversible_failures.py +0 -0
  375. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_rlm_patch.py +0 -0
  376. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_run_attribution_paths.py +0 -0
  377. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_runtime_tools.py +0 -0
  378. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_schemas.py +0 -0
  379. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_seal_lm_credential.py +0 -0
  380. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_seed_review_run.py +0 -0
  381. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_signature_inputs.py +0 -0
  382. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_span_references.py +0 -0
  383. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_span_sent_once.py +0 -0
  384. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_system_one.py +0 -0
  385. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_trace_context_concurrency.py +0 -0
  386. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_trace_fact_wire_contract.py +0 -0
  387. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_trace_facts.py +0 -0
  388. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_trace_render.py +0 -0
  389. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_traces_read.py +0 -0
  390. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_tracked_gepa.py +0 -0
  391. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_tracker_enrich.py +0 -0
  392. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_truncation.py +0 -0
  393. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_upload_auth_headers.py +0 -0
  394. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_valsets_api.py +0 -0
  395. {cmpnd-0.62.0 → cmpnd-0.63.0}/tests/test_xxh64.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cmpnd
3
- Version: 0.62.0
3
+ Version: 0.63.0
4
4
  Summary: DSPy observability and deployment SDK for cmpnd
5
5
  Project-URL: Homepage, https://cmpnd.ai
6
6
  Author-email: cmpnd <hello@cmpnd.ai>
@@ -29,17 +29,21 @@ adds one) and `switch` picks which stored key later commands use, the way the
29
29
  web app switches a session's active org. `list` reads
30
30
  /api/v1/auth/organizations and marks the orgs you hold a key for and the
31
31
  active one. `switch` needs no network call when the org's key is already
32
- stored. `cmpnd org leave` removes your own membership and drops that org's
33
- stored key.
32
+ stored. When none is stored for an org you are an approved member of, it
33
+ offers to mint one with the current key's scopes (`--mint` skips the
34
+ question), stores it and switches to it. `cmpnd org leave` removes your own
35
+ membership and drops that org's stored key.
34
36
 
35
37
  Always registered (no env gate); the server is the authority.
36
38
  """
37
39
 
38
40
  from __future__ import annotations
39
41
 
42
+ import socket
40
43
  import sys
41
44
  import urllib.parse
42
45
  from argparse import ArgumentParser, _SubParsersAction
46
+ from datetime import datetime, timezone
43
47
  from typing import Any
44
48
 
45
49
  from .. import api_client, credentials
@@ -91,6 +95,11 @@ def register(subparsers: _SubParsersAction[ArgumentParser]) -> None:
91
95
 
92
96
  switch = sub.add_parser("switch", help="Use the stored key for another of your organizations")
93
97
  switch.add_argument("org", help="Organization slug or id (from `org list`)")
98
+ switch.add_argument(
99
+ "--mint",
100
+ action="store_true",
101
+ help="When no key is stored for the org, mint one with the current key's scopes without asking",
102
+ )
94
103
  switch.set_defaults(func=handle_switch, _skip_resolve_config=True)
95
104
 
96
105
  leave = sub.add_parser("leave", help="Leave one of your organizations and drop its stored key")
@@ -214,12 +223,70 @@ def handle_switch(_unused_client: api_client.ApiClient | None, args: Any) -> Non
214
223
  api_key, _ = api_client.resolve_config(args)
215
224
  except api_client.CliError:
216
225
  raise _no_key_stored(f"'{target}'", endpoint, target) from None
217
- for row in _memberships(api_client.ApiClient(api_key, endpoint)):
226
+ client = api_client.ApiClient(api_key, endpoint)
227
+ for row in _memberships(client):
218
228
  if target in (row.get("id"), row.get("slug")):
219
- raise _no_key_stored(str(row.get("name") or target), endpoint, target)
229
+ _mint_and_switch(client, row, target, ask=not getattr(args, "mint", False))
230
+ return
220
231
  raise api_client.CliError(f"You are not a member of an organization '{target}' on {endpoint}.")
221
232
 
222
233
 
234
+ def _mint_and_switch(client: api_client.ApiClient, row: dict[str, Any], target: str, *, ask: bool) -> None:
235
+ """Mint a key for a member org that has none stored, store it and switch.
236
+
237
+ The request names no scopes, so serve grants the calling key's own
238
+ non-admin scopes and the new key is never broader than the one in use.
239
+ serve refuses with 400 or 403 when the calling key cannot mint, and the
240
+ browser login is then the way to get the key.
241
+ """
242
+ endpoint = client._endpoint
243
+ label = str(row.get("name") or row.get("slug") or target)
244
+ if row.get("approved") is False:
245
+ raise api_client.CliError(
246
+ f"Your membership in {label} is not approved yet, so no key can be minted for it. "
247
+ "An owner of the org approves it."
248
+ )
249
+ if ask:
250
+ if not sys.stdin.isatty():
251
+ raise api_client.CliError(
252
+ f"No key is stored for {label} on {endpoint}. Run {top.hint_org_switch(target, mint=True)} "
253
+ f"to mint one with the current key's scopes, or {top.hint_login(target)}."
254
+ )
255
+ try:
256
+ answer = input(f"No key is stored for {label}. Mint one with the current key's scopes? [Y/n] ")
257
+ except EOFError:
258
+ # Ctrl-D at the prompt declines.
259
+ print(file=sys.stderr)
260
+ answer = "n"
261
+ if answer.strip().lower() not in ("", "y", "yes"):
262
+ raise _no_key_stored(label, endpoint, target)
263
+
264
+ hostname = socket.gethostname() or "unknown"
265
+ name = f"CLI: {hostname} {datetime.now(timezone.utc).strftime('%Y-%m-%d')}"
266
+ try:
267
+ created = client.post("/api/v1/org/keys", json={"name": name, "org_id": row.get("id") or target})
268
+ except api_client.CliError as exc:
269
+ if exc.status_code in (400, 403):
270
+ raise api_client.CliError(
271
+ f"The current key could not mint a key for {label}: {exc}. Run {top.hint_login(target)} to store one."
272
+ ) from exc
273
+ raise
274
+ token = created.get("token") if isinstance(created, dict) else None
275
+ org_id = created.get("org_id") if isinstance(created, dict) else None
276
+ if not isinstance(token, str) or not token or not isinstance(org_id, str) or not org_id:
277
+ raise api_client.CliError(f"The response did not include a key for {label}. Nothing was stored.")
278
+ name_out = created.get("org_name")
279
+ slug_out = created.get("org_slug")
280
+ credentials.save(
281
+ endpoint,
282
+ token,
283
+ org_id=org_id,
284
+ org_name=name_out if isinstance(name_out, str) else None,
285
+ org_slug=slug_out if isinstance(slug_out, str) else None,
286
+ )
287
+ print(f"Minted a key for {name_out or slug_out or org_id} ({org_id}) and switched to it on {endpoint}")
288
+
289
+
223
290
  def _say_active_moved(endpoint: str, moved_to: str | None) -> None:
224
291
  if moved_to:
225
292
  k = {k.org_id: k for k in credentials.stored_keys(endpoint)}.get(moved_to)
@@ -180,8 +180,8 @@ def hint_login(org: str | None = None) -> str:
180
180
  return f"`{cmd}` in a terminal, then restart the shell" if _SHELL_MODE else f"`{cmd}`"
181
181
 
182
182
 
183
- def hint_org_switch(org: str) -> str:
184
- cmd = f"cmpnd org switch {org}"
183
+ def hint_org_switch(org: str, *, mint: bool = False) -> str:
184
+ cmd = f"cmpnd org switch {org}" + (" --mint" if mint else "")
185
185
  # Same reason as hint_login: the shell picks its key once, at startup.
186
186
  # Both are public, unlike the other hint helpers, because only org.py
187
187
  # calls them.
@@ -438,9 +438,14 @@ mode 0600. An active pointer names the org whose key commands use.
438
438
  is stored, and `active` marks the org of the key in use. When `--api-key` or
439
439
  `CMPND_API_KEY` is set, `active` marks that key's org.
440
440
  - `cmpnd org switch <slug-or-id>` makes another org's stored key active, with
441
- no network call. When no key is stored for that org, it says so and names
442
- the `cmpnd login --org` command to run. It refuses while `--api-key` or
443
- `CMPND_API_KEY` is set, because either one overrides the stored keys.
441
+ no network call. When no key is stored for an org where you are an approved
442
+ member, it asks whether to mint one with the current key's scopes, stores
443
+ it and switches to it. `--mint` mints without asking, and without a
444
+ terminal to ask in it mints only with `--mint`. The request names no
445
+ scopes, so serve grants the current key's non-admin scopes. When serve
446
+ refuses the mint, the error names the `cmpnd login --org` command to run.
447
+ `switch` refuses while `--api-key` or `CMPND_API_KEY` is set, because
448
+ either one overrides the stored keys.
444
449
  - `cmpnd org leave <slug-or-id>` removes your membership in that org and drops
445
450
  its stored key. If that key was active, the CLI switches to another stored
446
451
  key, preferring your new default org's, or clears the pointer, and says
@@ -5,7 +5,7 @@ name = "cmpnd"
5
5
  # it's new) — no git tags. Bump with `uv version --bump {minor,patch}` or by
6
6
  # hand. Runtime `cmpnd.__version__` reads installed metadata, so it tracks
7
7
  # this automatically. See docs/plans/2026-07-02-sdk-pypi-tag-free-publish.md.
8
- version = "0.62.0"
8
+ version = "0.63.0"
9
9
  description = "DSPy observability and deployment SDK for cmpnd"
10
10
  readme = "README.md"
11
11
  requires-python = ">=3.11"
@@ -6,6 +6,7 @@ import json
6
6
  from argparse import Namespace
7
7
  from unittest import mock
8
8
 
9
+ import httpx
9
10
  import pytest
10
11
 
11
12
  from cmpnd.cli import credentials
@@ -125,8 +126,8 @@ def _client_for(api_key: str):
125
126
  return client
126
127
 
127
128
 
128
- def _switch_args(target: str) -> Namespace:
129
- return Namespace(org=target, endpoint=EP, api_key=None)
129
+ def _switch_args(target: str, *, mint: bool = False) -> Namespace:
130
+ return Namespace(org=target, endpoint=EP, api_key=None, mint=mint)
130
131
 
131
132
 
132
133
  def _write_legacy(api_key: str) -> None:
@@ -396,14 +397,20 @@ class TestOrgSwitch:
396
397
  org.handle_switch(None, _switch_args("acme"))
397
398
  assert credentials.load(EP) == "cmpnd_acme"
398
399
 
399
- def test_member_without_a_stored_key_is_told_how_to_log_in(self, config_dir):
400
+ def test_member_without_a_stored_key_is_told_how_to_get_one(self, config_dir, monkeypatch):
401
+ # With no terminal to ask in and no --mint, nothing is minted: the
402
+ # error names both ways to get the key.
403
+ monkeypatch.setattr(org.sys.stdin, "isatty", lambda: False)
400
404
  credentials.save(EP, "cmpnd_own", org_id="o_own", org_slug="own")
401
405
  client = _client_for("cmpnd_own")
402
406
  client.get.return_value = MEMBERSHIPS
403
407
  with mock.patch.object(org.api_client, "ApiClient", return_value=client):
404
- with pytest.raises(CliError, match="cmpnd login --org beta"):
408
+ with pytest.raises(CliError) as err:
405
409
  org.handle_switch(None, _switch_args("beta"))
410
+ assert "cmpnd login --org beta" in str(err.value)
411
+ assert "cmpnd org switch beta --mint" in str(err.value)
406
412
  client.get.assert_called_once_with("/api/v1/auth/organizations")
413
+ client.post.assert_not_called()
407
414
  assert credentials.load(EP) == "cmpnd_own"
408
415
 
409
416
  def test_non_member_is_refused(self, config_dir):
@@ -433,3 +440,152 @@ class TestOrgSwitch:
433
440
  legacy.get.assert_called_once_with("/api/v1/auth/me")
434
441
  assert credentials.load(EP) == "cmpnd_legacy"
435
442
  assert credentials.active_org(EP) == "o_own"
443
+
444
+
445
+ MINTED_BETA = {
446
+ "id": "k_beta",
447
+ "name": "CLI: box 2026-10-29",
448
+ "token": "cmpnd_beta",
449
+ "scopes": ["traces:read"],
450
+ "org_id": "o_beta",
451
+ "org_name": "Beta",
452
+ "org_slug": "beta",
453
+ }
454
+
455
+
456
+ class TestOrgSwitchMintsAMissingKey:
457
+ """`org switch` to a member org with no stored key offers to mint one."""
458
+
459
+ @pytest.fixture
460
+ def own_key(self, config_dir):
461
+ credentials.save(EP, "cmpnd_own", org_id="o_own", org_slug="own")
462
+ client = _client_for("cmpnd_own")
463
+ client.get.return_value = MEMBERSHIPS
464
+ with mock.patch.object(org.api_client, "ApiClient", autospec=True) as ctor:
465
+ ctor.return_value = client
466
+ yield client, ctor
467
+
468
+ def test_mint_flag_mints_and_switches(self, own_key, capsys):
469
+ client, ctor = own_key
470
+ client.post.return_value = MINTED_BETA
471
+
472
+ org.handle_switch(None, _switch_args("beta", mint=True))
473
+
474
+ ctor.assert_called_once_with("cmpnd_own", EP)
475
+ client.post.assert_called_once()
476
+ assert credentials.load(EP) == "cmpnd_beta"
477
+ assert credentials.active_org(EP) == "o_beta"
478
+ # The key it switched from stays stored.
479
+ assert {k.org_id for k in credentials.stored_keys(EP)} == {"o_own", "o_beta"}
480
+ assert "Beta" in capsys.readouterr().out
481
+
482
+ def test_terminal_prompt_yes_mints(self, own_key, monkeypatch):
483
+ client, _ = own_key
484
+ client.post.return_value = MINTED_BETA
485
+ monkeypatch.setattr(org.sys.stdin, "isatty", lambda: True)
486
+ monkeypatch.setattr("builtins.input", lambda _prompt="": "")
487
+
488
+ org.handle_switch(None, _switch_args("beta"))
489
+
490
+ assert credentials.load(EP) == "cmpnd_beta"
491
+
492
+ def test_ctrl_d_at_the_prompt_declines(self, own_key, monkeypatch):
493
+ client, _ = own_key
494
+ monkeypatch.setattr(org.sys.stdin, "isatty", lambda: True)
495
+
496
+ def eof(_prompt=""):
497
+ raise EOFError
498
+
499
+ monkeypatch.setattr("builtins.input", eof)
500
+
501
+ with pytest.raises(CliError, match="cmpnd login --org beta"):
502
+ org.handle_switch(None, _switch_args("beta"))
503
+
504
+ client.post.assert_not_called()
505
+ assert credentials.load(EP) == "cmpnd_own"
506
+
507
+ def test_terminal_prompt_no_mints_nothing(self, own_key, monkeypatch):
508
+ client, _ = own_key
509
+ monkeypatch.setattr(org.sys.stdin, "isatty", lambda: True)
510
+ monkeypatch.setattr("builtins.input", lambda _prompt="": "n")
511
+
512
+ with pytest.raises(CliError, match="cmpnd login --org beta"):
513
+ org.handle_switch(None, _switch_args("beta"))
514
+
515
+ client.post.assert_not_called()
516
+ assert credentials.load(EP) == "cmpnd_own"
517
+
518
+ @pytest.mark.parametrize("status", [400, 403])
519
+ def test_a_refused_mint_falls_back_to_the_login_hint(self, own_key, status):
520
+ # A key without the scopes to mint, or a serve that refuses the
521
+ # request, leaves the browser login as the way to get the key.
522
+ client, _ = own_key
523
+ client.post.side_effect = CliError("the calling key holds no non-admin scopes to grant", status_code=status)
524
+
525
+ with pytest.raises(CliError) as err:
526
+ org.handle_switch(None, _switch_args("beta", mint=True))
527
+
528
+ assert "no non-admin scopes" in str(err.value)
529
+ assert "cmpnd login --org beta" in str(err.value)
530
+ assert credentials.load(EP) == "cmpnd_own"
531
+ assert {k.org_id for k in credentials.stored_keys(EP)} == {"o_own"}
532
+
533
+ def test_a_transport_failure_is_not_turned_into_a_login_hint(self, own_key):
534
+ client, _ = own_key
535
+ client.post.side_effect = CliError("could not connect")
536
+
537
+ with pytest.raises(CliError) as err:
538
+ org.handle_switch(None, _switch_args("beta", mint=True))
539
+
540
+ assert str(err.value) == "could not connect"
541
+ assert credentials.load(EP) == "cmpnd_own"
542
+
543
+ def test_pending_membership_is_not_offered_a_key(self, own_key):
544
+ client, _ = own_key
545
+ pending = {
546
+ "organizations": [
547
+ *MEMBERSHIPS["organizations"][:2],
548
+ {**MEMBERSHIPS["organizations"][2], "approved": False},
549
+ ],
550
+ "total": 3,
551
+ }
552
+ client.get.return_value = pending
553
+
554
+ with pytest.raises(CliError, match="not approved"):
555
+ org.handle_switch(None, _switch_args("beta", mint=True))
556
+
557
+ client.post.assert_not_called()
558
+ assert credentials.load(EP) == "cmpnd_own"
559
+
560
+
561
+ class TestOrgSwitchMintOverTheWire:
562
+ """The mint request as a real ``httpx.Client`` sends it."""
563
+
564
+ def test_mint_request_names_no_scopes_and_authenticates_with_the_current_key(self, config_dir):
565
+ credentials.save(EP, "cmpnd_own", org_id="o_own", org_slug="own")
566
+ sent: list[httpx.Request] = []
567
+
568
+ def handle(request: httpx.Request) -> httpx.Response:
569
+ sent.append(request)
570
+ if request.method == "GET" and request.url.path == "/api/v1/auth/organizations":
571
+ return httpx.Response(200, json=MEMBERSHIPS)
572
+ if request.method == "POST" and request.url.path == "/api/v1/org/keys":
573
+ return httpx.Response(201, json=MINTED_BETA)
574
+ return httpx.Response(404, json={"error": "not found"})
575
+
576
+ real = httpx.Client
577
+ with mock.patch("httpx.Client", lambda **kwargs: real(**kwargs, transport=httpx.MockTransport(handle))):
578
+ org.handle_switch(None, _switch_args("beta", mint=True))
579
+
580
+ posts = [r for r in sent if r.method == "POST"]
581
+ assert len(posts) == 1
582
+ assert posts[0].url == httpx.URL(f"{EP}/api/v1/org/keys")
583
+ assert posts[0].headers["X-API-Key"] == "cmpnd_own"
584
+ body = json.loads(posts[0].content)
585
+ # No scopes in the body: serve grants the calling key's own non-admin
586
+ # scopes, so the minted key is never broader than the one in use.
587
+ assert set(body) == {"name", "org_id"}
588
+ assert body["org_id"] == "o_beta"
589
+ assert body["name"].startswith("CLI: ")
590
+ assert credentials.load(EP) == "cmpnd_beta"
591
+ assert credentials.active_org(EP) == "o_beta"
@@ -505,7 +505,7 @@ wheels = [
505
505
 
506
506
  [[package]]
507
507
  name = "cmpnd"
508
- version = "0.62.0"
508
+ version = "0.63.0"
509
509
  source = { editable = "." }
510
510
  default-groups = ["dspy-3-4-1"]
511
511
  dependencies = [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes