Skip to content

Added basic PZ commands#86

Open
burstw0w wants to merge 22 commits into
BunnyWay:mainfrom
burstw0w:pullzone-commands
Open

Added basic PZ commands#86
burstw0w wants to merge 22 commits into
BunnyWay:mainfrom
burstw0w:pullzone-commands

Conversation

@burstw0w

@burstw0w burstw0w commented Jun 2, 2026

Copy link
Copy Markdown

Right now, you can do:

pz select - selects a pullzone, keeping it in context so all the other commands can be ran without specifying ID/name
pz deselect - removes context (this might not be needed, as you dont have that for your db commands)
pz create - creates a pullzone, accepts two params and those are [name] [origin url]
pz delete - deletes pullzone (if you don't use name, it will ask you if you want to delete the current zone that is in the context)
pz purge - purges pullzone (again, if you dont specify the name, uses context)
pz show - shows info about pullzone
pz list - lists your available pullzones
pz clone - clones your pullzone

Tested everything with bun run

@bogdan-at-bunny

Copy link
Copy Markdown

@codex review

@changeset-bot

changeset-bot Bot commented Jun 2, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 3863532

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 54b9cbc720

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/cli/src/commands/pz/clone.ts Outdated
Comment thread packages/cli/src/commands/pz/delete.ts Outdated
Comment thread packages/cli/src/commands/pz/index.ts Outdated
Comment thread packages/cli/src/commands/pz/clone.ts Outdated
Comment thread packages/cli/src/commands/pz/resolve-pullzone.ts Outdated
Comment thread packages/cli/src/commands/pz/resolve-pullzone.ts Outdated
@burstw0w

burstw0w commented Jun 2, 2026

Copy link
Copy Markdown
Author

@bogdan-at-bunny Resolved all the issues codex found. Can you run it again?

@jamie-at-bunny jamie-at-bunny left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @burstw0w, appreciate the PR and enthusiasm here ❤️

It's a solid first pass. I appreciate the thought of keeping existing patterns in other commands here in this one.

That said, we should cut the scope back to "billy basic". I would drop clone, use id instead of name sugar+magic, and use the type system we already have to ensure end to end type safety as much as possible. This will result in less code added, and more predictable results. We're planning to add a project schema soon where a Project schema owns multiple PZs, at which point name based lookups get ambiguous and the manifest likely needs to carry project context too. Let's anchor on the stable IDs now, and provide the link/unlink methods for single PZ contexts.

I left a few raw comments as I went through the code. Apologies for the delay in reviewing, my focus is purely on scripts and apps at the moment (and storage next), amongst other things.

Comment thread packages/cli/src/commands/pz/create.ts Outdated
type PullZoneManifest,
} from "./constants.ts";

interface PullZone {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer we didn't new interfaces and used what we could from the OpenAPI Spec, components["schemas"]["PullZoneModel"] should have what's needed here.

Name?: string | null;
}

interface CreateArgs {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

components["schemas"]["EdgeRuleV2Model"] should be used inline instead of declaring here, at worst, we can Pick from here, although I'd rather not limit scope and use the input type as is, which allows us to spread on more options.

Comment thread packages/cli/src/commands/pz/clone.ts Outdated
Comment thread packages/cli/src/commands/pz/clone.ts Outdated
Comment thread packages/cli/src/commands/pz/create.ts Outdated
Comment thread packages/cli/src/commands/pz/create.ts Outdated
const createSpin = spinner("Creating pull zone...");
createSpin.start();

await client.POST("/pullzone", {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens here if there is an error from the API?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewrote process of creating PZ completely, it now has a check for that as well.

Comment thread packages/cli/src/commands/pz/purge.ts Outdated
const spin = spinner("Purging cache...");
spin.start();

await client.POST("/pullzone/{id}/purgeCache", {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens with any error/response from the API here? Network or response error.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing, you are correct.

Added a check that will print an error if there is any.

Comment thread packages/cli/src/commands/pz/clone.ts Outdated
Comment thread packages/cli/src/commands/pz/clone.ts Outdated
Comment thread packages/cli/src/commands/pz/clone.ts Outdated
@burstw0w

burstw0w commented Jun 3, 2026

Copy link
Copy Markdown
Author

Scaled everything down a bit, and implemented most of fixes requested by @jamie-at-bunny

@jamie-at-bunny

Copy link
Copy Markdown
Member

@greptile-apps review

@greptile-apps

greptile-apps Bot commented Jun 4, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a pz namespace with seven pull zone management sub-commands (list, create, delete, link, unlink, purge, show), following the same manifest-based context pattern used by the existing db and scripts command families.

  • link, unlink, show, purge are clean implementations that use generated schema types and consistent spinner/error handling patterns.
  • link (interactive path) saves selected.Id directly to the manifest without a null/undefined guard, unlike the ID path which uses zone.Id ?? id; a zone without an Id in the API response would corrupt the manifest and silently break all context-aware commands.
  • delete fetches the zone name for the confirmation label without a spinner, unlike db delete and the rest of the pz command set.

Confidence Score: 4/5

Safe to merge with the interactive-path link fix applied; the remaining issue is a defensive guard for an edge case that is unlikely in production but leaves the manifest in a broken state if it fires.

The interactive path of pz link writes selected.Id — typed as number | null | undefined — directly to the manifest with no fallback. If selected.Id is absent, every subsequent context-aware command resolves to undefined for zoneId and fails with 'No pull zone specified' rather than a meaningful error. The ID-based path in the same file already has zone.Id ?? id as a guard; the interactive path should apply the same pattern.

packages/cli/src/commands/pz/link.ts — interactive selection path at line 107 needs a null/undefined guard on selected.Id before writing the manifest.

Important Files Changed

Filename Overview
packages/cli/src/cli.ts Registers the new pzNamespace in the CLI command list — a one-line addition alongside existing namespaces, no issues.
packages/cli/src/commands/pz/constants.ts Defines PULL_ZONE_MANIFEST filename and PullZoneManifest interface. Clean and minimal.
packages/cli/src/commands/pz/create.ts Implements pz create with spinner, error check, and post-create link prompt. Still uses as any for POST body and inline response type instead of generated schema types per CLAUDE.md (flagged in prior review).
packages/cli/src/commands/pz/delete.ts Implements pz delete with force flag and manifest cleanup. Pre-confirmation GET fetch lacks a spinner consistent with db delete.
packages/cli/src/commands/pz/index.ts Registers all seven pz sub-commands under the pz namespace. Clean with a TODO stub for future rules/hostnames sub-namespaces.
packages/cli/src/commands/pz/link.ts Implements interactive and ID-based pull zone linking. The ID path uses zone.Id ?? id as a safe fallback, but the interactive path saves selected.Id directly without a null/undefined guard — a potential manifest corruption path.
packages/cli/src/commands/pz/list.ts Lists pull zones with spinner, sorted by name, formatted as table or JSON. Clean implementation using generated schema types.
packages/cli/src/commands/pz/purge.ts Purges pull zone cache with spinner and proper error handling. Resolves zone ID from argument or manifest context consistently.
packages/cli/src/commands/pz/show.ts Shows pull zone details using formatKeyValue. Has spinner (added after prior review), uses generated schema types, and properly handles try/catch for API errors.
packages/cli/src/commands/pz/unlink.ts Simple unlink command that removes the manifest. removeManifest is a no-op if the file is missing, so safe to run without a linked zone.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    CLI[bunny pz] --> LIST[pz list]
    CLI --> CREATE[pz create]
    CLI --> LINK[pz link]
    CLI --> UNLINK[pz unlink]
    CLI --> SHOW[pz show]
    CLI --> PURGE[pz purge]
    CLI --> DELETE[pz delete]

    LINK --> MANIFEST[(pullzone.json)]
    CREATE -->|optionally| MANIFEST
    UNLINK --> MANIFEST
    DELETE -->|if linked zone| MANIFEST

    MANIFEST -->|resolves id| SHOW
    MANIFEST -->|resolves id| PURGE
    MANIFEST -->|resolves id| DELETE

    LIST -->|GET /pullzone| API[Bunny API]
    CREATE -->|POST /pullzone| API
    LINK -->|GET /pullzone or /pullzone-id| API
    SHOW -->|GET /pullzone-id| API
    PURGE -->|POST /pullzone-id/purgeCache| API
    DELETE -->|DELETE /pullzone-id| API
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    CLI[bunny pz] --> LIST[pz list]
    CLI --> CREATE[pz create]
    CLI --> LINK[pz link]
    CLI --> UNLINK[pz unlink]
    CLI --> SHOW[pz show]
    CLI --> PURGE[pz purge]
    CLI --> DELETE[pz delete]

    LINK --> MANIFEST[(pullzone.json)]
    CREATE -->|optionally| MANIFEST
    UNLINK --> MANIFEST
    DELETE -->|if linked zone| MANIFEST

    MANIFEST -->|resolves id| SHOW
    MANIFEST -->|resolves id| PURGE
    MANIFEST -->|resolves id| DELETE

    LIST -->|GET /pullzone| API[Bunny API]
    CREATE -->|POST /pullzone| API
    LINK -->|GET /pullzone or /pullzone-id| API
    SHOW -->|GET /pullzone-id| API
    PURGE -->|POST /pullzone-id/purgeCache| API
    DELETE -->|DELETE /pullzone-id| API
Loading

Fix All in Claude Code

Reviews (11): Last reviewed commit: "Merge branch 'main' into pullzone-comman..." | Re-trigger Greptile

Comment thread packages/cli/src/commands/pz/index.ts Outdated
Comment thread packages/cli/src/commands/pz/constants.ts Outdated
Comment on lines +48 to +57
const { data, error } = await client.POST("/pullzone", {
body: { Name: name, OriginUrl: url } as any,
});

createSpin.stop();

if (error) {
throw new UserError(`Failed to create pull zone: ${error}`);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 as any cast and inline type instead of generated schema types

The POST body is cast with as any and the response is typed with an inline { Id?: number; Name?: string | null } rather than using Pick<components["schemas"]["PullZone"], ...> from @bunny.net/openapi-client/generated/core.d.ts. CLAUDE.md explicitly requires: "Prefer generated schema types over inline primitives. Only fall back to string, any, or number when no generated type exists." Both casts bypass compile-time safety, so a field rename in the spec would silently break this command.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code

Comment thread packages/cli/src/commands/pz/link.ts Outdated
Comment thread packages/cli/src/commands/pz/show.ts Outdated
Comment thread packages/cli/src/commands/pz/index.ts Outdated
Comment on lines +1 to +25
import { defineNamespace } from "../../core/define-namespace.ts";
import { pzCreateCommand } from "./create.ts";
import { pzDeleteCommand } from "./delete.ts";
import { pzLinkCommand } from "./link.ts";
import { pzListCommand } from "./list.ts";
import { pzPurgeCommand } from "./purge.ts";
import { pzShowCommand } from "./show.ts";
import { pzUnlinkCommand } from "./unlink.ts";

// TODO: implement rules and hostnames subcommands
// const rulesNamespace = defineNamespace("rules", "Manage pull zone edge rules.", [...]);
// const hostnamesNamespace = defineNamespace("hostnames", "Manage pull zone hostnames.", [...]);

export const pzNamespace = defineNamespace(
"pz",
"Manage pull zones.",
[
pzListCommand,
pzCreateCommand,
pzDeleteCommand,
pzLinkCommand,
pzPurgeCommand,
pzShowCommand,
pzUnlinkCommand,
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 README.md and AGENTS.md not updated

CLAUDE.md explicitly requires: "When adding, changing, or removing commands or flags, update the corresponding sections in README.md — user-facing command docs and examples. AGENTS.md — architecture docs, command reference tree, and file listing." The new pz namespace and its seven sub-commands are absent from both documents.

Context Used: CLAUDE.md (source)

Fix in Claude Code

Comment thread packages/cli/src/commands/pz/link.ts
Comment thread packages/cli/src/commands/pz/list.ts Outdated
@burstw0w burstw0w requested a review from jamie-at-bunny June 5, 2026 13:51
Comment thread packages/cli/src/commands/pz/delete.ts

@jamie-at-bunny jamie-at-bunny left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome updates here @burstw0w, I'll get round to fully reviewing, and merging this Thursday/Friday. Thanks for your patience, awesome contribution 😻

@jamie-at-bunny

Copy link
Copy Markdown
Member

@burstw0w hey, i took this for a spin locally. It checks out, and does what we need.

Do you have any objections to me pushing a few commits here? One thing I'd like to implement is the prompt fallback if args/flags aren't provided, instead of an error response args are missing, it's nice to just ask for them from a DX point of view.

@burstw0w

Copy link
Copy Markdown
Author

@jamie-at-bunny Feel free to do, or request any changes you want.

@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Try greploops.

Comment on lines +106 to +108
saveManifest<PullZoneManifest>(PULL_ZONE_MANIFEST, {
id: selected.Id,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 selected.Id saved without nullish fallback

In the interactive path, selected.Id (typed as number | null | undefined from PullZoneModel) is written directly to the manifest. If the API ever returns a zone without an Id, the manifest is saved as { id: undefined }. Every subsequent command that reads the manifest — pz show, pz purge, pz delete — resolves zoneId as undefined and throws "No pull zone specified", making the linked context appear broken for no clear reason.

The direct-ID path already has the right pattern: id: zone.Id ?? id. The interactive path should apply the same guard before writing the manifest. Compare with link.ts line 59.

Fix in Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants