npm.io
0.2.1 • Published 19h ago

@dak-os/marketplace

Licence
MIT
Version
0.2.1
Deps
2
Size
41 kB
Vulns
0
Weekly
0

@dak-os/marketplace

The marketplace primitive of the Dak Stack — the MarketplaceListing format (a promoted, eval-gated skill packaged for cross-tenant sharing), the listing JSON Schema (which ships with the package), and the MarketplaceRegistry contract: publish / list / get / resolveForInstall over a git-portable directory of JSON listings. It depends only on @dak-os/skills (whose Skill / SkillStatus and eval PromotionDecision types it reuses) and ajv.

The first package of Stage 8 (the skills/agents marketplace — the capability flywheel), recorded in ADR-0022. It copies @dak-os/skills's shape — package = format + loader + contract; the eval GATE stays a consumer. MIT-licensed and independently adoptable.

What a listing is

A MarketplaceListing is the public face of a skill on the marketplace — its identity, how it's found, who shared it, and (the load-bearing part) why it's trustworthy:

Field What it is
id / version / name / description / status / triggers the listed skill's identity + discovery fields — the same types as a Skill's fields (reused, never duplicated)
publisher / license / category who shared it, under what license, and a coarse class for discovery
provenance the eval gate's promotion decision (@dak-os/skills/eval's PromotionDecision: baseline/candidate scores, delta, margin, regressions), plus the sourceTraces the skill was distilled from and the evalSuiteId that judged it
payload the installable content (ADR-0024): the SKILL.md playbook body + the travelling held-out evals suite (@dak-os/skills/eval's EvalCase[], non-empty). It travels with the listing because the marketplace is the only shared cross-tenant surface — an installer never reads the publisher's skills/ dir or spine. Deliberately no fixtures/artifacts: a consumer re-gates with its own outputs, never by replaying the publisher's
stats usage statistics — a placeholder in inc 1 (installs); take-rate accounting (Stage 8 inc 4) extends it

The trust floor

A listing is well-formed only if its provenance carries a PASSING gateprovenance.decision.promote must be trueand it can be re-verified: payload.evals must be non-empty (ADR-0024).

You may publish a skill you proved beat its baseline on a held-out eval suite, never a skill you merely wrote. validateListing enforces this both via the bundled schema (which pins decision.promote to const true and payload.evals to minItems: 1) and in code (defense in depth), and the registry checks it at the write boundary AND re-checks on every read — so a hand-edited or tampered file fails loud rather than flowing on as trusted.

This is the publisher's obligation. The installer's trust model is separate and stricter: installing a foreign skill re-gates it locally (never trust a foreign score) — Stage 8 inc 3, ADR-0024. This package guarantees a listing is structurally trustworthy; it does not ask you to believe its number.

What's here

Subpath What it is
@dak-os/marketplace The barrel — the whole surface below.
@dak-os/marketplace/listing The MarketplaceListing format + MarketplaceProvenance / MarketplaceStats, validateListing / isWellFormedListing (schema + trust-floor checks), listingFromSkill (build a listing from a promoted skill + its decision), and InvalidListingError.
@dak-os/marketplace/registry The MarketplaceRegistry contract (publish / list / get / resolveForInstall) and LocalFileMarketplaceRegistry, the git-portable reference impl over a marketplace/ directory of <id>.json listings.

The canonical listing.schema.json ships under schema/ and is loaded by the format itself (import.meta.dir) — the format is the package's, not the consumer's content dir.

The seam: the package is the contract, the gate is a consumer

Like @dak-os/skills (ADR-0015), @dak-os/marketplace carries the vocabulary (the listing format, the registry contract) and the mechanics a host needs at the ends — publish (write a listing out) and resolveForInstall (read one in for the install path). The eval gate that produces a listing's provenance.decision stays a consumer in the app (src/eval/*), wired to the host's model and event store — it is the mechanism, the package is the stable contract.

The marketplace is the one cross-tenant surface

The reference registry is backed by a plain marketplace/ directory of JSON files, committed to git. This is deliberate: the marketplace is the one surface that crosses the tenant boundary, so it must not live on any tenant's spine (tenant-isolated by construction — Stage 7). A directory of files is a surface every tenant can read and a publisher can PR into, with no boundary to cross and no spine to breach.

Install

bun add @dak-os/marketplace        # or: npm install @dak-os/marketplace

Until the packages are on npm (publishing is tracked in advisor-plans/002), consume them from the monorepo: clone https://github.com/twofoldtech-dakota/dak and depend on packages/marketplace via file:/workspace resolution.

Use

import { LocalFileMarketplaceRegistry } from "@dak-os/marketplace";
import { listingFromSkill } from "@dak-os/marketplace/listing";

const registry = new LocalFileMarketplaceRegistry("marketplace");

// Publish: package a promoted skill + the gate decision that promoted it. Rejected unless the gate passed.
const listing = listingFromSkill(skill, promotionDecision, {
  publisher: "dak",
  license: "MIT",
  category: "writing",
  evalSuiteId: "doc-drafting-suite@3",
});
registry.publish(listing);

// Discover + resolve for install (the install path then re-gates locally — never trust a foreign score).
const all = registry.list();
const chosen = registry.resolveForInstall("doc-drafting");

No build step — the package exports its .ts source directly and runs under Bun (and resolves under tsc with moduleResolution: bundler). See ../README.md for the conventions every Dak Stack package follows.

Test

bun test

The tests are standalone — they build synthetic listings in a temp dir and exercise the format, the trust floor (a non-passing gate is rejected), the registry round-trip (publish → list → get), republish-replaces, read-time re-validation (a tampered file fails loud), and id path-traversal guarding, against the bundled schema — proving @dak-os/marketplace needs no Dak content or network.