cmpnd 0.78.0__tar.gz → 0.80.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.
- {cmpnd-0.78.0 → cmpnd-0.80.0}/PKG-INFO +1 -1
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/admin.py +67 -3
- {cmpnd-0.78.0 → cmpnd-0.80.0}/docs/cli.md +17 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/pyproject.toml +1 -1
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_admin.py +118 -5
- {cmpnd-0.78.0 → cmpnd-0.80.0}/uv.lock +1 -1
- {cmpnd-0.78.0 → cmpnd-0.80.0}/.gitignore +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/AGENTS.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/CLAUDE.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/README.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/_traces.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/callback.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/cli/views.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/client.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/configuration.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/context.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/credentials.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/deployment.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/models.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/optimization.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/program_naming.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/references.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/docs/client.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/docs/reading-traces.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/docs/sdk-instrumentation.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/README.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/cmpnd_ingest/program_names.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/install-test-env.sh +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/release-gate.sh +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/release-real-models.sh +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/selfhost_compat.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/_credential_route.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/conftest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/continuation_task.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_optimize_continuation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/load/drain.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/_provider.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/_readiness.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/_reliability.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/_result.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/mint_bedrock_token.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_bedrock_converse.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_deploy_programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_multi_client_stress.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_optimize_client_committee.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_optimize_continuation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_optimize_loop.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_optimize_stress.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_provider_roles.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_readiness.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_real_provider_twins.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_reliability.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_reliability_classify.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_rlm_wasm.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/real_models/test_trace_search.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_authored.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_authored_binding_credentials.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_callback.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_client.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_client_floor.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_client_program_names.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_context.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_credential_kwargs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_credential_selection.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_credential_values.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_deploy.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_eval.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_eval_program_id.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_execute.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_exporter.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_exporter_program_names.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_identity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_integration.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_models.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_no_signature_hash_on_the_wire.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_optimization_phase_callback.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_optimization_program_names.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_optimize.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_program_id_deprecation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_program_identity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_program_names_in_deploy.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_program_naming.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_programs_create.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_release_gate_script.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_release_real_models_script.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_replaced_names.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_retry.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_schemas.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_seal_lm_credential.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_span_references.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_system_one.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_upload_auth_headers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.80.0}/tests/test_xxh64.py +0 -0
|
@@ -9,7 +9,8 @@ queue. `cmpnd admin user …` is the operator's user-lifecycle surface (list,
|
|
|
9
9
|
inspect, create, approve/reject the pending-signup queue, promote/demote admin,
|
|
10
10
|
disable/enable, reset a password, and manage a user's API-key scopes). `cmpnd
|
|
11
11
|
admin org plan …` and `cmpnd admin org overrides …` move an org between plans
|
|
12
|
-
and override one of its limits, `cmpnd admin
|
|
12
|
+
and override one of its limits, `cmpnd admin org flags …` pins a feature flag
|
|
13
|
+
for one org, `cmpnd admin plans …` edits each plan's
|
|
13
14
|
default limits and feature flags, and `cmpnd admin signup …` sets who may sign up
|
|
14
15
|
and the plan a new account's org lands on. `cmpnd admin feature-flags …` reads and
|
|
15
16
|
toggles the server's runtime flags. These hit
|
|
@@ -153,6 +154,25 @@ def _register_org(sub: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
153
154
|
override_delete.add_argument("name", help="Limit name")
|
|
154
155
|
override_delete.set_defaults(func=handle_org_overrides_delete)
|
|
155
156
|
|
|
157
|
+
flags = osub.add_parser("flags", help="An org's own feature flags")
|
|
158
|
+
fsub = flags.add_subparsers(dest="org_flags_action")
|
|
159
|
+
flags_list = fsub.add_parser(
|
|
160
|
+
"list", help="List every flag with the org's, its plan's and the global value, and what serve resolves"
|
|
161
|
+
)
|
|
162
|
+
_org_id_arg(flags_list)
|
|
163
|
+
flags_list.set_defaults(func=handle_org_flags_list)
|
|
164
|
+
flag_update = fsub.add_parser("update", help="Pin a feature flag on (or, with --disabled, off) for the org")
|
|
165
|
+
_org_id_arg(flag_update)
|
|
166
|
+
flag_update.add_argument("name", help="Flag name; the flag must already exist")
|
|
167
|
+
flag_update.add_argument("--disabled", action="store_true", help="Pin the flag off (default is on)")
|
|
168
|
+
flag_update.set_defaults(func=handle_org_flags_update)
|
|
169
|
+
flag_delete = fsub.add_parser(
|
|
170
|
+
"delete", help="Delete the org's row for a flag so it follows its plan and the global value"
|
|
171
|
+
)
|
|
172
|
+
_org_id_arg(flag_delete)
|
|
173
|
+
flag_delete.add_argument("name", help="Flag name")
|
|
174
|
+
flag_delete.set_defaults(func=handle_org_flags_delete)
|
|
175
|
+
|
|
156
176
|
|
|
157
177
|
def _register_user(sub: _SubParsersAction[ArgumentParser]) -> None:
|
|
158
178
|
user = sub.add_parser("user", help="User lifecycle management")
|
|
@@ -182,8 +202,14 @@ def _register_user(sub: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
182
202
|
create.add_argument("--admin", dest="admin", action="store_true", help="Grant platform-admin")
|
|
183
203
|
create.set_defaults(func=handle_user_create)
|
|
184
204
|
|
|
185
|
-
approve = usub.add_parser("approve", help="Approve a pending
|
|
205
|
+
approve = usub.add_parser("approve", help="Approve a pending membership")
|
|
186
206
|
approve.add_argument("id", help="User id")
|
|
207
|
+
approve.add_argument(
|
|
208
|
+
"--org-id",
|
|
209
|
+
dest="org_id",
|
|
210
|
+
default="",
|
|
211
|
+
help="Org whose membership to approve (default: the user's active org)",
|
|
212
|
+
)
|
|
187
213
|
approve.set_defaults(func=handle_user_approve)
|
|
188
214
|
|
|
189
215
|
promote = usub.add_parser("promote", help="Grant platform-admin")
|
|
@@ -382,6 +408,21 @@ def handle_org_overrides_delete(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
382
408
|
return client.delete(f"/api/v1/admin/organizations/{args.org_id}/limits/{args.name}")
|
|
383
409
|
|
|
384
410
|
|
|
411
|
+
def handle_org_flags_list(client: api_client.ApiClient, args: Any) -> Any:
|
|
412
|
+
return client.get(f"/api/v1/admin/organizations/{args.org_id}/feature-flags")
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def handle_org_flags_update(client: api_client.ApiClient, args: Any) -> Any:
|
|
416
|
+
return client.put(
|
|
417
|
+
f"/api/v1/admin/organizations/{args.org_id}/feature-flags/{args.name}",
|
|
418
|
+
json={"enabled": not args.disabled},
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def handle_org_flags_delete(client: api_client.ApiClient, args: Any) -> Any:
|
|
423
|
+
return client.delete(f"/api/v1/admin/organizations/{args.org_id}/feature-flags/{args.name}")
|
|
424
|
+
|
|
425
|
+
|
|
385
426
|
# --- plan handlers --------------------------------------------------------
|
|
386
427
|
|
|
387
428
|
|
|
@@ -457,7 +498,30 @@ def handle_user_create(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
457
498
|
|
|
458
499
|
|
|
459
500
|
def handle_user_approve(client: api_client.ApiClient, args: Any) -> Any:
|
|
460
|
-
|
|
501
|
+
user_path = f"/api/v1/admin/users/{args.id}"
|
|
502
|
+
org_id = args.org_id or client.get(user_path)["org_id"]
|
|
503
|
+
try:
|
|
504
|
+
return client.post(f"{user_path}/approve", json={"org_id": org_id})
|
|
505
|
+
except api_client.CliError as err:
|
|
506
|
+
if not _route_unmounted(err):
|
|
507
|
+
raise
|
|
508
|
+
# A server older than the approve route approves only the active org,
|
|
509
|
+
# through PATCH.
|
|
510
|
+
if args.org_id:
|
|
511
|
+
raise api_client.CliError(
|
|
512
|
+
"This server cannot approve a membership in a named org. Upgrade it, or omit --org-id "
|
|
513
|
+
"to approve the membership in the user's active org."
|
|
514
|
+
) from err
|
|
515
|
+
return client.patch(user_path, json={"is_approved": True})
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def _route_unmounted(err: api_client.CliError) -> bool:
|
|
519
|
+
"""Whether a 404 came from a server that has no such route.
|
|
520
|
+
|
|
521
|
+
Go's ServeMux answers an unmounted route with plain text. A mounted route's
|
|
522
|
+
own 404 is JSON, such as an approval for a membership that does not exist.
|
|
523
|
+
"""
|
|
524
|
+
return err.status_code == 404 and "404 page not found" in str(err)
|
|
461
525
|
|
|
462
526
|
|
|
463
527
|
def handle_user_promote(client: api_client.ApiClient, args: Any) -> Any:
|
|
@@ -494,6 +494,8 @@ stores one key per org per endpoint in `~/.config/cmpnd/credentials.json`, or
|
|
|
494
494
|
`$CMPND_CONFIG_DIR/credentials.json` when that variable is set. The file has
|
|
495
495
|
mode 0600. An active pointer names the org whose key commands use.
|
|
496
496
|
`cmpnd login` stores a new key beside the others and makes its org active.
|
|
497
|
+
When you belong to more than one org, the approval page asks which org the
|
|
498
|
+
key is for and starts on your active org in the web app.
|
|
497
499
|
|
|
498
500
|
- `cmpnd login --org <slug-or-id>` mints a key for that org instead of the
|
|
499
501
|
active one. The approval page names the org, and serve refuses an org where
|
|
@@ -891,6 +893,14 @@ EMAIL` withdraws the open owner invites sent to that address, and the links
|
|
|
891
893
|
stop working. `cmpnd admin org show` lists pending addresses under
|
|
892
894
|
`seed.pending_owner_emails`.
|
|
893
895
|
|
|
896
|
+
`cmpnd admin user approve USER_ID --org-id ORG_ID` approves the user's pending
|
|
897
|
+
membership in that org, which need not be their active org. Without
|
|
898
|
+
`--org-id` it approves the membership in their active org, and a server
|
|
899
|
+
older than the approve route approves it the way older releases did. Serve applies the
|
|
900
|
+
operator queue's rules. A member waiting to join an org that has its own owner
|
|
901
|
+
belongs to that owner's queue, so serve refuses it with 400. `cmpnd admin user
|
|
902
|
+
reject USER_ID --org-id ORG_ID` declines a pending membership.
|
|
903
|
+
|
|
894
904
|
### Plans, limits and signups
|
|
895
905
|
|
|
896
906
|
Every org has a plan, `free`, `pro` or `enterprise`, and each plan sets
|
|
@@ -927,6 +937,13 @@ PLAN NAME [--disabled]` pins a flag for the plan, and `flags delete` hands the
|
|
|
927
937
|
plan back to the global default. A plan's flag must already exist, so create
|
|
928
938
|
it first with `cmpnd admin feature-flags set`.
|
|
929
939
|
|
|
940
|
+
`cmpnd admin org flags list ORG_ID` shows every flag with the org's own row,
|
|
941
|
+
its plan's row and the global row, each `null` when absent, and `enabled`, the
|
|
942
|
+
value serve resolves for the org. `cmpnd admin org flags update ORG_ID NAME
|
|
943
|
+
[--disabled]` pins a flag for that org alone, which beats its plan and the
|
|
944
|
+
global value. `flags delete ORG_ID NAME` hands the org back to its plan and the
|
|
945
|
+
global value. The operator org page writes the same rows.
|
|
946
|
+
|
|
930
947
|
`cmpnd admin signup show` reports who may sign up and the plan a new account's
|
|
931
948
|
org lands on. `cmpnd admin signup update --mode closed|invite|open` overrides
|
|
932
949
|
the installation's `CMPND_SERVE_SIGNUP_MODE`, and `--mode installation` follows
|
|
@@ -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.
|
|
8
|
+
version = "0.80.0"
|
|
9
9
|
description = "DSPy observability and deployment SDK for cmpnd"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.11"
|
|
@@ -2,8 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import json
|
|
5
6
|
from argparse import Namespace
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from unittest import mock
|
|
6
9
|
|
|
10
|
+
import httpx
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
from cmpnd.cli import api_client
|
|
7
14
|
from cmpnd.cli.commands import admin
|
|
8
15
|
|
|
9
16
|
|
|
@@ -183,11 +190,15 @@ class TestAdminUser:
|
|
|
183
190
|
},
|
|
184
191
|
)
|
|
185
192
|
|
|
186
|
-
def
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
193
|
+
def test_approve_parser_takes_an_optional_org(self):
|
|
194
|
+
from argparse import ArgumentParser
|
|
195
|
+
|
|
196
|
+
parser = ArgumentParser()
|
|
197
|
+
admin.register(parser.add_subparsers())
|
|
198
|
+
args = parser.parse_args(["admin", "user", "approve", "u_1", "--org-id", "o_2"])
|
|
199
|
+
assert args.org_id == "o_2"
|
|
200
|
+
assert args.func is admin.handle_user_approve
|
|
201
|
+
assert parser.parse_args(["admin", "user", "approve", "u_1"]).org_id == ""
|
|
191
202
|
|
|
192
203
|
def test_promote_patches_is_admin_true(self, mock_client):
|
|
193
204
|
mock_client.patch.return_value = {"id": "u_1", "is_admin": True}
|
|
@@ -413,3 +424,105 @@ class TestAdminSignup:
|
|
|
413
424
|
with pytest.raises(SystemExit):
|
|
414
425
|
args.func(mock_client, args)
|
|
415
426
|
mock_client.patch.assert_not_called()
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
def _serve(answer: Callable[[httpx.Request], httpx.Response]):
|
|
430
|
+
"""A real ApiClient over a captured transport: `sent` collects each request."""
|
|
431
|
+
sent: list[httpx.Request] = []
|
|
432
|
+
|
|
433
|
+
def handle(request: httpx.Request) -> httpx.Response:
|
|
434
|
+
sent.append(request)
|
|
435
|
+
return answer(request)
|
|
436
|
+
|
|
437
|
+
real = httpx.Client
|
|
438
|
+
patch = mock.patch("httpx.Client", lambda **kwargs: real(**kwargs, transport=httpx.MockTransport(handle)))
|
|
439
|
+
return api_client.ApiClient("k", "http://serve.test"), sent, patch
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def _wire(sent: list[httpx.Request]) -> list[tuple[str, str, object]]:
|
|
443
|
+
return [(r.method, r.url.path, json.loads(r.content) if r.content else None) for r in sent]
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
# Go's ServeMux answers an unmounted route with this plain-text 404.
|
|
447
|
+
_UNMOUNTED = httpx.Response(404, text="404 page not found\n")
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
class TestAdminUserApproveWire:
|
|
451
|
+
def test_approve_posts_the_named_org(self):
|
|
452
|
+
client, sent, patch = _serve(lambda _r: httpx.Response(200, json={"newly_approved": True}))
|
|
453
|
+
with patch:
|
|
454
|
+
result = admin.handle_user_approve(client, _parse("user", "approve", "u_1", "--org-id", "o_2"))
|
|
455
|
+
assert _wire(sent) == [("POST", "/api/v1/admin/users/u_1/approve", {"org_id": "o_2"})]
|
|
456
|
+
assert result["newly_approved"] is True
|
|
457
|
+
|
|
458
|
+
def test_approve_without_org_approves_the_active_org(self):
|
|
459
|
+
def answer(request: httpx.Request) -> httpx.Response:
|
|
460
|
+
if request.method == "GET":
|
|
461
|
+
return httpx.Response(200, json={"id": "u_1", "org_id": "o_1"})
|
|
462
|
+
return httpx.Response(200, json={"newly_approved": True})
|
|
463
|
+
|
|
464
|
+
client, sent, patch = _serve(answer)
|
|
465
|
+
with patch:
|
|
466
|
+
admin.handle_user_approve(client, _parse("user", "approve", "u_1"))
|
|
467
|
+
assert _wire(sent) == [
|
|
468
|
+
("GET", "/api/v1/admin/users/u_1", None),
|
|
469
|
+
("POST", "/api/v1/admin/users/u_1/approve", {"org_id": "o_1"}),
|
|
470
|
+
]
|
|
471
|
+
|
|
472
|
+
def test_approve_without_org_falls_back_to_patch_on_a_server_without_the_route(self):
|
|
473
|
+
def answer(request: httpx.Request) -> httpx.Response:
|
|
474
|
+
if request.method == "GET":
|
|
475
|
+
return httpx.Response(200, json={"id": "u_1", "org_id": "o_1"})
|
|
476
|
+
if request.method == "POST":
|
|
477
|
+
return _UNMOUNTED
|
|
478
|
+
return httpx.Response(200, json={"id": "u_1", "is_approved": True})
|
|
479
|
+
|
|
480
|
+
client, sent, patch = _serve(answer)
|
|
481
|
+
with patch:
|
|
482
|
+
result = admin.handle_user_approve(client, _parse("user", "approve", "u_1"))
|
|
483
|
+
assert _wire(sent)[-1] == ("PATCH", "/api/v1/admin/users/u_1", {"is_approved": True})
|
|
484
|
+
assert result["is_approved"] is True
|
|
485
|
+
|
|
486
|
+
def test_approve_with_org_on_a_server_without_the_route_says_so(self):
|
|
487
|
+
client, sent, patch = _serve(lambda _r: _UNMOUNTED)
|
|
488
|
+
with patch, pytest.raises(api_client.CliError, match="--org-id"):
|
|
489
|
+
admin.handle_user_approve(client, _parse("user", "approve", "u_1", "--org-id", "o_2"))
|
|
490
|
+
assert [r.method for r in sent] == ["POST"]
|
|
491
|
+
|
|
492
|
+
def test_a_missing_membership_is_not_mistaken_for_an_old_server(self):
|
|
493
|
+
def answer(request: httpx.Request) -> httpx.Response:
|
|
494
|
+
if request.method == "GET":
|
|
495
|
+
return httpx.Response(200, json={"id": "u_1", "org_id": "o_1"})
|
|
496
|
+
return httpx.Response(404, json={"error": "no membership for that user in that org"})
|
|
497
|
+
|
|
498
|
+
client, sent, patch = _serve(answer)
|
|
499
|
+
with patch, pytest.raises(api_client.CliError, match="no membership"):
|
|
500
|
+
admin.handle_user_approve(client, _parse("user", "approve", "u_1"))
|
|
501
|
+
assert [r.method for r in sent] == ["GET", "POST"]
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
class TestAdminOrgFlagsWire:
|
|
505
|
+
def _run(self, *argv: str) -> list[tuple[str, str, object]]:
|
|
506
|
+
client, sent, patch = _serve(lambda _r: httpx.Response(200, json={"org_id": "o_1", "flags": []}))
|
|
507
|
+
args = _parse("org", "flags", *argv)
|
|
508
|
+
with patch:
|
|
509
|
+
args.func(client, args)
|
|
510
|
+
return _wire(sent)
|
|
511
|
+
|
|
512
|
+
def test_list_reads_every_flag_as_it_resolves_for_the_org(self):
|
|
513
|
+
assert self._run("list", "o_1") == [("GET", "/api/v1/admin/organizations/o_1/feature-flags", None)]
|
|
514
|
+
|
|
515
|
+
def test_update_pins_the_flag_on_by_default(self):
|
|
516
|
+
assert self._run("update", "o_1", "beta") == [
|
|
517
|
+
("PUT", "/api/v1/admin/organizations/o_1/feature-flags/beta", {"enabled": True})
|
|
518
|
+
]
|
|
519
|
+
|
|
520
|
+
def test_update_disabled_pins_the_flag_off(self):
|
|
521
|
+
assert self._run("update", "o_1", "beta", "--disabled") == [
|
|
522
|
+
("PUT", "/api/v1/admin/organizations/o_1/feature-flags/beta", {"enabled": False})
|
|
523
|
+
]
|
|
524
|
+
|
|
525
|
+
def test_delete_removes_the_orgs_row(self):
|
|
526
|
+
assert self._run("delete", "o_1", "beta") == [
|
|
527
|
+
("DELETE", "/api/v1/admin/organizations/o_1/feature-flags/beta", None)
|
|
528
|
+
]
|
|
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
|
|
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
|