mirror of
https://github.com/openclaw/clawhub.git
synced 2026-08-14 00:47:57 +00:00
fix(routing): protect publisher aliases from plugin collisions
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { isReservedPublicOwnerHandle } from "./publicRouteReservations";
|
||||
import {
|
||||
isReservedOpenClawExtensionHandle,
|
||||
isReservedPublicOwnerHandle,
|
||||
} from "./publicRouteReservations";
|
||||
|
||||
describe("public route reservations", () => {
|
||||
it.each(["admin", "clawhub", "docs", "plugins", "skills"])(
|
||||
@@ -9,6 +12,11 @@ describe("public route reservations", () => {
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["codex", "tencent"])("reserves @%s as an OpenClaw alias", (handle) => {
|
||||
expect(isReservedOpenClawExtensionHandle(handle)).toBe(true);
|
||||
expect(isReservedPublicOwnerHandle(handle)).toBe(false);
|
||||
});
|
||||
|
||||
it("does not normalize at-sign prefixes", () => {
|
||||
expect(isReservedPublicOwnerHandle("@clawhub")).toBe(false);
|
||||
});
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import { OPENCLAW_EXTENSION_SLUG_TO_PACKAGE } from "clawhub-schema";
|
||||
|
||||
const RESERVED_PUBLIC_OWNER_HANDLES = new Set(["admin", "clawhub", "docs", "plugins", "skills"]);
|
||||
const RESERVED_OPENCLAW_EXTENSION_HANDLES = new Set(
|
||||
Object.keys(OPENCLAW_EXTENSION_SLUG_TO_PACKAGE),
|
||||
);
|
||||
const RESERVED_UNSCOPED_PACKAGE_NAMES = new Set(["publish"]);
|
||||
|
||||
export function isReservedPublicOwnerHandle(handle: string | undefined | null) {
|
||||
return Boolean(handle && RESERVED_PUBLIC_OWNER_HANDLES.has(handle.trim().toLowerCase()));
|
||||
}
|
||||
|
||||
export function isReservedOpenClawExtensionHandle(handle: string | undefined | null) {
|
||||
return Boolean(handle && RESERVED_OPENCLAW_EXTENSION_HANDLES.has(handle.trim().toLowerCase()));
|
||||
}
|
||||
|
||||
export function isReservedUnscopedPackageName(name: string | undefined | null) {
|
||||
return Boolean(name && RESERVED_UNSCOPED_PACKAGE_NAMES.has(name.trim().toLowerCase()));
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { isPackageBlockedFromPublic } from "./lib/packageSecurity";
|
||||
import { toPublicPublisher } from "./lib/public";
|
||||
import {
|
||||
formatReservedPublicOwnerHandleMessage,
|
||||
isReservedOpenClawExtensionHandle,
|
||||
isReservedPublicOwnerHandle,
|
||||
} from "./lib/publicRouteReservations";
|
||||
import {
|
||||
@@ -1078,6 +1079,9 @@ async function ensureOrgPublisherHandleWithActor(
|
||||
if (!normalizePublisherHandle(args.memberHandle)) {
|
||||
throw new ConvexError("memberHandle required when creating org publisher");
|
||||
}
|
||||
if (!existingPublisher && !existingUser && isReservedOpenClawExtensionHandle(handle)) {
|
||||
throw new ConvexError(formatReservedPublicOwnerHandleMessage(handle));
|
||||
}
|
||||
|
||||
const publisherId = await ctx.db.insert("publishers", {
|
||||
kind: "org",
|
||||
@@ -1686,6 +1690,9 @@ async function createOrgPublisherForUser(
|
||||
if (await isHandleReservedForAnotherUser(ctx, handle, args.actorUserId)) {
|
||||
throw new ConvexError(`Handle "@${handle}" is reserved for another user`);
|
||||
}
|
||||
if (isReservedOpenClawExtensionHandle(handle)) {
|
||||
throw new ConvexError(formatReservedPublicOwnerHandleMessage(handle));
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const publisherId = await ctx.db.insert("publishers", {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { syncGitHubProfile } from "./lib/githubAccount";
|
||||
import { toPublicUser } from "./lib/public";
|
||||
import {
|
||||
formatReservedPublicOwnerHandleMessage,
|
||||
isReservedOpenClawExtensionHandle,
|
||||
isReservedPublicOwnerHandle,
|
||||
} from "./lib/publicRouteReservations";
|
||||
import {
|
||||
@@ -760,6 +761,10 @@ async function canUserClaimHandle(
|
||||
if (await isHandleReservedForAnotherUser(ctx, normalizedHandle, userId)) return false;
|
||||
|
||||
const publisher = await getPublisherByHandle(ctx, normalizedHandle);
|
||||
if (isReservedOpenClawExtensionHandle(normalizedHandle)) {
|
||||
const existingUser = await getUserByHandleOrPersonalPublisher(ctx, normalizedHandle);
|
||||
return existingUser?._id === userId;
|
||||
}
|
||||
if (!publisher || publisher.deletedAt || publisher.deactivatedAt) return true;
|
||||
return publisher.kind === "user" && publisher.linkedUserId === userId;
|
||||
}
|
||||
@@ -2056,6 +2061,9 @@ async function ensurePublisherHandleWithActor(
|
||||
if (existing?.deletedAt || existing?.deactivatedAt) {
|
||||
throw new Error("Handle belongs to a deleted or deactivated user");
|
||||
}
|
||||
if (!existing && isReservedOpenClawExtensionHandle(normalizedHandle)) {
|
||||
throw new ConvexError(formatReservedPublicOwnerHandleMessage(normalizedHandle));
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const displayName = args.displayName?.trim() || normalizedHandle;
|
||||
|
||||
Vendored
+1
@@ -5,6 +5,7 @@ export * from "./catalogMetadata.js";
|
||||
export * from "./docsLinks.js";
|
||||
export * from "./license.js";
|
||||
export * from "./openclawContract.js";
|
||||
export * from "./openClawExtensionSlugs.js";
|
||||
export * from "./packages.js";
|
||||
export * from "./pluginCategories.js";
|
||||
export { ApiRoutes, LegacyApiRoutes } from "./routes.js";
|
||||
|
||||
Vendored
+1
@@ -4,6 +4,7 @@ export * from "./catalogMetadata.js";
|
||||
export * from "./docsLinks.js";
|
||||
export * from "./license.js";
|
||||
export * from "./openclawContract.js";
|
||||
export * from "./openClawExtensionSlugs.js";
|
||||
export * from "./packages.js";
|
||||
export * from "./pluginCategories.js";
|
||||
export { ApiRoutes, LegacyApiRoutes } from "./routes.js";
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACrD,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACzD,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACrD,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACzD,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC"}
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
export declare const OPENCLAW_EXTENSION_SLUG_TO_PACKAGE: {
|
||||
readonly acpx: "@openclaw/acpx";
|
||||
readonly "active-memory": "@openclaw/active-memory";
|
||||
readonly alibaba: "@openclaw/alibaba-provider";
|
||||
readonly "alibaba-provider": "@openclaw/alibaba-provider";
|
||||
readonly "amazon-bedrock": "@openclaw/amazon-bedrock-provider";
|
||||
readonly "amazon-bedrock-mantle": "@openclaw/amazon-bedrock-mantle-provider";
|
||||
readonly "amazon-bedrock-mantle-provider": "@openclaw/amazon-bedrock-mantle-provider";
|
||||
readonly "amazon-bedrock-provider": "@openclaw/amazon-bedrock-provider";
|
||||
readonly anthropic: "@openclaw/anthropic-provider";
|
||||
readonly "anthropic-provider": "@openclaw/anthropic-provider";
|
||||
readonly "anthropic-vertex": "@openclaw/anthropic-vertex-provider";
|
||||
readonly "anthropic-vertex-provider": "@openclaw/anthropic-vertex-provider";
|
||||
readonly arcee: "@openclaw/arcee-provider";
|
||||
readonly "arcee-provider": "@openclaw/arcee-provider";
|
||||
readonly "azure-speech": "@openclaw/azure-speech";
|
||||
readonly bluebubbles: "@openclaw/bluebubbles";
|
||||
readonly bonjour: "@openclaw/bonjour";
|
||||
readonly brave: "@openclaw/brave-plugin";
|
||||
readonly "brave-plugin": "@openclaw/brave-plugin";
|
||||
readonly browser: "@openclaw/browser-plugin";
|
||||
readonly "browser-plugin": "@openclaw/browser-plugin";
|
||||
readonly byteplus: "@openclaw/byteplus-provider";
|
||||
readonly "byteplus-provider": "@openclaw/byteplus-provider";
|
||||
readonly cerebras: "@openclaw/cerebras-provider";
|
||||
readonly "cerebras-provider": "@openclaw/cerebras-provider";
|
||||
readonly chutes: "@openclaw/chutes-provider";
|
||||
readonly "chutes-provider": "@openclaw/chutes-provider";
|
||||
readonly "cloudflare-ai-gateway": "@openclaw/cloudflare-ai-gateway-provider";
|
||||
readonly "cloudflare-ai-gateway-provider": "@openclaw/cloudflare-ai-gateway-provider";
|
||||
readonly codex: "@openclaw/codex";
|
||||
readonly comfy: "@openclaw/comfy-provider";
|
||||
readonly "comfy-provider": "@openclaw/comfy-provider";
|
||||
readonly "copilot-proxy": "@openclaw/copilot-proxy";
|
||||
readonly deepgram: "@openclaw/deepgram-provider";
|
||||
readonly "deepgram-provider": "@openclaw/deepgram-provider";
|
||||
readonly deepinfra: "@openclaw/deepinfra-provider";
|
||||
readonly "deepinfra-provider": "@openclaw/deepinfra-provider";
|
||||
readonly deepseek: "@openclaw/deepseek-provider";
|
||||
readonly "deepseek-provider": "@openclaw/deepseek-provider";
|
||||
readonly "device-pair": "@openclaw/device-pair";
|
||||
readonly "diagnostics-otel": "@openclaw/diagnostics-otel";
|
||||
readonly "diagnostics-prometheus": "@openclaw/diagnostics-prometheus";
|
||||
readonly diffs: "@openclaw/diffs";
|
||||
readonly "diffs-language-pack": "@openclaw/diffs-language-pack";
|
||||
readonly discord: "@openclaw/discord";
|
||||
readonly "document-extract": "@openclaw/document-extract-plugin";
|
||||
readonly "document-extract-plugin": "@openclaw/document-extract-plugin";
|
||||
readonly duckduckgo: "@openclaw/duckduckgo-plugin";
|
||||
readonly "duckduckgo-plugin": "@openclaw/duckduckgo-plugin";
|
||||
readonly elevenlabs: "@openclaw/elevenlabs-speech";
|
||||
readonly "elevenlabs-speech": "@openclaw/elevenlabs-speech";
|
||||
readonly exa: "@openclaw/exa-plugin";
|
||||
readonly "exa-plugin": "@openclaw/exa-plugin";
|
||||
readonly fal: "@openclaw/fal-provider";
|
||||
readonly "fal-provider": "@openclaw/fal-provider";
|
||||
readonly feishu: "@openclaw/feishu";
|
||||
readonly "file-transfer": "@openclaw/file-transfer";
|
||||
readonly firecrawl: "@openclaw/firecrawl-plugin";
|
||||
readonly "firecrawl-plugin": "@openclaw/firecrawl-plugin";
|
||||
readonly fireworks: "@openclaw/fireworks-provider";
|
||||
readonly "fireworks-provider": "@openclaw/fireworks-provider";
|
||||
readonly "github-copilot": "@openclaw/github-copilot-provider";
|
||||
readonly "github-copilot-provider": "@openclaw/github-copilot-provider";
|
||||
readonly google: "@openclaw/google-plugin";
|
||||
readonly "google-meet": "@openclaw/google-meet";
|
||||
readonly "google-plugin": "@openclaw/google-plugin";
|
||||
readonly googlechat: "@openclaw/googlechat";
|
||||
readonly gradium: "@openclaw/gradium-speech";
|
||||
readonly "gradium-speech": "@openclaw/gradium-speech";
|
||||
readonly groq: "@openclaw/groq-provider";
|
||||
readonly "groq-provider": "@openclaw/groq-provider";
|
||||
readonly huggingface: "@openclaw/huggingface-provider";
|
||||
readonly "huggingface-provider": "@openclaw/huggingface-provider";
|
||||
readonly imessage: "@openclaw/imessage";
|
||||
readonly inworld: "@openclaw/inworld-speech";
|
||||
readonly "inworld-speech": "@openclaw/inworld-speech";
|
||||
readonly irc: "@openclaw/irc";
|
||||
readonly kilocode: "@openclaw/kilocode-provider";
|
||||
readonly "kilocode-provider": "@openclaw/kilocode-provider";
|
||||
readonly kimi: "@openclaw/kimi-provider";
|
||||
readonly "kimi-coding": "@openclaw/kimi-provider";
|
||||
readonly "kimi-provider": "@openclaw/kimi-provider";
|
||||
readonly line: "@openclaw/line";
|
||||
readonly litellm: "@openclaw/litellm-provider";
|
||||
readonly "litellm-provider": "@openclaw/litellm-provider";
|
||||
readonly "llm-task": "@openclaw/llm-task";
|
||||
readonly lmstudio: "@openclaw/lmstudio-provider";
|
||||
readonly "lmstudio-provider": "@openclaw/lmstudio-provider";
|
||||
readonly lobster: "@openclaw/lobster";
|
||||
readonly matrix: "@openclaw/matrix";
|
||||
readonly mattermost: "@openclaw/mattermost";
|
||||
readonly "memory-core": "@openclaw/memory-core";
|
||||
readonly "memory-lancedb": "@openclaw/memory-lancedb";
|
||||
readonly "memory-wiki": "@openclaw/memory-wiki";
|
||||
readonly microsoft: "@openclaw/microsoft-speech";
|
||||
readonly "microsoft-foundry": "@openclaw/microsoft-foundry";
|
||||
readonly "microsoft-speech": "@openclaw/microsoft-speech";
|
||||
readonly "migrate-claude": "@openclaw/migrate-claude";
|
||||
readonly "migrate-hermes": "@openclaw/migrate-hermes";
|
||||
readonly minimax: "@openclaw/minimax-provider";
|
||||
readonly "minimax-provider": "@openclaw/minimax-provider";
|
||||
readonly mistral: "@openclaw/mistral-provider";
|
||||
readonly "mistral-provider": "@openclaw/mistral-provider";
|
||||
readonly moonshot: "@openclaw/moonshot-provider";
|
||||
readonly "moonshot-provider": "@openclaw/moonshot-provider";
|
||||
readonly msteams: "@openclaw/msteams";
|
||||
readonly "nextcloud-talk": "@openclaw/nextcloud-talk";
|
||||
readonly nostr: "@openclaw/nostr";
|
||||
readonly nvidia: "@openclaw/nvidia-provider";
|
||||
readonly "nvidia-provider": "@openclaw/nvidia-provider";
|
||||
readonly ollama: "@openclaw/ollama-provider";
|
||||
readonly "ollama-provider": "@openclaw/ollama-provider";
|
||||
readonly "open-prose": "@openclaw/open-prose";
|
||||
readonly openai: "@openclaw/openai-provider";
|
||||
readonly "openai-provider": "@openclaw/openai-provider";
|
||||
readonly opencode: "@openclaw/opencode-provider";
|
||||
readonly "opencode-go": "@openclaw/opencode-go-provider";
|
||||
readonly "opencode-go-provider": "@openclaw/opencode-go-provider";
|
||||
readonly "opencode-provider": "@openclaw/opencode-provider";
|
||||
readonly openrouter: "@openclaw/openrouter-provider";
|
||||
readonly "openrouter-provider": "@openclaw/openrouter-provider";
|
||||
readonly openshell: "@openclaw/openshell-sandbox";
|
||||
readonly "openshell-sandbox": "@openclaw/openshell-sandbox";
|
||||
readonly perplexity: "@openclaw/perplexity-plugin";
|
||||
readonly "perplexity-plugin": "@openclaw/perplexity-plugin";
|
||||
readonly "phone-control": "@openclaw/phone-control";
|
||||
readonly "qa-channel": "@openclaw/qa-channel";
|
||||
readonly "qa-lab": "@openclaw/qa-lab";
|
||||
readonly "qa-matrix": "@openclaw/qa-matrix";
|
||||
readonly qianfan: "@openclaw/qianfan-provider";
|
||||
readonly "qianfan-provider": "@openclaw/qianfan-provider";
|
||||
readonly qqbot: "@openclaw/qqbot";
|
||||
readonly qwen: "@openclaw/qwen-provider";
|
||||
readonly "qwen-provider": "@openclaw/qwen-provider";
|
||||
readonly runway: "@openclaw/runway-provider";
|
||||
readonly "runway-provider": "@openclaw/runway-provider";
|
||||
readonly searxng: "@openclaw/searxng-plugin";
|
||||
readonly "searxng-plugin": "@openclaw/searxng-plugin";
|
||||
readonly senseaudio: "@openclaw/senseaudio-provider";
|
||||
readonly "senseaudio-provider": "@openclaw/senseaudio-provider";
|
||||
readonly sglang: "@openclaw/sglang-provider";
|
||||
readonly "sglang-provider": "@openclaw/sglang-provider";
|
||||
readonly signal: "@openclaw/signal";
|
||||
readonly "skill-workshop": "@openclaw/skill-workshop";
|
||||
readonly slack: "@openclaw/slack";
|
||||
readonly stepfun: "@openclaw/stepfun-provider";
|
||||
readonly "stepfun-provider": "@openclaw/stepfun-provider";
|
||||
readonly "synology-chat": "@openclaw/synology-chat";
|
||||
readonly synthetic: "@openclaw/synthetic-provider";
|
||||
readonly "synthetic-provider": "@openclaw/synthetic-provider";
|
||||
readonly "talk-voice": "@openclaw/talk-voice";
|
||||
readonly tavily: "@openclaw/tavily-plugin";
|
||||
readonly "tavily-plugin": "@openclaw/tavily-plugin";
|
||||
readonly telegram: "@openclaw/telegram";
|
||||
readonly tencent: "@openclaw/tencent-provider";
|
||||
readonly "tencent-provider": "@openclaw/tencent-provider";
|
||||
readonly "thread-ownership": "@openclaw/thread-ownership";
|
||||
readonly tlon: "@openclaw/tlon";
|
||||
readonly together: "@openclaw/together-provider";
|
||||
readonly "together-provider": "@openclaw/together-provider";
|
||||
readonly tokenjuice: "@openclaw/tokenjuice";
|
||||
readonly "tts-local-cli": "@openclaw/tts-local-cli";
|
||||
readonly twitch: "@openclaw/twitch";
|
||||
readonly venice: "@openclaw/venice-provider";
|
||||
readonly "venice-provider": "@openclaw/venice-provider";
|
||||
readonly "vercel-ai-gateway": "@openclaw/vercel-ai-gateway-provider";
|
||||
readonly "vercel-ai-gateway-provider": "@openclaw/vercel-ai-gateway-provider";
|
||||
readonly vllm: "@openclaw/vllm-provider";
|
||||
readonly "vllm-provider": "@openclaw/vllm-provider";
|
||||
readonly "voice-call": "@openclaw/voice-call";
|
||||
readonly volcengine: "@openclaw/volcengine-provider";
|
||||
readonly "volcengine-provider": "@openclaw/volcengine-provider";
|
||||
readonly voyage: "@openclaw/voyage-provider";
|
||||
readonly "voyage-provider": "@openclaw/voyage-provider";
|
||||
readonly vydra: "@openclaw/vydra-provider";
|
||||
readonly "vydra-provider": "@openclaw/vydra-provider";
|
||||
readonly "web-readability": "@openclaw/web-readability-plugin";
|
||||
readonly "web-readability-plugin": "@openclaw/web-readability-plugin";
|
||||
readonly webhooks: "@openclaw/webhooks";
|
||||
readonly whatsapp: "@openclaw/whatsapp";
|
||||
readonly xai: "@openclaw/xai-plugin";
|
||||
readonly "xai-plugin": "@openclaw/xai-plugin";
|
||||
readonly xiaomi: "@openclaw/xiaomi-provider";
|
||||
readonly "xiaomi-provider": "@openclaw/xiaomi-provider";
|
||||
readonly zai: "@openclaw/zai-provider";
|
||||
readonly "zai-provider": "@openclaw/zai-provider";
|
||||
readonly zalo: "@openclaw/zalo";
|
||||
readonly zalouser: "@openclaw/zalouser";
|
||||
};
|
||||
export declare function getOpenClawExtensionPackageName(slug: string): string | null;
|
||||
export declare function getOpenClawPackageCandidateNames(requestedName: string): string[];
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
export const OPENCLAW_EXTENSION_SLUG_TO_PACKAGE = {
|
||||
acpx: "@openclaw/acpx",
|
||||
"active-memory": "@openclaw/active-memory",
|
||||
alibaba: "@openclaw/alibaba-provider",
|
||||
"alibaba-provider": "@openclaw/alibaba-provider",
|
||||
"amazon-bedrock": "@openclaw/amazon-bedrock-provider",
|
||||
"amazon-bedrock-mantle": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"amazon-bedrock-mantle-provider": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"amazon-bedrock-provider": "@openclaw/amazon-bedrock-provider",
|
||||
anthropic: "@openclaw/anthropic-provider",
|
||||
"anthropic-provider": "@openclaw/anthropic-provider",
|
||||
"anthropic-vertex": "@openclaw/anthropic-vertex-provider",
|
||||
"anthropic-vertex-provider": "@openclaw/anthropic-vertex-provider",
|
||||
arcee: "@openclaw/arcee-provider",
|
||||
"arcee-provider": "@openclaw/arcee-provider",
|
||||
"azure-speech": "@openclaw/azure-speech",
|
||||
bluebubbles: "@openclaw/bluebubbles",
|
||||
bonjour: "@openclaw/bonjour",
|
||||
brave: "@openclaw/brave-plugin",
|
||||
"brave-plugin": "@openclaw/brave-plugin",
|
||||
browser: "@openclaw/browser-plugin",
|
||||
"browser-plugin": "@openclaw/browser-plugin",
|
||||
byteplus: "@openclaw/byteplus-provider",
|
||||
"byteplus-provider": "@openclaw/byteplus-provider",
|
||||
cerebras: "@openclaw/cerebras-provider",
|
||||
"cerebras-provider": "@openclaw/cerebras-provider",
|
||||
chutes: "@openclaw/chutes-provider",
|
||||
"chutes-provider": "@openclaw/chutes-provider",
|
||||
"cloudflare-ai-gateway": "@openclaw/cloudflare-ai-gateway-provider",
|
||||
"cloudflare-ai-gateway-provider": "@openclaw/cloudflare-ai-gateway-provider",
|
||||
codex: "@openclaw/codex",
|
||||
comfy: "@openclaw/comfy-provider",
|
||||
"comfy-provider": "@openclaw/comfy-provider",
|
||||
"copilot-proxy": "@openclaw/copilot-proxy",
|
||||
deepgram: "@openclaw/deepgram-provider",
|
||||
"deepgram-provider": "@openclaw/deepgram-provider",
|
||||
deepinfra: "@openclaw/deepinfra-provider",
|
||||
"deepinfra-provider": "@openclaw/deepinfra-provider",
|
||||
deepseek: "@openclaw/deepseek-provider",
|
||||
"deepseek-provider": "@openclaw/deepseek-provider",
|
||||
"device-pair": "@openclaw/device-pair",
|
||||
"diagnostics-otel": "@openclaw/diagnostics-otel",
|
||||
"diagnostics-prometheus": "@openclaw/diagnostics-prometheus",
|
||||
diffs: "@openclaw/diffs",
|
||||
"diffs-language-pack": "@openclaw/diffs-language-pack",
|
||||
discord: "@openclaw/discord",
|
||||
"document-extract": "@openclaw/document-extract-plugin",
|
||||
"document-extract-plugin": "@openclaw/document-extract-plugin",
|
||||
duckduckgo: "@openclaw/duckduckgo-plugin",
|
||||
"duckduckgo-plugin": "@openclaw/duckduckgo-plugin",
|
||||
elevenlabs: "@openclaw/elevenlabs-speech",
|
||||
"elevenlabs-speech": "@openclaw/elevenlabs-speech",
|
||||
exa: "@openclaw/exa-plugin",
|
||||
"exa-plugin": "@openclaw/exa-plugin",
|
||||
fal: "@openclaw/fal-provider",
|
||||
"fal-provider": "@openclaw/fal-provider",
|
||||
feishu: "@openclaw/feishu",
|
||||
"file-transfer": "@openclaw/file-transfer",
|
||||
firecrawl: "@openclaw/firecrawl-plugin",
|
||||
"firecrawl-plugin": "@openclaw/firecrawl-plugin",
|
||||
fireworks: "@openclaw/fireworks-provider",
|
||||
"fireworks-provider": "@openclaw/fireworks-provider",
|
||||
"github-copilot": "@openclaw/github-copilot-provider",
|
||||
"github-copilot-provider": "@openclaw/github-copilot-provider",
|
||||
google: "@openclaw/google-plugin",
|
||||
"google-meet": "@openclaw/google-meet",
|
||||
"google-plugin": "@openclaw/google-plugin",
|
||||
googlechat: "@openclaw/googlechat",
|
||||
gradium: "@openclaw/gradium-speech",
|
||||
"gradium-speech": "@openclaw/gradium-speech",
|
||||
groq: "@openclaw/groq-provider",
|
||||
"groq-provider": "@openclaw/groq-provider",
|
||||
huggingface: "@openclaw/huggingface-provider",
|
||||
"huggingface-provider": "@openclaw/huggingface-provider",
|
||||
imessage: "@openclaw/imessage",
|
||||
inworld: "@openclaw/inworld-speech",
|
||||
"inworld-speech": "@openclaw/inworld-speech",
|
||||
irc: "@openclaw/irc",
|
||||
kilocode: "@openclaw/kilocode-provider",
|
||||
"kilocode-provider": "@openclaw/kilocode-provider",
|
||||
kimi: "@openclaw/kimi-provider",
|
||||
"kimi-coding": "@openclaw/kimi-provider",
|
||||
"kimi-provider": "@openclaw/kimi-provider",
|
||||
line: "@openclaw/line",
|
||||
litellm: "@openclaw/litellm-provider",
|
||||
"litellm-provider": "@openclaw/litellm-provider",
|
||||
"llm-task": "@openclaw/llm-task",
|
||||
lmstudio: "@openclaw/lmstudio-provider",
|
||||
"lmstudio-provider": "@openclaw/lmstudio-provider",
|
||||
lobster: "@openclaw/lobster",
|
||||
matrix: "@openclaw/matrix",
|
||||
mattermost: "@openclaw/mattermost",
|
||||
"memory-core": "@openclaw/memory-core",
|
||||
"memory-lancedb": "@openclaw/memory-lancedb",
|
||||
"memory-wiki": "@openclaw/memory-wiki",
|
||||
microsoft: "@openclaw/microsoft-speech",
|
||||
"microsoft-foundry": "@openclaw/microsoft-foundry",
|
||||
"microsoft-speech": "@openclaw/microsoft-speech",
|
||||
"migrate-claude": "@openclaw/migrate-claude",
|
||||
"migrate-hermes": "@openclaw/migrate-hermes",
|
||||
minimax: "@openclaw/minimax-provider",
|
||||
"minimax-provider": "@openclaw/minimax-provider",
|
||||
mistral: "@openclaw/mistral-provider",
|
||||
"mistral-provider": "@openclaw/mistral-provider",
|
||||
moonshot: "@openclaw/moonshot-provider",
|
||||
"moonshot-provider": "@openclaw/moonshot-provider",
|
||||
msteams: "@openclaw/msteams",
|
||||
"nextcloud-talk": "@openclaw/nextcloud-talk",
|
||||
nostr: "@openclaw/nostr",
|
||||
nvidia: "@openclaw/nvidia-provider",
|
||||
"nvidia-provider": "@openclaw/nvidia-provider",
|
||||
ollama: "@openclaw/ollama-provider",
|
||||
"ollama-provider": "@openclaw/ollama-provider",
|
||||
"open-prose": "@openclaw/open-prose",
|
||||
openai: "@openclaw/openai-provider",
|
||||
"openai-provider": "@openclaw/openai-provider",
|
||||
opencode: "@openclaw/opencode-provider",
|
||||
"opencode-go": "@openclaw/opencode-go-provider",
|
||||
"opencode-go-provider": "@openclaw/opencode-go-provider",
|
||||
"opencode-provider": "@openclaw/opencode-provider",
|
||||
openrouter: "@openclaw/openrouter-provider",
|
||||
"openrouter-provider": "@openclaw/openrouter-provider",
|
||||
openshell: "@openclaw/openshell-sandbox",
|
||||
"openshell-sandbox": "@openclaw/openshell-sandbox",
|
||||
perplexity: "@openclaw/perplexity-plugin",
|
||||
"perplexity-plugin": "@openclaw/perplexity-plugin",
|
||||
"phone-control": "@openclaw/phone-control",
|
||||
"qa-channel": "@openclaw/qa-channel",
|
||||
"qa-lab": "@openclaw/qa-lab",
|
||||
"qa-matrix": "@openclaw/qa-matrix",
|
||||
qianfan: "@openclaw/qianfan-provider",
|
||||
"qianfan-provider": "@openclaw/qianfan-provider",
|
||||
qqbot: "@openclaw/qqbot",
|
||||
qwen: "@openclaw/qwen-provider",
|
||||
"qwen-provider": "@openclaw/qwen-provider",
|
||||
runway: "@openclaw/runway-provider",
|
||||
"runway-provider": "@openclaw/runway-provider",
|
||||
searxng: "@openclaw/searxng-plugin",
|
||||
"searxng-plugin": "@openclaw/searxng-plugin",
|
||||
senseaudio: "@openclaw/senseaudio-provider",
|
||||
"senseaudio-provider": "@openclaw/senseaudio-provider",
|
||||
sglang: "@openclaw/sglang-provider",
|
||||
"sglang-provider": "@openclaw/sglang-provider",
|
||||
signal: "@openclaw/signal",
|
||||
"skill-workshop": "@openclaw/skill-workshop",
|
||||
slack: "@openclaw/slack",
|
||||
stepfun: "@openclaw/stepfun-provider",
|
||||
"stepfun-provider": "@openclaw/stepfun-provider",
|
||||
"synology-chat": "@openclaw/synology-chat",
|
||||
synthetic: "@openclaw/synthetic-provider",
|
||||
"synthetic-provider": "@openclaw/synthetic-provider",
|
||||
"talk-voice": "@openclaw/talk-voice",
|
||||
tavily: "@openclaw/tavily-plugin",
|
||||
"tavily-plugin": "@openclaw/tavily-plugin",
|
||||
telegram: "@openclaw/telegram",
|
||||
tencent: "@openclaw/tencent-provider",
|
||||
"tencent-provider": "@openclaw/tencent-provider",
|
||||
"thread-ownership": "@openclaw/thread-ownership",
|
||||
tlon: "@openclaw/tlon",
|
||||
together: "@openclaw/together-provider",
|
||||
"together-provider": "@openclaw/together-provider",
|
||||
tokenjuice: "@openclaw/tokenjuice",
|
||||
"tts-local-cli": "@openclaw/tts-local-cli",
|
||||
twitch: "@openclaw/twitch",
|
||||
venice: "@openclaw/venice-provider",
|
||||
"venice-provider": "@openclaw/venice-provider",
|
||||
"vercel-ai-gateway": "@openclaw/vercel-ai-gateway-provider",
|
||||
"vercel-ai-gateway-provider": "@openclaw/vercel-ai-gateway-provider",
|
||||
vllm: "@openclaw/vllm-provider",
|
||||
"vllm-provider": "@openclaw/vllm-provider",
|
||||
"voice-call": "@openclaw/voice-call",
|
||||
volcengine: "@openclaw/volcengine-provider",
|
||||
"volcengine-provider": "@openclaw/volcengine-provider",
|
||||
voyage: "@openclaw/voyage-provider",
|
||||
"voyage-provider": "@openclaw/voyage-provider",
|
||||
vydra: "@openclaw/vydra-provider",
|
||||
"vydra-provider": "@openclaw/vydra-provider",
|
||||
"web-readability": "@openclaw/web-readability-plugin",
|
||||
"web-readability-plugin": "@openclaw/web-readability-plugin",
|
||||
webhooks: "@openclaw/webhooks",
|
||||
whatsapp: "@openclaw/whatsapp",
|
||||
xai: "@openclaw/xai-plugin",
|
||||
"xai-plugin": "@openclaw/xai-plugin",
|
||||
xiaomi: "@openclaw/xiaomi-provider",
|
||||
"xiaomi-provider": "@openclaw/xiaomi-provider",
|
||||
zai: "@openclaw/zai-provider",
|
||||
"zai-provider": "@openclaw/zai-provider",
|
||||
zalo: "@openclaw/zalo",
|
||||
zalouser: "@openclaw/zalouser",
|
||||
};
|
||||
export function getOpenClawExtensionPackageName(slug) {
|
||||
const normalizedSlug = slug.trim().toLowerCase();
|
||||
if (!normalizedSlug)
|
||||
return null;
|
||||
return (OPENCLAW_EXTENSION_SLUG_TO_PACKAGE[normalizedSlug] ?? null);
|
||||
}
|
||||
export function getOpenClawPackageCandidateNames(requestedName) {
|
||||
if (requestedName.includes("/"))
|
||||
return [requestedName];
|
||||
const officialPackageName = getOpenClawExtensionPackageName(requestedName);
|
||||
return Array.from(new Set([officialPackageName, `@openclaw/${requestedName}`, requestedName].filter((name) => typeof name === "string" && name.length > 0)));
|
||||
}
|
||||
//# sourceMappingURL=openClawExtensionSlugs.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -5,6 +5,7 @@ export * from "./catalogMetadata.js";
|
||||
export * from "./docsLinks.js";
|
||||
export * from "./license.js";
|
||||
export * from "./openclawContract.js";
|
||||
export * from "./openClawExtensionSlugs.js";
|
||||
export * from "./packages.js";
|
||||
export * from "./pluginCategories.js";
|
||||
export { ApiRoutes, LegacyApiRoutes } from "./routes.js";
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
export const OPENCLAW_EXTENSION_SLUG_TO_PACKAGE = {
|
||||
acpx: "@openclaw/acpx",
|
||||
"active-memory": "@openclaw/active-memory",
|
||||
alibaba: "@openclaw/alibaba-provider",
|
||||
"alibaba-provider": "@openclaw/alibaba-provider",
|
||||
"amazon-bedrock": "@openclaw/amazon-bedrock-provider",
|
||||
"amazon-bedrock-mantle": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"amazon-bedrock-mantle-provider": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"amazon-bedrock-provider": "@openclaw/amazon-bedrock-provider",
|
||||
anthropic: "@openclaw/anthropic-provider",
|
||||
"anthropic-provider": "@openclaw/anthropic-provider",
|
||||
"anthropic-vertex": "@openclaw/anthropic-vertex-provider",
|
||||
"anthropic-vertex-provider": "@openclaw/anthropic-vertex-provider",
|
||||
arcee: "@openclaw/arcee-provider",
|
||||
"arcee-provider": "@openclaw/arcee-provider",
|
||||
"azure-speech": "@openclaw/azure-speech",
|
||||
bluebubbles: "@openclaw/bluebubbles",
|
||||
bonjour: "@openclaw/bonjour",
|
||||
brave: "@openclaw/brave-plugin",
|
||||
"brave-plugin": "@openclaw/brave-plugin",
|
||||
browser: "@openclaw/browser-plugin",
|
||||
"browser-plugin": "@openclaw/browser-plugin",
|
||||
byteplus: "@openclaw/byteplus-provider",
|
||||
"byteplus-provider": "@openclaw/byteplus-provider",
|
||||
cerebras: "@openclaw/cerebras-provider",
|
||||
"cerebras-provider": "@openclaw/cerebras-provider",
|
||||
chutes: "@openclaw/chutes-provider",
|
||||
"chutes-provider": "@openclaw/chutes-provider",
|
||||
"cloudflare-ai-gateway": "@openclaw/cloudflare-ai-gateway-provider",
|
||||
"cloudflare-ai-gateway-provider": "@openclaw/cloudflare-ai-gateway-provider",
|
||||
codex: "@openclaw/codex",
|
||||
comfy: "@openclaw/comfy-provider",
|
||||
"comfy-provider": "@openclaw/comfy-provider",
|
||||
"copilot-proxy": "@openclaw/copilot-proxy",
|
||||
deepgram: "@openclaw/deepgram-provider",
|
||||
"deepgram-provider": "@openclaw/deepgram-provider",
|
||||
deepinfra: "@openclaw/deepinfra-provider",
|
||||
"deepinfra-provider": "@openclaw/deepinfra-provider",
|
||||
deepseek: "@openclaw/deepseek-provider",
|
||||
"deepseek-provider": "@openclaw/deepseek-provider",
|
||||
"device-pair": "@openclaw/device-pair",
|
||||
"diagnostics-otel": "@openclaw/diagnostics-otel",
|
||||
"diagnostics-prometheus": "@openclaw/diagnostics-prometheus",
|
||||
diffs: "@openclaw/diffs",
|
||||
"diffs-language-pack": "@openclaw/diffs-language-pack",
|
||||
discord: "@openclaw/discord",
|
||||
"document-extract": "@openclaw/document-extract-plugin",
|
||||
"document-extract-plugin": "@openclaw/document-extract-plugin",
|
||||
duckduckgo: "@openclaw/duckduckgo-plugin",
|
||||
"duckduckgo-plugin": "@openclaw/duckduckgo-plugin",
|
||||
elevenlabs: "@openclaw/elevenlabs-speech",
|
||||
"elevenlabs-speech": "@openclaw/elevenlabs-speech",
|
||||
exa: "@openclaw/exa-plugin",
|
||||
"exa-plugin": "@openclaw/exa-plugin",
|
||||
fal: "@openclaw/fal-provider",
|
||||
"fal-provider": "@openclaw/fal-provider",
|
||||
feishu: "@openclaw/feishu",
|
||||
"file-transfer": "@openclaw/file-transfer",
|
||||
firecrawl: "@openclaw/firecrawl-plugin",
|
||||
"firecrawl-plugin": "@openclaw/firecrawl-plugin",
|
||||
fireworks: "@openclaw/fireworks-provider",
|
||||
"fireworks-provider": "@openclaw/fireworks-provider",
|
||||
"github-copilot": "@openclaw/github-copilot-provider",
|
||||
"github-copilot-provider": "@openclaw/github-copilot-provider",
|
||||
google: "@openclaw/google-plugin",
|
||||
"google-meet": "@openclaw/google-meet",
|
||||
"google-plugin": "@openclaw/google-plugin",
|
||||
googlechat: "@openclaw/googlechat",
|
||||
gradium: "@openclaw/gradium-speech",
|
||||
"gradium-speech": "@openclaw/gradium-speech",
|
||||
groq: "@openclaw/groq-provider",
|
||||
"groq-provider": "@openclaw/groq-provider",
|
||||
huggingface: "@openclaw/huggingface-provider",
|
||||
"huggingface-provider": "@openclaw/huggingface-provider",
|
||||
imessage: "@openclaw/imessage",
|
||||
inworld: "@openclaw/inworld-speech",
|
||||
"inworld-speech": "@openclaw/inworld-speech",
|
||||
irc: "@openclaw/irc",
|
||||
kilocode: "@openclaw/kilocode-provider",
|
||||
"kilocode-provider": "@openclaw/kilocode-provider",
|
||||
kimi: "@openclaw/kimi-provider",
|
||||
"kimi-coding": "@openclaw/kimi-provider",
|
||||
"kimi-provider": "@openclaw/kimi-provider",
|
||||
line: "@openclaw/line",
|
||||
litellm: "@openclaw/litellm-provider",
|
||||
"litellm-provider": "@openclaw/litellm-provider",
|
||||
"llm-task": "@openclaw/llm-task",
|
||||
lmstudio: "@openclaw/lmstudio-provider",
|
||||
"lmstudio-provider": "@openclaw/lmstudio-provider",
|
||||
lobster: "@openclaw/lobster",
|
||||
matrix: "@openclaw/matrix",
|
||||
mattermost: "@openclaw/mattermost",
|
||||
"memory-core": "@openclaw/memory-core",
|
||||
"memory-lancedb": "@openclaw/memory-lancedb",
|
||||
"memory-wiki": "@openclaw/memory-wiki",
|
||||
microsoft: "@openclaw/microsoft-speech",
|
||||
"microsoft-foundry": "@openclaw/microsoft-foundry",
|
||||
"microsoft-speech": "@openclaw/microsoft-speech",
|
||||
"migrate-claude": "@openclaw/migrate-claude",
|
||||
"migrate-hermes": "@openclaw/migrate-hermes",
|
||||
minimax: "@openclaw/minimax-provider",
|
||||
"minimax-provider": "@openclaw/minimax-provider",
|
||||
mistral: "@openclaw/mistral-provider",
|
||||
"mistral-provider": "@openclaw/mistral-provider",
|
||||
moonshot: "@openclaw/moonshot-provider",
|
||||
"moonshot-provider": "@openclaw/moonshot-provider",
|
||||
msteams: "@openclaw/msteams",
|
||||
"nextcloud-talk": "@openclaw/nextcloud-talk",
|
||||
nostr: "@openclaw/nostr",
|
||||
nvidia: "@openclaw/nvidia-provider",
|
||||
"nvidia-provider": "@openclaw/nvidia-provider",
|
||||
ollama: "@openclaw/ollama-provider",
|
||||
"ollama-provider": "@openclaw/ollama-provider",
|
||||
"open-prose": "@openclaw/open-prose",
|
||||
openai: "@openclaw/openai-provider",
|
||||
"openai-provider": "@openclaw/openai-provider",
|
||||
opencode: "@openclaw/opencode-provider",
|
||||
"opencode-go": "@openclaw/opencode-go-provider",
|
||||
"opencode-go-provider": "@openclaw/opencode-go-provider",
|
||||
"opencode-provider": "@openclaw/opencode-provider",
|
||||
openrouter: "@openclaw/openrouter-provider",
|
||||
"openrouter-provider": "@openclaw/openrouter-provider",
|
||||
openshell: "@openclaw/openshell-sandbox",
|
||||
"openshell-sandbox": "@openclaw/openshell-sandbox",
|
||||
perplexity: "@openclaw/perplexity-plugin",
|
||||
"perplexity-plugin": "@openclaw/perplexity-plugin",
|
||||
"phone-control": "@openclaw/phone-control",
|
||||
"qa-channel": "@openclaw/qa-channel",
|
||||
"qa-lab": "@openclaw/qa-lab",
|
||||
"qa-matrix": "@openclaw/qa-matrix",
|
||||
qianfan: "@openclaw/qianfan-provider",
|
||||
"qianfan-provider": "@openclaw/qianfan-provider",
|
||||
qqbot: "@openclaw/qqbot",
|
||||
qwen: "@openclaw/qwen-provider",
|
||||
"qwen-provider": "@openclaw/qwen-provider",
|
||||
runway: "@openclaw/runway-provider",
|
||||
"runway-provider": "@openclaw/runway-provider",
|
||||
searxng: "@openclaw/searxng-plugin",
|
||||
"searxng-plugin": "@openclaw/searxng-plugin",
|
||||
senseaudio: "@openclaw/senseaudio-provider",
|
||||
"senseaudio-provider": "@openclaw/senseaudio-provider",
|
||||
sglang: "@openclaw/sglang-provider",
|
||||
"sglang-provider": "@openclaw/sglang-provider",
|
||||
signal: "@openclaw/signal",
|
||||
"skill-workshop": "@openclaw/skill-workshop",
|
||||
slack: "@openclaw/slack",
|
||||
stepfun: "@openclaw/stepfun-provider",
|
||||
"stepfun-provider": "@openclaw/stepfun-provider",
|
||||
"synology-chat": "@openclaw/synology-chat",
|
||||
synthetic: "@openclaw/synthetic-provider",
|
||||
"synthetic-provider": "@openclaw/synthetic-provider",
|
||||
"talk-voice": "@openclaw/talk-voice",
|
||||
tavily: "@openclaw/tavily-plugin",
|
||||
"tavily-plugin": "@openclaw/tavily-plugin",
|
||||
telegram: "@openclaw/telegram",
|
||||
tencent: "@openclaw/tencent-provider",
|
||||
"tencent-provider": "@openclaw/tencent-provider",
|
||||
"thread-ownership": "@openclaw/thread-ownership",
|
||||
tlon: "@openclaw/tlon",
|
||||
together: "@openclaw/together-provider",
|
||||
"together-provider": "@openclaw/together-provider",
|
||||
tokenjuice: "@openclaw/tokenjuice",
|
||||
"tts-local-cli": "@openclaw/tts-local-cli",
|
||||
twitch: "@openclaw/twitch",
|
||||
venice: "@openclaw/venice-provider",
|
||||
"venice-provider": "@openclaw/venice-provider",
|
||||
"vercel-ai-gateway": "@openclaw/vercel-ai-gateway-provider",
|
||||
"vercel-ai-gateway-provider": "@openclaw/vercel-ai-gateway-provider",
|
||||
vllm: "@openclaw/vllm-provider",
|
||||
"vllm-provider": "@openclaw/vllm-provider",
|
||||
"voice-call": "@openclaw/voice-call",
|
||||
volcengine: "@openclaw/volcengine-provider",
|
||||
"volcengine-provider": "@openclaw/volcengine-provider",
|
||||
voyage: "@openclaw/voyage-provider",
|
||||
"voyage-provider": "@openclaw/voyage-provider",
|
||||
vydra: "@openclaw/vydra-provider",
|
||||
"vydra-provider": "@openclaw/vydra-provider",
|
||||
"web-readability": "@openclaw/web-readability-plugin",
|
||||
"web-readability-plugin": "@openclaw/web-readability-plugin",
|
||||
webhooks: "@openclaw/webhooks",
|
||||
whatsapp: "@openclaw/whatsapp",
|
||||
xai: "@openclaw/xai-plugin",
|
||||
"xai-plugin": "@openclaw/xai-plugin",
|
||||
xiaomi: "@openclaw/xiaomi-provider",
|
||||
"xiaomi-provider": "@openclaw/xiaomi-provider",
|
||||
zai: "@openclaw/zai-provider",
|
||||
"zai-provider": "@openclaw/zai-provider",
|
||||
zalo: "@openclaw/zalo",
|
||||
zalouser: "@openclaw/zalouser",
|
||||
} as const;
|
||||
|
||||
export function getOpenClawExtensionPackageName(slug: string): string | null {
|
||||
const normalizedSlug = slug.trim().toLowerCase();
|
||||
if (!normalizedSlug) return null;
|
||||
|
||||
return (
|
||||
OPENCLAW_EXTENSION_SLUG_TO_PACKAGE[
|
||||
normalizedSlug as keyof typeof OPENCLAW_EXTENSION_SLUG_TO_PACKAGE
|
||||
] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
export function getOpenClawPackageCandidateNames(requestedName: string): string[] {
|
||||
if (requestedName.includes("/")) return [requestedName];
|
||||
|
||||
const officialPackageName = getOpenClawExtensionPackageName(requestedName);
|
||||
return Array.from(
|
||||
new Set(
|
||||
[officialPackageName, `@openclaw/${requestedName}`, requestedName].filter(
|
||||
(name): name is string => typeof name === "string" && name.length > 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -25,6 +25,9 @@ Publisher profiles:
|
||||
- Canonical page: `/<handle>`
|
||||
- Legacy compatibility pages: `/user/<handle>`, `/p/<handle>`, `/u/<handle>`,
|
||||
and `/orgs/<handle>` redirect to `/<handle>`
|
||||
- Existing publisher handles that predate an official OpenClaw alias remain
|
||||
reachable at `/user/<handle>` so the alias can keep its canonical top-level
|
||||
shortcut.
|
||||
|
||||
Skills:
|
||||
|
||||
@@ -111,6 +114,9 @@ This means official OpenClaw aliases are reserved before skills at the root.
|
||||
That is intentional: `https://clawhub.ai/codex` must show the official OpenClaw
|
||||
Codex plugin even if a skill named `codex` exists.
|
||||
|
||||
Publisher handle creation and rename paths reserve the full official alias set.
|
||||
This prevents new profiles from taking over those top-level plugin shortcuts.
|
||||
|
||||
## Collision policy
|
||||
|
||||
Do not make every `/:owner/:slug` path a universal package route. Owners can
|
||||
|
||||
@@ -62,6 +62,23 @@ describe("users route redirect", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps existing official-alias publishers reachable from the legacy profile route", async () => {
|
||||
const route = (await import("../routes/user/$handle")).Route as unknown as {
|
||||
__config: {
|
||||
beforeLoad: (args: {
|
||||
params: { handle: string };
|
||||
search: Record<string, unknown>;
|
||||
}) => unknown;
|
||||
};
|
||||
};
|
||||
|
||||
redirectMock.mockClear();
|
||||
expect(() =>
|
||||
route.__config.beforeLoad({ params: { handle: "tencent" }, search: {} }),
|
||||
).not.toThrow();
|
||||
expect(redirectMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("redirects legacy org profile routes to publisher profiles", async () => {
|
||||
const route = (await import("../routes/orgs/$handle")).Route as unknown as {
|
||||
__config: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Link } from "@tanstack/react-router";
|
||||
import { Download } from "lucide-react";
|
||||
import { formatCompactStat } from "../lib/numberFormat";
|
||||
import { buildPublisherProfileHref } from "../lib/ownerRoute";
|
||||
import {
|
||||
type PublicPublisherListItem,
|
||||
type PublicPublisherPublishedItem,
|
||||
@@ -41,8 +42,7 @@ export function PublisherListItem({ publisher, variant = "list" }: PublisherList
|
||||
|
||||
return (
|
||||
<Link
|
||||
to="/$slug"
|
||||
params={{ slug: handle }}
|
||||
to={buildPublisherProfileHref(handle)}
|
||||
className={`publisher-card publisher-card-${variant}`}
|
||||
aria-label={`Publisher: ${publisher.displayName}`}
|
||||
>
|
||||
|
||||
@@ -1,214 +1,5 @@
|
||||
const OPENCLAW_EXTENSION_SLUG_TO_PACKAGE = {
|
||||
acpx: "@openclaw/acpx",
|
||||
"active-memory": "@openclaw/active-memory",
|
||||
alibaba: "@openclaw/alibaba-provider",
|
||||
"alibaba-provider": "@openclaw/alibaba-provider",
|
||||
"amazon-bedrock": "@openclaw/amazon-bedrock-provider",
|
||||
"amazon-bedrock-mantle": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"amazon-bedrock-mantle-provider": "@openclaw/amazon-bedrock-mantle-provider",
|
||||
"amazon-bedrock-provider": "@openclaw/amazon-bedrock-provider",
|
||||
anthropic: "@openclaw/anthropic-provider",
|
||||
"anthropic-provider": "@openclaw/anthropic-provider",
|
||||
"anthropic-vertex": "@openclaw/anthropic-vertex-provider",
|
||||
"anthropic-vertex-provider": "@openclaw/anthropic-vertex-provider",
|
||||
arcee: "@openclaw/arcee-provider",
|
||||
"arcee-provider": "@openclaw/arcee-provider",
|
||||
"azure-speech": "@openclaw/azure-speech",
|
||||
bluebubbles: "@openclaw/bluebubbles",
|
||||
bonjour: "@openclaw/bonjour",
|
||||
brave: "@openclaw/brave-plugin",
|
||||
"brave-plugin": "@openclaw/brave-plugin",
|
||||
browser: "@openclaw/browser-plugin",
|
||||
"browser-plugin": "@openclaw/browser-plugin",
|
||||
byteplus: "@openclaw/byteplus-provider",
|
||||
"byteplus-provider": "@openclaw/byteplus-provider",
|
||||
cerebras: "@openclaw/cerebras-provider",
|
||||
"cerebras-provider": "@openclaw/cerebras-provider",
|
||||
chutes: "@openclaw/chutes-provider",
|
||||
"chutes-provider": "@openclaw/chutes-provider",
|
||||
"cloudflare-ai-gateway": "@openclaw/cloudflare-ai-gateway-provider",
|
||||
"cloudflare-ai-gateway-provider": "@openclaw/cloudflare-ai-gateway-provider",
|
||||
codex: "@openclaw/codex",
|
||||
comfy: "@openclaw/comfy-provider",
|
||||
"comfy-provider": "@openclaw/comfy-provider",
|
||||
"copilot-proxy": "@openclaw/copilot-proxy",
|
||||
deepgram: "@openclaw/deepgram-provider",
|
||||
"deepgram-provider": "@openclaw/deepgram-provider",
|
||||
deepinfra: "@openclaw/deepinfra-provider",
|
||||
"deepinfra-provider": "@openclaw/deepinfra-provider",
|
||||
deepseek: "@openclaw/deepseek-provider",
|
||||
"deepseek-provider": "@openclaw/deepseek-provider",
|
||||
"device-pair": "@openclaw/device-pair",
|
||||
"diagnostics-otel": "@openclaw/diagnostics-otel",
|
||||
"diagnostics-prometheus": "@openclaw/diagnostics-prometheus",
|
||||
diffs: "@openclaw/diffs",
|
||||
"diffs-language-pack": "@openclaw/diffs-language-pack",
|
||||
discord: "@openclaw/discord",
|
||||
"document-extract": "@openclaw/document-extract-plugin",
|
||||
"document-extract-plugin": "@openclaw/document-extract-plugin",
|
||||
duckduckgo: "@openclaw/duckduckgo-plugin",
|
||||
"duckduckgo-plugin": "@openclaw/duckduckgo-plugin",
|
||||
elevenlabs: "@openclaw/elevenlabs-speech",
|
||||
"elevenlabs-speech": "@openclaw/elevenlabs-speech",
|
||||
exa: "@openclaw/exa-plugin",
|
||||
"exa-plugin": "@openclaw/exa-plugin",
|
||||
fal: "@openclaw/fal-provider",
|
||||
"fal-provider": "@openclaw/fal-provider",
|
||||
feishu: "@openclaw/feishu",
|
||||
"file-transfer": "@openclaw/file-transfer",
|
||||
firecrawl: "@openclaw/firecrawl-plugin",
|
||||
"firecrawl-plugin": "@openclaw/firecrawl-plugin",
|
||||
fireworks: "@openclaw/fireworks-provider",
|
||||
"fireworks-provider": "@openclaw/fireworks-provider",
|
||||
"github-copilot": "@openclaw/github-copilot-provider",
|
||||
"github-copilot-provider": "@openclaw/github-copilot-provider",
|
||||
google: "@openclaw/google-plugin",
|
||||
"google-meet": "@openclaw/google-meet",
|
||||
"google-plugin": "@openclaw/google-plugin",
|
||||
googlechat: "@openclaw/googlechat",
|
||||
gradium: "@openclaw/gradium-speech",
|
||||
"gradium-speech": "@openclaw/gradium-speech",
|
||||
groq: "@openclaw/groq-provider",
|
||||
"groq-provider": "@openclaw/groq-provider",
|
||||
huggingface: "@openclaw/huggingface-provider",
|
||||
"huggingface-provider": "@openclaw/huggingface-provider",
|
||||
imessage: "@openclaw/imessage",
|
||||
inworld: "@openclaw/inworld-speech",
|
||||
"inworld-speech": "@openclaw/inworld-speech",
|
||||
irc: "@openclaw/irc",
|
||||
kilocode: "@openclaw/kilocode-provider",
|
||||
"kilocode-provider": "@openclaw/kilocode-provider",
|
||||
kimi: "@openclaw/kimi-provider",
|
||||
"kimi-coding": "@openclaw/kimi-provider",
|
||||
"kimi-provider": "@openclaw/kimi-provider",
|
||||
line: "@openclaw/line",
|
||||
litellm: "@openclaw/litellm-provider",
|
||||
"litellm-provider": "@openclaw/litellm-provider",
|
||||
"llm-task": "@openclaw/llm-task",
|
||||
lmstudio: "@openclaw/lmstudio-provider",
|
||||
"lmstudio-provider": "@openclaw/lmstudio-provider",
|
||||
lobster: "@openclaw/lobster",
|
||||
matrix: "@openclaw/matrix",
|
||||
mattermost: "@openclaw/mattermost",
|
||||
"memory-core": "@openclaw/memory-core",
|
||||
"memory-lancedb": "@openclaw/memory-lancedb",
|
||||
"memory-wiki": "@openclaw/memory-wiki",
|
||||
microsoft: "@openclaw/microsoft-speech",
|
||||
"microsoft-foundry": "@openclaw/microsoft-foundry",
|
||||
"microsoft-speech": "@openclaw/microsoft-speech",
|
||||
"migrate-claude": "@openclaw/migrate-claude",
|
||||
"migrate-hermes": "@openclaw/migrate-hermes",
|
||||
minimax: "@openclaw/minimax-provider",
|
||||
"minimax-provider": "@openclaw/minimax-provider",
|
||||
mistral: "@openclaw/mistral-provider",
|
||||
"mistral-provider": "@openclaw/mistral-provider",
|
||||
moonshot: "@openclaw/moonshot-provider",
|
||||
"moonshot-provider": "@openclaw/moonshot-provider",
|
||||
msteams: "@openclaw/msteams",
|
||||
"nextcloud-talk": "@openclaw/nextcloud-talk",
|
||||
nostr: "@openclaw/nostr",
|
||||
nvidia: "@openclaw/nvidia-provider",
|
||||
"nvidia-provider": "@openclaw/nvidia-provider",
|
||||
ollama: "@openclaw/ollama-provider",
|
||||
"ollama-provider": "@openclaw/ollama-provider",
|
||||
"open-prose": "@openclaw/open-prose",
|
||||
openai: "@openclaw/openai-provider",
|
||||
"openai-provider": "@openclaw/openai-provider",
|
||||
opencode: "@openclaw/opencode-provider",
|
||||
"opencode-go": "@openclaw/opencode-go-provider",
|
||||
"opencode-go-provider": "@openclaw/opencode-go-provider",
|
||||
"opencode-provider": "@openclaw/opencode-provider",
|
||||
openrouter: "@openclaw/openrouter-provider",
|
||||
"openrouter-provider": "@openclaw/openrouter-provider",
|
||||
openshell: "@openclaw/openshell-sandbox",
|
||||
"openshell-sandbox": "@openclaw/openshell-sandbox",
|
||||
perplexity: "@openclaw/perplexity-plugin",
|
||||
"perplexity-plugin": "@openclaw/perplexity-plugin",
|
||||
"phone-control": "@openclaw/phone-control",
|
||||
"qa-channel": "@openclaw/qa-channel",
|
||||
"qa-lab": "@openclaw/qa-lab",
|
||||
"qa-matrix": "@openclaw/qa-matrix",
|
||||
qianfan: "@openclaw/qianfan-provider",
|
||||
"qianfan-provider": "@openclaw/qianfan-provider",
|
||||
qqbot: "@openclaw/qqbot",
|
||||
qwen: "@openclaw/qwen-provider",
|
||||
"qwen-provider": "@openclaw/qwen-provider",
|
||||
runway: "@openclaw/runway-provider",
|
||||
"runway-provider": "@openclaw/runway-provider",
|
||||
searxng: "@openclaw/searxng-plugin",
|
||||
"searxng-plugin": "@openclaw/searxng-plugin",
|
||||
senseaudio: "@openclaw/senseaudio-provider",
|
||||
"senseaudio-provider": "@openclaw/senseaudio-provider",
|
||||
sglang: "@openclaw/sglang-provider",
|
||||
"sglang-provider": "@openclaw/sglang-provider",
|
||||
signal: "@openclaw/signal",
|
||||
"skill-workshop": "@openclaw/skill-workshop",
|
||||
slack: "@openclaw/slack",
|
||||
stepfun: "@openclaw/stepfun-provider",
|
||||
"stepfun-provider": "@openclaw/stepfun-provider",
|
||||
"synology-chat": "@openclaw/synology-chat",
|
||||
synthetic: "@openclaw/synthetic-provider",
|
||||
"synthetic-provider": "@openclaw/synthetic-provider",
|
||||
"talk-voice": "@openclaw/talk-voice",
|
||||
tavily: "@openclaw/tavily-plugin",
|
||||
"tavily-plugin": "@openclaw/tavily-plugin",
|
||||
telegram: "@openclaw/telegram",
|
||||
tencent: "@openclaw/tencent-provider",
|
||||
"tencent-provider": "@openclaw/tencent-provider",
|
||||
"thread-ownership": "@openclaw/thread-ownership",
|
||||
tlon: "@openclaw/tlon",
|
||||
together: "@openclaw/together-provider",
|
||||
"together-provider": "@openclaw/together-provider",
|
||||
tokenjuice: "@openclaw/tokenjuice",
|
||||
"tts-local-cli": "@openclaw/tts-local-cli",
|
||||
twitch: "@openclaw/twitch",
|
||||
venice: "@openclaw/venice-provider",
|
||||
"venice-provider": "@openclaw/venice-provider",
|
||||
"vercel-ai-gateway": "@openclaw/vercel-ai-gateway-provider",
|
||||
"vercel-ai-gateway-provider": "@openclaw/vercel-ai-gateway-provider",
|
||||
vllm: "@openclaw/vllm-provider",
|
||||
"vllm-provider": "@openclaw/vllm-provider",
|
||||
"voice-call": "@openclaw/voice-call",
|
||||
volcengine: "@openclaw/volcengine-provider",
|
||||
"volcengine-provider": "@openclaw/volcengine-provider",
|
||||
voyage: "@openclaw/voyage-provider",
|
||||
"voyage-provider": "@openclaw/voyage-provider",
|
||||
vydra: "@openclaw/vydra-provider",
|
||||
"vydra-provider": "@openclaw/vydra-provider",
|
||||
"web-readability": "@openclaw/web-readability-plugin",
|
||||
"web-readability-plugin": "@openclaw/web-readability-plugin",
|
||||
webhooks: "@openclaw/webhooks",
|
||||
whatsapp: "@openclaw/whatsapp",
|
||||
xai: "@openclaw/xai-plugin",
|
||||
"xai-plugin": "@openclaw/xai-plugin",
|
||||
xiaomi: "@openclaw/xiaomi-provider",
|
||||
"xiaomi-provider": "@openclaw/xiaomi-provider",
|
||||
zai: "@openclaw/zai-provider",
|
||||
"zai-provider": "@openclaw/zai-provider",
|
||||
zalo: "@openclaw/zalo",
|
||||
zalouser: "@openclaw/zalouser",
|
||||
} as const;
|
||||
|
||||
export function getOpenClawExtensionPackageName(slug: string): string | null {
|
||||
const normalizedSlug = slug.trim().toLowerCase();
|
||||
if (!normalizedSlug) return null;
|
||||
|
||||
return (
|
||||
OPENCLAW_EXTENSION_SLUG_TO_PACKAGE[
|
||||
normalizedSlug as keyof typeof OPENCLAW_EXTENSION_SLUG_TO_PACKAGE
|
||||
] ?? null
|
||||
);
|
||||
}
|
||||
|
||||
export function getOpenClawPackageCandidateNames(requestedName: string): string[] {
|
||||
if (requestedName.includes("/")) return [requestedName];
|
||||
|
||||
const officialPackageName = getOpenClawExtensionPackageName(requestedName);
|
||||
return Array.from(
|
||||
new Set(
|
||||
[officialPackageName, `@openclaw/${requestedName}`, requestedName].filter(
|
||||
(name): name is string => typeof name === "string" && name.length > 0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
export {
|
||||
getOpenClawExtensionPackageName,
|
||||
getOpenClawPackageCandidateNames,
|
||||
OPENCLAW_EXTENSION_SLUG_TO_PACKAGE,
|
||||
} from "clawhub-schema";
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildPublisherProfileHref,
|
||||
isOwnerRouteHandleOrIdSegment,
|
||||
isOwnerRouteHandleSegment,
|
||||
isOwnerRouteScopeSegment,
|
||||
} from "./ownerRoute";
|
||||
|
||||
describe("owner route segments", () => {
|
||||
it("uses the legacy profile route for official alias collisions", () => {
|
||||
expect(buildPublisherProfileHref("tencent")).toBe("/user/tencent");
|
||||
expect(buildPublisherProfileHref("steipete")).toBe("/steipete");
|
||||
});
|
||||
|
||||
it("accepts npm-compatible publisher handle characters", () => {
|
||||
expect(isOwnerRouteHandleSegment("example.tools")).toBe(true);
|
||||
expect(isOwnerRouteHandleSegment("lab_1")).toBe(true);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { getOpenClawExtensionPackageName } from "./openClawExtensionSlugs";
|
||||
|
||||
const OWNER_ROUTE_HANDLE_PATTERN = /^[a-zA-Z0-9](?:[a-zA-Z0-9._-]{0,38}[a-zA-Z0-9])?$/;
|
||||
const OWNER_ROUTE_SCOPE_PATTERN = /^@[a-zA-Z0-9](?:[a-zA-Z0-9._-]{0,38}[a-zA-Z0-9])?$/;
|
||||
|
||||
@@ -22,7 +24,13 @@ function routeSegment(value: string) {
|
||||
}
|
||||
|
||||
export function buildPublisherProfileHref(handle: string) {
|
||||
return `/${routeSegment(handle)}`;
|
||||
return isLegacyPublisherProfileHandle(handle)
|
||||
? `/user/${routeSegment(handle)}`
|
||||
: `/${routeSegment(handle)}`;
|
||||
}
|
||||
|
||||
export function isLegacyPublisherProfileHandle(handle: string) {
|
||||
return Boolean(getOpenClawExtensionPackageName(handle));
|
||||
}
|
||||
|
||||
export function buildSkillDetailHref(owner: string, slug: string) {
|
||||
|
||||
@@ -92,4 +92,15 @@ describe("slug route resolution", () => {
|
||||
});
|
||||
expect(fetchSkillPageDataMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps official OpenClaw aliases ahead of colliding publisher handles", async () => {
|
||||
queryMock.mockResolvedValue({ _id: "publishers:tencent", handle: "tencent" });
|
||||
|
||||
await expect(resolveTopLevelSlugRoute("tencent")).resolves.toEqual({
|
||||
kind: "plugin",
|
||||
name: "@openclaw/tencent-provider",
|
||||
href: "/openclaw/plugins/tencent-provider",
|
||||
});
|
||||
expect(fetchSkillPageDataMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Card, CardContent } from "../../components/ui/card";
|
||||
import { Skeleton } from "../../components/ui/skeleton";
|
||||
import { formatCompactStat } from "../../lib/numberFormat";
|
||||
import { buildPublisherMeta } from "../../lib/og";
|
||||
import { buildPublisherProfileHref } from "../../lib/ownerRoute";
|
||||
import { buildPublisherProfileHref, isLegacyPublisherProfileHandle } from "../../lib/ownerRoute";
|
||||
import type {
|
||||
PublicPublisher,
|
||||
PublicPublisherCatalogDisplay,
|
||||
@@ -25,6 +25,8 @@ import { readPublicDownloadCount } from "../../lib/publicUser";
|
||||
|
||||
export const Route = createFileRoute("/user/$handle")({
|
||||
beforeLoad: ({ params }) => {
|
||||
if (isLegacyPublisherProfileHandle(params.handle)) return;
|
||||
|
||||
throw redirect({
|
||||
href: buildPublisherProfileHref(params.handle),
|
||||
replace: true,
|
||||
|
||||
Reference in New Issue
Block a user