cmpnd 0.78.0__tar.gz → 0.79.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.79.0}/PKG-INFO +1 -1
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/admin.py +31 -2
- {cmpnd-0.78.0 → cmpnd-0.79.0}/docs/cli.md +10 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/pyproject.toml +1 -1
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_admin.py +91 -5
- {cmpnd-0.78.0 → cmpnd-0.79.0}/uv.lock +1 -1
- {cmpnd-0.78.0 → cmpnd-0.79.0}/.gitignore +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/AGENTS.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/CLAUDE.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/README.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/_assessments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/_authored_import.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/_deployments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/_exporter_pool.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/_gepa_patch.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/_http.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/_program_patch.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/_rlm_patch.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/_runtime_tools.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/_timespec.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/_traces.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/_valsets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/authored.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/callback.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/api_client.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/assessments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/auth.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/datasets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/deployments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/evals.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/login.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/optimizations.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/org.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/provider_credentials.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/tool_servers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/top.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/traces.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/commands/valsets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/credentials.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/deprecation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/pagination.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/shell.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/trace_render.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/tree_help.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/verbs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/cli/views.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/client.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/configuration.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/context.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/credentials.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/dataset_sync.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/datasets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/decorators.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/deployment.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/deployment_metadata.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/deployment_runs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/eval_handler.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/execution.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/exporter.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/exporter_http.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/helpers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/identity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/imaging.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/ir/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/ir/decode.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/ir/encode.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/ir/hash.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/ir/program.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/ir/reflection.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/ir/types.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/ir/xxh64.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/lm_configuration.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/mcp_servers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/models.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/named_programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/optimization.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/optimizers/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/optimizers/gepa.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/optimizers/gepa_callback.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/optimizers/resume.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/optimizers/tracker.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/packaging.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/program_naming.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/references.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/retry.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/cmpnd/traces.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/docs/2026-04-02-code-review.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/docs/client.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/docs/custom-proposer.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/docs/plans/2026-04-05-train-val-dataset-capture-design.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/docs/plans/2026-04-05-train-val-dataset-capture.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/docs/reading-traces.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/docs/sdk-instrumentation.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/examples/generalizing_proposer.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/examples/local_ollama.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/LINEAGE.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/README.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/cmpnd_ingest/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/cmpnd_ingest/bodies.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/cmpnd_ingest/client_floor.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/cmpnd_ingest/exporter_http.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/cmpnd_ingest/program_names.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/cmpnd_ingest/protocols.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/cmpnd_ingest/py.typed +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/cmpnd_ingest/request_log.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/cmpnd_ingest/retry.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/cmpnd_ingest/trace_facts.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/ingest/pyproject.toml +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/audit-sdist.sh +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/capture_review_run.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/check-pip-install.sh +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/check_release_artifacts.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/fixtures/review_run.json.gz +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/gepa_resume_bug.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/install-test-env.sh +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/release-gate.sh +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/release-real-models.sh +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/require_version_bump.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/seed_review_run.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/selfhost_compat.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/scripts/test-in-linux.sh +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/stubs/dspy/__init__.pyi +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/stubs/dspy/primitives/__init__.pyi +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/stubs/dspy/primitives/prediction.pyi +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/stubs/dspy/utils/__init__.pyi +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/stubs/dspy/utils/callback.pyi +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/stubs/dspy/utils/dummies.pyi +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/_credential_route.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/_deployment_signature_helpers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/_exporter_helpers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/_fake_clock.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/_identity_helpers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/_resolve_refs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/_traced_run.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/conftest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/committee_task.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/conftest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/continuation_task.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/data/committee_train.ndjson.gz +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/data/committee_val.ndjson.gz +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/fake_lm.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/helpers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/target_capabilities.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_assessments_cli.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_capture_prompts.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_composite_module.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_dataset_auto_creation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_deploy_execute.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_deploy_pandas.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_deploy_programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_edge_cases.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_example_hash.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_gepa_optimization.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_interpreter_spans.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_lm_accuracy.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_module_tracing.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_optimization_lineage.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_optimization_metric_identity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_optimize_authored_metric.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_optimize_continuation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_optimize_custom_proposer.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_optimize_failures.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_optimize_loop.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_optimize_review_committee.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_optimize_server_owned_completion.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_parallel_export.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_project_attribution.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_react_tool_spans.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_save_load.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_trace_structure.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_traces_read.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_user_attribution.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/e2e/test_valsets_cli.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/01_predict_qa.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/02_predict_renamed_field.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/03_chain_of_thought_qa.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/04_predict_richer_types.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/05_composite_two_predicts.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/06_multichaincomparison.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/07_program_of_thought.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/08_opaque_module.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/09_predict_pydantic_field.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/10_react.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/11_refine.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/12_retrieve.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/13_knn.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/14_parallel.json +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/identity_vectors/README.md +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/ingest_conformance.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/integration/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/integration/authored_optimize/judge.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/integration/authored_optimize/optimize_hosted.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/integration/conftest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/integration/hosted_mcp_tool_optimize_probe.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/integration/hosted_optimize_probe.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/integration/optimization_run_probe.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/integration/test_cli_deploy_smoketest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/integration/test_logs_renders_traces.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/integration/test_optimization_run.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/integration/test_optimizing_progress.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/integration/test_unhappy_path.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/load/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/load/__main__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/load/drain.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/load/ingest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/load/latency_transport.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/load/rlm_shape.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/load/rlm_size_report.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/load/sweep.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/load/test_rlm_shape.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/load/test_rlm_size_report.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/conftest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/seed_parity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/test_auth.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/test_datasets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/test_deployments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/test_evals.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/test_health.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/test_modules.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/test_optimizations.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/test_programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/test_signatures.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/test_trace_streaming.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/parity/test_traces.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/_provider.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/_readiness.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/_reliability.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/_result.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/mint_bedrock_token.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_bedrock_converse.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_deploy_programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_multi_client_stress.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_optimize_client_committee.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_optimize_continuation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_optimize_loop.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_optimize_loop_wasm.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_optimize_stress.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_provider_roles.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_readiness.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_real_provider_twins.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_reliability.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_reliability_classify.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_rlm_wasm.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/real_models/test_trace_search.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/span_reference_fixture.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_adapter_reentrancy.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_assessments_api.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_authored.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_authored_binding_credentials.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_authored_bundle_collisions.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_authored_metric_contract.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_authored_named_models.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_authored_optimize.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_callback.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_candidate_lineage.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/conftest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_api_client.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_assessments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_auth.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_credentials.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_datasets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_deployment_runs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_deployments.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_evals.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_login.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_main.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_optimizations.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_org.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_pagination.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_parity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_programs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_provider_credentials.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_schema_sample.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_shell.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_tool_servers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_top.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_traces.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_traces_program_and_module.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_traces_search_wire.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_tree_help.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cli/test_valsets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_client.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_client_floor.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_client_program_names.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_clustering/__init__.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_clustering/conftest.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_clustering/test_edge_cases.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_clustering/test_hash_consistency.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_clustering/test_type_conversion.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_cmp551_grouping_identity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_configuration.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_context.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_context_propagation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_credential_kwargs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_credential_selection.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_credential_values.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_dataset_polling.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_dataset_sync.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_datasets.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_decision_client_spans.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_decorators.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_deploy.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_deploy_mcp_tool_references.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_deploy_program_id.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_deployment_metadata.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_deployment_runs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_deployments_read.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_eval.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_eval_example_attribution.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_eval_program_id.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_eval_start.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_eval_trace_grouping.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_exception_paths.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_execute.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_execute_run_identity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_exporter.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_exporter_program_names.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_exporter_queue.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_exporter_real.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_fake_lm.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_gepa_callback.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_gepa_integration.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_gepa_output_normalization.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_gepa_resume.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_identity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_imaging.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_import_weight.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_ingest_conformance.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_ingest_second_producer.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_integration.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_interpreter_callback.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_ir_encoder_properties.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_ir_golden_vectors.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_ir_roundtrip.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_lm15_native_calls.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_lm_configuration.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_lm_usage_shapes.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_mcp_tools.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_models.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_multi_client_stress_readiness.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_no_signature_hash_on_the_wire.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_optimization_phase_callback.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_optimization_program_names.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_optimization_progress.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_optimization_start.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_optimize.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_optimize_budget.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_optimize_lift.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_optimize_lms.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_optimize_proposer.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_optimize_reflection_lm.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_optimize_terminal_failure.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_packaging.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_packaging_callable_source.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_packaging_mcp_tools.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_packaging_program_source.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_poll_until.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_program_id_deprecation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_program_identity.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_program_lineage_persist.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_program_names_in_deploy.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_program_naming.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_programs_create.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_project_payload.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_real_provider_twins_readiness.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_reflector_properties.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_reflector_smoke.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_release_gate_script.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_release_real_models_script.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_replaced_names.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_repro_reactv2_bugs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_require_version_bump.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_retry.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_reversible_failures.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_rlm_patch.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_run_attribution_paths.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_runtime_tools.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_schemas.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_seal_lm_credential.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_seed_review_run.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_signature_inputs.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_span_references.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_span_sent_once.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_system_one.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_trace_context_concurrency.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_trace_fact_wire_contract.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_trace_facts.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_trace_render.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_traces_read.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_tracked_gepa.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_tracker_enrich.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_truncation.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_upload_auth_headers.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_valsets_api.py +0 -0
- {cmpnd-0.78.0 → cmpnd-0.79.0}/tests/test_xxh64.py +0 -0
|
@@ -182,8 +182,14 @@ def _register_user(sub: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
182
182
|
create.add_argument("--admin", dest="admin", action="store_true", help="Grant platform-admin")
|
|
183
183
|
create.set_defaults(func=handle_user_create)
|
|
184
184
|
|
|
185
|
-
approve = usub.add_parser("approve", help="Approve a pending
|
|
185
|
+
approve = usub.add_parser("approve", help="Approve a pending membership")
|
|
186
186
|
approve.add_argument("id", help="User id")
|
|
187
|
+
approve.add_argument(
|
|
188
|
+
"--org-id",
|
|
189
|
+
dest="org_id",
|
|
190
|
+
default="",
|
|
191
|
+
help="Org whose membership to approve (default: the user's active org)",
|
|
192
|
+
)
|
|
187
193
|
approve.set_defaults(func=handle_user_approve)
|
|
188
194
|
|
|
189
195
|
promote = usub.add_parser("promote", help="Grant platform-admin")
|
|
@@ -457,7 +463,30 @@ def handle_user_create(client: api_client.ApiClient, args: Any) -> Any:
|
|
|
457
463
|
|
|
458
464
|
|
|
459
465
|
def handle_user_approve(client: api_client.ApiClient, args: Any) -> Any:
|
|
460
|
-
|
|
466
|
+
user_path = f"/api/v1/admin/users/{args.id}"
|
|
467
|
+
org_id = args.org_id or client.get(user_path)["org_id"]
|
|
468
|
+
try:
|
|
469
|
+
return client.post(f"{user_path}/approve", json={"org_id": org_id})
|
|
470
|
+
except api_client.CliError as err:
|
|
471
|
+
if not _route_unmounted(err):
|
|
472
|
+
raise
|
|
473
|
+
# A server older than the approve route approves only the active org,
|
|
474
|
+
# through PATCH.
|
|
475
|
+
if args.org_id:
|
|
476
|
+
raise api_client.CliError(
|
|
477
|
+
"This server cannot approve a membership in a named org. Upgrade it, or omit --org-id "
|
|
478
|
+
"to approve the membership in the user's active org."
|
|
479
|
+
) from err
|
|
480
|
+
return client.patch(user_path, json={"is_approved": True})
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
def _route_unmounted(err: api_client.CliError) -> bool:
|
|
484
|
+
"""Whether a 404 came from a server that has no such route.
|
|
485
|
+
|
|
486
|
+
Go's ServeMux answers an unmounted route with plain text. A mounted route's
|
|
487
|
+
own 404 is JSON, such as an approval for a membership that does not exist.
|
|
488
|
+
"""
|
|
489
|
+
return err.status_code == 404 and "404 page not found" in str(err)
|
|
461
490
|
|
|
462
491
|
|
|
463
492
|
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
|
|
@@ -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.79.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,78 @@ 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"]
|
|
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
|
|
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
|