Skip to content

CLI commands

CommandStatusNotes
loginworksAuthorization code + PKCE on a loopback redirect; --device-code for machines with no browser. Verified against the real Autodesk tenant — see below
logoutworksRemoves the cached token, refresh token included; purely local
whoamiworks (needs API)Identity and quota
env createworks (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 / lsworks (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 getworks (needs API)
env selectworksLocal only — no API call, works offline
env renewworks (needs API)Extends from now, not from the old expiry; states it first. Refused while the environment is still provisioning
env archiveworks (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 deletestub (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 createworks (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 / lsworks (needs API)Pages through everything
component getworks (needs API)
component delete / rmworks (needs API)Confirms first unless --yes
skill install / listworksInstalls the embedded agent skills; no API needed
config get/set/pathworks
doctorworksChecks login, reachability, and the selected environment; exits non-zero if something is wrong
versionworksWorks without a valid config

“works (needs API)” means the command is complete and tested; it needs a deployed control plane to talk to.

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.

Terminal window
adsk-experiment login # browser, usually zero clicks
adsk-experiment login --device-code # SSH, containers, no browser
adsk-experiment login --account you@autodesk.com # when you have more than one

Authorization 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.

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://localhost under 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 with AADSTS7000218; 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 of allowPublicClient, which enables only --device-code.
  • An exposed API scope, access_as_user, so api://<client-id>/.default resolves 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.