CLI commands
Commands
Section titled “Commands”| Command | Status | Notes |
|---|---|---|
login | works | Authorization code + PKCE on a loopback redirect; --device-code for machines with no browser. Verified against the real Autodesk tenant — see below |
logout | works | Removes the cached token, refresh token included; purely local |
whoami | works (needs API) | Identity and quota |
env create | works (needs API) | --wait polls to a terminal phase. Creates the environment’s GitHub repository from the template, grants the creator push access, and protects its default branch — so a git push to main is refused and a change goes on a branch and through a pull request. That is what makes the commit path scanned at all: the security scans run on pull requests and not on pushes (D.4, ADR-0035). No approving review is required, and the repository is asked for with auto-merge allowed, so a pull request can normally be told to merge itself when its checks pass (gh pr merge --auto) — whether GHES’s generate endpoint honours that field is the one part of this not yet confirmed against a live create, so the log line says which way it went. Pass --github-user (your GHES login, e.g. from gh api user --hostname git.autodesk.com --jq .login) so the platform grants push to that login directly — the fallback of resolving one from your Autodesk email only matches a GHES account’s public email field, which is empty by default on an SSO-provisioned account, so it finds nobody for most real users. The environment record is also the durable account of the experiment, and it is never deleted (I.1, ADR-0054 § Amended) — there are no flags for the descriptive part: it is the --name and --description you already gave, because asking a second time is what leaves most environments undescribed |
env list / ls | works (needs API) | Pages through everything; flags anything expiring within 48h. Shows what the control plane considers still running, which includes teardowns in progress — an owner who asked for one needs to watch it finish. --all adds the experiments that have finished, and since records are now kept indefinitely (ADR-0054 § Amended) that is the whole history rather than a recent window |
env get | works (needs API) | |
env select | works | Local only — no API call, works offline |
env renew | works (needs API) | Extends from now, not from the old expiry; states it first. Refused while the environment is still provisioning |
env archive | works (needs API) | Confirms first unless --yes; teardown is async, and the quota slot frees immediately. Archives the environment’s GitHub repository and revokes the creator’s push access before anything else happens; it never deletes a repository. That runs first precisely so a failure is retryable — exit 8 here means nothing was reclaimed, and re-running is safe. Archiving is not deletion: the record stays, renewable, until the purge stage exists |
env delete | stub (exit 9) | The purge past archived that removes the record for good. Not built (roadmap F.2); resolves the reference, then exits 9 not_implemented so a script can tell “not built” from “broken” |
component create | works (needs API) | Registers something to publish. Synchronous — nothing to wait for. Validates the name, type, and source path locally, so a typo costs no round trip |
component list / ls | works (needs API) | Pages through everything |
component get | works (needs API) | |
component delete / rm | works (needs API) | Confirms first unless --yes |
skill install / list | works | Installs the embedded agent skills; no API needed |
config get/set/path | works | |
doctor | works | Checks login, reachability, and the selected environment; exits non-zero if something is wrong |
version | works | Works without a valid config |
“works (needs API)” means the command is complete and tested; it needs a deployed control plane to talk to.
What doctor checks
Section titled “What doctor checks”The three questions behind almost every failure: am I signed in, can I reach the control plane, and which environment am I acting on.
It checks for no locally installed tools, because there are none to check.
Nothing is built or deployed from a workstation, so this CLI and a browser are
the whole prerequisite list. It previously probed docker, kubectl, and
aws; a probe for a binary the product never invokes is a fabricated
prerequisite in front of a capability whose entire point is having none.
Reachability and authentication are separate checks on purpose — they are
different problems with opposite fixes, and conflating them sends people to the
wrong place. git belongs here once the commit path exists, since the agent
commits and pushes locally; adding it before then would repeat the mistake.
Exits non-zero when a check fails, so adsk-experiment doctor && … works as a gate.
Warnings do not fail it: no environment selected is normal on a fresh install.
“Harness access” reports whether the caller reaches Harness through a
group Entra actually knows is assigned to it, and can only ever warn. It
redeems the same refresh token login already cached for a second,
Graph-scoped token (GroupMember.Read.All — already a consented delegated
scope on this registration; no new app registration, no secret, no new
consent prompt), finds the caller’s Entra groups whose name looks
Harness-related (SG-HARNESS-*, or the separately-named
SG-SSO-Enterprise Harness Sandbox), then checks each group’s own app role
assignments for the “Enterprise Harness Production” Enterprise Application.
Group membership alone is not the check — a caller can hold plenty of
Harness-flavoured groups and still have none of them actually assigned, and
checking the caller’s own assignments directly
(/me/appRoleAssignments) was tried and disproven: a real account in 37
such groups showed zero matches there, then authenticated to the Harness
MCP successfully anyway. Checking each group’s assignments instead is
what actually found the match. Even that corrected version is strong
circumstantial evidence rather than a confirmed fact — nothing here has
inspected the Harness MCP’s own OAuth client to prove it checks this
specific application — so a wrong guess must still never fail doctor.
See cli/internal/graph’s package doc and checkHarnessAccess’s own
comment for the full account.
Signing in
Section titled “Signing in”adsk-experiment login # browser, usually zero clicksadsk-experiment login --device-code # SSH, containers, no browseradsk-experiment login --account you@autodesk.com # when you have more than oneAuthorization code + PKCE on a loopback redirect, the same as az, gh, and
aws sso. Your browser already holds the company SSO session, so it usually
completes without a click. No client secret is involved — a secret shipped
to every laptop is not a secret, and PKCE gives the same protection without one.
ADR-0011
records the reasoning.
The listener that receives the redirect binds an ephemeral port on 127.0.0.1
only, checks the state parameter, answers exactly one request, and is gone
before the command returns.
login prints the account it signed in as. Pass --account to name one up
front; if the browser session hands back a different identity the command fails
rather than quietly signing you in as somebody else.
The only thing stored is your own token for the control plane — at
~/.adsk-experiment-ee/token.json, mode 0600, written atomically. No AWS, Kubernetes,
or registry credential is ever written to your machine.
You should not have to run it again
Section titled “You should not have to run it again”offline_access is requested, so the cache holds a refresh token and every
later command renews the session in the background before it expires. Nothing
prompts, nothing pops up a browser.
That is also the whole story for anything unattended. This CLI schedules
nothing — no cron entry, no daemon. If you wire adsk-experiment into your own cron,
launchd, or an agent skill on the machine where you signed in, it keeps working
because the renewal is silent.
It does not keep working forever, and that part is not fixable here. Entra
rotates refresh tokens but does not make them permanent: they lapse after a long
idle period and are revoked outright by a password change, an MFA
re-registration, or a conditional-access change. When that happens the command
exits 3 and says to run adsk-experiment login. Keeping a session alive
indefinitely would need a service principal and a client secret on the
workstation, which is exactly what this design avoids.
What login needs from the Entra app registration
Section titled “What login needs from the Entra app registration”Both requirements are already satisfied, confirmed end to end against the
real tenant on 2026-08-10 — a zero-click sign-in returning a token whose aud
and iss match what the API Gateway authorizer checks:
http://localhostunder the “Mobile and desktop applications” platform, which is what allows the code exchange with no client secret. Under “Web” instead, Entra treats the CLI as a confidential client and rejects it withAADSTS7000218; the CLI translates that code into the platform change it means, rather than passing through Entra’s own message, which asks for a client credential and sends people off to create a secret that cannot help. Independent ofallowPublicClient, which enables only--device-code.- An exposed API scope,
access_as_user, soapi://<client-id>/.defaultresolves to it rather than falling through to the app’s delegated Graph permissions.
One thing worth improving, though nothing depends on it: access_as_user is the
template default and says nothing about what it grants, which makes for a poor
audit record. A scope named for the thing it permits would read better. Pointing
the CLI at one is a ADSK_EXPERIMENT_EE_AUTH_SCOPE change, not a release.