You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(cli): cursor-based pagination (--limit/--continue) for list commands - #344
Moves the CLI's list commands onto cli-engine's cursor pagination model (--limit as page size, --continue <token> for the next page). Each response carries a cursor block (has_more, continue_from, and total/remaining where the backend reports them) and a ready-to-run next action, replacing the earlier --offset windowing and its pagination block. No cli-engine bump is needed: origin/main already pins 0.10.2, which includes the feature.
What changed
Backed by the service's own cursor or page token
domain list (v3 page token), platform app list (App Registry Relay cursor)
Paged client-side over the full response (--offset jumps straight to a position)
api parameter list, api response list, api search
platform actions list
hosting domain list
Continuation behavior
The engine's next-page action replays the command's own arguments, so after a direct jump it reads … --offset 20 --continue <token>. --continue takes precedence over --offset/--page, so that action advances as printed (regression tests execute it).
Tokens escape % and , in values, so an opaque server page token round-trips exactly. A --continue value this command did not issue is rejected with a validation error and a fix instead of quietly restarting from page one: a truncated token, a key the command never produces (for example a dns list token given to platform actions list), a non-numeric value, a page size outside the command's range, or a page below 1. A blank --continue still starts from the beginning.
--page on dns list and email list must be at least 1, and --offset on the client-side lists must be 0 or more; both fail at argument parsing.
For the client-side commands the global --filter is applied to the whole collection before slicing, so offsets, cursor.total and cursor.remaining reflect the filtered rows.
Data accuracy
dns list reports cursor.total/cursor.remaining when the API returns a total.
email list decides whether another page exists from the response's rel=next link, not from the item count.
Hosting app/deployment/log lists report cursor.total when the service sends totalItems.
Supporting changes
ApplicationsList.graphql now takes $first/$after and returns pageInfo; the platform app client and its tests were ported onto the generated client.
Shared token helpers in pagination_token.rs, and page_items/page_continuation/page_limit helpers for hosting.
How the user experience changes
Across every converted list
Before
After
Page size
--limit was a window over the full result (for several commands the default was "everything"; --limit 0 meant "all")
--limit is the page size and must be at least 1; --limit 0 is rejected. Every command has a default and a maximum (below)
cursor: {limit, count, has_more, continue_from?, total?, remaining?} (total/remaining only where the backend reports them)
Next-step hint
gddy … --limit 3 --offset 3
gddy … --continue "limit:3,offset:3" (opaque token; for server-backed lists it is the service's own cursor)
Jumping to a position
--offset on every list
--offset on client-side lists, --page on dns list and email list; none on lists backed by a forward-only server cursor
--continue takes precedence over --offset/--page, so the next-step hint can be run exactly as printed even after a jump.
A --continue value that isn't one the command issued is now an error rather than "start over", so a bad copy/paste can't silently repeat page one. The Unicode/punycode name display (--punycode) works on the paged domain list, dns list and hosting domain list, and the printed next-step hint carries the flag.
Command by command
Command
Before
After
platform app list
Everything by default; --limit/--offset sliced the result (max 200)
One page of 50 (max 200) from the App Registry's Relay cursor; --continue
domain list
Everything by default (up to 10,000 domains, fetching every page); --limit/--offset (max 500); reported total
One page of 100 (max 200) using the service's page token; --continue. No total: the v3 API does not return one
dns list
Every record by default (all pages fetched); --limit/--offset (max 500)
One page of 50 (max 100); --page N to jump; cursor.total and cursor.remaining from the API
email list
25 by default (max 500), --offset
25 by default (max 100); --page N to jump; "more pages" now comes from the response's next link, not the item count
hosting app list
All pages fetched by default; --limit was a total cap
One page of 50 (max 100); --limit is the page size; --continue
hosting deployment list
All pages fetched by default; --limit was a total cap
One page of 50 (max 100); --limit is the page size; --continue
hosting log list
All pages fetched by default; --limit was a total cap (max 500)
One page of 100 (max 500); --limit is the page size; --continue
hosting domain list
All domains by default; --limit capped the total
One page of 50 (max 200), paged client-side over the full list; --limit, --offset, --continue. Reads every page the service offers, but only as many as the requested window needs (not with --filter)
hosting subscription list
Looped on a next link while ignoring the token (could re-fetch page 1 indefinitely); --limit shortened the list
Reads every page the service offers (bounded), so slot totals and the suggested plan cover all plans; --limit still only shortens the list shown
api parameter list, api response list
20 by default (max 200), --offset
Same default and maximum; --continue, and --offset still works
api search
Everything by default (max 100)
One page of 50 (max 100); --continue, and --offset still works
platform actions list
50 by default (max 200), --offset
Same default and maximum; --continue, and --offset still works
--filter on the client-side lists (api parameter/response list, api search, platform actions list, hosting domain list) is applied to the whole collection before paging, so offsets and counts reflect the filtered rows. On server-backed lists the engine still filters the page the backend returned, so a filtered page can be shorter than --limit.
Examples
Captured from real runs of this branch and of origin/main (0.2.31); platform actions list needs no network.
gddy platform actions list --limit 3
before pagination: {"total":7,"offset":0,"limit":3,"count":3,"has_more":true}
next: gddy platform action list --fields "name,description" --limit 3 --offset 3
after cursor: {"limit":3,"count":3,"total":7,"remaining":4,"continue_from":"limit:3,offset:3","has_more":true,"self_sufficient_limit":true}
next: gddy platform action list --fields "name,description" --continue "limit:3,offset:3"
gddy platform actions list --limit 3 --offset 3 (a jump, then the printed next step)
before next: gddy platform action list --fields "name,description" --limit 3 --offset 6
after next: gddy platform action list --offset 3 --fields "name,description" --continue "limit:3,offset:6"
(runs as printed: the token wins over the replayed --offset)
--limit 0, which used to mean "all"
before gddy api search commerce --limit 0 -> all 37 rows
after gddy api search commerce --limit 0 -> error: invalid value '0' for '--limit <limit>': limit 0 must be greater than 0
gddy dns list <domain> --limit 1 on a real zone (test environment)
before pagination: {"total":7,"offset":0,"limit":1,"count":1,"has_more":true}
next: gddy dns list <domain> --fields "type,name,data,ttl" --limit 1 --offset 1
after cursor: {"limit":1,"count":1,"total":7,"remaining":6,"continue_from":"<token>","has_more":true,"self_sufficient_limit":true}
next: gddy dns list <domain> --fields "type,name,data,ttl" --continue "limit:1,page:2"
gddy domain list --limit 5 followed through --continue, against an account with 13 domains (test environment): pages of 5, 5 and 3 rows, 13 distinct domains, no duplicates, has_more false on the last page.
Hosting domain and subscription lists
The Hosting spec types listDomains and listSubscriptions as paged but declares no paging request parameters, and the service rejects pageSize/pageToken on GET /subscriptions with a 400 (checked against the test environment). The hosting team confirmed any next links will follow the API collections-and-pagination standard. The old code looped on a next link while the client discarded the token, so a next link would have re-fetched page 1 indefinitely.
Because the services don't paginate these endpoints yet, both commands gather the whole collection and work from it, so they are ready when next links start to appear:
The first request carries no paging parameters. If the response has a links[rel=next], its query string is replayed unchanged on the same path, host and credentials (never a host or path taken from the link), up to 20 pages. Each followed page goes through the same generated type as the first.
hosting domain list then pages that complete list client-side (--limit, --offset, --continue), and stops reading once it holds enough for the requested window. With --filter it reads everything, and cursor.total/cursor.remaining are left out whenever it stopped before the end.
hosting subscription list always reads every page, because its slot totals and the plan it suggests are only right over all plans. Its output shape is unchanged.
A link that can't be followed (nothing to replay, a repeated link, or the page cap) ends the listing and prints a warning to stderr instead of failing.
platform app list prints the same kind of warning if the App Registry reports another page but returns no cursor. Follow-up: DEVEX-1340 asks the hosting team to declare the paging parameters in the spec; once the services paginate, these two commands switch to the service's own continuation token in a follow-up release. When cli-engine#131 (CommandResult::with_warning) lands, the stderr warnings move to a structured result warning.
Test plan
Merged with the latest main (internationalized domain names, auto-renew and transfer-lock commands); the paged lists keep --punycode
cargo check --workspace
cargo clippy --workspace -- -D warnings
cargo test --workspace
cargo fmt --check
./rust/scripts/check-module-size.sh
--help renders --limit/--continue (and --offset/--page where applicable) on every converted command
Next-page action executed as printed after an --offset jump (api parameter list, platform actions list); --filter plus offset jump plus continuation on platform actions list
Live (test environment, read-only): hosting subscription list output unchanged; domain list walked across three real pages (5, 5, 3 rows; 13 distinct, no duplicates); dns list reports total/remaining on a real zone
Not exercised live: multi-page continuation for dns list, email list, platform app list and the hosting lists (the test account has too little data or no apps), and following next links on the hosting domain and subscription lists (no endpoint returns one today; covered by mock-server tests)
…ands
List commands now page with cli-engine's cursor model. Each page reports a
`cursor` block (`has_more`, `continue_from`, and `total`/`remaining` when the
backend knows them) plus a ready-to-run next action, replacing the earlier
`--offset` windowing and its `pagination` block. `--limit` is the page size;
`--continue <token>` fetches the next page.
Backed by the service's own cursor or page token:
- domain list (v3 page token), platform app list (App Registry Relay cursor)
- dns list and email list (page number)
- hosting app list, deployment list and log list (page token)
Paged client-side over the full response, with `--offset` to jump straight to
a position:
- api parameter list, api response list, api search
- platform actions list
- hosting domain list
Defaults when `--limit` is omitted: platform app list 50, domain list 100,
dns list 50, email list 25, hosting app/deployment list 50, hosting log list
100, hosting domain list 50. Each command has a maximum page size.
`hosting domain list` and `hosting subscription list` call endpoints whose
spec declares no paging parameters, and the service rejects `pageSize` and
`pageToken` there. They make one request, and print a warning to stderr if the
response reports a further page instead of silently showing a partial list.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…aging
- A `--continue` token now takes precedence over `--offset`/`--page`. The
next-page action replays the command's own arguments, so after a direct
jump it reads `... --offset 20 --continue <token>`; the two flags no longer
conflict and the token decides the page, so the action advances instead of
failing to parse or repeating the same page.
- `dns list --page` and `email list --page` reject 0 at parse time, since pages
are 1-based.
- `dns list` reports `cursor.total` and `cursor.remaining` when the API returns
a total.
- `email list` decides whether another page exists from the response's
`rel=next` link rather than from the item count, so a full final page no
longer offers an empty next page.
- The incomplete-list warning test asserts the shipped wording, and the
warning's formatting matches rustfmt.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Unresolved cursor preservation, limit validation, and pagination metadata issues can produce incorrect or incomplete results.
No open issues 🎉
4 previously missed issues
In code that hasn't changed since last review
Escape opaque page tokens in continuation encoding
rust/src/domain/list.rs:221
The domain API's page token is opaque, but this comma-delimited encoding does not escape it. A next link containing pageToken=abc%2Cdef produces token:abc,def,direction:forward; decode_resume then sends abc instead of abc,def on the next request. Encode the token value and decode it symmetrically so continuation preserves the server's cursor exactly. Add a round-trip regression covering commas, percent signs, and plus signs.
Preserve optional totalItems in paginated responses
rust/src/hosting/common.rs:325
This drops totalItems even when the hosting response supplies it. The hosting spec's shared PagedList includes that field and the client preserves it, but application, deployment, and log lists return cursors without total. This contradicts the PR's stated contract of including counts where the backend reports them. Carry the optional total into the continuation and leave it absent when unavailable.
Validate inherited continuation page-size limits
rust/src/pagination_token.rs:132
Limits read from --continue bypass the command's page-size bounds in both paginate_known_collection and resolve_page_request. Here, limit:-1 parses as i64 and converts to usize::MAX, so api search list --continue 'limit:-1,offset:0' returns all matching rows despite its maximum of 100. In the page-based resolver, email list --continue 'limit:101,page:2' or limit:0,page:2 instead sends an invalid page size to the backend. Validate inherited sizes against each command's positive range before slicing or making requests, returning a local validation error or falling back to the validated request limit. Cover negative, zero, and oversized token limits.
Reject continuations with missing endCursor
rust/src/platform/app/commands/list.rs:43
When hasNextPage is true but endCursor is null, this fallback reports a successful response with has_more: false even though the backend says more applications exist. The client forwards both fields without validating this combination, so callers collecting pages stop with an incomplete list. Return a structured error for (true, None) and reserve CursorContinuation::done() for hasNextPage: false.
- `--continue` tokens escape `%` and `,` in values, so an opaque server page
token (domain list) round-trips byte for byte instead of being truncated at
an embedded comma.
- A page size carried by a `--continue` token is only honored when it is
within the command's `1..=max` range; otherwise the request's own validated
`--limit` applies. A token can no longer widen a page past the maximum
(`limit:-1` no longer returns everything) or send the backend an invalid size.
- Hosting app, deployment and log lists report `cursor.total` when the service
returns `totalItems`.
- `platform app list` warns on stderr when the App Registry reports another
page but returns no cursor to fetch it, instead of ending silently.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Addressed the four 'previously missed' findings from the latest review in fcd4089 (they had no review thread to resolve):
Opaque page tokens (domain list): continuation tokens now escape % and , in values, so a server page token containing a comma round-trips exactly. Round-trip test covers commas, percent signs, plus signs and combinations.
Hosting totalItems: app, deployment and log lists now set cursor.total when the service returns it; absent otherwise (the service only sends it when asked via totalRequired, which we don't request).
Inherited --continue limits: a token's page size is honored only within the command's 1..=max; otherwise the request's own validated --limit applies. Tests cover negative, zero and oversized values for both the offset and page-based resolvers.
hasNextPage without endCursor (platform app list): deliberately a stderr warning rather than an error, matching how the hosting lists treat an incomplete listing, so the list ends but no longer looks complete. This is a judgment call; happy to make it a hard error if preferred.
Commands that hold their whole collection (platform actions list, hosting
domain list, api parameter list, api response list, api search) now apply the
global `--filter` before slicing, using the engine's own filter. An offset jump
selects among the matching rows, `cursor.total` and `cursor.remaining` count
only those, and an invalid filter expression is reported instead of ignored.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The reason will be displayed to describe this comment to others. Learn more.
This seems to make has_more depend entirely on a rel=next link. Since MailboxList.links defaults to an empty vec, a response that omits links would read as the last page even when it's full. The previous version seemed to fall back on the item count. Do we know the mailbox API reliably returns links? If not, keeping the count fallback might be safer.
The reason will be displayed to describe this comment to others. Learn more.
Checked this against the live service rather than guessing. The test-environment response does include a links array (self, first), but my account has a single mailbox, so I could not see whether a full page carries rel=next. The spec says next is absent only on the last page, which is what the current code relies on, but you are right that a response with no links at all would read as the last page. Leaving this thread open: an option is to use the next link when links is present and fall back to the item count only when links is missing entirely. That is a judgment call about trusting the spec, so I want a decision before changing it.
`hosting domain list` and `hosting subscription list` call endpoints that do
not accept paging parameters today (the service rejects them) but are specified
to follow the API collections-and-pagination standard, so a `next` link can
appear. Both now gather the whole collection, following `next` links by
replaying the link's query on the same path, host and credentials (at most 20
pages), and then work from that complete list.
- `hosting domain list` pages the list client-side with `--limit`, `--offset`
and `--continue`, and stops reading once it holds enough for the requested
window. Totals are left out when it stopped before the end. `--filter`
applies to the whole list first.
- `hosting subscription list` always reads every page, so slot totals and the
suggested plan cover all plans, and keeps its existing output shape.
- A link that cannot be followed ends the listing and prints a warning to
stderr instead of failing.
- The App Registry warning no longer names an internal service.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A `--continue` value that this command did not issue now fails with a
validation error and a fix, instead of quietly restarting from the first page
where repeated rows could go unnoticed. That covers a truncated token, a pair
with no value, a key the command never issues (for example a `dns list` token
passed to `platform actions list`), a non-numeric value, and a page size
outside the command's range. A blank `--continue` still starts from the
beginning.
`--offset` on `api parameter list`, `api response list`, `api search` and
`hosting domain list` must now be 0 or greater, and is rejected by argument
parsing rather than treated as 0.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Hosting listing pages read after the first (domains and subscriptions) no
longer put their response body in `--debug transport` output, matching the
first page, which already traces status and headers only.
- `hosting domain list` leaves `cursor.total` and `cursor.remaining` out
whenever it did not read the whole collection, including when a next link
could not be followed, not only when it stopped early on purpose.
- A `--continue` token with `page:0` is rejected for `dns list` and
`email list`, so it can no longer repeat the first page.
- The paging tests for these listings move to their own file to keep each
test file under the size limit.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Preserve continuation token handling for empty results
rust/src/api/search.rs:65
The empty-results branch discards --continue, so a search with no matches accepts malformed tokens that the normal path rejects. It also ignores valid embedded page sizes: --continue 'limit:1,offset:1' reports cursor.limit: 50 instead of 1 when --limit is omitted. Resolve and pass the supplied continuation here too, so token validation and cursor metadata remain consistent regardless of whether the search finds any rows.
…e empty-search tokens
- A hosting listing now recognises a repeated page after the required filter
has been added to the link, so a link that omits it and a later one that
includes it are not read twice. Previously the same plans could be counted
twice and inflate the subscription slot totals.
- `api search` handles `--continue` the same way when nothing matches: a
malformed token is rejected and a valid one's page size is reported.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Addressed the 'previously missed' finding from the latest review in 0d946d3 (no review thread to resolve): api search with no matches now resolves --continue like a search with results, so a malformed token is rejected and a valid token's page size is what cursor.limit reports (previously the empty branch discarded the token and reported the default). A regression test covers both, and fails with the old behavior restored.
When a hosting listing is read across several pages, the request that produced
the first page now counts as already read, and two requests are compared by
their set of parameters rather than their order. A next link that points back
at the first page, or repeats a page with its parameters in another order, ends
the listing instead of fetching those plans again and inflating the
subscription slot totals.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Keeps the Unicode/punycode display of domain names (`--punycode`) on the paged
`domain list`, `dns list` and `hosting domain list`, and drops the
`--limit`/`--offset` windowing that main's `domain list` still carried.
The page token read from a hosting `next` link is now percent-decoded once. It
is passed on to the generated client, which encodes it again, so a token that
was left encoded reached the service as a different value (`a%252Bb` instead of
`a%2Bb`) and the next page could not be fetched. This affects the application,
deployment and log lists' `--continue` tokens.
Co-Authored-By: Claude Sonnet 5.5 <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
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
Moves the CLI's list commands onto cli-engine's cursor pagination model (
--limitas page size,--continue <token>for the next page). Each response carries acursorblock (has_more,continue_from, andtotal/remainingwhere the backend reports them) and a ready-to-run next action, replacing the earlier--offsetwindowing and itspaginationblock. Nocli-enginebump is needed:origin/mainalready pins 0.10.2, which includes the feature.What changed
Backed by the service's own cursor or page token
domain list(v3 page token),platform app list(App Registry Relay cursor)dns list,email list(page number)hosting app list,hosting deployment list,hosting log list(page token)Paged client-side over the full response (
--offsetjumps straight to a position)api parameter list,api response list,api searchplatform actions listhosting domain listContinuation behavior
… --offset 20 --continue <token>.--continuetakes precedence over--offset/--page, so that action advances as printed (regression tests execute it).%and,in values, so an opaque server page token round-trips exactly. A--continuevalue this command did not issue is rejected with a validation error and a fix instead of quietly restarting from page one: a truncated token, a key the command never produces (for example adns listtoken given toplatform actions list), a non-numeric value, a page size outside the command's range, or a page below 1. A blank--continuestill starts from the beginning.--pageondns listandemail listmust be at least 1, and--offseton the client-side lists must be 0 or more; both fail at argument parsing.--filteris applied to the whole collection before slicing, so offsets,cursor.totalandcursor.remainingreflect the filtered rows.Data accuracy
dns listreportscursor.total/cursor.remainingwhen the API returns a total.email listdecides whether another page exists from the response'srel=nextlink, not from the item count.cursor.totalwhen the service sendstotalItems.Supporting changes
ApplicationsList.graphqlnow takes$first/$afterand returnspageInfo; theplatform appclient and its tests were ported onto the generated client.pagination_token.rs, andpage_items/page_continuation/page_limithelpers for hosting.How the user experience changes
Across every converted list
--limitwas a window over the full result (for several commands the default was "everything";--limit 0meant "all")--limitis the page size and must be at least 1;--limit 0is rejected. Every command has a default and a maximum (below)--limit N --offset M--continue <token>from the previous responsepagination: {total, offset, limit, count, has_more}cursor: {limit, count, has_more, continue_from?, total?, remaining?}(total/remainingonly where the backend reports them)gddy … --limit 3 --offset 3gddy … --continue "limit:3,offset:3"(opaque token; for server-backed lists it is the service's own cursor)--offseton every list--offseton client-side lists,--pageondns listandemail list; none on lists backed by a forward-only server cursor--continuetakes precedence over--offset/--page, so the next-step hint can be run exactly as printed even after a jump.A
--continuevalue that isn't one the command issued is now an error rather than "start over", so a bad copy/paste can't silently repeat page one. The Unicode/punycode name display (--punycode) works on the pageddomain list,dns listandhosting domain list, and the printed next-step hint carries the flag.Command by command
platform app list--limit/--offsetsliced the result (max 200)--continuedomain list--limit/--offset(max 500); reportedtotal--continue. Nototal: the v3 API does not return onedns list--limit/--offset(max 500)--page Nto jump;cursor.totalandcursor.remainingfrom the APIemail list--offset--page Nto jump; "more pages" now comes from the response'snextlink, not the item counthosting app list--limitwas a total cap--limitis the page size;--continuehosting deployment list--limitwas a total cap--limitis the page size;--continuehosting log list--limitwas a total cap (max 500)--limitis the page size;--continuehosting domain list--limitcapped the total--limit,--offset,--continue. Reads every page the service offers, but only as many as the requested window needs (not with--filter)hosting subscription listnextlink while ignoring the token (could re-fetch page 1 indefinitely);--limitshortened the list--limitstill only shortens the list shownapi parameter list,api response list--offset--continue, and--offsetstill worksapi search--continue, and--offsetstill worksplatform actions list--offset--continue, and--offsetstill works--filteron the client-side lists (api parameter/response list,api search,platform actions list,hosting domain list) is applied to the whole collection before paging, so offsets and counts reflect the filtered rows. On server-backed lists the engine still filters the page the backend returned, so a filtered page can be shorter than--limit.Examples
Captured from real runs of this branch and of
origin/main(0.2.31);platform actions listneeds no network.gddy platform actions list --limit 3gddy platform actions list --limit 3 --offset 3(a jump, then the printed next step)--limit 0, which used to mean "all"gddy dns list <domain> --limit 1on a real zone (test environment)gddy domain list --limit 5followed through--continue, against an account with 13 domains (test environment): pages of 5, 5 and 3 rows, 13 distinct domains, no duplicates,has_morefalse on the last page.Hosting domain and subscription lists
The Hosting spec types
listDomainsandlistSubscriptionsas paged but declares no paging request parameters, and the service rejectspageSize/pageTokenonGET /subscriptionswith a 400 (checked against the test environment). The hosting team confirmed anynextlinks will follow the API collections-and-pagination standard. The old code looped on anextlink while the client discarded the token, so anextlink would have re-fetched page 1 indefinitely.Because the services don't paginate these endpoints yet, both commands gather the whole collection and work from it, so they are ready when
nextlinks start to appear:links[rel=next], its query string is replayed unchanged on the same path, host and credentials (never a host or path taken from the link), up to 20 pages. Each followed page goes through the same generated type as the first.hosting domain listthen pages that complete list client-side (--limit,--offset,--continue), and stops reading once it holds enough for the requested window. With--filterit reads everything, andcursor.total/cursor.remainingare left out whenever it stopped before the end.hosting subscription listalways reads every page, because its slot totals and the plan it suggests are only right over all plans. Its output shape is unchanged.platform app listprints the same kind of warning if the App Registry reports another page but returns no cursor. Follow-up: DEVEX-1340 asks the hosting team to declare the paging parameters in the spec; once the services paginate, these two commands switch to the service's own continuation token in a follow-up release. When cli-engine#131 (CommandResult::with_warning) lands, the stderr warnings move to a structured result warning.Test plan
main(internationalized domain names, auto-renew and transfer-lock commands); the paged lists keep--punycodecargo check --workspacecargo clippy --workspace -- -D warningscargo test --workspacecargo fmt --check./rust/scripts/check-module-size.sh--helprenders--limit/--continue(and--offset/--pagewhere applicable) on every converted command--offsetjump (api parameter list,platform actions list);--filterplus offset jump plus continuation onplatform actions listhosting subscription listoutput unchanged;domain listwalked across three real pages (5, 5, 3 rows; 13 distinct, no duplicates);dns listreportstotal/remainingon a real zonedns list,email list,platform app listand the hosting lists (the test account has too little data or no apps), and followingnextlinks on the hosting domain and subscription lists (no endpoint returns one today; covered by mock-server tests)🤖 Generated with Claude Code