cloudflare-to-claude-fix 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,153 @@
1
+ <p align="center">
2
+ <img width="400px" src="https://i.imgur.com/RQP3lma.png" />
3
+ </p>
4
+
5
+ A Cloudflare Workers **Queue consumer** that fires a **Claude Code routine** whenever a Workers build fails. Required Workers Paid and Claude Pro.
6
+
7
+ ---
8
+
9
+ ## Setup
10
+
11
+ ### 1. Clone / copy this project
12
+
13
+ ```bash
14
+ git clone <your-repo>
15
+ cd cloudflare-to-claude-fix
16
+ npm install
17
+ ```
18
+
19
+ ### 2. Create the Cloudflare Queue
20
+
21
+ ```bash
22
+ # Main queue
23
+ wrangler queues create workers-build-events
24
+
25
+ # Dead-letter queue (catches messages that fail all 3 retries)
26
+ wrangler queues create workers-build-events-dlq
27
+ ```
28
+
29
+ ### 3. Enable Event Subscriptions on your target Worker
30
+
31
+ In the Cloudflare dashboard:
32
+
33
+ 1. Open **Workers & Pages** → select the Worker you want to monitor
34
+ 2. Go to **Settings** → **Event Subscriptions**
35
+ 3. Set the queue to `workers-build-events`
36
+ 4. Enable: **Build started**, **Build succeeded**, **Build failed**, **Build cancelled**
37
+
38
+ This Worker only acts on `status === "failed"` events; the rest are acknowledged and discarded.
39
+
40
+ ### 4. Create a Claude Code routine
41
+
42
+ 1. Go to [claude.ai/code/routines](https://claude.ai/code/routines)
43
+ 2. Click **New routine**
44
+ 3. Configure:
45
+ - **Prompt:** e.g. *"A Cloudflare Workers build has failed. The build ID, branch, commit, author and error log are below. Investigate the error, identify the root cause, and push a fix to the branch. Summarise what you changed."*
46
+ - **Repository:** the repo that this Worker is deployed from
47
+ 4. Under **Select a trigger** → **Add another trigger** → choose **API**
48
+ 5. Click **Generate token** — copy the token (shown **once**)
49
+ 6. Copy the full fire URL shown in the modal (format: `https://api.anthropic.com/v1/claude_code/routines/trig_<id>/fire`)
50
+
51
+ ### 5. Store secrets in Wrangler
52
+
53
+ ```bash
54
+ # Required
55
+ wrangler secret put ROUTINE_FIRE_URL
56
+ # paste: https://api.anthropic.com/v1/claude_code/routines/trig_<id>/fire
57
+
58
+ wrangler secret put ROUTINE_FIRE_TOKEN
59
+ # paste: sk-ant-oat01-...
60
+
61
+ # Optional — post session link to Slack or Discord
62
+ wrangler secret put NOTIFY_WEBHOOK_URL
63
+ # paste: https://hooks.slack.com/services/... (Slack)
64
+ # or: https://discord.com/api/webhooks/... (Discord)
65
+ ```
66
+
67
+ > **Never** commit secrets to source control or paste them in plaintext anywhere.
68
+
69
+ ### 6. Deploy
70
+
71
+ ```bash
72
+ wrangler deploy
73
+ ```
74
+
75
+ ---
76
+
77
+ ## How it works
78
+
79
+ 1. Cloudflare Builds publishes a `BuildEvent` message to `workers-build-events` when a build status changes.
80
+ 2. This consumer Worker receives the batch. Non-failure messages are `ack()`-ed immediately.
81
+ 3. For `status === "failed"` messages the Worker:
82
+ - Formats the `build_id`, `worker_name`, `branch`, `commit_hash`, `author`, `timestamp`, and `error_messages` into a single plaintext block (≤ 65,536 chars).
83
+ - POSTs that block to the Claude Code routine `/fire` endpoint.
84
+ - If `NOTIFY_WEBHOOK_URL` is set, posts the resulting `claude_code_session_url` to Slack/Discord so your team can watch the live debugging session.
85
+ 4. If the fire request fails, the message is `retry()`-ed up to 3 times (configured in `wrangler.toml`). After 3 failures the message lands in `workers-build-events-dlq`.
86
+
87
+ ---
88
+
89
+ ## Claude Code routine `/fire` API quick-reference
90
+
91
+ ```
92
+ POST https://api.anthropic.com/v1/claude_code/routines/{routine_id}/fire
93
+ Authorization: Bearer sk-ant-oat01-...
94
+ anthropic-version: 2023-06-01
95
+ anthropic-beta: experimental-cc-routine-2026-04-01
96
+ Content-Type: application/json
97
+
98
+ { "text": "<up to 65,536 chars of context>" }
99
+ ```
100
+
101
+ **Response:**
102
+
103
+ ```json
104
+ {
105
+ "type": "routine_fire",
106
+ "claude_code_session_id": "session_01...",
107
+ "claude_code_session_url": "https://claude.ai/code/session_01..."
108
+ }
109
+ ```
110
+
111
+ | Status | Cause |
112
+ | ------- | -------------------------------------------------------------- |
113
+ | `400` | Missing beta header, text > 65 536 chars, or routine is paused |
114
+ | `401` | Wrong or missing bearer token |
115
+ | `403` | Account doesn't have Claude Code on the web |
116
+ | `404` | Routine ID not found |
117
+ | `429` | Daily run allowance exhausted |
118
+
119
+ ---
120
+
121
+ ## Revoking / rotating the routine token
122
+
123
+ 1. Open [claude.ai/code/routines](https://claude.ai/code/routines) → edit the routine
124
+ 2. Click the API trigger → **Generate token** (this immediately revokes the old one)
125
+ 3. Update the secret: `wrangler secret put ROUTINE_FIRE_TOKEN`
126
+
127
+ ---
128
+
129
+ ## Local testing
130
+
131
+ Send a synthetic failed-build message to the queue:
132
+
133
+ ```bash
134
+ wrangler queues publish workers-build-events \
135
+ --message '{
136
+ "build_id": "build_test001",
137
+ "status": "failed",
138
+ "worker_name": "my-api",
139
+ "branch": "feat/new-endpoint",
140
+ "commit_hash": "abc1234",
141
+ "author": "alex@example.com",
142
+ "error_messages": ["Error: Cannot find module '\''./utils'\''", " at Object.<anonymous> (src/index.ts:3:1)"],
143
+ "timestamp": "2026-05-01T19:00:00Z"
144
+ }'
145
+ ```
146
+
147
+ Then tail logs to verify:
148
+
149
+ ```bash
150
+ wrangler tail cloudflare-to-claude-fix
151
+ ```
152
+
153
+ ---
package/package.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "cloudflare-to-claude-fix",
3
+ "version": "1.0.1",
4
+ "type": "module",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/OpenSourceAGI/appdemo-dev-tools/tree/master/packages/cloudflare-to-claude-fix"
8
+ },
9
+ "homepage": "https://github.com/OpenSourceAGI/appdemo-dev-tools/tree/master/packages/cloudflare-to-claude-fix",
10
+ "scripts": {
11
+ "deploy": "wrangler deploy",
12
+ "dev": "wrangler dev",
13
+ "tail": "wrangler tail"
14
+ },
15
+ "devDependencies": {
16
+ "wrangler": "^3.x"
17
+ }
18
+ }
package/src/index.js ADDED
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Cloudflare Workers Queue Consumer
3
+ * Fires a Claude Code routine when a Workers build fails.
4
+ *
5
+ * Event flow:
6
+ * CF Workers Builds → Queue → this Worker → Claude Code /fire endpoint
7
+ */
8
+
9
+ const MAX_TEXT_BYTES = 65_536;
10
+
11
+ function truncate(s) {
12
+ if (s.length <= MAX_TEXT_BYTES) return s;
13
+ return (
14
+ s.slice(0, MAX_TEXT_BYTES - 200) +
15
+ "\n\n[... log truncated to fit 65,536-char limit ...]"
16
+ );
17
+ }
18
+
19
+ function buildPayloadText(evt) {
20
+ const lines = [
21
+ `⚠️ Cloudflare Workers build FAILED`,
22
+ `Worker : ${evt.worker_name}`,
23
+ `Build : ${evt.build_id}`,
24
+ `Branch : ${evt.branch ?? "(unknown)"}`,
25
+ `Commit : ${evt.commit_hash ?? "(unknown)"}`,
26
+ `Author : ${evt.author ?? "(unknown)"}`,
27
+ `Time : ${evt.timestamp}`,
28
+ ``,
29
+ `=== Error log ===`,
30
+ ];
31
+
32
+ if (evt.error_messages && evt.error_messages.length > 0) {
33
+ lines.push(...evt.error_messages);
34
+ } else {
35
+ lines.push(
36
+ "(no structured error messages in event; check Logpush for full output)",
37
+ );
38
+ }
39
+
40
+ return truncate(lines.join("\n"));
41
+ }
42
+
43
+ async function fireRoutine(text, env) {
44
+ const res = await fetch(env.ROUTINE_FIRE_URL, {
45
+ method: "POST",
46
+ headers: {
47
+ Authorization: `Bearer ${env.ROUTINE_FIRE_TOKEN}`,
48
+ "anthropic-version": "2023-06-01",
49
+ "anthropic-beta": "experimental-cc-routine-2026-04-01",
50
+ "Content-Type": "application/json",
51
+ },
52
+ body: JSON.stringify({ text }),
53
+ });
54
+
55
+ if (!res.ok) {
56
+ const body = await res.text();
57
+ throw new Error(`Routine fire failed (${res.status}): ${body}`);
58
+ }
59
+
60
+ return res.json();
61
+ }
62
+
63
+ async function notifyWebhook(evt, sessionUrl, webhookUrl) {
64
+ const text =
65
+ `🤖 Claude Code is investigating the failed \`${evt.worker_name}\` build ` +
66
+ `(branch: \`${evt.branch ?? "?"}\`, commit: \`${evt.commit_hash?.slice(0, 7) ?? "?"}\`).\n` +
67
+ `👉 Live session: ${sessionUrl}`;
68
+
69
+ await fetch(webhookUrl, {
70
+ method: "POST",
71
+ headers: { "Content-Type": "application/json" },
72
+ body: JSON.stringify({ text, content: text }), // works for both Slack and Discord
73
+ });
74
+ }
75
+
76
+ export default {
77
+ async queue(batch, env) {
78
+ for (const message of batch.messages) {
79
+ const evt = message.body;
80
+
81
+ if (evt.status !== "failed") {
82
+ message.ack();
83
+ continue;
84
+ }
85
+
86
+ try {
87
+ console.log(
88
+ `[cloudflare-to-claude-fix] Build ${evt.build_id} for worker "${evt.worker_name}" failed — firing routine.`,
89
+ );
90
+
91
+ const text = buildPayloadText(evt);
92
+ const { claude_code_session_url } = await fireRoutine(text, env);
93
+
94
+ console.log(
95
+ `[cloudflare-to-claude-fix] Routine fired. Session: ${claude_code_session_url}`,
96
+ );
97
+
98
+ if (env.NOTIFY_WEBHOOK_URL) {
99
+ await notifyWebhook(
100
+ evt,
101
+ claude_code_session_url,
102
+ env.NOTIFY_WEBHOOK_URL,
103
+ );
104
+ }
105
+
106
+ message.ack();
107
+ } catch (err) {
108
+ console.error(
109
+ `[cloudflare-to-claude-fix] Error processing build ${evt.build_id}:`,
110
+ err,
111
+ );
112
+ message.retry();
113
+ }
114
+ }
115
+ },
116
+ };
package/wrangler.jsonc ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ // ── Core ────────────────────────────────────────────────────────────────────
3
+ "name": "cloudflare-to-claude-fix",
4
+ "main": "src/index.js",
5
+ "compatibility_date": "2025-11-01",
6
+
7
+ // ── Queue consumer ──────────────────────────────────────────────────────────
8
+ // Create the queues first:
9
+ // wrangler queues create workers-build-events
10
+ // wrangler queues create workers-build-events-dlq
11
+ "queues": {
12
+ "consumers": [
13
+ {
14
+ "queue": "workers-build-events",
15
+ // Max messages pulled per invocation
16
+ "max_batch_size": 10,
17
+ // Seconds to wait to fill a batch before invoking early
18
+ "max_batch_timeout": 30,
19
+ // Per-message retry attempts before routing to DLQ
20
+ "max_retries": 3,
21
+ // Catches messages that exhaust all retries
22
+ "dead_letter_queue": "workers-build-events-dlq",
23
+ },
24
+ ],
25
+ },
26
+
27
+ // ── Secrets ─────────────────────────────────────────────────────────────────
28
+ // Set these with `wrangler secret put <NAME>` — never hardcode values here.
29
+ //
30
+ // ROUTINE_FIRE_URL — https://api.anthropic.com/v1/claude_code/routines/trig_<id>/fire
31
+ // ROUTINE_FIRE_TOKEN — sk-ant-oat01-... (from Claude Code routine API trigger)
32
+ // NOTIFY_WEBHOOK_URL — (optional) Slack or Discord incoming webhook URL
33
+ "vars": {},
34
+ }