freshcontext-ops-pulse
v0.2.1
Published
Local-first Cloudflare Workers, D1, cron, and observability diagnosis CLI.
Downloads
374
Maintainers
Readme
FreshContext Ops Pulse
Cloudflare metrics in. Plain-English diagnosis out.
What Is Ops Pulse?
FreshContext Ops Pulse is a local-first diagnosis CLI for Cloudflare Workers, D1, cron schedules, and Workers Observability logs.
It turns raw Cloudflare signals into plain-English status, evidence, and recommendations.
It was built while stabilizing FreshContext, but it can be used with any Cloudflare Workers project.
Ops Pulse helps answer:
- Is this Worker actually broken?
- Is it just idle?
- Was cron expected in this window?
- Are errors fresh or old?
- Are external calls noisy but not fatal?
- What should I inspect first?
- What should I avoid touching?
Who Is It For?
- solo builders running Cloudflare Workers
- people with D1-backed APIs
- MCP server maintainers
- feed/API builders
- small teams without a full observability stack
- developers who want local diagnosis before adding dashboards
Quickstart
From a local checkout:
npm install
npm run build
npm run dev -- --help
npm run dev -- report --hours 1
npm run dev -- logs --worker my-worker --hours 1
npm run dev -- assaysPackage-style commands after installing the package or using the packed tarball:
npx freshcontext-ops-pulse --help
npx freshcontext-ops-pulse report --hours 1
npx freshcontext-ops-pulse logs --worker my-worker --hours 1
npx freshcontext-ops-pulse assaysGlobal install is supported by the CLI package shape:
npm install -g freshcontext-ops-pulse
freshops --helpfreshcontext-ops-pulse is published on npm. What is on the registry can trail
this repository, so do not assume an npm install matches a checkout — ask:
npm view freshcontext-ops-pulse version # what the registry has
node -p "require('./package.json').version" # what this checkout isWhere they differ, the checkout is ahead and is the one to prefer.
Releases are cut by pushing a v* tag (.github/workflows/npm-publish.yml).
That workflow typechecks, tests, builds, packs and audits before it stages the
release; it refuses a tag that does not name the version in package.json; and
it skips a version already on the registry rather than failing on it.
It authenticates by trusted publishing (OIDC), not by a stored token. There is
no NPM_TOKEN and nothing to expire — the token this repository used until
September died silently somewhere between 15 May and 17 September, and only a
deliberate probe found it. npm exchanges the workflow's short-lived identity for
publish rights at the moment of publishing.
And it stages rather than publishes. npm stage publish queues the version;
nobody can install it until a maintainer approves it with 2FA, either through
npm stage approve <stage-id> or the package's Staged Packages tab on
npmjs.com, and npm holds approval open until its own malware scan finishes. That
is npm's default for trusted publishing configurations created after 3 September
2026, and it is the right default here regardless: a tool that refuses to state
more than it has established should not put a release in front of users without
a person looking at it first.
.github/workflows/npm-publish-identity.yml prints the OIDC claims this
repository presents, so the npm-side configuration can be filled in from
observed values. Every field on that form is an exact, case-sensitive match
against a claim, and a single wrong character fails at publish time with a
message that does not say which field is wrong.
Those guards protect a tag only if the commit it points at carries them.
GitHub Actions runs the workflow file from the ref that triggered it, not from
main, so a v* tag placed on a commit older than the guards runs whatever
npm-publish.yml said at that commit. Observed, not theorised: a v0.1.1 tag
placed on 858adbb ran the May version of this workflow, which went straight to
npm publish with no guard between.
Historical markers therefore do NOT use the v* prefix, because a tag that
records where an old release came from should not start a release. They use
published/<version> — for example published/0.1.1 — which records the same
fact and matches no workflow trigger. v* is reserved for a release cut from a
commit that carries the current publish workflow.
The only tag in this repository is v0.1.0, but the publication path of both
published versions is established by registry metadata rather than by Git
history. npm records a gitHead per version. 0.1.0 carries 95c065f, which
is the v0.1.0 tag, published on Node 20.20.2 — the workflow's runtime at the
time. 0.1.1 carries 858adbb ("Normalize npm package metadata"), published
fourteen minutes later on Node 25.6.1, a runtime that workflow does not use; it
was published by hand. gitHead records the HEAD of the publishing checkout and
not a hash of the tarball, so it does not establish that the working tree was
clean at that commit.
Environment Variables
Create a local .env from .env.example:
cp .env.example .envSet:
CLOUDFLARE_ACCOUNT_ID=
CLOUDFLARE_API_TOKEN=Optional resource selection:
CLOUDFLARE_WORKERS=my-worker,my-cron-worker
CLOUDFLARE_D1_DATABASES=my-databaseYour Cloudflare token stays local. Do not commit .env, do not paste tokens into issues/chats/logs, and use the narrowest permissions that let the commands you need read metrics.
Recommended Cloudflare Token Permissions
Based on the current implementation, useful permissions include:
- Account:
Account Analytics:Readfor GraphQL Analytics API. - Account:
D1:Readfor D1 database lookup and D1 analytics support. - Account:
Workers Scripts:Readfor Cron Trigger schedule lookup. - Account:
Workers Observability:Writefor the current Workers Observability telemetry query endpoint.
Exact permissions may depend on which commands you run and how Cloudflare exposes analytics for your account. Start with the minimum read-only permissions needed for Workers analytics/logs and D1 read access. The Observability telemetry query API currently documents a write-scoped permission even though Ops Pulse only reads/query logs.
Using Ops Pulse With Your Own Cloudflare Project
Ops Pulse does not require FreshContext.
Point it at your own Workers and databases through environment variables, then inspect specific windows:
freshops report --hours 1
freshops logs --worker my-worker --hours 1
freshops errors --worker my-worker --hours 24
freshops workers --hours 6
freshops d1 --hours 24Use your Cloudflare token permissions to control what Ops Pulse can read.
Current configuration is environment-variable based. A richer config file for named projects, expected assay targets, and default windows is planned.
FreshContext Preset / Example
FreshContext was the first real system Ops Pulse was used on.
Default Workers:
freshcontext-mcpfresh-hn-feedfresh-jobs-feed
Default D1 databases:
fresh-hn-feed-dbfresh-jobs-feed-db
Override these defaults with CLOUDFLARE_WORKERS and CLOUDFLARE_D1_DATABASES.
Commands
npm run dev -- report --hours 24
npm run dev -- workers --hours 24
npm run dev -- d1 --hours 24
npm run dev -- errors --worker freshcontext-mcp --hours 24
npm run dev -- logs --hours 24
npm run dev -- assays
npm run dev -- daily --hours 24
npm run dev -- report --mock
npm run dev -- doctorFlags:
--hours 24--worker freshcontext-mcp--json--mock
doctor — what can this machine actually reach?
Run this first, especially on a new machine or a corporate network.
npm run dev -- doctor
npm run dev -- doctor --verbose # show the evidence behind each row
npm run dev -- doctor --json
npm run dev -- doctor --health-endpoint https://example.com/health --mcp-endpoint ""It probes each capability separately and reports each on its own terms:
Configuration AVAILABLE
Cloudflare authentication AVAILABLE
Worker analytics AVAILABLE
D1 databases AVAILABLE
Workers Observability UNKNOWN - HTTP 403 from an unidentified responder
Canonical endpoint AVAILABLE
MCP application UNKNOWN - the response did not match the request we sent
5 available - 2 unknownThere is no overall pass/fail line, on purpose. A single verdict would have to decide what an unknown row means, and that is the judgement you are running this to make for yourself.
The five states are distinct and none of them is a synonym for another:
| State | Meaning |
| --- | --- |
| AVAILABLE | Probed, and it worked. |
| UNAVAILABLE | Probed, and the API itself established that these credentials may not use it. |
| UNKNOWN | Probed, but what answered could not be established. |
| NOT CONFIGURED | Nothing to probe. |
| NOT CHECKED | Never probed, because something it depends on was unavailable. |
UNKNOWN is the one worth understanding. A 401, 403, 429 or 5xx does not
prove the service refused you: a corporate proxy, VPN, egress policy, WAF or
access product can produce any of them without the request ever arriving. Ops
Pulse only reports UNAVAILABLE when the response carries a Cloudflare API
envelope, or — for MCP — a JSON-RPC reply carrying the request id it chose.
Otherwise it tells you it could not establish who answered, which is the fact
you need before you start changing tokens or redeploying anything.
NOT CHECKED is likewise not a soft failure. It means the row was skipped
because its prerequisite was unavailable, so it carries no evidence at all.
Exit code is 1 only when the Configuration row itself is NOT CONFIGURED,
i.e. doctor could not run. Disabling an optional endpoint probe deliberately
produces NOT CONFIGURED rows and does not affect the exit code. An UNKNOWN
row does not set a non-zero exit either: it is a finding, not a failure of the
command.
Set --mcp-endpoint "" or --health-endpoint "" to skip a probe you do not
have. Those rows then read NOT CONFIGURED rather than pretending to a result.
Diagnostic Assays
npm run dev -- assays
npm run dev -- assays --worker freshcontext-mcp --hours 1Assays are small read-only checks that return status, reason, evidence, and recommendation. See docs/ASSAYS.md.
Currently implemented:
- Cache Correctness: limited by Workers Observability visibility; returns
unknownwhen cache fields are not visible. - Version Consistency: FreshContext-specific probe against the default health endpoint.
- Tool Discovery: FreshContext-specific MCP
initializeandtools/listprobe.
Planned/generic assays are documented in docs/ASSAYS.md rather than presented as fully implemented.
API Surfaces
Implemented:
- Cloudflare GraphQL Analytics API for Worker metrics via
workersInvocationsAdaptive. - Cloudflare GraphQL Analytics API for D1 metrics via
d1AnalyticsAdaptiveGroups. - Cloudflare D1 REST API to resolve configured database names to database IDs.
- Cloudflare Worker Cron schedules REST API via
/workers/scripts/{script_name}/schedules. - Best-effort Cloudflare GraphQL scheduled invocation query via
workersInvocationsScheduled. - Workers Observability telemetry API via
/workers/observability/telemetry/query.
The CLI fails gracefully when a query shape, permission, or dataset is unavailable. It reports the limitation in Notes instead of inventing data. Mock output is only used when --mock is passed.
Privacy
Ops Pulse is local-first and uses your Cloudflare credentials from your local environment. See PRIVACY.md.
Known Limitations
- Worker CPU and wall-time quantile fields are discovered with GraphQL introspection. If Cloudflare rejects those fields, the CLI retries without them and notes that p90/p95 timing was unavailable.
- D1 analytics filters use database IDs. The CLI resolves configured names through the D1 REST API, so
D1:Readis needed. - Cron "last seen" uses best-effort GraphQL scheduled invocation data. If unavailable, the report still lists configured schedules when the REST schedule endpoint works.
- Workers Observability query field names can vary by account data shape. Logs/errors degrade to notes if Cloudflare rejects a filter.
- Assay probes are partly FreshContext-specific today; generic assay target configuration is planned.
- No hosted dashboard, browser login, deployment, Slack/email delivery, billing, or third-party AI/API calls are included.
References
- GraphQL Analytics API: https://developers.cloudflare.com/analytics/graphql-api/
- Worker metrics GraphQL tutorial: https://developers.cloudflare.com/analytics/graphql-api/tutorials/querying-workers-metrics/
- D1 metrics and analytics: https://developers.cloudflare.com/d1/observability/metrics-analytics/
- Workers Observability telemetry query API: https://developers.cloudflare.com/api/resources/workers/subresources/observability/subresources/telemetry/methods/query/
- Cron Trigger schedule API: https://developers.cloudflare.com/api/resources/workers/subresources/scripts/subresources/schedules/methods/get/
