Skip to content

feat: add marketplace credits gateway skeleton#326

Open
zozo123 wants to merge 11 commits into
openclaw:mainfrom
zozo123:feat/payment-marketplace-skeleton
Open

feat: add marketplace credits gateway skeleton#326
zozo123 wants to merge 11 commits into
openclaw:mainfrom
zozo123:feat/payment-marketplace-skeleton

Conversation

@zozo123

@zozo123 zozo123 commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • adds a disabled-by-default marketplace credits gateway skeleton for Crabbox
  • exposes GET /v1/marketplace/status and POST /v1/marketplace/quotes on the coordinator
  • adds crabbox marketplace status and crabbox marketplace quote for gateway visibility and smart-routing quote previews
  • documents the OpenRouter-like product boundary: one Crabbox billing relationship, one credit balance, provider adapters behind the broker, and direct-provider mode preserved
  • adds routing-group skeleton fields (priority, weight, active/enabled) inspired by AI gateway failover/load-balancing patterns
  • documents hosted OpenClaw, hosted Crabbox (crabbox.sh), and self-hosted/BYOK payment-operator modes so billing/legal/support ownership stays deployment-configured

Notes

This is intentionally not a live billing implementation. It does not capture payment, mutate a credit ledger, reserve credits, enforce credits during lease creation, or settle providers. The PR makes those boundaries explicit so maintainers can review the contract before real-money code lands.

Closes #282

Verification

  • npm ci --prefix worker
  • npm run format:check --prefix worker
  • npm run check --prefix worker
  • npm run lint --prefix worker
  • npm test --prefix worker -- marketplace.test.ts fleet.test.ts
  • go test ./internal/cli
  • node scripts/check-command-docs.mjs
  • git diff --check

@clawsweeper

clawsweeper Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 14, 2026, 4:30 AM ET / 08:30 UTC.

Summary
The PR adds a disabled-by-default marketplace credits gateway preview with Worker status/quote APIs, CLI marketplace commands, CRABBOX_MARKETPLACE_* env vars, docs, and tests.

Reproducibility: yes. for the review findings: source inspection of the latest PR head shows the CLI max-credits omission, invalid JSON 500 path, and product-specific docs. Runtime behavior proof from a real coordinator setup is still missing.

Review metrics: 2 noteworthy metrics.

  • Changed Surface: 14 files, +2398/-58. The diff spans Worker API, CLI, docs, and tests, so this needs product/API ownership beyond routine code checks.
  • Public Interfaces Added: 2 API routes, 2 CLI subcommands, 8 env vars. These names can become compatibility commitments even while the feature is disabled by default.

Merge readiness
Overall: 🧂 unranked krab
Proof: 🧂 unranked krab
Patch quality: 🦪 silver shellfish
Result: blocked until real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Add redacted terminal output or logs showing crabbox marketplace status and crabbox marketplace quote against a real coordinator setup; redact private IPs, API keys, endpoints, and account data.
  • [P1] Fix non-positive CLI max-credits handling and invalid/missing quote JSON handling.
  • Replace OpenClaw-specific marketplace docs with neutral examples and get maintainer approval for the public marketplace contract.

Proof guidance:

  • [P1] Needs real behavior proof before merge: The PR lists commands and self-review notes but does not include redacted terminal output, logs, screenshots, recordings, or linked artifacts showing the marketplace CLI/API behavior after the change. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] Merging would create public marketplace API, CLI, JSON, and CRABBOX_MARKETPLACE_* configuration contracts before the linked billing/routing product boundary is approved.
  • [P1] The PR still lacks after-change real behavior proof for the new CLI/API paths.
  • [P1] The CLI can silently drop a user-supplied non-positive --max-credits ceiling, turning an intended capped quote into an uncapped quote.
  • [P1] The quote route can return a 500 for invalid or empty JSON instead of a client-facing 400 validation response.
  • [P1] The generic marketplace docs still include OpenClaw-specific hosted/payment examples despite repository policy.

Maintainer options:

  1. Approve Contract And Harden First (recommended)
    Pause merge until maintainers approve the marketplace API/CLI/env contract, then fix validation, neutral docs, and real behavior proof before landing.
  2. Accept Experimental Public Surface
    Maintainers may intentionally land the preview if they are comfortable owning later migrations for the marketplace API and configuration names.
  3. Return To Design Issue
    If the billing and routing contract is not ready, keep implementation out of main and continue the product decision in Suggestion: Adding a payment layer with credit as a gateway for all providers, including bidding similar to Open Router. #282.

Next step before merge

  • [P1] Manual review is needed because this creates a public marketplace/billing contract and lacks contributor real behavior proof; automation can help only after maintainers approve the direction and the contributor supplies proof.

Security
Cleared: No concrete credential, dependency, CI, or supply-chain regression was found; the remaining billing/security concerns are product-boundary risks, not implemented secret handling.

Review findings

  • [P2] Reject non-positive CLI max credits — internal/cli/marketplace.go:62-64
  • [P2] Return 400 for invalid quote JSON — worker/src/fleet.ts:8252
  • [P2] Keep marketplace docs product-neutral — docs/features/marketplace-credits.md:35-36
Review details

Best possible solution:

Approve the provider-neutral marketplace contract in #282 first, then land a preview slice with neutral docs, strict CLI/API validation, compatibility expectations, focused tests, and redacted real coordinator proof.

Do we have a high-confidence way to reproduce the issue?

Yes for the review findings: source inspection of the latest PR head shows the CLI max-credits omission, invalid JSON 500 path, and product-specific docs. Runtime behavior proof from a real coordinator setup is still missing.

Is this the best way to solve the issue?

No. The skeleton is useful design signal, but the public marketplace contract needs maintainer approval before merge, and the validation, docs, and proof blockers should be resolved first.

Full review comments:

  • [P2] Reject non-positive CLI max credits — internal/cli/marketplace.go:62-64
    The Worker now rejects non-positive maxCredits, but the CLI wrapper still only serializes the field when the parsed value is greater than zero. Passing --max-credits 0 or a negative value is therefore sent as an uncapped quote instead of a flag error; track whether the flag was supplied and reject non-positive values before calling the coordinator.
    Confidence: 0.9
  • [P2] Return 400 for invalid quote JSON — worker/src/fleet.ts:8252
    readJson() can throw before marketplaceQuote() reaches its shape validation, for example on an empty or malformed JSON body. This handler only converts MarketplaceInputError to 400, so the top-level FleetCoordinator catch returns a 500 for bad client input; catch parse failures here and return the same invalid-request 400 path.
    Confidence: 0.84
  • [P2] Keep marketplace docs product-neutral — docs/features/marketplace-credits.md:35-36
    This generic Crabbox feature doc still names OpenClaw-hosted/openclaw.ai payment-operator examples, but the repository policy says new generic docs should avoid project-specific workflows unless the file is explicitly legacy or release-history related. Replace these with neutral tenant/operator placeholders before shipping the marketplace docs.
    Confidence: 0.91

Overall correctness: patch is incorrect
Overall confidence: 0.87

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against ccc27374948c.

Label changes

Label justifications:

  • P3: This is a speculative marketplace feature skeleton with no current regression or urgent operational impact.
  • merge-risk: 🚨 compatibility: The PR adds public marketplace API, CLI, JSON, and environment-variable surfaces that may need migration if the product contract changes.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR lists commands and self-review notes but does not include redacted terminal output, logs, screenshots, recordings, or linked artifacts showing the marketplace CLI/API behavior after the change. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

What I checked:

  • Repository policy applied: AGENTS.md was read fully; its product-positioning guidance applies because the PR adds generic Crabbox marketplace docs that still name OpenClaw-specific hosted/payment examples. (AGENTS.md:9, ccc27374948c)
  • Current main does not implement the gateway: A current-main search found no marketplace CLI/API/config implementation beyond unrelated Azure marketplace image wording, so this PR is not obsolete or implemented on main. (ccc27374948c)
  • Public Worker API added: The PR wires GET /v1/marketplace/status and POST /v1/marketplace/quotes into the coordinator route table. (worker/src/fleet.ts:704, d6c41c534ca7)
  • CLI max-credits validation defect remains: The CLI only sends MaxCredits when the parsed flag value is greater than zero, so a user-supplied zero or negative ceiling is silently omitted before the worker can reject it. (internal/cli/marketplace.go:62, d6c41c534ca7)
  • Malformed quote JSON still escapes as a 500: The new quote handler only converts MarketplaceInputError to 400; readJson can throw before marketplaceQuote shape validation runs, and FleetCoordinator's top-level catch returns 500. (worker/src/fleet.ts:8252, d6c41c534ca7)
  • Product-specific docs remain: The new generic marketplace feature doc still names OpenClaw-hosted and openclaw.ai payment-operator examples despite the repository's product-neutral documentation policy. (docs/features/marketplace-credits.md:35, d6c41c534ca7)

Likely related people:

  • Peter Steinberger: Current-main history and blame tie the coordinator route table, CLI command framework, cost/usage boundary, and AGENTS.md product policy to Peter Steinberger commits. (role: recent coordinator, CLI, and cost-boundary contributor; confidence: high; commits: ccc27374948c, 7844f3bd39f3, 66a0f997185e; files: worker/src/fleet.ts, worker/src/usage.ts, internal/cli/coordinator.go)
  • zozo123: Beyond this PR and the linked proposal, prior merged history shows zozo123 contributed provider and delegated-run features that are adjacent to cross-provider routing work. (role: adjacent provider contributor and proposal author; confidence: medium; commits: 8b246f6f96b7, 3b92643ab361, b2356e0f5173; files: worker, internal, docs)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@zozo123 zozo123 marked this pull request as ready for review June 13, 2026 11:54
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P3 Low-risk cleanup, docs, polish, ergonomics, or speculative feature. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. labels Jun 13, 2026
@zozo123

zozo123 commented Jun 13, 2026

Copy link
Copy Markdown
Contributor Author

Autoreview (codex) + improvements + push to finish line for #326

Ran the autoreview helper per skill (from agent-skills copy, --mode branch --base origin/main on the PR checkout of feat/payment-marketplace-skeleton).

Review runs & findings (all verified by reading the exact code in worker/src/marketplace.ts and adjacent):

  • Initial: 3 P2s (allowlist bypass in quoteProviders, no target compat check in quoteCandidate, fractional/0 TTL via positiveInt producing 0 credits).
  • After fixes + commit: 2 P3s remained (invalid TTLs silent default, malformed providers -> 500).
  • After more input guards + commit: 1 P2 (Azure macOS not covered in providerSupportsTarget) + P3s on class pricing fallback and dropped providers.
  • After sibling fixes + refinement for class-only leaseCost + commit: 2 P3s on silent drop for partial providers list and invalid maxCredits.
  • After final strictness (reject partial explicit list, reject bad maxCredits) + commit: engine hit credits (final run failed with "out of credits", no new structured output).

All reported findings fixed in this stack of commits on the branch (before the credits failure):

  • Enforce ALLOWED_PROVIDERS: explicit requests now intersected; error if none or (for explicit lists) if any dropped.
  • providerSupportsTarget + checks: linux-only for hetzner/gcp; macos only aws; windows aws+azure; sets unavailable + reason.
  • TTL: positiveInt now ceils; supplied invalid (<=0/nonfinite) now errors with invalid_ttl (only undefined omits to default).
  • Input validation: providers must be array (or error invalid_providers); class/serverType must be string; invalid maxCredits now errors; class/serverType guards before nonEmpty/.trim.
  • Class-only pricing: leaseCost base fallback now uses "standard" (not class name) when serverType derived from class (rate-card class keys like :beast still produce accurate cost when present; addresses the generic fallback misprice for documented --class usage).
  • Tests: Go ./internal/cli marketplace paths green after each round.

Pushed the fix commits (f29a462..26c450a on the fork branch, which updates this PR head). The skeleton is improved with the routing/cost/input contract hardened while keeping the preview surfaces and "one credit card, smart routing" model.

The last engine run was blocked by credits before confirming 0 findings, but every finding from successful runs was read, reproduced in the source, and fixed at the right (marketplace.ts) surface with no over-scope refactors. Focused Go tests re-ran clean.

If credits allow, a post-push autoreview on the new tip would be the final clean signal. Otherwise this is the improved state ready for review/merge + the publish step.

(Also see prior comment for the broader agentic/Airbyte vision requirements as review tasks.)

zozo123 and others added 11 commits June 14, 2026 10:55
…k unavailable for unsupported provider/target; ceil fractional TTLs (autoreview P2 findings)

- quoteProviders now intersects user-requested providers against status.supportedProviders (from CRABBOX_MARKETPLACE_ALLOWED_PROVIDERS) before unique/return; rejects with invalid_provider if none allowed.
- quoteCandidate now checks providerSupportsTarget (linux-only for hetzner/gcp etc. per existing backends); sets unavailableReason and available=false.
- positiveInt for ttlSeconds now uses ceil+max(1,...) so 0.5->1, never 0 credits from raw JSON (CLI path unaffected).
- Added small providerSupportsTarget helper next to quoteTarget/quoteStrategy for scope.
- Go tests still green; no other surfaces touched.

These close the contract violations for routing policy and compatible candidates in the quote API.
…explicit macOS/windows rules) per lease compat (autoreview P2 sibling)
…); validate providers array shape to avoid 500 on malformed JSON (autoreview P3s)

- Distinguish undefined ttlSeconds (use default) from supplied invalid (<=0, non-finite, string etc.): throw MarketplaceInputError 'invalid_ttl' for the latter so clients get 400 instead of silent wrong-duration quote.
- Early guard in marketplaceQuote: if providers supplied and not array, or body not object, throw proper MarketplaceInputError (caught as 400) instead of letting .length/.filter throw TypeError (500).
- These close the input-trust issues for the quote endpoint while keeping the happy path unchanged.
… nonEmpty (prevents .trim crash on number/object JSON; autoreview P3 input validation)
…s name) as serverType for leaseCost base fallback (accurate rate-card path for documented --class beast remains; addresses autoreview P2 on class pricing)
…side the allowlist (no silent drop); reject supplied but invalid maxCredits (P3 input validation from final autoreview)
… Worker CI format

Two changes, both within the no-billing skeleton boundary:

1. CI fix: run oxfmt on src/marketplace.ts so the Worker `format:check`
   step passes (the only failing check on the PR).

2. One step ahead: implement the weighted same-priority load balancing the
   PR already documents but left as a dead `weight` field. Adds a `weighted`
   routing strategy that ranks candidates within the winning priority tier by
   `weight`, and a per-candidate `routeShare` (0..1) previewing how traffic
   would split across equivalent providers (weights 3:1 -> share 75%/25%).
   Priority still selects the failover tier; weight load-balances inside it.
   Preview-only: routes no traffic, captures no payment, moves no credits.

Surfaced through worker (marketplace.ts), coordinator client + CLI printer
(`share=NN%`), with worker + Go tests and updated command/feature docs.

Verification: worker format:check / lint / check / check:node / build, full
worker tests (482), go test ./internal/cli marketplace tests, gofmt,
check-command-docs.mjs, git diff --check — all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e rounding (self-review follow-up)

Driven by an adversarial self-review of the weighted-routing commit. Verified
findings fixed, plus the next in-scope step. Still no billing/real routing.

One step ahead — routingPlan:
- The quote now returns an explicit failover ladder under the weighted strategy:
  an ordered array of priority tiers (highest first), each with its available
  members (provider, routeKey, weight, routeShare) and an `active` flag on the
  tier that serves the selected candidate. Makes the documented routing-groups
  contract (priority failover + weighted load balancing) reviewable as data
  instead of implicit in candidate sort order. Preview-only: routes nothing.
- Single-source computation: buildRoutingPlan() is the one place tiers/shares are
  derived; the flat candidate.routeShare is mirrored from the active tier so the
  two views can never diverge.

Verified fixes from the review:
- routeShare now uses largest-remainder allocation, so per-tier shares sum to
  exactly 1 (independent 4dp rounding drifted, e.g. 1:1:1 -> 0.9999). The CLI
  renders integer percents with largest-remainder too, so a tier always reads
  100% (e.g. 34%/33%/33%).
- Removed the dead `weight > 0 ? : 0` guards (weight is always >= 1).
- Quote ID now folds in strategy + maxCredits so distinct requests don't collide.
- Docs: clarified selection is deterministic (heaviest available, not
  probabilistic); documented routingPlan in command + feature docs; ticked
  roadmap item 5.

Tests (worker 484 total; go cli marketplace 4):
- equal-weight (1:1:1) shares sum to exactly 1; maxCredits-excluded candidate
  dropped from the weighted tier + denominator; strategy-distinct quote IDs;
  parameterized non-weighted strategies leave routeShare/routingPlan unset;
  CLI negative `share=` guard for cheapest; CLI routing-plan ladder render
  (largest-remainder display totals 100%, active + failover tiers).

Verification: worker format:check/lint/check/check:node/build + full tests (484),
go test ./internal/cli marketplace tests, go vet, gofmt, check-command-docs.mjs,
git diff --check — all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…te + routing paths

Tighten the disabled-by-default credits gateway skeleton without adding any
real money movement:

- worker: reject non-string provider/target/strategy and non-string
  providers[] members up front with clear MarketplaceInputError codes
  (previously a numeric provider produced a misleading invalid_provider
  via the allowlist filter, and a non-string target/strategy fell through
  to the union check).
- worker tests: cover the new shape-validation codes, the non-object
  request guard, the explicit-provider-outside-allowlist rejection, and a
  guard that enabling the gateway never flips payments/ledger/enforcement on.
- worker fleet tests: cover the HTTP 409 disabled path and the HTTP 400
  malformed-input path for /v1/marketplace/quotes.
- cli tests: add direct unit coverage for parseMarketplaceTTL,
  marketplaceRoutePercents (largest-remainder per-tier 100% allocation),
  and marketplaceShareSuffix (weighted-share/failover rendering).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@zozo123 zozo123 force-pushed the feat/payment-marketplace-skeleton branch from f4112ed to d6c41c5 Compare June 14, 2026 08:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P3 Low-risk cleanup, docs, polish, ergonomics, or speculative feature. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Suggestion: Adding a payment layer with credit as a gateway for all providers, including bidding similar to Open Router.

1 participant