feat(web) Private space - #274
Open
cuongdcdev wants to merge 124 commits into
Open
Conversation
- DefiLlama: Programmatic access to DeFi metrics (TVL, protocols, chains, yields) - Tavily: A search engine built for LLMs/agents to run real-time structured web queries
…skills - tavily_search_skills - trading_skill_v2 - woo_copilot - woo_product_copywriter - wp_content_research_writer - wp_copy_editor - wp_publisher - wp_seo_auditor - wp_social_sentiment_miner
WASM Tools: - coingecko: Universal cryptocurrency price and market oracle. Retrieves current prices, market tables, detailed metadata, historical charts, candles, and trending assets. - etherscan: EVM explorer across 60+ EVM-compatible networks. Retrieves wallet balances, transaction histories, and ERC-20/721/1155 token transfers. - jina: Web reader (URLs to markdown), screenshot capturer, and web/academic/image search utility. - pikespeak: NEAR Protocol indexer and wealth portfolio tracker (native/token balances, validator APY, and DeFi positions like Rhea). Agent Skills: - brand_reputation_monitor: Monitors brand and keyword mentions on major social platforms (X, Reddit, TikTok, Facebook, Threads), runs sentiment analysis, and prepares response drafts. - crypto_trend_sentiment: Cross-references CoinGecko trending crypto assets with discussions and hype on major social platforms (X, Reddit, TikTok, Threads) to gauge market sentiment. - local_business_lead_generator: Harvests local business leads from Google Maps, scrapes websites for contact emails, and formats prospect lists. - smart_buyer_assistant: Scouts e-commerce platforms (Google Shopping, Amazon, eBay, etc.) for specs, reviews, and price drops. Other: - defillama: Update README.md with a screenshot link.
…youtube) and YouTube skills Add 4 new WASM tools and 3 YouTube domain skills: WASM Tools: - frankfurter-fx: Foreign exchange (FX) rates, batch conversions, and historical rate analytics via Central Bank open data. - messari: Crypto market assets, historical metrics, market intelligence, and crypto news feed. - serper: High-speed Google search API integration (organic search, news, images, and places). - youtube: YouTube Data API v3 integration (video metadata, channel stats, playlist items, and video transcripts/captions). Skills: - youtube_recap_expert: Video transcript summarization, key takeaway extraction, and structured video recaps. - youtube_content_insights: In-depth YouTube channel performance analytics, video metrics auditing, and engagement insights. - youtube_competitor_analyst: Competitive benchmarking, content gap analysis, and video strategy advisor for YouTube creators.
…ing: - bluesky-analytics, coingecko, crypto-ta-engine, defillama, etherscan - firecrawl, frankfurter-fx, jina, messari, nearcatalog - pikespeak, serper, tavily, wordpress, and youtube
IronHub previously assumed every tool manifest had to be reconstructed from legacy capabilities JSON and forced that translation through the Reborn v3 generator. Native Reborn packages already carry their authoritative manifest, including per-method schemas, network targets, credential audiences, and runtime metadata. Regenerating those packages as v3 could collapse their method surface into a generic invoke tool and reject valid v2 credential layouts even though IronClaw supports both manifest versions. Validate checked-in native v2/v3 manifests against the tool directory and Cargo metadata, verify every referenced schema can be packaged, and publish the native manifest unchanged. Keep capabilities-to-v3 generation only as a compatibility path for legacy tools that do not yet ship manifest.toml, and limit translation regression tests to that legacy set.
Add native Reborn v2 manifests, first-class method schemas and prompts, updated WASM sources, and catalog version bumps for the manually tested tool set. Updated: bluesky-analytics, coingecko, crypto-ta-engine, defillama, etherscan, firecrawl, frankfurter-fx, jina, messari, nearcatalog, pikespeak, serper, tavily, wordpress, and youtube.
Resolve warnings promoted to errors by the IronHub CI matrix for both wasm32-wasip2 release builds and native test builds. Apply idiomatic formatting, prefix handling, conditional compilation, and narrowly scoped argument-count allowances without changing tool behavior.
Marketplace detail pages previously resolved repository README images against the browser route, exposed misleading action and trigger totals, and rendered credentialed tools as unauthenticated when their security declarations lived outside the narrow legacy shape understood by the catalog. Serve repository-relative images through a constrained image-only asset route, and normalize authentication, credential audiences, network targets, effects, and permissions from both legacy capabilities JSON and native Reborn manifests. Keep true credential-free tools explicit while presenting Bearer, API key, Basic, query parameter, and OAuth with PKCE models in user-facing language. Remove action and trigger counters, action-based sorting, inferred limits, and the confusing create-skill action. Reorganize technical details around access, network permissions, implementation metadata, activation keywords, and source resources, while promoting Install to Agent in the mobile detail header. Update installation examples to the ironclaw ironhub command namespace and add an accessible, reduced-motion-aware scroll-to-top control outside the agents workspace. Cover asset confinement, manifest normalization, and catalog sorting fallbacks with focused regression tests.
Make IronHub's public marketplace, collections, and use cases easier to find, preview, cite, and consume across search engines, social platforms, and AI agents. Generate descriptive page metadata, canonical URLs, branded social cards, and schema.org structured data from public catalog records. Publish crawler controls and a resilient sitemap while preventing authenticated and partner-only pages from being indexed. Support agent-readable discovery through llms.txt, Markdown indexes, and appendable .md URLs such as /usecases/<id>.md. Generate these representations directly from the existing source records, return canonical response headers, and advertise them as typed alternatives from the corresponding HTML pages.
Co-authored-by: cuongdcdev <12132077+cuongdcdev@users.noreply.github.com>
# Conflicts: # scripts/check-extension-manifests.sh # tools/crypto-ta-engine/Cargo.lock # tools/nearcatalog/Cargo.lock # web/features/marketplace/components/marketplace-screen.tsx
… patch versions - Update 15 verified WASM tools () with extension manifest v3 schemas. - Include mandatory policies required for IronClaw 1.1+.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-authored-by: cuongdcdev <12132077+cuongdcdev@users.noreply.github.com>
Fix skill companion release asset names
…, and rate limiting Implements task group 2 (wt-artifact-api) of add-private-workspace: - PATCH /api/private-artifacts/[id]: partial update of title/description/ visibility/sourceUrl; unknown or immutable fields (name/version/type) -> 400. - DELETE /api/private-artifacts/[id]: cascades content rows via Prisma, then best-effort S3 prefix cleanup (log-only on failure). - DELETE /api/private-artifacts/[id]/content/[kind]: removes one content row + its S3 object, added additively next to the existing PUT handler. - POST /api/private-artifacts/[id]/token: mints a 1-hour manifest-scope token via the existing mintArtifactToken and returns an absolute manifest URL. - New reusable fixed-window rate limiter (web/lib/http/rate-limit.ts, swappable store), applied to the two public token routes (manifest and content-by-token) with 429 + Retry-After. - Stub web/lib/storage/index.ts (deleteObject/deleteByPrefix + other S3 lib signatures) so this worktree typechecks ahead of wt-storage's real implementation; marked for integration replacement. All mutation routes stay session + requireActiveOrganization() + org-scoped (404 on cross-org access) + assertSameOriginRequest guarded.
…storage
Add web/lib/storage/ (put/get/delete/deleteByPrefix/presigned-URL) backed by
@aws-sdk/client-s3, configured from S3_* env vars (S3_PUBLIC_ENDPOINT for
presigned URLs). Replace PrivateArtifactContent.bytes with storageKey in the
schema/migration, rewrite content.ts to upload to S3 under
private-artifacts/{orgId}/{artifactId}/{kind} and compute sha256 server-side,
and switch the token download route to a 302 redirect to a <=5min presigned
URL (Cache-Control: no-store) instead of proxying bytes.
Adds a storage smoke script (pnpm storage:smoke) for the local dev stack and
unit/route tests (pnpm test:storage) that mock the S3 SDK and Prisma via
node:test module mocking, plus a small ESM loader so plain `node --test` can
resolve this repo's "@/..." tsconfig path alias.
…ons API
Add web/app/api/orgs routes for listing/creating/renaming orgs, switching the
active org, leaving/removing/re-roling members with owner/admin/member
permission checks and last-owner protection, and full invitation lifecycle
(create, list pending-by-email, list org invitations, accept/reject/cancel)
with 7-day expiry and no email sending. Business logic lives in
lib/orgs/{roles,service,invitations}.ts with unit tests covering the
permission matrix, duplicate/member 409s, expiry filtering, and last-owner
protection.
Managing an item and editing it are one job, but they lived on two routes: "Add instructions file" in Files had to link away and back, and every change cost a round trip. The edit forms move into features/partner/components as SkillEditor and ToolEditor, the item page renders one of them inline, and the old routes redirect so links already handed out still land somewhere useful. Copy install link, Download and Publish move up beside the title. Fixes a data-loss bug on the way: the skill form seeded `description` from the stored file's frontmatter alone, while the catalog reads it off the artifact record. An item whose description lived only on the record opened with a blank field, and saving wrote that blank straight back over the catalog copy. Frontmatter still wins when it has one; otherwise the record does. Drops *.capabilities.json from the workspace UI. manifest.toml (schema reborn.extension_manifest.v3) carries the effects, default permission and secrets handles it used to, so presenting it as a file the owner edits made two sources of truth out of one — and the editor made it `required`, which silently blocked every save for a tool that shipped without one. Ingest, checks and the signed manifest are untouched, so nothing changes for an agent. In its place the Files section lists the schema and prompt files the package declares, by the path the agent will ask for. Each stored file gets a Download button, "Download instructions" reads "Download skill", and the instructions section names the file it edits.
Four selects side by side did not fit the workspace's content width: at 1440 they measured 658px against 888px of room, so they took a row of their own and pushed the view toggle onto a third. They also drew the eye every time whether or not anything was filtered. A new FilterMenu primitive holds them behind one button with a count of how many are set, which is the only thing worth seeing at a glance. Search, Filters and the view toggle now share one row, and search keeps 546px of it. Each select gained a visible label inside the panel, so the "Type: Skills" prefixes that stood in for one are gone.
An owner could see that a .zip was stored for their tool, and its size, and nothing else about it. The files that archive actually contains -- the thing they are being asked to trust and replace -- were only ever visible to ingest. `listBundleEntries` exposes what `listZipEntries` already parsed: the central directory alone, so no entry is inflated and a 25MB archive stays cheap to read. Directory records are dropped (a zip may or may not carry them, and the folder tree is derivable from the paths either way) and so are symlinks, which `inspectExtensionBundle` rejects outright. The listing is computed per request rather than recorded at upload time. It is a property of the bytes in storage, and deriving it there means it cannot drift from them the way a persisted copy could.
A tool is created from a single .zip and every file the hub stores for it is extracted from that archive, but the workspace then presented those files as three separately managed things -- Program file, Setup details, Uploaded package -- each with its own card and its own upload. Replacing one of them alone was the worst case: swapping the .wasm left manifest.toml describing bytes that were no longer stored. So the whole package is now the unit of change. The tool editor's step 3 is Package: what is stored, what is inside it, and one drop target that replaces all of it. The .wasm-only upload is gone. The generic Files section is a skill's single instructions file now and nothing else, and the per-file downloads went with it -- the header still offers the whole package. What is inside is a plain file tree built from the archive's own paths, folders first, sizes on the right. Not the manifest's declared assets: the reader is asking what they uploaded, not what the installer will match. Long listings state how many files they left out rather than truncating silently. Uploading runs immediately rather than on Save changes. The PUT rewrites every stored content row and the entire declared asset set server-side, which is not something to fold into a form submit that could half-apply -- the drop target says so. Also puts the item's type first in the header badges, and moves the "add the required files" reason out of the Files section that a tool no longer renders, so the disabled Copy install link keeps its description.
…w panel "Capabilities file is valid JSON" warned every tool that stores no *.capabilities.json, which is now the normal case and, since the file disappeared from the workspace, one the owner cannot act on: nothing offers to add or fix a document they never see. A row on the panel that exists to say why publishing is blocked has to be actionable. Nothing is lost by removing it. *.capabilities.json is the legacy carrier of data reborn.extension_manifest.v3 moved into manifest.toml, and the agent files whatever we publish under `legacy/capabilities.json` and never reads it (ironclaw-contract.ts). The one bound that still matters -- its size -- is checked by agent_contract via verification.ts's `capabilities_size`, which is untouched.
`core` is a 569KB crash dump that was committed by accident. Removed, and `core` / `core.*` added to .gitignore so the next one stays out.
"MVP" was a note to ourselves about how finished the thing was, sitting
in the address bar of the product members use every day. The workspace
now lives at /dashboard, and its list screen -- previously the doubled
/mvp/dashboard -- is /dashboard/catalog, which says what the page shows.
Every old address still resolves: next.config redirects /mvp,
/mvp/dashboard and /mvp/:path* permanently, with the /mvp/dashboard rule
ahead of the wildcard so it does not land on /dashboard/dashboard. Links
already shared or bookmarked keep working.
The env var behind the route gate keeps its NEXT_PUBLIC_DISABLE_MVP_ROUTE
name even though the flag reading it is now isWorkspaceRouteDisabled: it
is already set in deployed environments, and renaming it would silently
un-hide the workspace wherever the new name is not set yet.
Route checks that were spelled as `pathname.startsWith("/mvp")` in three
places are one `isWorkspaceRoute` helper, and the sessionStorage key for
the catalog's view preference follows the rename.
Labels and toasts on the moved screens are aligned with the wording the
rest of the workspace already uses ("Back to your catalog", "<name>
created"), rather than leaving each screen with its own phrasing.
The header offered a different set of controls depending on where you were: an account button on marketing pages, a notification bell only inside the workspace, a Docs button competing with the workspace's own Developer Docs link. A signed-in member now gets the same cluster everywhere -- bell, theme, account menu -- grouped into one capsule, with the account button replaced by a real menu (UserMenu) instead of a link. UserMenu reads useSearchParams, so it sits behind a Suspense boundary: without one it would force the root layout, and therefore every route, out of static rendering. ToastProvider moves to the root layout. It used to be mounted twice -- once around the header's bell, once around the workspace shell -- which gave the two halves of the same screen separate feedback stacks. One provider, one stack. The workspace sidebar drops the support card and the mobile sheet it carried; navigation on small screens is the site's own mobile nav, and the sidebar no longer duplicates it.
The agent install form minted a fresh shared key on every visit and every tab switch, so anyone who looked away from the page had to export the variable and restart their agent again to match it. A key this browser generated is now remembered and offered back. Only generated keys are stored. A pasted key already lives on the user's agent, and copying it into this browser would spread the secret for nothing. What is stored is still a shared secret in localStorage, readable by any script on this origin -- that is the trade, and the alternative was a key that could not survive a reload. Anything in storage that could not have come from the generator is ignored rather than shown back as if the hub had issued it. Also stops truncating account names: a 64-character implicit NEAR account id has no natural break points, so it wraps rather than ellipsising into something the owner cannot read.
Tool identifier and version now come from the name and the uploaded package, as a skill's already do, so neither is typed. A name another item holds gets a -2 suffix server-side instead of a 409 about a field that is no longer on screen. The minted install link renders in full on the manage page: a clipboard write rejects whenever the document is not focused, and that left the author with nothing.
Sub-accounts, .tg, testnet and implicit accounts are valid invitees: account ids are matched against NEAR's own grammar, and the address better-near-auth stores for them is no longer re-validated as an email, which is what rejected work.efiz.near as "Invalid email address". The members page lists invitations through the org route instead of the BetterAuth client, so each row can show the NEAR account id it was addressed to rather than a temp-<hex>@ placeholder. Also: TRUSTED_ORIGINS is read into the auth config, and dev requests from *.nearcatalog.xyz are allowed.
- Add icons to sidebar sub-navigation (Skills, Tools, Souls) - Add icons to catalog KPI stat cards (Total items, Skills, Tools, Souls, Drafts) - Optimize KPI row for mobile with smooth horizontal scroll - Inline search and filter toolbar on mobile - Rename 'Your catalog' to 'Catalog' for consistency with workspace navigation
A private artifact was unreachable whenever a public entry shared its slug: resolution tried public first and returned it, so the install handshake signed the wrong artifact. Dispatch on the requested source instead, and let a private miss answer 404 rather than falling through. Membership stays inside the query so a non-member and an absent name are answered identically; only a member learns the organization's name.
Version was absent from the mutable field set, so an author could never bump it and the intended update model — publish a new version, install replaces the old — was unreachable. Making it mutable alone would let a published version's bytes change under installers who already verified its digest. A new publishedVersion column records the version the current files were published as, and content and asset writes are refused while it matches. Bump the version and the artifact reopens for editing. Version must move strictly forward, build metadata excluded from precedence, so 1.0.0+a to 1.0.0+b is refused rather than silently accepted as a new version naming different bytes.
A soul is one SOUL.md, optionally with a README. The README is hub-only: it never enters the manifest and never enters the digest, and the agent-facing content route refuses it before verifying the token, so a valid install token cannot reach it by guessing the URL. Souls follow the same visibility rules as every other type. A public one is read as the opening of an installer's system prompt, ahead of their memory and tools, so install shows the full text first — regardless of visibility, and enforced by the prop union rather than by convention. Digest is skill_artifact_digest's no-files branch over the SOUL.md hash.
A loadout holds tools, skills and at most one soul, private or public, one level deep. Private items come from hub storage; public ones resolve live from the release and the Iliad backend rather than being copied, so upstream revocation still reaches an installer. Each item's digest is pinned at publish. A private item that drifts blocks the install; a public one that moves upstream is reported to the owner but does not block, because no source retains the bytes that were pinned. Publishing re-pins every item, so republishing is the repair. An upstream release change marks affected loadouts for re-verification, which runs lazily on read and unconditionally at install. Install is all-or-nothing and names every failing item. Tokens carry a loadout scope authorized against membership per request, and rate limits are keyed per item so a multi-item install is not throttled partway through. The publish gate measures the assembled manifest against the agent's ceiling instead of capping item count. Install itself is presented as unavailable, never mocked: it needs a multi-entry payload the agent does not have yet. Also fixes a duplicate Publish button and a duplicate DOM id on the manage page, where the responsive layout rendered the primary action twice and aria-describedby resolved to the hidden copy.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Private Space
Adds the signed-in half of the hub: a place to submit, store, review and
publish your own skills and tools.
/dashboard— catalog, item page, edit, members,settings, one header control cluster across routes.
before ingest; content lives in S3-compatible storage; owners can browse
and download what is stored.
of placeholders; published items install through IronClaw.
plugin, invitations by email or NEAR account id.
path runs offline.
Tools
feat(tools): sync 15 reborn WASM tools with manifest v3 and bumped patch versions
tools/) with extension manifest v3 schemas.origin_gate_matrixpolicies required for IronClaw 1.1+.NOTE mandatory
origin_gate_matrixtag is required for Ironclaw 1.1+ BUT this will cause tool installation on Ironclaw 1.0 to fail.Already created a report here nearai/ironclaw#7320 and a proposa, Firat is working on a fix
Type
Artifact checklist
Tool PRs:
tools/<tool-name>/README.mdDocs/process PRs:
tracking.mdor README shipped-catalog change is neededtracking.mdand README