@eyeauras/teamcity-cli
v0.4.1
Published
AI-friendly TeamCity CLI built with AI CLI Factory.
Readme
TeamCity CLI
teamcity-cli is the first executable integration built on the reusable
@eyeauras/cli-factory npm package. The package under this directory owns TeamCity REST paths,
locators, DTOs, and command vocabulary; generic command-tree, profile, credential, permission,
output, and JSON-RPC behavior stays in packages/core.
Ordinary text, JSON and XML responses have no tool-imposed byte ceiling. Invalid Content-Length and incomplete or mismatched unencoded/identity transfers still fail. Compressed wire length is syntax-checked without comparing it with decoded size. Stream errors explain the failure and preserve redacted native causes and stack traces without dumping response bodies. UTF-8 decoding preserves an initial BOM. Specialized discard probes stop reading unwanted bytes after 64 KiB; this does not reject an operation or truncate returned content. No automatic retry or command replay is added.
URL and token input
Copy the service base address, for example https://teamcity.example.test/ or https://example.test/teamcity/. Include any context path and port; omit /app/rest, build/project pages and query strings. HTTP and HTTPS are supported.
Create/copy an access token in My Settings & Tools > Access Tokens (the user profile).
See the TeamCity user-profile guide. Paste only its value: FICTIONAL_TOKEN_VALUE is a fictional example.
Do not include Bearer , Authorization:, or surrounding quotes. The same guidance appears in
profile configure --help, auth login --help, and before interactive input.
New URL/token input trims outer whitespace, including pasted CRLF. URL trailing slashes are
normalized without losing the context path; internal token symbols remain unchanged. Empty input,
headers/quoted tokens and obvious API/page URLs fail with a safe hint before authentication or
persistence. This applies to onboarding, profile URL writes, login, environment/stdin candidates,
execute and JSON-RPC (which never prompts). Existing data is retained; use profile set or
profile configure to correct an old URL. Failed configure input preserves working credentials.
Installation
Install the scoped npm package to get the teamcity-cli command:
npm install --global @eyeauras/teamcity-cli
teamcity-cli --help
teamcity-cli profile configureRequires Node.js 22+ and npm. The package includes compiled JavaScript and installs Core
automatically; no repository checkout, .NET, TypeScript compiler or browser is needed.
Interactive profile configuration guides endpoint/authentication setup; standalone secrets
use the OS credential store. Existing teamcity-cli profiles and permissions are unchanged.
Licensed under MIT. See the release guide.
Command tree
| Command | Purpose | Permission |
|---|---|---|
| server status | Show server version, role, and clock | ReadOnly |
| projects list, projects show <id> | Discover projects | ReadOnly |
| jobs list, jobs show <id> | Discover build configurations | ReadOnly |
| jobs run <id> | Queue one build, optionally with --branch, --comment, one-off --param values and --wait for its result | Update |
| builds list, builds show <id>, builds show --job <id> --latest | Inspect builds across all branches and states, one --branch, or the latest finished build of a job | ReadOnly |
| builds wait <id>, builds diagnose <id> | Wait for a result; summarize problems and failed tests | ReadOnly |
| builds tests/problems/changes <id> | Diagnose a build | ReadOnly |
| builds cancel <id> | Cancel a running build | Update |
| queue list | Inspect queued builds | ReadOnly |
| queue cancel <id> | Cancel a queued build while preserving cancellation metadata | Update |
| agents list, agents show <id> | Inspect build agents | ReadOnly |
| projects create/set/move/delete | Author project identity, hierarchy and allowed fields | Update |
| jobs create/set/move/delete | Author build configurations | Update |
| projects parameters list/show, jobs parameters list/show | Inspect safe parameter values and metadata | ReadOnly |
| projects parameters create/set/delete, jobs parameters create/set/delete | Manage plain parameters | Update |
| jobs steps list/show | Inspect ordered step summaries | ReadOnly |
| jobs steps create/replace/delete | Manage build steps | Update |
| vcs roots list/show | Discover existing roots without connection properties | ReadOnly |
| jobs vcs list/show, jobs vcs checkout-rules show | Inspect root attachments/rules | ReadOnly |
| jobs vcs attach/replace/detach, jobs vcs checkout-rules set | Manage attachments, not shared roots | Update |
| jobs triggers list/show, jobs features list/show | Inspect redacted extension summaries | ReadOnly |
| jobs triggers create/replace/delete, jobs features create/replace/delete | Manage extensions; disabled unless explicitly enabled | Update |
| jobs snapshot-dependencies list/show | Inspect upstream build dependencies | ReadOnly |
| jobs snapshot-dependencies create/replace/delete | Manage upstream dependencies | Update |
| jobs templates list | Inspect attached templates in priority order | ReadOnly |
| jobs templates attach/detach | Attach/detach existing templates, not create/delete templates | Update |
| jobs agent-requirements/artifact-dependencies list/show | Inspect redacted rule summaries | ReadOnly |
| jobs agent-requirements/artifact-dependencies create/replace/delete | Manage requirements and artifact dependencies | Update |
| jobs steps/features/triggers/agent-requirements/artifact-dependencies fields show/set | Read/write allowed scalar settings | Read: ReadOnly; write: Update |
| jobs steps/features parameters list/show/replace/set | Inspect redacted metadata or change non-secret plugin properties | Read: ReadOnly; write: Update |
| jobs output-parameters list/show/create/set/delete | Manage plain output parameters | Read: ReadOnly; write: Update |
| projects features list/show/create/replace/delete | Manage project features | Read: ReadOnly; write: Update |
| projects templates list/create, projects templates default show/set/clear | Create templates and manage the project default | Read: ReadOnly; write: Update |
| jobs templates show, jobs aliases/branches/tags | Inspect attachments and job metadata | ReadOnly |
| projects parent, projects/jobs fields show | Inspect hierarchy and allowed fields | ReadOnly |
| pools list/show/create/delete, pools fields show/set | Inspect/manage agent pools and names | Read: ReadOnly; write: Update |
| pools agents list/assign, pools projects list/assign/unassign | Manage pool membership, not member lifetime | Read: ReadOnly; write: Update |
| agents enabled/authorized/policy/pool show/set, agents fields show/set, agents delete | Manage agent eligibility and membership | Read: ReadOnly; write: Update |
| agents compatible-jobs/incompatible-jobs, queue show/compatible-agents | Inspect scheduling identities | ReadOnly |
| queue position show/set, queue tags list/add | Inspect positions/tags or change queued work | Read: ReadOnly; write: Update |
| builds number/status-text show/set, builds pin show/set | Manage display metadata and pin status | Read: ReadOnly; write: Update |
| builds comment set/clear, builds delete | Change annotations or delete a build/history | Update |
| builds tags list/add/replace | Inspect/manage public tags | Read: ReadOnly; write: Update |
| builds statistics list/show, builds status/finish-date/canceled-info, builds fields show | Inspect selected build evidence | ReadOnly |
| changes show/parents | Inspect change metadata and direct parents, not source files | ReadOnly |
Every collection command with --limit <count> and --start <offset> returns a page, whether
builds list, projects list, queue list, audit list or one of the other 22. The page's shape:
{ "count": 2, "items": [ ... ], "hasMore": true, "nextStart": 2 }How a page is filled. --limit is the most items to return, not the size of one request.
- Each request asks for one item more than it keeps. If it arrives, more exist: reading goes on
after the kept items, or
hasMoreistrueonce--limitis reached. - It follows TeamCity's continuation, including the
lookupLimitexpansions that TeamCity adds after scanning 5000 entities. It reads only the numbers innextHref, never the link itself. - TeamCity continues after the items it served, or at the same start with a deeper
lookupLimitwhen it served none. The CLI follows only such a continuation and computes the next start itself, so no item is read twice or skipped. - It stops after 10 requests, after 30 seconds, or on a continuation it does not follow.
- A single request keeps at most 1,000 items, so it asks for at most 1,001.
What the fields mean.
hasMore: falseonly when TeamCity confirmed the end.hasMore: nullmeans not established: the budget ran out, the continuation was not followed, or a full page came without a continuation.nextStartis the--startvalue that continues after the items returned. It isnullwhen there is none, including when nothing was read:--startcannot carry alookupLimit. Paging on whilenextStartis notnullalways makes progress. Offsets can shift while new builds arrive, so this is not a snapshot.
When a later request fails. The command exits 1 with the list.incomplete code. The items
already read are still printed, with hasMore: null. A later request stopped by Ctrl+C reports
interrupted instead.
Human output shows the items as a table. builds list, builds tests and builds problems add a
line such as Shown: 20. More results: yes; continue with --start 20. Other lists end with their
fields, such as count: 100 hasMore: null nextStart: 100. queue delete-page is a mutation and
still deletes exactly one requested page. Run a branch without a leaf, such as
teamcity-cli builds, to see its generated help and options.
Paging defaults remain --limit 100 --start 0. These options accept decimal digits with an optional
minus and leading zeros, but reject whitespace, plus signs, fractions and exponents. The start must
be a nonnegative safe integer (-0 remains valid); limit accepts any positive safe integer. Invalid options fail before
profile onboarding or credential access. Each option now reports one static error for syntax, unsafe
integers and range failures. The existing strict numeric build/queue-cancel/agent-show ID parsers
retain positive safe-integer validation and use the same decimal grammar; other numeric service
validators are unchanged. JSON options retain their existing non-echoing errors, repeat order and
service-specific body validation.
Single-owner parameter, step, extension, dependency and attachment lists use the native scoped endpoints and preserve
server order; these endpoints do not support paging options. vcs roots list --project <id>
filters by direct project. Local v2 coverage is 449/449 unique REST method/path pairs (100%):
235/235 GET and214/214 mutation routes; no inventory rows remain unexposed. This does not promise
every parameter/payload variant or live mutation verification. Config-parameter reset and bulk
unmute report native acknowledgement without verified postconditions. Review counters exclude the
17-route baseline; future expansion follows the authoring-review practice.
Author a job
Configure a profile first and grant Update only in the intended environment. These commands have real side effects; the example IDs and values are synthetic, and the VCS root must already exist:
teamcity-cli projects create Example --name Example --profile uat
teamcity-cli jobs create Example_Build --project Example --name Build --profile uat
teamcity-cli jobs parameters create Example_Build env.MODE --value debug --profile uat
teamcity-cli jobs steps create Example_Build --name Echo --type simpleRunner --property "script.content=echo hello" --property use.custom.script=true --profile uat
teamcity-cli jobs vcs attach Example_Build --root Example_Git --checkout-rules "+:src=>." --profile uat
teamcity-cli jobs show Example_Build --profile uat --json
teamcity-cli jobs run Example_Build --profile uat --jsonProject creation defaults its parent to TeamCity's standard _Root; --parent overrides it.
Both create commands accept --description. projects set <id> <field> <value> permits only
name, description, archived; jobs set permits name, description, paused. Booleans
must be true or false. Move with projects move <id> --parent <id> or
jobs move <id> --project <id>. No create command promises client-side upsert or conflict semantics.
Parameters use list <owner-id>, show/delete <owner-id> <name> and
create/set <owner-id> <name> --value <value>. This slice supports only non-secret values.
Password, hidden, unknown-type and credential-named values are omitted with redacted: true.
An absent/empty TeamCity type spec means a plain parameter; supported visible types are text,
select and checkbox. Full raw type specs are never returned. Create/set preflight only metadata
and reject protected, unknown or malformed metadata before writing; only create tolerates a 404.
This is a safety check, not an atomic guarantee against concurrent server changes.
Do not put credentials into plain parameters, script text or argv: name/type heuristics cannot
identify arbitrary secrets stored under innocent names. Credential provisioning is outside S1.
Steps accept --name, --type, and repeatable --property <key=value> (split on the first =;
duplicate/empty keys fail). jobs steps replace <job-id> <step-id> fully replaces writable input:
omitted properties are not preserved and no GET/merge runs first. Output omits raw plugin payloads
and script content; only a small allowlist of known, non-secret setting values is exposed.
jobs vcs replace <job-id> <root-id> --checkout-rules <rules> keeps the specified root identity.
jobs vcs checkout-rules set <job-id> <root-id> --rules <rules> changes rules only; an empty string
clears them. jobs vcs detach removes only the job attachment, never the shared root.
Root discovery returns identity/type/project summaries, never connection properties.
Deletion always targets one explicit ID/name. Deleting a project/job can cascade through its
server-owned children, settings and history according to TeamCity policy; there is no extra
client-side sweep or undo. Deleting _Root is refused. Successful empty responses return an
acknowledgement; a missing entity remains an HTTP error, not a successful delete.
Mutations are not automatically retried. HTTP errors expose status only, not remote body text;
malformed JSON errors contain no response excerpts.
builds list deliberately disables TeamCity's implicit build filter and includes default and
non-default branches. This keeps running, failed, canceled, personal and branch builds visible,
instead of reporting an older successful default-branch build.
Run, wait and diagnose
teamcity-cli builds list --job Demo_Tests --branch main --limit 20
teamcity-cli builds show --job Demo_Tests --branch main --latest
teamcity-cli jobs run Demo_Tests --branch main --param env.MODE=test --wait --timeout 10m
teamcity-cli builds wait 101
teamcity-cli builds diagnose 101Choosing a build.
--branch <name>limitsbuilds listto one branch as TeamCity names it. A name containing locator characters is sent as an explicit value condition, so TeamCity never re-parses it as locator syntax.builds showtakes exactly one selector: a numeric build ID, or--job <id> --latest, optionally with--branch. Mixing them, or giving--job/--branchwithout--latest, fails before any request.--latestreads once and returns the first finished, non-personal build in TeamCity's order (newest start first), whatever its result, including failed and canceled builds. Without--branchit searches all branches; in human mode stderr names the scope.- Queued and running builds are listed with
builds list --state queued|running. TeamCity lists queued builds in queue order, not newest first, so "latest" never means "next in queue". - Latest is resolved per call. To keep working on one build, reuse the returned ID.
Running.
jobs runsends one POST to the build queue. Without--waitit returns{ "accepted": true, "build": {...} }: the build was queued, which does not mean it will succeed.- Each
--param name=valueapplies to that one build only and never changes the job configuration. Repeat the option for more parameters; duplicate names are rejected. - Parameter values are not echoed in output or errors. Never pass secrets in argv; secret parameters are not supported here.
- If the queue response is lost or unreadable, or TeamCity or a proxy answers with a 5xx status,
the outcome is unknown. The command fails with
run.unknownOutcomeand suggestsbuilds list --job <id> --state any, preceded by the same list for the run's--branchwhen it had one. Both list builds in every state, because an accepted build can leave the queue within seconds. The command never repeats the POST. --branchselects the branch to build; without it TeamCity builds the job's default branch.
Waiting. jobs run --wait (still Update) and builds wait <id> (ReadOnly) only read the
build: GET /builds/id:<id>, every --interval (default 5s, 1s–10m). --timeout (default 10m,
1s–24h) starts when waiting starts: for jobs run --wait, once the build is accepted. It covers
queue time and each read, but not the queue request itself. Queueing and waiting use one client,
so one profile and credential. Nothing ever cancels the build. The results are:
| Result | Exit | Output |
|---|---|---|
| Finished SUCCESS | 0 | { build, outcome: "succeeded" }, plus accepted: true for run |
| Finished FAILURE or failed to start | 1 | same result with outcome: "failed" |
| Canceled | 1 | outcome: "canceled" |
| Finished without a known result | 1 | outcome: "unknown" |
| --timeout elapsed | 124 | last known state, outcome: "timedOut"; the build continues |
| Ctrl+C | 130 | last known state, outcome: "interrupted"; the build continues |
| The build disappeared during the wait | 1 | last known state, outcome: "missing" |
| A read failed after the build was accepted | 1 | the accepted build and ID, with a builds wait <id> suggestion |
Failed outcomes follow Core's contract:
- CLI: the result goes to stdout and the reason to stderr.
- JSON-RPC: an error whose
dataholdscode,exitCode,resultand follow-up argv. - Programmatic
execute: rejects with aCliErrorthat carries.result.
builds show still succeeds for a failed build: it is only a read. In human mode, state changes
appear on stderr, for example Build 101: running. JSON output is one final value.
Diagnosing. builds diagnose <id> reads the build with its test and problem counters. It adds
up to 10 problem occurrences and up to 20 failed test identities, never stack traces or build
logs. Each section is one of:
complete, titledall Nin human output when it has items, such asProblems (all 2);truncated, with more available throughbuilds problemsorbuilds tests --status failure; human output saysfirst 20 of 25;unavailable, with the reasondenied,not-foundorfailed.
An unavailable section sets partial: true and exits 1 with the data: missing information is
never reported as "no failures". The sections are read concurrently and are not an atomic
snapshot.
Ctrl+C. The packaged executable turns the first Ctrl+C into a local stop; a second one exits
immediately. A stopped wait keeps its last known state, as above. Any other interrupted command
exits 130. An interrupted read reports interrupted, and an interrupted download keeps its
message about the saved data. An interrupted write keeps its unknown outcome:
run.unknownOutcome for a queue request, request.unknownOutcome for others.
Triggers, features, dependencies and templates
These are configuration mutations gated by Update, not immediate build launches. Use the intended profile and existing upstream jobs/templates. Examples below are synthetic:
teamcity-cli jobs triggers create Example_Build --type vcsTrigger --profile uat
teamcity-cli jobs triggers list Example_Build --profile uat --json
teamcity-cli jobs features create Example_Build --type swabra --profile uat
teamcity-cli jobs snapshot-dependencies create Example_Build --source Example_Compile --property sync-revisions=true --profile uat
teamcity-cli jobs templates attach Example_Build --template Example_Template --profile uat
teamcity-cli jobs templates list Example_Build --profile uat --json
teamcity-cli jobs templates detach Example_Build Example_Template --inline-settings --profile uatTriggers/features accept create <job-id>, show/delete <job-id> <trigger-id|feature-id> and
replace <job-id> <trigger-id|feature-id>. Both create and replace require --type, accept
repeatable non-secret --property <key=value>, and default to disabled. Supply --enabled
only when activation is intended. Replacement is a full writable replacement: omitted properties
and trigger customization are not retained. No hidden GET/merge or retries occur. Trigger
buildCustomization and credential provisioning are not supported by this slice.
Snapshot dependencies use the same list/show/create/replace/delete shape, with required
--source <upstream-job-id> instead of --type for writes. Their dependency ID is the upstream
build configuration ID. Direct self-dependency is rejected locally; TeamCity validates cycles and
plugin settings. Removing a dependency never deletes its upstream job.
Trigger/dependency path IDs accept letters, digits, _ and -, not raw locators such as id:....
Feature IDs are literal encoded path segments. All three families return metadata and redacted
property names/types, not property values, even when a plugin supplies unknown fields.
Template attach preserves other attachments. By default it sends optimizeSettings=false;
--optimize-settings asks TeamCity to remove redundant local settings. Detach affects only the
named attachment and never deletes the template. By default inlineSettings=false means inherited
settings are not copied; --inline-settings copies them locally before detachment. Template
creation is available through projects templates create; jobs templates replace-all replaces
attachments in explicit order, and clear --confirm detaches all. Dependency per-property
subroutes remain unsupported.
Extended job and project configuration
Examples below change only explicitly named resources, but may affect future builds. All IDs are synthetic; activate Update only for the intended profile:
teamcity-cli jobs agent-requirements create Example_Build --type equals --parameter env.MODE --value debug --profile uat
teamcity-cli jobs artifact-dependencies create Example_Build --source Example_Compile --rules "*.zip=>artifacts" --revision lastSuccessful --profile uat
teamcity-cli jobs features fields set Example_Build FEATURE_1 disabled true --profile uat
teamcity-cli jobs steps parameters replace Example_Build RUNNER_1 --property use.custom.script=true --profile uat
teamcity-cli jobs output-parameters create Example_Build env.RESULT --value ready --profile uat
teamcity-cli projects templates create Example Example_Template --name Template --profile uat
teamcity-cli projects templates default set Example --template Example_Template --profile uat
teamcity-cli projects templates default clear Example --profile uatRequirements need --type and --parameter; --value depends on the requirement type. Artifact
dependencies need --source, --rules, and --revision; optional --revision-value, --branch
and --clean express revision selection and destination cleanup. Both rule families are active
unless --disabled is supplied. Direct self-dependency is refused; the server validates revisions,
cycles and plugin semantics. Replace is full writable replacement, not a hidden read/merge.
The fields show/set subtree allows disabled for these rules, triggers and features; steps
also allow name. Boolean text must be exactly true/false. Step/feature parameters replace
replaces the complete property set; no --property clears it. parameters set changes one named
non-secret property. These plugin reads expose metadata only, including single-property show;
they do not return raw property/script values. Output parameters reuse ordinary plain-parameter
validation, protected-metadata preflight and redaction.
Project features accept --type and repeatable non-secret properties, have no disabled toggle,
and take effect immediately. Project template creation requires an ID and --name. Clearing the
own default does not delete the template and may reveal an inherited default. jobs templates show
inspects one attachment. jobs branches is bounded with --limit/--start; aliases, tags and
project-template/feature lists preserve native scoped order without paging.
Operator commands
Pool and agent changes can alter where builds run. Pool IDs are non-negative integers; pool 0
is the default and cannot be deleted. Agent/build/change IDs must be positive integers. Pool
fields supports only name. Assigning an agent moves its pool membership; unassigning a project
removes membership, never the project. Deleting an agent requires inactivity, validated by TeamCity.
teamcity-cli pools create --name ExamplePool --profile uat
teamcity-cli pools agents assign 1 7 --profile uat
teamcity-cli pools projects assign 1 Example --profile uat
teamcity-cli agents policy set 7 selected --job Example_Build --profile uat
teamcity-cli agents enabled set 7 false --comment "Maintenance" --profile uat
teamcity-cli queue position set first --build 42 --profile uat
teamcity-cli builds pin set 42 true --comment "Investigating" --profile uat
teamcity-cli builds tags add 42 --tag review --profile uat
teamcity-cli builds statistics list 42 --profile uat --jsonagents enabled/authorized set and builds pin set accept strict true/false plus an optional
non-secret --comment. Agent fields expose only id, name, connected, enabled, authorized;
only enabled/authorized are writable. authToken is forbidden, not hidden behind ReadOnly.
Policy any forbids --job; selected accepts repeated job IDs, rejects duplicates, and with no
jobs permits none. It fully replaces selection. Policy output is {policy, jobs}. Agent/pool and
compatibility responses expose identities, not raw parameters, credentials or unmet requirements.
Queue position show accepts a positive integer, first or last; set supports only 1, first
or last and requires --build <id>. No auto-polling or implicit launch/cancel occurs. Tag add
requires at least one repeatable --tag and preserves other tags. Build tag replace with no tags
clears public tags. These operations never target private users' tags. Tag add returns a local
acknowledgement and discards any successful response body; list/replace returns names only.
Build number/status-text writes require a running build, enforced by TeamCity. Build comment set
uses --text; clear removes the comment. Build delete can delete history/artifacts under server
policy; there is no local undo. Statistics remain numeric strings to avoid precision loss and
reject nonnumeric payloads without echoing them. Generic build fields permit only id, buildTypeId,
state, branchName; status/finish-date/number use named commands. Absent agent pool and cancellation
comment normalize JSON null/empty responses to null. Other JSON endpoints decode strictly.
Pool list and pool-agent list are pages, like every other --limit/--start collection. Pool
projects, agent compatibility, queue-compatible agents, tags, statistics and direct parent changes
are native scoped lists, not pages.
Unrequested nested server/user/credential fields are excluded. Metadata itself may still be private:
do not publish real output or use it as a fixture without sanitization. There is no schema-free
JSON/HTTP escape hatch or unbounded global queue clearing. Later sections describe the explicit
credential/cloud/file surfaces.
Bulk configuration and queue controls
replace-all replaces complete collections; omitted items are removed. Job steps/features/triggers,
requirements/dependencies/VCS entries and project features accept repeated --item <json> with
strict family-specific fields, not raw REST bodies. Parameters use repeated --property name=value;
metadata preflights prevent replacing protected settings. parameters clear --confirm deliberately
deletes all own parameters, including protected ones. value/type/raw-type show/set only exposes
plain values or safe type names; raw secret/type text is never returned.
Project pools, pool projects, project child ordering and template attachments have explicit full
replacement commands. queue delete-page --job Example_Build --limit 10 --confirm deletes only
one bounded page for one job. queue delete <id> removes cancellation history as well, unlike
queue cancel. queue reorder --build <id> preserves explicit ID order. Queue pause requires a
reason, and queue approval approve <id> approves only that build as the current user, not its chain.
All these writes require Update and are mock-tested, not exercised against a live server.
Build triage and evidence
builds batch requires repeated --build <id> (distinct IDs). Status and show are reads;
cancel, delete, comment, pin and tags are Update operations.
A batch write returns { count, errorCount, items: [{ buildId, succeeded }] }. TeamCity marks only
the failed items; its per-item message is never shown. Counts that disagree with the items mean
success is unknown, and the command fails. When any item fails, the command exits 1 with the
batch.partial code, and the complete per-item result is still printed. Two other writes can
succeed partly:
builds set-statuswith reported errors exits 1 withbuild.statusPartial;builds vcs-labels addwith aFAILEDlabel exits 1 withlabels.partial.
finish/finish-at return accepted timestamps, not proof of completion. start-agentless starts
queued work without an agent. Log append rejects service-message controls. VCS labels mutate an
external VCS and require one --root-instance.
Investigations use a strict typed item, for example:
{"target":{"kind":"test","projectId":"Example","testId":"9223372036854775807"},"state":"TAKEN","assignee":3,"resolution":"whenFixed","comment":"Synthetic example"}Pass it with investigations create --item <json> or replace; create-many repeats items.
Target may instead be {"kind":"job","jobId":"Build"} or a project/problem target.
Show/delete take only --target <json>. Replacement is server-side delete/create and not atomic.
The CLI validates the entire body first and never retries that mutation.
Mutes use {"project":"Example","tests":["123"],"resolution":"manually"}; replace project with
jobs:["Build"], tests with problems:["123"], or choose whenFixed/atTime (time required).
One scope/target kind, no implicit all-problems mute. Create/create-many/show/delete are explicit.
Tests/problems expose list/show/occurrence; occurrence requires --build. IDs stay strings.
Runtime builds output-parameters/resulting-properties list and changes attributes return
names only. Runtime parameter exists <build-id> <name> discards the scalar body without output,
hash or file; 404 stays an error because it can mean a missing build too. Native build occurrence
collections are unpaged and excluded from the fixed real-service proof.
Accounts, access policy and issued tokens
User/group/node writes require Admin, token issuance/revocation/local owned-token cleanup requires Credentials. Both categories default off; granting Update does not enable either. They are local safety gates, not remote authorization. No live administrative proof runs in CI.
users exposes bounded list, identity-only create/update/delete, direct groups, roles,
resolved permissions, property names/existence, safe identity fields and remote session commands.
groups exposes identities, direct parents, roles and properties. User update preserves omitted
collections; role/group replacement is explicit and can be non-atomic. No items clears direct
assignments; the server retains All Users membership. Role scope is exactly one --global or
--project <id>. Replace uses repeated --item '{"roleId":"R","project":"Example"}'.
Password/email workflows are not exposed; username-only create does not guarantee login.
users tokens is current-user only. To issue into the selected profile's OS keyring:
teamcity-cli users tokens create ExampleToken --alias automation --expires 20991231T235959+0000 --restriction '{"permission":"VIEW_PROJECT","project":"Example"}' --profile uatExpiry or explicit --no-expiration is required; choose restrictions or explicitly
--same-permissions. The latter inherits the user's remote rights, not the local ReadOnly gate.
The one-time value is stored as a private issued-token:<alias> secure record and never printed.
Existing aliases fail before HTTP and never overwrite the active auth credential. The preflight
is not an atomic reservation across multiple processes. If storage fails after issuance, the
command reports it without secrets; revoke the named remote token explicitly, without retrying.
users tokens delete <name> --alias automation first checks the alias's recorded remote name,
revokes remotely, then removes that owned record. Without alias it only revokes remotely.
users tokens forget --alias automation removes only the local record, not the remote token.
Forget owned aliases before deleting a profile; factory auth logout/profile cleanup does not
discover arbitrary issued aliases. If a profile was removed first, recreate that same profile name
and use explicit forget. No unrelated credential is silently deleted.
server nodes omits internal URLs. Responsibility set accepts only CAN_PROCESS_BUILD_MESSAGES
and true/false; the response describes enabled configuration, not effective transition.
server api-version is distinct from the local CLI --version.
Cloud, VCS and versioned settings
cloud profiles/images/instances describes remote cloud profiles, not CLI connection profiles.
Image/instance commands require --cloud-profile, --image and, when applicable, --instance.
Start may incur cost. Stop schedules graceful shutdown; delete/force-stop require --confirm
and may interrupt running work. Acknowledgement is not proof of the final provider state.
vcs roots create currently provisions anonymous Git with explicit HTTPS URL/branch/project/name.
Credential-bearing URLs are rejected. Property reads return names/existence only; replace/clear
operate on the whole native map and can break attached jobs. vcs instances exposes polling,
commit-hook notifications, saved branch/revision state and allowlisted fields. notify-commit
reports scheduling only for HTTP202, not other successful statuses.
projects versioned-settings covers configuration/effective configuration, typed scalar fields,
context parameters, check/commit/load/status and secure-value mappings. Config replacement requires
an explicit synchronization mode; enabled mode also requires VCS root and format. Omitted fields
use documented TeamCity defaults (current build settings, UI editing on, changes off, portable
DSL on); secure values stay outside VCS. An existing-root conflict requires the caller to choose
importDecision, never an automatic overwrite decision. Commit/load need confirmation.
Context replacement removes omitted entries; values are never returned in context lists.
config fields reset <project> vcsRootId --confirm attempts the documented native DELETE.
Older official server code can fail after changing configuration; current-server successful
clear semantics are not verified. resetRequested means actual HTTP2xx only, not a verified clear.
Inspect configuration before retrying; the CLI does not retry or claim rollback.
Explicit credential inputs
Use credentials import <alias> --env <VARIABLE_NAME> to import only the named environment
variable into the current profile's OS keyring. Requires Credentials; existing aliases fail.
No raw credential argv, environment scanning, plaintext fallback or automatic auth-token reuse.
credentials forget <alias> removes only that input entry. Versioned-settings token mappings use
--mapping remote-name=alias; lists expose names and deletes retain local aliases.
| Secure namespace | Purpose | Explicit cleanup |
|---|---|---|
| token | CLI authentication | auth logout |
| issued-token:<alias> | Named REST access token plus ownership metadata | users tokens forget --alias <alias> |
| input-secret:<alias> | Explicit inputs, resolved values, pool registration tokens | credentials forget <alias> |
| secure-reference:<alias> | Project secure-value reference, not its underlying value | projects secure forget-reference <alias> |
Clean up owned aliases before deleting their profile. Recreating the same profile name permits explicit cleanup if necessary; unrelated entries are never silently erased. Namespace/alias preflight is not a cross-process atomic reservation. A remote operation and local storage are not atomic: failure after remote success reports partial/unknown outcome without automatic retry.
Server administration and deployments
audit returns action/timestamp metadata without comments or related private payloads. health
requires exactly one project/global scope; detail also requires a category and fails if not unique.
roles defines server roles; user/group roles assigns them. Create takes a name and optional
permission/include IDs, never a caller-supplied role ID. Role writes require Admin.
deployments creates/deletes dashboards within projects. Instance upsert supplies an explicit
initial state/date history; append-state adds one entry. State uses uppercase native values
and TeamCity timestamps. Parent IDs remain explicit. Destructive/upsert commands require confirmation.
server authentication replace --item <json> --confirm replaces the complete module list and
can lock out users. Omitted scalar settings are preserved; missing modules are an error. Typed
modules accept non-secret properties plus secrets:[{"name":"password","alias":"directory"}].
It never reads and rewrites masked existing credentials. server settings set is a typed
numeric/boolean partial update, excluding encryption changes, paths and URLs.
server cleanup configure changes a future schedule, not immediate cleanup. server backup start
schedules a timestamped config/database backup, excluding logs/personal changes/running builds/
supplementary data; returned filename is not completion proof. License commands use input-secret
aliases even where native REST embeds a key in the URL. Server fields are allowlisted; no
superuser-token field. XML-only server rest-plugin is projected to ordinary JSON metadata.
pools tokens create <id> --ttl <seconds> --store-as <alias> mints one-time registration tokens,
not user access tokens. Repeat aliases for a batch; TTL is an explicit positive safe integer in seconds.
Every returned token must be stored before success is reported. Values are never printed.
mutes delete-many --id <id> --confirm accepts distinct explicit IDs, preflights each
full scope/target/resolution and sends one native Mutes DELETE. Current docs define body/void
response but do not establish handler identity/atomicity. Actual2xx returns
serverAcknowledged:true,postconditionVerified:false, not a claimed deletion count. No retry or
rollback claim. Like config reset, this is documented native action coverage, not live proof.
Files, server paths and secure resolution
The four trees are jobs files, builds artifacts, vcs instances files and server files.
Each has list/children/metadata/download/archive. Server areas are explicitly logs, backups or
dataDirectory; no custom areas. Relative paths reject traversal, encoded segments, absolute/UNC
paths and backslashes. Lists/ZIP selection are bounded, nonrecursive, exclude hidden files and do
not browse archives. No automatic href traversal. Artifact reads disable usage recording and
parameter resolution; job VCS reads disable parameter resolution.
teamcity-cli builds artifacts list 12345 --limit 20 --profile uat --json
teamcity-cli builds artifacts download 12345 dist/application.zip --output application.zip --profile uat --json
teamcity-cli builds source 12345 src/example.ts --output example.ts --profile uat --json
teamcity-cli users avatar download 123 --size 64 --output avatar.png --profile uat --jsonDownloads require a new basename and save only below
AppArguments.AppDataDirectory/downloads, using Core's shared identity-checked private staging
under TempDirectory. Result is
{path,bytes,sha256,mediaType} for an actual retained file. Downloads have no default byte limit.
An optional --max-bytes sets a caller-selected budget with no tool-imposed upper ceiling. HTTP206 partial responses are
rejected without publication or automatic retry; downloads do not support ranges/resume.
No overwrite, redirect,
symlink/junction escape, automatic opening, execution or extraction. Atomic no-clobber publication
requires same-profile hard-link support; unsupported filesystems fail closed. PNG/ZIP/SVG types
and signatures are validated; SVG/icon success says nothing about the build's success.
The fresh staging directory and file use private POSIX modes/current-user ACLs; the published hard
link retains the file protection. Core never changes AppData ancestor/download-directory ACLs and
does not repair a user data root deliberately made public. Detectable directory/staged/destination
replacement is rejected without deleting unknown files. A same-user process can still race after
the final identity check. Data can be sensitive:
do not publish downloads or turn them into fixtures without explicit sanitization. File cleanup
follows profile AppData semantics; it is not permission to silently erase existing user data.
The local, ungated downloads list, downloads delete <name> and downloads clean built-ins
inspect or explicitly remove saved files: only regular files directly in the selected profile's
downloads directory, never links, subdirectories, staging or other profiles.
settings-path on jobs/projects/VCS roots and builds artifacts-path return a typed absolute
SERVER path. They neither download XML nor read that path on the local machine.
The following require Credentials even though two native routes are GET:
teamcity-cli projects secure create-reference Example --value-secret input --store-as reference --profile uat
teamcity-cli projects secure resolve Example --reference reference --store-as resolved --profile uat
teamcity-cli builds resolve-parameter 12345 env.MODE --store-as resolved-mode --profile uatValues go only from/to profile keyring aliases, preserving whitespace. A parameter expression returned unchanged is unresolved, not success. Aliases are collision-checked; no secrets in JSON, RPC, diagnostics or files. These routes and sensitive downloads are excluded from live proof.
Profiles and authentication
The public CLI has no compiled-in TeamCity URL. The virtual default profile exists immediately,
but service commands cannot run until its required URL and authentication mode are configured.
Configure separate profiles whenever URLs or security realms differ:
teamcity-cli profile configure uat --url https://teamcity-uat.example.com --token-stdin
teamcity-cli profile configure production --url https://teamcity.example.com --token-stdin
teamcity-cli profile set-default production
teamcity-cli auth status --profile production --jsonIn an ordinary terminal, running teamcity-cli or a service command with an incomplete profile
starts the same guided configuration. JSON, JSON-RPC, redirected/non-TTY input, and programmatic
execution never prompt; they fail before networking and print an actionable profile configure
command.
JetBrains currently exposes a public TeamCity server that supports guest REST reads. Create it as an explicit demo profile; the CLI never selects it or contacts it automatically:
teamcity-cli profile configure jetbrains-demo --url https://teamcity.jetbrains.com --guest
teamcity-cli server status --profile jetbrains-demo --jsonGuest mode uses TeamCity's /guestAuth/app/rest path and sends no Authorization header. Its
available data and uptime are controlled by JetBrains. To convert a guest profile to token auth,
reconfigure it with --no-guest --token-stdin.
Use profile set <name> --url <url> to update an existing profile. A default profile always
exists. To remove one, make another profile default first and then run profile delete <name>;
the final remaining profile cannot be deleted.
For token profiles, configuration validates the token through the current-user REST endpoint before
the common package stores it in the platform credential store. auth login remains available for
credential rotation. Tokens never enter profile JSON. For automation, pipe the token to
--token-stdin or set TEAMCITY_TOKEN; do not put it in a command-line argument.
Permission fail-safe
ReadOnly is enabled by default. Ordinary service writes require profile-specific Update;
administrative writes require Admin and credential workflows require Credentials, both separately
disabled by default. Categories are not hierarchical. For example:
teamcity-cli permissions list --profile uat
teamcity-cli permissions grant Update --profile uat
teamcity-cli jobs run Example_Build --profile uat --branch main --json
teamcity-cli permissions revoke Update --profile uatGranting Update in UAT does not grant it in Production. This local gate reduces accidental AI
actions; it does not replace TeamCity's own authorization.
Human output
Help leads with everyday work:
- The root lists
Everyday(builds,jobs,projects,queue,agents), thenTriage,Administrationand Core's localConfiguration. builds --helpstarts withlist,show,wait,diagnose,tests,problemsandchanges, followed byFiles,ControlandEvidence.jobs,projects,queueandagentsstart with their everyday commands: list and show, plusjobs runandqueue cancel.- Examples appear at the root and on
jobsandbuilds.
A typo such as teamcity-cli bulds is reported as an unknown command with a suggestion. An argument
error shows one usage line, not the full help. Commander's help <name> with an unknown name still
prints the root help.
Some commands print a compact view instead of every field:
builds listprints a table: BUILD, JOB, BRANCH, STATE, RESULT, AGE. RESULT is shown only for finished builds; a running build's intermediate status is not a result.builds showprints a summary withNext:suggestions:builds waitfor a queued or running build,builds diagnosefor a failed one, and the artifact list for a finished one.jobs runandbuilds waitprint the same summary with the outcome. The title saysQueued:only while a new build still waits in the queue.builds testsprints STATUS, FLAGS (new, muted, ignored, investigated), DURATION and TEST. Test details and stack traces appear only in--json.builds problemsprints TYPE, FLAGS and a one-line DESCRIPTION, worded asbuilds diagnosewords it.- File listings print NAME, SIZE and MODIFIED.
- Downloads print the full local path, byte count and SHA-256 of the saved file.
In a terminal, builds list fits its width by shortening only BRANCH, builds problems only
DESCRIPTION, and Core's downloads list only its PATH. Build and job IDs, test names and file
names are never cut; an impossible fit wraps. Redirected output is not shortened. --json and
JSON-RPC return the same complete data as before.
Machine-oriented output
Append --json to any leaf command for stable machine-readable output:
teamcity-cli builds list --state running --limit 20 --json
teamcity-cli builds problems 12345 --jsonFor several calls in one Node process, start teamcity-cli --json-rpc and send one JSON-RPC 2.0
object per line:
{"jsonrpc":"2.0","id":1,"method":"cli.execute","params":{"argv":["server","status","--profile","uat"]}}
{"jsonrpc":"2.0","id":2,"method":"cli.execute","params":{"argv":["jobs","list","--profile","production"]}}With --json, a failure writes one line to stderr and nothing to stdout, except the result of a
failed outcome:
{"error":{"code":"permission.denied","message":"Permission 'Update' is disabled for profile 'uat'.","exitCode":1,"profile":"uat","next":[["permissions","grant","Update","--profile","uat"]]}}JSON-RPC returns the same fields, apart from message, in error.data, and adds result for a
failed outcome. Every next argv can be sent back to cli.execute unchanged. TeamCity adds these
codes to Core's usage, usage.jsonRpc, permission.denied, profile.notFound,
profile.notConfigured, interrupted and error:
| Code | Meaning |
|---|---|
| http.unauthorized, http.forbidden, http.notFound, http.conflict, http.rejected, http.serverError | TeamCity answered with that HTTP status, also for a refused download; the response body is never read or shown |
| request.failed | A read was lost in transit; nothing was changed, so it can be retried. A lost download reports Core's download error instead |
| request.unknownOutcome | A write was lost in transit; it may or may not have been applied, and is never repeated |
| run.unknownOutcome | The queue request's outcome is unknown; check builds list --job <id> --state any |
| build.failed, build.canceled, build.unknown, build.missing | Waited build outcome, with the build as data |
| wait.timeout (exit 124), wait.interrupted (exit 130), wait.failed | Observation stopped; the build continues on the server |
| diagnose.partial, list.incomplete, batch.partial, build.statusPartial, labels.partial | Partial data or a partial write, printed in full and exiting 1 |
Tests and local integration proof
The default suite is offline. MSW verifies the native fetch boundary, including exact REST
methods, paths, locators, requested fields, mutation bodies, permission denial before networking,
profile isolation, and credential-safe errors.
Run the integration suite through the repository command:
npm testFor development/debug proof-of-work, configure a real local profile once and run:
npm run test:integration --workspace @eyeauras/teamcity-cli -- --profile <name>This command builds Core and TeamCity and then runs the compiled CLI as child processes. It uses the selected current-user profile and its OS-keyring credential, not test-only URL/token inputs. The fixed 19-row inventory covers authentication, permission inspection, server, bounded projects, jobs, builds/tests/problems/changes, the latest finished build of a listed finished build's job, queue, agents, VCS root discovery and a persistent JSON-RPC session. It deliberately excludes unpaged scoped parameter/step/extension/dependency/attachment lists; their contracts and all authoring/operator mutations are covered offline, not claimed as live-verified. All new v2 routes are contract-tested offline; the fixed live inventory was not broadened.
Only ReadOnly service commands are invoked, with at most three items per collection. Empty lists
are valid and dependent detail checks are explicitly skipped. Output contains pass/count/skip
summaries, never raw payloads or discovered identifiers. No fixture or artifact is recorded.
The proof is outside npm test and refuses CI/CD environments before launching the CLI. It is a
local development tool, not a regression suite or production availability monitor.
The shared Core proof invoker limits each child to 30 seconds and each stdout/stderr stream to
64 KiB. Oversized responses now fail instead of using unbounded capture. It strips inherited
TEAMCITY_TOKEN case-insensitively and applies the shared CI preflight.
See the integration authoring guide for how this product references the common package and how to start the next in-repo or external integration.
