Repository navigation
feat: add the project administration and eval resources the CLI calls - #192
Open
brightsparc wants to merge 1 commit into
Open
brightsparc wants to merge 1 commit into
brightsparc wants to merge 1 commit into
Conversation
Ports the namespaces introspection-rust-sdk#123 added. Control Plane: projects (list/get only), api_keys, applications (idps, secrets, keys), credentials, variables, integrations, judges and oidc.me. Data Plane: images on the client and the Runner, tasks.commands with a wait capped at MAX_COMMAND_WAIT_SECONDS, and kind/commands on tasks.create so an eval task can accept commands. Secrets the API returns once or takes on write are pydantic SecretStr. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016njWGNXD7FLvM4zmH3zs7j
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ports the project administration and eval resources from the Rust SDK (introspection-org/introspection-rust-sdk#123) into the Python SDK. None of them were on Python
mainbefore this PR.What's added
client.projectslist(project=, limit=, next=),get(slug_or_id)(the path segment is percent-encoded). Read-only, see below.client.api_keyslist(project=, expiring=)(unwraps{"api_keys": [...]}, not paginated),create(returnsApiKeyCreatedwith the one-timeapi_key),update,delete(expires_in=)(rotation grace),acknowledgeclient.applicationslist/get/create/update/delete, plus.idps(paginated list, create, delete),.secrets(paginated list, create returns the one-timesecret,delete(expires_in=)) and.keys(unpaginated list, create with no body, delete)client.credentialslist(project=, runtime_id=, runtime=, environment=),get,create,update,delete. The secret is write-onlyclient.variableslist,create,update,delete(the API has no read-one route)client.integrationslist/get/create/update/delete. Org-wide, so it takes noproject. The wire fieldtypeis theintegration_type=kwargclient.judgeslist(project=, runtime_group_id=),get,update(enabled=, sample_rate=)client.oidcme().feature_flagsvalues keep theirbool/int/float/strtypeclient.images/runner.imageslist(kind=, status=, name=, runtime_id=),get,create(name=, source=, provenance=, scan=),delete.sourceis akind-tagged union:ContextImageSourceorMirrorImageSource.Image.pull_referenceis the digest-pinned referencetasks.commands(client and Runner)create(task_id, ExecCommand / UploadCommand / DownloadCommand / tagged dict),list(task_id, status=),get(task_id, command_id, wait_seconds=)tasks.createkind=andcommands=kwargs, plusTaskKind.EVAL, so an eval task can accept commands. This matches the follow-up commit on the Rust PR. Before this,Taskfailed to parsekind: "eval"Other details:
Most project-scoped CP routes take an optional
project=(slug or id). It is sent as?project=and only when given, because it's needed only when the credential doesn't already name a project.Secrets are redacted. These use pydantic
SecretStr, sorepr,strandmodel_dump_jsonprint**********:client_secretsecretcodeThe request models serialize the plaintext only in the JSON body, through a private
WireSecrettype. Write methods acceptstrorSecretStr.MAX_COMMAND_WAIT_SECONDS = 25is public, fromintrospection_sdk.runner_resources. The API allows 60s, but the SDK's HTTP timeout is 30s.Environment(development/staging/production) is inintrospection_sdk.schemas.lanes.ImagesResource,TaskCommandsResourceand thecommands/imagesmembers are added to the data-plane Protocols. mypy checks that the client and Runner still satisfy them.The sync twins are generated by
scripts/gen_sync.py. There's a shared private_crud.pybase, which is why duplication went down (see test results).README.mdhas two new sections, and there's a new example atexamples/introspection_examples/api/project_admin.py. The example isn't added toexamples.ymlbecause these routes need an owner CP session, and the workflow only has an API key.Projects are read-only (
list+get), on purpose. The user confirmed the SDK must not create, update or delete projects; that stays in the dashboard and API.projects:writeandprojects:deleteare in no API-key, Application/service-account or CLI-login ceiling (seeintrospection_common/permissions.pyandscopes.pyin introspection-cloud). Only an owner or admin's browser CP session carries them, so those methods would 403 for every SDK credential. Pythonmainhad no project write methods, so nothing was removed. A test assertsProjectshas nocreate,updateordelete.Parity
This mirrors introspection-org/introspection-rust-sdk#123, with one difference: Rust has project
create/update/deleteand this PR does not.Out of scope
/v1/remotes/connect: a CLI-owned protobuf WebSocket.Tests and checks run (all locally, all passing)
uv run ruff format --check .anduv run ruff check .uv run python scripts/gen_sync.py: no diff under_sync/.uv run ty checkanduv run mypy.uv run pytest tests/ --cov=introspection_sdk: 823 passed, 10 deselected (the live walks), 92.57% total coverage against the 80% floor.tests/rest/test_project_admin.pyandtests/rest/test_images.py(49 tests) pin:?project=/expires_in/ filter params[]andFalseare sentkind-tagged image source and command unions, and that bad tags or a shell-string command are refused before sendinguv run deptry .: no issues.npx --yes jscpd@5.4.1: passes. Duplication went from 16.22% on main to 16.03%, so.jscpd.jsonthresholdis ratcheted from 16.3 to 16.1.griffe check ... --against v0.29.1 -e scripts/griffe_reexports.py: no breakages.pre-commit runon the changed files: all hooks pass (gitleaks, ruff, ty, gen-sync, json/toml/whitespace).uv run --project examples python scripts/check_examples.py: passes.scripts/check_api_contract.pygains 56 surfaces covering every new body, read model and list/delete/read query set.mainsource (--spec dp-src.json --cp-spec cp.json): no drift on any new surface. The only change from the baseline is thatkindandcommandsdrop out of the existingTaskCreate"missing" list.lane/project_idbrowser-session selectors are exempt.project_idexemptions, and others.API Contractjob compares against the published docs.introspection.dev specs, which lack these routes. It is already red onmainfor the same reason (members, connections and ShareUpdate are missing there too), so this PR doesn't change its state.Live tests
tests/live/test_live_admin.pyadds ten walks that mirror the Rust walks:oidc_me, api keys, applications, credentials, variables, integrations, judges, projects (read), images and task commands. The Rust tolerances are kept:service_accountapp, and the IdP attach must be refused with 400 or 403.The walks use
INTROSPECTION_LIVE=1,INTROSPECTION_BASE_API_URL,INTROSPECTION_CP_SESSION(sent alone asCookie: intro_cp_session=...),INTROSPECTION_PROJECT,INTROSPECTION_DP_URLandINTROSPECTION_MEMBER_TOKEN. They skip rather than fail when any is missing.They were not run against a deployment (no stack was available). I only checked that they're collected, deselected by default, and skip cleanly with and without
INTROSPECTION_LIVE=1.Relationship to #175. #175 adds the general live suite (
tests/live/with its ownconftest.pyand_support.py) and isn't onmainyet. This PR doesn't touch that branch:livemarker andaddoptschange topyproject.toml, byte-for-byte, and the same emptytests/live/__init__.py, so the two merge without conflict.pytest tests/live -m livepicks up both files.Generated by Claude Code