feat: plugins edge catalog domain#73
Conversation
📝 WalkthroughWalkthroughUpdates plugin hosting defaults from corvus.profiletailors.com to plugins.corvus.profiletailors.com across workflows, runtime config, tests, and docs; adds a new plugins-edge Cloudflare Worker app (with R2) to serve /catalog.json, /revocations.json, and /artifacts/*. Changes
Sequence DiagramsequenceDiagram
participant Client as Client
participant Edge as plugins-edge (Cloudflare Worker)
participant R2 as R2 Bucket
participant Cache as Edge Cache
Client->>Edge: GET /catalog.json
alt Cached & ETag matches
Edge-->>Client: 304 Not Modified
else Fetch from R2
Edge->>Edge: Normalize path, validate
Edge->>R2: Get CATALOG_OBJECT_KEY
R2-->>Edge: catalog.json
Edge->>Edge: Build response (ETag, Cache-Control, CORS)
Edge-->>Cache: Store (catalog policy)
Edge-->>Client: 200 + catalog.json
end
Client->>Edge: GET /artifacts/{key}
Edge->>Edge: Sanitize artifact key
Edge->>R2: Get artifact object
R2-->>Edge: artifact blob
Edge->>Edge: Detect Content-Type, set cache
Edge-->>Client: 200 + artifact + CORS
Client->>Edge: HEAD /revocations.json
Edge->>R2: Get REVOCATIONS_OBJECT_KEY
R2-->>Edge: revocations.json
Edge-->>Client: 200 (headers only)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Contributor ReportUser: @yacosta738
Contributor Report evaluates based on public GitHub activity. Analysis period: 2025-02-24 to 2026-02-24 |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
clients/agent-runtime/src/config/schema.rs (1)
2221-2252:⚠️ Potential issue | 🟠 MajorMigrate legacy
corvus.profiletailors.comURLs too.
migrate_deprecated_plugin_registry_urlsonly rewritesplugins.corvus.ai. Existing configs that still point atcorvus.profiletailors.comwon’t be migrated and may keep a stale host once the legacy domain is retired. Consider migrating both legacy hosts and updating the warning message accordingly.💡 Suggested fix
- const OLD_PLUGIN_HOST: &str = "plugins.corvus.ai"; + const OLD_PLUGIN_HOSTS: [&str; 2] = ["plugins.corvus.ai", "corvus.profiletailors.com"]; const NEW_PLUGIN_HOST: &str = "plugins.corvus.profiletailors.com"; - fn migrate_url_host(raw_url: &str, old_host: &str, new_host: &str) -> Option<String> { + fn migrate_url_host(raw_url: &str, old_hosts: &[&str], new_host: &str) -> Option<String> { let mut parsed = Url::parse(raw_url).ok()?; let host = parsed.host_str()?; - if host == old_host || host.ends_with(&format!(".{old_host}")) { + if old_hosts + .iter() + .any(|old_host| host == *old_host || host.ends_with(&format!(".{old_host}"))) + { parsed.set_host(Some(new_host)).ok()?; return Some(parsed.to_string()); } None } @@ - if let Some(migrated) = migrate_url_host(&source.url, OLD_PLUGIN_HOST, NEW_PLUGIN_HOST) { + if let Some(migrated) = + migrate_url_host(&source.url, &OLD_PLUGIN_HOSTS, NEW_PLUGIN_HOST) + { source.url = migrated; changed = true; } @@ - if let Some(migrated) = - migrate_url_host(source_url.as_str(), OLD_PLUGIN_HOST, NEW_PLUGIN_HOST) - { + if let Some(migrated) = + migrate_url_host(source_url.as_str(), &OLD_PLUGIN_HOSTS, NEW_PLUGIN_HOST) + { *source_url = migrated; changed = true; revocation_changed = true; } @@ - "Migrated deprecated plugin registry host entries from 'plugins.corvus.ai' \ - to 'plugins.corvus.profiletailors.com' in plugins.sources and \ + "Migrated deprecated plugin registry host entries to \ + 'plugins.corvus.profiletailors.com' in plugins.sources and \ plugins.revocation.source_urls"Also applies to: 2337-2341
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@clients/agent-runtime/src/config/schema.rs` around lines 2221 - 2252, The migration only targets OLD_PLUGIN_HOST "plugins.corvus.ai" so update migrate_deprecated_plugin_registry_urls to also recognize and rewrite legacy corvus.profiletailors.com hosts: add a second constant (e.g., LEGACY_PLUGIN_HOST = "corvus.profiletailors.com") and/or extend migrate_url_host checks to match both OLD_PLUGIN_HOST and LEGACY_PLUGIN_HOST (including subdomains), then replace occurrences in both loops (the one iterating config.plugins.sources and the one iterating config.plugins.revocation.source_urls) to set the host to NEW_PLUGIN_HOST when either legacy host is found; also update any related warning/log message text emitted by migrate_deprecated_plugin_registry_urls to mention both legacy hosts so users see which URLs were migrated.clients/web/apps/plugins/README.md (1)
33-33:⚠️ Potential issue | 🟡 MinorStale domain:
corvus.profiletailors.comshould be updated toplugins.corvus.profiletailors.com.The prod default still references the old domain, which contradicts the PR's migration objective. Since this section describes the production URL for this app (and Lines 14-15 note the move to plugins-edge), consider either updating the domain or adding a deprecation note.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@clients/web/apps/plugins/README.md` at line 33, Update the stale production URL string "Prod default: `https://corvus.profiletailors.com`" in the README to the new domain "https://plugins.corvus.profiletailors.com" (or alternatively add a short deprecation note next to that line stating the old corvus.profiletailors.com has moved to plugins.corvus.profiletailors.com) so the README matches the PR's migration to plugins-edge.
🧹 Nitpick comments (2)
clients/web/apps/plugins-edge/src/index.ts (2)
45-59: 405 response missing CORS headers.If a browser sends a non-simple method (e.g.,
DELETE), the 405 response lacks CORS headers, so the browser will report a CORS error rather than the intended 405 status. If the Worker is primarily server-to-server this is fine, but for consistency you may want to include CORS headers on error responses too.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@clients/web/apps/plugins-edge/src/index.ts` around lines 45 - 59, The 405 response returned in fetch misses CORS headers so browsers get a CORS error instead of the intended status; update the fetch handler to include corsHeaders() on the Method Not Allowed response by merging or replacing METHOD_NOT_ALLOWED_HEADERS with corsHeaders() (or combining both) so the Response created in the isSupportedMethod() branch includes the same CORS headers as the OPTIONS and success paths.
141-193: Consider returning 304 without entity headers likeContent-Length.HTTP semantics (RFC 9110 §15.4.5) state a 304 response "must not contain content" and typically should not carry
Content-Length. Currently the 304 response at Lines 176-179 inherits the fullheadersobject includingContent-Length. While most clients handle this gracefully, stripping it would be more spec-compliant.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@clients/web/apps/plugins-edge/src/index.ts` around lines 141 - 193, serveObject currently returns a 304 with the full headers object including entity headers; change the If-None-Match branch so it creates a copy of headers and strips entity headers (at minimum remove RESPONSE_HEADERS.contentLength, and also remove RESPONSE_HEADERS.contentType and any other content-* headers you deem appropriate) before returning new Response(null, { status: 304, headers: dedupedHeaders }); locate the If-None-Match check in serveObject and perform the header deletion there so all other responses keep the original headers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@clients/web/apps/plugins-edge/README.md`:
- Line 44: Replace the incorrect custom domain string
"corvus.profiletailors.com" with "plugins.corvus.profiletailors.com" in the
Cloudflare route instruction in the README (the line containing Configure custom
domain route for this worker), ensuring the README matches the domain used in
schema.rs, the workflow, and other docs.
In `@clients/web/apps/plugins-edge/src/index.ts`:
- Around line 64-84: The responses for /catalog.json and /revocations.json call
serveObject with withCors set to false, causing missing
Access-Control-Allow-Origin headers for browser clients; update the two
serveObject invocations (for catalog.json and revocations.json) to pass
withCors=true (matching the artifacts behavior and OPTIONS handler) so GET
responses include CORS headers, or if they must remain server-only, add a clear
inline comment above those calls documenting that they are intentionally
CORS-disabled and only used by server-to-server consumers.
- Around line 110-117: normalizePathname currently calls decodeURIComponent
which can throw a URIError for malformed percent-encodings; wrap the
decodeURIComponent call in a try/catch inside normalizePathname and handle only
URIError (re-throw other errors), and when a URIError occurs fall back to using
the original pathname (or its percent-encoded form) with duplicate-slash
normalization (i.e., apply .replace(/\/{2,}/g, "/")) so the function returns a
safe normalized path instead of letting the Worker crash.
---
Outside diff comments:
In `@clients/agent-runtime/src/config/schema.rs`:
- Around line 2221-2252: The migration only targets OLD_PLUGIN_HOST
"plugins.corvus.ai" so update migrate_deprecated_plugin_registry_urls to also
recognize and rewrite legacy corvus.profiletailors.com hosts: add a second
constant (e.g., LEGACY_PLUGIN_HOST = "corvus.profiletailors.com") and/or extend
migrate_url_host checks to match both OLD_PLUGIN_HOST and LEGACY_PLUGIN_HOST
(including subdomains), then replace occurrences in both loops (the one
iterating config.plugins.sources and the one iterating
config.plugins.revocation.source_urls) to set the host to NEW_PLUGIN_HOST when
either legacy host is found; also update any related warning/log message text
emitted by migrate_deprecated_plugin_registry_urls to mention both legacy hosts
so users see which URLs were migrated.
In `@clients/web/apps/plugins/README.md`:
- Line 33: Update the stale production URL string "Prod default:
`https://corvus.profiletailors.com`" in the README to the new domain
"https://plugins.corvus.profiletailors.com" (or alternatively add a short
deprecation note next to that line stating the old corvus.profiletailors.com has
moved to plugins.corvus.profiletailors.com) so the README matches the PR's
migration to plugins-edge.
---
Nitpick comments:
In `@clients/web/apps/plugins-edge/src/index.ts`:
- Around line 45-59: The 405 response returned in fetch misses CORS headers so
browsers get a CORS error instead of the intended status; update the fetch
handler to include corsHeaders() on the Method Not Allowed response by merging
or replacing METHOD_NOT_ALLOWED_HEADERS with corsHeaders() (or combining both)
so the Response created in the isSupportedMethod() branch includes the same CORS
headers as the OPTIONS and success paths.
- Around line 141-193: serveObject currently returns a 304 with the full headers
object including entity headers; change the If-None-Match branch so it creates a
copy of headers and strips entity headers (at minimum remove
RESPONSE_HEADERS.contentLength, and also remove RESPONSE_HEADERS.contentType and
any other content-* headers you deem appropriate) before returning new
Response(null, { status: 304, headers: dedupedHeaders }); locate the
If-None-Match check in serveObject and perform the header deletion there so all
other responses keep the original headers.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
clients/web/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml,!**/pnpm-lock.yaml
📒 Files selected for processing (15)
.github/workflows/publish-plugins.ymlclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/plugins/mod.rsclients/web/README.mdclients/web/apps/docs/src/content/docs/en/guides/plugins.mdclients/web/apps/docs/src/content/docs/es/guides/plugins.mdclients/web/apps/plugins-edge/.gitignoreclients/web/apps/plugins-edge/README.mdclients/web/apps/plugins-edge/package.jsonclients/web/apps/plugins-edge/src/index.tsclients/web/apps/plugins-edge/tsconfig.jsonclients/web/apps/plugins-edge/wrangler.tomlclients/web/apps/plugins/README.mdclients/web/build.gradle.ktsclients/web/package.json
Deploying corvus-plugins with
|
| Latest commit: |
36d6bd7
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://18c236ab.corvus-plugins.pages.dev |
| Branch Preview URL: | https://feat-plugins-edge-catalog-do.corvus-plugins.pages.dev |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
corvus-plugins-edge | 36d6bd7 | Feb 24 2026, 10:43 AM |
Deploying corvus with
|
| Latest commit: |
36d6bd7
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://ef40fd47.corvus-42x.pages.dev |
| Branch Preview URL: | https://feat-plugins-edge-catalog-do.corvus-42x.pages.dev |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
clients/agent-runtime/src/config/schema.rs (2)
2969-2981:⚠️ Potential issue | 🔴 Critical
migrate_deprecated_plugin_registry_urls_no_op_on_new_urlswill fail until themigrate_url_hostguard is added.
Config::default()populates sources and revocation withplugins.corvus.profiletailors.com. Due to the subdomain false-positive described above,changedistruefor a default config, soassert!(!changed)panics. This test correctly captures the desired invariant; it will pass once the guard is applied.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@clients/agent-runtime/src/config/schema.rs` around lines 2969 - 2981, The test migrate_deprecated_plugin_registry_urls_no_op_on_new_urls fails because migrate_deprecated_plugin_registry_urls is treating the new host as deprecated due to a subdomain false-positive; update migrate_deprecated_plugin_registry_urls to check the URL host using the migrate_url_host guard (or equivalent host-check function) before mutating any entries so Config::default() (which sets plugins.corvus.profiletailors.com) is not considered changed; specifically, in migrate_deprecated_plugin_registry_urls examine each source URL and revocation source via migrate_url_host (or add that guard) and only perform replacement/mutation when migrate_url_host indicates the host is an actual deprecated host, leaving config.plugins.sources and config.plugins.revocation.source_urls untouched for the new host.
2226-2241:⚠️ Potential issue | 🔴 Critical
host_matchesfalse-positively matches the new host as a subdomain of the legacy host — migration runs on every startup
NEW_PLUGIN_HOST("plugins.corvus.profiletailors.com") is a subdomain ofLEGACY_PLUGIN_HOST("corvus.profiletailors.com"), so"plugins.corvus.profiletailors.com".ends_with(".corvus.profiletailors.com")evaluates totrue. As a result:
migrate_url_hostreturnsSome(unchanged_url)for every URL already on the new host.migrate_deprecated_plugin_registry_urlsalways returnstrue→ triggersconfig.save()and the warning log on every startup once the config is on the new host.- The
migrate_deprecated_plugin_registry_urls_no_op_on_new_urlstest (Line 2972) will fail becausechangedistruefor a freshly-defaulted config.Add an early-return guard in
migrate_url_hostto skip URLs already on the target host:🐛 Proposed fix
fn migrate_url_host(raw_url: &str, old_hosts: &[&str], new_host: &str) -> Option<String> { let mut parsed = Url::parse(raw_url).ok()?; let host = parsed.host_str()?; + // Guard: already on the new host — nothing to do. + if host == new_host { + return None; + } if old_hosts .iter() .any(|old_host| host_matches(host, old_host)) { parsed.set_host(Some(new_host)).ok()?; return Some(parsed.to_string()); } None }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@clients/agent-runtime/src/config/schema.rs` around lines 2226 - 2241, migrate_url_host is treating the new host as a legacy match because host_matches uses ends_with; to fix, add an early guard in migrate_url_host after parsing the URL: if host == new_host { return None; } so URLs already on the target host are skipped (keep the existing old_hosts check and parsed.set_host(Some(new_host)) logic unchanged); this uses the functions host_matches and migrate_url_host and the parsed.set_host call to locate where to apply the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@clients/agent-runtime/src/config/schema.rs`:
- Around line 2969-2981: The test
migrate_deprecated_plugin_registry_urls_no_op_on_new_urls fails because
migrate_deprecated_plugin_registry_urls is treating the new host as deprecated
due to a subdomain false-positive; update
migrate_deprecated_plugin_registry_urls to check the URL host using the
migrate_url_host guard (or equivalent host-check function) before mutating any
entries so Config::default() (which sets plugins.corvus.profiletailors.com) is
not considered changed; specifically, in migrate_deprecated_plugin_registry_urls
examine each source URL and revocation source via migrate_url_host (or add that
guard) and only perform replacement/mutation when migrate_url_host indicates the
host is an actual deprecated host, leaving config.plugins.sources and
config.plugins.revocation.source_urls untouched for the new host.
- Around line 2226-2241: migrate_url_host is treating the new host as a legacy
match because host_matches uses ends_with; to fix, add an early guard in
migrate_url_host after parsing the URL: if host == new_host { return None; } so
URLs already on the target host are skipped (keep the existing old_hosts check
and parsed.set_host(Some(new_host)) logic unchanged); this uses the functions
host_matches and migrate_url_host and the parsed.set_host call to locate where
to apply the change.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
clients/agent-runtime/src/config/schema.rsclients/web/apps/plugins-edge/README.mdclients/web/apps/plugins-edge/src/index.tsclients/web/apps/plugins/README.md
✅ Files skipped from review due to trivial changes (1)
- clients/web/apps/plugins/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
- clients/web/apps/plugins-edge/src/index.ts
This pull request updates the official Corvus plugin catalog and revocation URLs throughout the codebase and documentation to use the new
plugins.corvus.profiletailors.comdomain. It also introduces a new Cloudflare Worker project,plugins-edge, for serving plugin assets and metadata from R2 storage. The changes ensure consistency in plugin distribution endpoints and improve documentation for both developers and users.Plugin URL Migration and Consistency
schema.rs,mod.rs), GitHub Actions workflows, and documentation to usehttps://plugins.corvus.profiletailors.cominstead of the legacycorvus.profiletailors.comdomain. This includes default values, migration logic, tests, and user-facing docs. [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15]New Cloudflare Worker for Plugin Distribution
plugins-edgeapp (Cloudflare Worker + R2) with its ownREADME.md,.gitignore, andpackage.json. This worker serves/catalog.json,/revocations.json, and immutable plugin artifacts from R2, with secure defaults and optimized cache strategies. [1] [2] [3]Documentation and Developer Experience
README.mdto document the newplugins-edgeapp, its development and deployment scripts, and its role as the production source for plugin metadata and artifacts. [1] [2] [3] [4] [5]These changes standardize plugin distribution endpoints and introduce a scalable, edge-optimized solution for serving plugin assets.
Summary by CodeRabbit
New Features
Updates
Bug Fixes / Migration