feat(actors): add apify actors exists, ls --name-only, and improve push name-collision UX#1260
Draft
DaveHanns wants to merge 1 commit into
Draft
feat(actors): add apify actors exists, ls --name-only, and improve push name-collision UX#1260DaveHanns wants to merge 1 commit into
apify actors exists, ls --name-only, and improve push name-collision UX#1260DaveHanns wants to merge 1 commit into
Conversation
…actors ls`, and improve name-collision UX on push
Scripting-friendly name checks
------------------------------
`apify actors ls` and `apify push` are used in automation (CI, agent
scripts, template scaffolding) that needs to answer "is this Actor name
free?" before creating one. Today there is no built-in way to do that:
- `apify actors ls --my` prints a decorated table (Runs / Last run /
Build columns) intended for a terminal, and does an extra `.get()` +
runs list per row for hydration. Callers scripting a collision check
have to reach for `curl` against `/v2/acts?my=1` or grep the table.
- There is no cheap "does this name exist?" primitive at all, so
scripts fall back to grepping `apify actors info <name>`'s error
output.
This adds:
- `apify actors ls --name-only` — prints one `<username>/<name>` per
line and short-circuits before the per-row hydration `Promise.all`.
Composes with `--my`, `--limit`, `--offset`, `--desc`.
- `apify actors exists <name>` — new subcommand. Exits 0 if the Actor
exists, 1 if not. Accepts a short name (resolved in the logged-in
user's namespace), a fully qualified `<username>/<name>`, or an
Actor ID. `--json` emits `{ exists, query, resolvedId, actor }`.
Combined, `apify actors exists my-crawler || apify push` is now the
canonical scripted collision check, replacing the raw-API workaround.
`apify push` collision UX
-------------------------
Two changes to make destination + failure modes obvious in the CLI
transcript:
1. When `apify push` finds an existing Actor under the current user's
namespace and reuses it, it now emits an `Info:` line
("Updating existing Actor <username>/<name> (id=…)."). Previously
this branch was silent and looked identical to creating a new
Actor, so a user who forgot they had already claimed a name would
silently overwrite their prior version.
2. When `apifyClient.actors().create()` fails with a name-conflict
error (reserved name, race with a parallel process, etc.), the raw
API message ("Actor name is already used") is now rewritten with
actionable next steps: rename in `.actor/actor.json`, push to an
existing Actor by id, list your own names with the new
`--name-only` flag, or query with `apify actors exists`.
Surfaced during an evaluation of Apify surfaces for agent-driven Actor development.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.
Summary
Adds two small, scripting-friendly primitives for asking "is this Actor name available?" and improves the CLI transcript when
apify pushreuses an existing Actor or fails on a name conflict.apify actors exists <name>— exits0if the Actor exists,1if not. Accepts a short name (resolved in the logged-in user's namespace), a fully qualified<username>/<name>, or an Actor ID.--jsonemits{ exists, query, resolvedId, actor }.apify actors ls --name-only— prints one<username>/<name>per line and short-circuits before the per-row hydrationPromise.all(which normally fetches an extra.get()+ runs list per row for the table). Composes with--my,--limit,--offset,--desc.apify pushtranscript when the Actor already exists in your namespace: previously silent — now emitsInfo: Updating existing Actor <username>/<name> (id=…).apify pusherror whenPOST /v2/actsfails on a duplicate/reserved name: the raw API message (e.g.Actor name is already used) is rewritten with actionable next steps.Motivation
apify actors ls --myandapify pushare the entry points people reach for when scripting Actor management (CI pipelines, template scaffolders, agent-driven tooling). Two workflows are awkward today:1. "Is this name free?" has no first-class answer
apify actors ls --myprints a decorated table (Runs / Last run / Build columns) that is hard to grep and, per row, does an extraapifyClient.actor(id).get()+ runs list fetch just for the display. Callers scripting a collision check end up reaching for the raw REST API instead — e.g.With this PR:
And the canonical scripted "create only if free" pattern becomes:
apify actors exists my-crawler || apify push2.
apify pushis silent when it reuses an existing ActorThe relevant branch in
src/commands/actors/push.tstoday (before this PR) is:If a user (or an automated pipeline) forgets they have already used a given name in their namespace, the push silently overwrites the previously deployed Actor with no visible signal. After this PR the transcript shows explicitly:
3.
POST /v2/actsduplicate-name errors are opaqueThe pre-flight
apify.actor("<user>/<name>").get()doesn't catch (a) reserved / conflicting names, or (b) a race where a parallel process claimed the name between the.get()and the.create(). When that happens the surfaced error is the raw API message with no next step:After this PR:
Design notes
exists:0= exists (name is taken),1= not found. Chosen so a bareapify actors exists foo && echo takenidiom works without extra flags, matchingtest,grep,gh repo view,kubectl get.exists: if the argument has no/and doesn't look like an Actor ID (/^[A-Za-z0-9]{17}$/), it's resolved as<logged-in-user>/<name>, soapify actors exists my-crawlertargets the user's own namespace by default.--name-onlyshort-circuits before hydration: the existinglscode path awaits a per-rowapifyClient.actor(id).get()+ runs list for the table. When the caller only needs identifiers, that work is wasted (and can be slow for--limit 100), so--name-onlyreturns straight fromapifyClient.actors().list().pushis defensive: the name-conflict detection matches onalready used,not available, orduplicatein the error message. If the pattern doesn't match, the original error is re-thrown unchanged.Test plan
apify actors ls --my --name-onlyprints<username>/<name>lines and exits0.apify actors ls --my --name-onlywith zero Actors prints nothing and exits0.apify actors ls --my --name-only --jsonstill returns the JSON payload (the flag is a no-op under--json).apify actors exists <name-you-own>→ exit0, printsActor "<user>/<name>" exists (id=…)..apify actors exists <name-that-does-not-exist>→ exit1, printsActor "<user>/<name>" does not exist..apify actors exists <name> --json→ exit0/1and JSON withexists: true/false.apify actors exists apify/hello-worldresolves the fully-qualified name (doesn't prepend the logged-in username).apify pushin a directory whose.actor/actor.jsonnames an Actor the user already owns printsInfo: Updating existing Actor ….apify pushwherePOST /v2/actsfails with a duplicate-name response prints the rewritten error with the four suggested next steps.pnpm tsc --noEmitpasses.Files changed
src/commands/actors/exists.ts— new command.src/commands/actors/ls.ts— add--name-onlyflag; short-circuit before hydration.src/commands/actors/push.ts— emitInfo: Updating existing Actor …; rewrite name-conflict errors with actionable next steps.src/commands/actors/_index.ts— registerActorsExistsCommand.Surfaced during an evaluation of Apify surfaces for agent-driven Actor development.