Skip to content

Conversation

@petebacondarwin
Copy link
Contributor

@petebacondarwin petebacondarwin commented Jan 22, 2026

This refactoring moves the telemetry events (wrangler command started/completed/errored) from the yargs middleware in index.ts to the command handler in register-yargs-command.ts.

Benefits:

  • Telemetry is now sent after config is loaded, allowing access to send_metrics and hasAssets
  • Telemetry is only sent for commands that use defineCommand
    • pubsub (deprecated/legacy)
    • cloudchamber (migrated to use defineCommand)
    • containers (migrated to use defineCommand)

Key changes:

  • Add CommandHandledError wrapper class to signal when telemetry has been sent
  • Add getErrorType() function to classify errors for telemetry
  • Send telemetry events in register-yargs-command.ts after config is loaded
  • Preserve fallback telemetry in index.ts for yargs validation errors
  • Handle nested wrangler.parse() calls by not double-wrapping CommandHandledError
  • Properly unwrap CommandHandledError when writing command-failed output

  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: refactoring

A picture of a cute animal (not mandatory, but encouraged)


Open with Devin

@changeset-bot
Copy link

changeset-bot bot commented Jan 22, 2026

⚠️ No Changeset found

Latest commit: 7e9344a

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.

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

@vicb
Copy link
Contributor

vicb commented Jan 23, 2026

Note this second bullet means that command telemetry is no longer sent for "non-Command" commands:

  • cloudchamber (deprecated/legacy)
  • pubsub (deprecated/legacy)
  • containers (need to migrate)

@petebacondarwin could you please create an issue to clean the deprecated stuff and add this to the v5 milestone.

@NuroDev has a PR touching the "non-Command" commands and IMO we should not introduce (convoluted) code for something that is deprecated/going to be removed soon if non criticial.

If I read this correctly, we only need to migrate the containers command to use the command registry to have something cleaner.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 23, 2026

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@12055

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@12055

miniflare

npm i https://pkg.pr.new/miniflare@12055

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@12055

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@12055

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@12055

@cloudflare/vitest-pool-workers

npm i https://pkg.pr.new/@cloudflare/vitest-pool-workers@12055

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@12055

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@12055

wrangler

npm i https://pkg.pr.new/wrangler@12055

commit: 7e9344a

@petebacondarwin petebacondarwin force-pushed the pbd/clean-command-metrics branch from 4f1c154 to 3fd5325 Compare January 23, 2026 09:37
@petebacondarwin petebacondarwin changed the title WIP: refactor(wrangler): move telemetry events from yargs middleware to command handlers refactor(wrangler): move telemetry events from yargs middleware to command handlers Jan 23, 2026
@petebacondarwin petebacondarwin marked this pull request as ready for review January 23, 2026 12:08
@petebacondarwin petebacondarwin requested review from a team as code owners January 23, 2026 12:08
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View issues and 7 additional flags in Devin Review.

Open in Devin Review

…mmand handlers

This refactoring moves the telemetry events (wrangler command started/completed/errored)
from the yargs middleware in index.ts to the command handler in register-yargs-command.ts.

Benefits:
- Telemetry is now sent after config is loaded, allowing access to send_metrics and hasAssets
- Telemetry is only sent for commands that use defineCommand

Key changes:
- Add CommandHandledError wrapper class to signal when telemetry has been sent
- Add getErrorType() function to classify errors for telemetry
- Send telemetry events in register-yargs-command.ts after config is loaded
- Preserve fallback telemetry in index.ts for yargs validation errors
- Handle nested wrangler.parse() calls by not double-wrapping CommandHandledError
- Properly unwrap CommandHandledError when writing command-failed output
…reateCommand

Migrate containers and cloudchamber CLI commands from the legacy yargs
defineCommand approach to the new createCommand pattern. This aligns
these commands with the updated telemetry handling where events are
dispatched directly from command handlers rather than middleware.
…hers

Move the requests tracking from per-dispatcher instance to module-level
scope so that all dispatchers share the same Set of pending requests.
This ensures all metrics requests are awaited before Wrangler exits,
regardless of which dispatcher created them.

- Add module-level pendingRequests Set with auto-cleanup via .finally()
- Export waitForAllMetricsDispatches() to await all pending requests
- Remove the per-dispatcher requests getter
@petebacondarwin petebacondarwin force-pushed the pbd/clean-command-metrics branch from 3fd5325 to 7e9344a Compare January 23, 2026 14:16
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View issue and 9 additional flags in Devin Review.

Open in Devin Review

Copy link
Contributor

@vicb vicb left a comment

Choose a reason for hiding this comment

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

Thanks Pete!

Looks great for the first pass!

Do you think it is possible to split this PR in 2?

  • the metrcis
  • the migration of the commands

If it's easy for an AI maybe we should do that?

I take a quick look at what Devin flagged, some of that should be looked at I think.

I'll do a second (after you look at if the PR can be separated)

);
// so testing the actual UI will be harder than expected
// TODO: think better on how to test UI actions
expect(std.out).toMatchInlineSnapshot(MOCK_DEPLOYMENTS_COMPLEX_RESPONSE);
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't look inline to me 🤔

"
⛅️ wrangler x.x.x
──────────────────
{
Copy link
Contributor

Choose a reason for hiding this comment

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

Before the changes, the output was valid JSON, was that a req?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. I'll find out.

wrangler cloudchamber registries credentials [domain] get a temporary password for a specific domain
wrangler cloudchamber registries remove [domain] removes the registry at the given domain
wrangler cloudchamber registries list list registries configured for this account
Configure registries via Cloudchamber [alpha]
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this wanted ?

-> % wrangler r2 
wrangler r2

📦 Manage R2 buckets & objects

COMMANDS
  wrangler r2 object  Manage R2 objects
  wrangler r2 bucket  Manage R2 buckets
  wrangler r2 sql     Send queries and manage R2 SQL [open-beta]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Perhaps I missed a namespace definition here...

return Buffer.from(`v1:${credentials.password}`).toString("base64");
}

// --- New createCommand-based commands ---
Copy link
Contributor

Choose a reason for hiding this comment

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

delete ?

throw err;
}

// Send "errored" event
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// Send "errored" event

Comment on lines 275 to 284
const code =
"code" in outputErr ? (outputErr.code as number) : undefined;
const message =
"message" in outputErr ? (outputErr.message as string) : undefined;
writeOutput({
type: "command-failed",
version: 1,
code,
message,
});
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: looks weird to use shorthand to write longer code

Suggested change
writeOutput({
type: "command-failed",
version: 1,
code: "code" in outputErr ? (outputErr.code as number) : undefined,
message: "message" in outputErr ? (outputErr.message as string) : undefined,
});

Copy link
Contributor Author

@petebacondarwin petebacondarwin Jan 23, 2026

Choose a reason for hiding this comment

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

I disagree with this suggestion.
I find it easier to parse logic in creating a variable outside of an object literal.

Copy link
Contributor

@vicb vicb Jan 23, 2026

Choose a reason for hiding this comment

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

valid personal pref.
I pref my way because it also clearly tells you that the var is not used elsewhere, easier to parse for me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But it turns out that we don't need to do any casting for message!

}

const durationMs = Date.now() - startTime;
// Fallback telemetry for errors that occurred before handler ran
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO "Fallback" is more confusing than helping

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll reword

"wrangler command completed",
{
// Send "started" event (since handler never got to send it)
dispatcher.sendCommandEvent("wrangler command started", {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any interest in sending a start to send an error right after?

maybe the errorType could be used to flag that instead of sending 2 events.

If there is a actually a need, it would be nice to explain why in a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The telemetry around commands always comes in pairs:

  • start + completed
  • start + errored

So this keeps that symmetry and makes reporting on the telemetry simpler.

Copy link
Contributor

@vicb vicb Jan 23, 2026

Choose a reason for hiding this comment

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

My question was "why".

Here it makes no sense to send 2 back to back event (on top of that there is a free form errorType)

A "why" could be a limitation of the backend, I think it should be documented.

@petebacondarwin
Copy link
Contributor Author

Thanks Pete!

Looks great for the first pass!

Do you think it is possible to split this PR in 2?

  • the metrcis
  • the migration of the commands

If it's easy for an AI maybe we should do that?

I take a quick look at what Devin flagged, some of that should be looked at I think.

I'll do a second (after you look at if the PR can be separated)

@vicb - The PR is made up of 3 commits, which are exactly that separation. Is this enough for you?

Comment on lines +70 to +72
"
⛅️ wrangler x.x.x
──────────────────
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Same question about JSON output...

petebacondarwin added a commit that referenced this pull request Jan 23, 2026
The comments for the PR review were taking from the previous PR (#12055)
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

🔴 1 issue in files not directly in the diff

🔴 OpenAPI.BASE cached globally causes containers vs cloudchamber requests to hit the wrong API base after first command (packages/wrangler/src/cloudchamber/common.ts:182-190)

fillOpenAPIConfiguration() only sets OpenAPI.BASE when it is currently empty, but the base URL depends on the scope (containers vs cloudchamber).

Actual behavior: if the first command that runs sets OpenAPI.BASE to /accounts/:id/cloudchamber, then later running a containers command will reuse that base and send containers requests to the cloudchamber API prefix (or vice versa).

Expected behavior: OpenAPI.BASE should be configured per-scope (or reset/updated on each call), so that containers calls go to /accounts/:id/containers and cloudchamber calls go to /accounts/:id/cloudchamber.

Impact: commands can make requests to incorrect endpoints, leading to failures or potentially unintended API interactions.

Click to expand

In fillOpenAPIConfiguration(), the base is only set once:

if (OpenAPI.BASE.length === 0) {
  const [base] = await wrap(getAPIUrl(config, accountId, scope));
  OpenAPI.BASE = base;
}

This ignores the scope parameter on subsequent calls. See packages/wrangler/src/cloudchamber/common.ts:182-190.

Recommendation: Make OpenAPI.BASE include the scope in its cache key (e.g., store per-scope base in a map), or always recompute and assign OpenAPI.BASE each time fillOpenAPIConfiguration() is called (and ensure concurrent calls can’t race).

View issue and 12 additional flags in Devin Review.

Open in Devin Review

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

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

2 participants