Skip to content

Publish the read side as @profullstack/crawlproof, and make crawlproof a command - #241

Merged
ralyodio merged 2 commits into
masterfrom
crawlproof-cli-package
Sep 6, 2026
Merged

Publish the read side as @profullstack/crawlproof, and make crawlproof a command#241
ralyodio merged 2 commits into
masterfrom
crawlproof-cli-package

Conversation

@ralyodio

@ralyodio ralyodio commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Stacked on #240 — merge that first. This branch adds only the last two commits.

crawlproof dashboard was a lie: there was no crawlproof on PATH and the CLI had no bin, so every command in its own help needed you to cd into the checkout and type npm run cli --. Two changes fix that.

crawlproof is a command now

bin/crawlproof.mjs resolves the repo from its own location and runs tsx against cli/index.ts, preferring the checkout's tsx and falling back to npx so a fresh clone still starts. It relays the child's signal rather than inventing an exit code, so a TUI killed with ctrl+c does not look like a clean exit.

The token also falls back to the token field of ~/.crawlproof.json — same shape and same reasoning as ~/.coinpay.json. Using the CLI should not be conditional on remembering to export a secret first.

packages/cli@profullstack/crawlproof

So cli-tools can vendor it the way it vendors hqtui, and any box can npm i -g @profullstack/crawlproof.

Two commands only, deliberately: dashboard and stats. Both are token-authed and pure HTTP, which is exactly what makes them publishable — audit needs the audit engines and their model SDKs, sweep needs a cron secret. Those stay in the repo.

It is a build artifact, not a second copy. lib/dashboard/* and cli/dashboard.ts stay where the suite already covers them and where the in-repo CLI already imports them; build.mjs bundles the same files with esbuild (44kb) and leaves hqtui and the CoinPay SDK external, since both have their own release cadence. One implementation, no drift.

The stats printer moves to lib/dashboard/stats-text.ts and returns a string instead of writing to stdout — two CLIs print it now, and a printer that owns the process can be neither shared nor tested.

Verified

From the built bundle, not the source:

  • crawlproof stats crawlproof.com against production, reading the token from ~/.crawlproof.json with nothing exported: 172 visitors, 58 pageviews, sources and pages as expected.
  • crawlproof dashboard --json against a local server on the production environment: all 52 sites, all three feeds, zero source errors.
  • The TUI itself rendered through a pty harness from the bundle.

Full suite green: 2,125 tests (19 new), tsc --noEmit clean.

Note

A bare flag takes the next bare word as its value, so crawlproof stats --json site.com swallows the site. That is the existing parser's behaviour, shared with the in-repo CLI; this PR pins it in a test so it stays a decision rather than becoming a surprise, but does not change it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HvWJ4336pxTFRdRbvsTQeD

ralyodio and others added 2 commits September 6, 2026 09:36
The CLI had no bin and was not on PATH, so every command in its own help and
in this PR's description was a lie unless you first cd'd into the checkout and
typed `npm run cli --`. `crawlproof dashboard` now works from anywhere.

bin/crawlproof.mjs resolves the repo from its own location and runs tsx against
cli/index.ts, preferring the checkout's tsx and falling back to npx so a fresh
clone still starts. It relays the child's signal rather than inventing an exit
code, so a TUI killed with ctrl+c does not look like a clean exit.

The token now also falls back to the `token` field of ~/.crawlproof.json, the
same shape and the same reasoning as ~/.coinpay.json: using the CLI should not
be conditional on remembering to export a secret first.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvWJ4336pxTFRdRbvsTQeD
A dashboard you can only run by cd'ing into a Next.js checkout is not installed
anywhere. packages/cli publishes the two commands a box actually wants —
`dashboard` and `stats` — so cli-tools can vendor it the way it vendors hqtui,
and any machine can `npm i -g @profullstack/crawlproof`.

Only those two, deliberately. Both are token-authed and pure HTTP, which is
what makes them publishable: `audit` needs the audit engines and their model
SDKs, and `sweep` needs a cron secret. Those stay in the repo.

The package is a **build artifact**, not a second copy. lib/dashboard/* and
cli/dashboard.ts stay where the suite already covers them and where the in-repo
CLI already imports them; build.mjs bundles the same files with esbuild and
leaves hqtui and the CoinPay SDK external, since both have their own release
cadence. There is one implementation and it cannot drift.

The stats printer moves to lib/dashboard/stats-text.ts and returns a string
rather than writing to stdout, because two CLIs print it now and a printer that
owns the process can be neither shared nor tested.

Verified from the bundle against production: `crawlproof stats crawlproof.com`
reads the token out of ~/.crawlproof.json with nothing exported, and the
dashboard collects all 52 sites with zero source errors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvWJ4336pxTFRdRbvsTQeD
@ralyodio
ralyodio force-pushed the crawlproof-cli-package branch from bfeec68 to 988b04b Compare September 6, 2026 09:37
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

39 finding(s)

HIGH/CRITICAL: 2 | MEDIUM: 28 | LOW: 9

Severity Rule Location
HIGH tls-verification-disabled lib/onion.ts:48
HIGH secret-generic-credential lib/sp/platforms/facebook.ts:32
MEDIUM js-unescaped-html-sink app/(app)/dashboard/admin/email-broadcast/EmailBroadcastForm.tsx:125
MEDIUM js-unescaped-html-sink app/(app)/dashboard/projects/[id]/autoblog/articles/[articleId]/page.tsx:214
MEDIUM js-unescaped-html-sink app/(marketing)/blog/[slug]/page.tsx:67
MEDIUM js-unescaped-html-sink app/(marketing)/blog/[slug]/page.tsx:97
MEDIUM js-unescaped-html-sink app/(marketing)/blog/[slug]/page.tsx:104
MEDIUM js-unescaped-html-sink app/(marketing)/blog/[slug]/page.tsx:110
MEDIUM js-unescaped-html-sink app/(marketing)/recent/page.tsx:186
MEDIUM js-unescaped-html-sink app/(marketing)/recent/page.tsx:190
MEDIUM js-unescaped-html-sink app/c/[project]/[slug]/page.tsx:77
MEDIUM js-unescaped-html-sink app/c/[project]/page.tsx:57
MEDIUM js-unescaped-html-sink app/careers.js/route.ts:228
MEDIUM js-unescaped-html-sink app/careers.js/route.ts:285
MEDIUM js-unescaped-html-sink app/layout.tsx:129
MEDIUM js-open-redirect app/login/form.tsx:39
MEDIUM js-unescaped-html-sink app/r/[token]/page.tsx:176
MEDIUM js-open-redirect app/signup/form.tsx:43
MEDIUM js-open-redirect components/billing/buy-credits-modal.tsx:98
MEDIUM js-unescaped-html-sink components/json-ld.tsx:8
MEDIUM js-unescaped-html-sink components/report/markdown-view.tsx:15
MEDIUM js-unescaped-html-sink lib/careers/page-templates.ts:198
MEDIUM redos-nested-quantifier lib/emailMarkdown.ts:41
MEDIUM redos-nested-quantifier lib/emailMarkdown.ts:324
MEDIUM redos-nested-quantifier lib/lx/articleGen.ts:99
MEDIUM redos-nested-quantifier lib/tracker/agent-gate.ts:61
MEDIUM sh-remote-script-execution prober/deploy/provision.sh:30
MEDIUM sql-template-interpolation scripts/detect-slot-themes.ts:31
MEDIUM sql-template-interpolation scripts/purge-constructed-keywords.ts:163
MEDIUM sql-template-interpolation scripts/purge-offniche-keywords.ts:124
LOW secret-generic-credential app/(marketing)/docs/autoblog-webhook/page.tsx:145
LOW secret-generic-credential lib/sp/platforms/linkedin.ts:25
LOW js-dynamic-code-execution tests/careers-page-templates.test.ts:21
LOW js-dynamic-code-execution tests/careers-widget-script.test.ts:19
LOW js-dynamic-code-execution tests/careers-widget-script.test.ts:69
LOW js-dynamic-code-execution tests/contract/ad-visitor-id.test.ts:51
LOW js-dynamic-code-execution tests/contract/ad-visitor-id.test.ts:52
LOW secret-generic-credential tests/contract/posthog-integration.test.ts:13
LOW secret-generic-credential tests/lead-campaign.test.ts:16

Snippets are redacted; ThreatCrush never prints matched credential material.

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedesbuild@​0.25.12911007390100

View full report

Comment thread packages/cli/src/cli.ts
if (site) query.set("site", site);

const res = await fetch(`${apiBase(args)}/api/tracker/v1/stats?${query.toString()}`, {
headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
@ralyodio
ralyodio merged commit 759dfc7 into master Sep 6, 2026
10 checks passed
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.

2 participants