Skip to content
Merged
Show file tree
Hide file tree
Changes from 84 commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
4046ae2
feat(market): switch all three markets to the unified plugin API
Aug 21, 2026
6252820
feat(market): adopt the contracts/v1 package layout
Aug 24, 2026
beee3b2
fix(market): review follow-ups for the contracts/v1 layout
Aug 24, 2026
be0850b
feat: derive skill files from the attachment tree
Aug 24, 2026
c9702e1
fix(market): synthesize tree-skill filename, guard mcp pagination
Aug 24, 2026
b705ddc
fix(market): clear i18n gate + address review consistency findings
Aug 24, 2026
718753b
test(e2e): mock unified plugin surface for experts market
Aug 24, 2026
51594bd
test(e2e): mock unified plugin surface for mcp + skills markets
Aug 25, 2026
cfc8c22
test(e2e): SK2 count reflects unified catalog-wide category totals
Aug 25, 2026
f3cb374
fix: resolve #1532 review P1s (count, icon, category, atomicity, pars…
Aug 25, 2026
d4d403e
fix: connector icon undefined-sentinel + category form guard (#1532 r…
Aug 25, 2026
c7f70b6
chore: re-trigger CI and review after octo-marketplace#67 merge
Aug 26, 2026
59fa8bf
feat(market): prototype-aligned UI restructure + full-page skill/expe…
Aug 26, 2026
0a57568
Merge remote-tracking branch 'origin/feat/unified-plugin-market' into…
Aug 26, 2026
a63e328
Merge remote-tracking branch 'upstream/main' into feat/market-ui-rest…
Aug 26, 2026
b10eba7
feat(market): full-page squad (专家团) editor + write layer
Aug 26, 2026
99085a4
style(skillmarket): align 官方发布 color with the expert market
Aug 26, 2026
b00101a
feat(market): reduce discovery sort to 最新/最热, black-and-white styling
Aug 26, 2026
dca5933
feat(market): drop the "共 N 个" total and detail view/download counts
Aug 26, 2026
e57ed37
fix(market): restore total in McpMarketListPage render destructure
Aug 26, 2026
2731bcd
feat(market): add 最新/最热 sort to the connector (MCP) market
Aug 26, 2026
105ef5d
feat(market): make connector categories dynamic (backend taxonomy)
Aug 26, 2026
acc68f9
feat(market): default the discovery sort to 最新 (latest)
Aug 26, 2026
f2810ab
fix(market): center the expert/squad editor content column
Aug 26, 2026
a2a3384
fix(market): expert delete toast uses the common Toast, no trailing p…
Aug 26, 2026
c17ea32
feat(market): put category pills and sort on one row (wrap + sort top…
Aug 26, 2026
ee28bd8
feat(skillmarket): drop the internal skill name from the card meta row
Aug 26, 2026
7587175
feat(squad-editor): hide member plugin id, make leader mandatory
Aug 26, 2026
eadba71
feat(expert): wire 新建/编辑 skill via a from-scratch SkillEditorPage
Aug 26, 2026
06446a0
fix(market): squad leaderless-load auto-promote + remove dead CSS/i18n
Aug 26, 2026
6ee0d1f
feat(market): unified/atomic save for expert & squad create (draft ch…
Aug 26, 2026
59c1849
feat(market): drop the "市场 ·" prefix from market page titles
Aug 26, 2026
162f3c0
feat(market): drop hero descriptions, expert tabs to top, skills firs…
Aug 26, 2026
88218d7
feat(market): 我的发布 page — rename title + one card per row
Aug 26, 2026
a26c3a1
feat(market): 我的发布 header divider + sidebar order 技能/连接器/专家
Aug 26, 2026
f158d6b
feat(market): reorder 我的发布 sub-tabs to 技能/连接器/专家/专家团
Aug 26, 2026
860774c
style(market): make the 我的发布 header divider more visible
Aug 27, 2026
fa1ce12
feat(market): 我的发布 horizontal row cards (avatar + content + edit/dele…
Aug 27, 2026
b7e4f4f
feat(market): drop the published/private badge from 我的发布 row cards
Aug 27, 2026
398d869
feat(market): revert expert/squad create+edit to the Bot flow, skill …
Aug 27, 2026
0ed8fc3
style(market): make the 我的发布 header divider clearly visible
Aug 27, 2026
07843de
feat(market): hide connector category pills in 我的发布 (category shown o…
Aug 27, 2026
180c42a
feat(market): skill row meta shows category only; connector row shows…
Aug 27, 2026
ad743d7
feat(skill): 添加 button forwards to Bot via shared PromptForwardModal
Aug 27, 2026
866e41e
style(mcp): move connector/expert card primary action to bottom-right
Aug 27, 2026
95e8fb1
feat(mcp): connector card action forwards a connect prompt
Aug 27, 2026
7dd0258
i18n(market): relabel skill/connector card primary action to 添加到 Bot
Aug 27, 2026
98daf82
style(mcp): drop the Plug icon from the connector 添加到 Bot button
Aug 27, 2026
50b0832
feat(skill): Bot 上架 modal uses the shared forward-to-Bot flow
Aug 27, 2026
617ffae
style(mcp): divider above 我的发布 in the market sidebar
Aug 27, 2026
a64758f
fix(market): pre-PR review fixes (P0 slug key, P1 spaceId, e2e totals…
Aug 27, 2026
98be082
refactor(market): remove the unreachable full-page editor cluster
Aug 27, 2026
e0a1652
fix(market): resolve e2e-p0 failures (C37 strict-mode, SK1 stale nav)
Aug 27, 2026
22292a8
fix(market): address PR #1584 review (P1-1/P1-2/P1-3 + P2s)
Aug 27, 2026
250f4c7
feat(base): forward-to-Bot modal — unified title, editable prompt, ho…
Aug 27, 2026
a27f963
feat(market): 我的发布 card stats + expert tag filter + unified search/title
Aug 27, 2026
ac409b4
Merge remote-tracking branch 'upstream/main' into feat/market-ui-rest…
Aug 27, 2026
24fc5a9
test(e2e): migrate C38/C39/C40 MCP handlers to the unified plugin sur…
Aug 27, 2026
e291a67
feat(market): emit cowork-plugin 2.0 $schema in plugin upsert bodies
Aug 27, 2026
a1c849c
test(e2e): route C39/C40 through the connector 我的发布 view
Aug 27, 2026
9ee048f
Merge remote-tracking branch 'origin/feat/unified-plugin-market' into…
Aug 27, 2026
6a46fef
fix(market): correct connector create-modal category default and visi…
Aug 27, 2026
4abafbf
refactor(market): drop /plugins/publish; create/edit is a single upsert
Aug 27, 2026
dca273d
feat(skill): send caller-declared version on skill upsert
Aug 28, 2026
c9cf60c
feat(market): show tool count instead of install count on connector m…
Aug 28, 2026
21fb1aa
feat(skill): make version editable on metadata edit and always submit it
Aug 28, 2026
0f1f10d
fix(skill): blank the edit modal's default category instead of a lega…
Aug 28, 2026
d3edf0a
fix(market): sanitize skill prompt space id; ignore track on skill mi…
Aug 28, 2026
7f53304
test(market): pin the emitted 2.0 $schema on connector and skill writ…
Aug 28, 2026
10be408
fix(market): per-surface forward-modal copy; fix edit-disable + mapVi…
Aug 28, 2026
a155e22
fix(market): expert publish hint per kind; drop dead editor plumbing …
Aug 28, 2026
8068c89
feat(market): render 我的发布 as a data table
Aug 31, 2026
f96dbef
feat(market): drop 更新时间 column; hide 浏览/下载 for connectors
Aug 31, 2026
aa3136f
Merge remote-tracking branch 'upstream/main' into feat/market-ui-rest…
Aug 31, 2026
6224aaf
fix(market): resolve review blockers on the 我的发布 table migration
Aug 31, 2026
2ff526d
chore: re-trigger CI (e2e flake in unrelated @CH24 chat spec)
Aug 31, 2026
4fb15b4
fix(mcp): harden mcp.json secret redaction on public detail surfaces
Aug 31, 2026
d7f1e7c
fix(mcp): close redaction bypasses + surface non-404 relation failures
Aug 31, 2026
14781cb
fix(mcp): redact a query token in a positional-URL stdio arg
Sep 1, 2026
9c3f4ef
fix(mcp): rebuild redacted mcp.json from a whitelist (close fail-open…
Sep 1, 2026
a879f3d
chore: retrigger CI (e2e-p0 flake: unmocked /summary proxy request, 1…
Sep 1, 2026
6c56b5d
fix(mcp): echo unmodeled state on full-replace connector/skill writes
Sep 1, 2026
4519494
chore: retrigger CI (unit-tests flake: prosemirror getClientRects unc…
Sep 1, 2026
542925e
fix(mcp): mask secret-shaped values on connector detail + harden reda…
Sep 1, 2026
e10c76c
test(mcp): use low-entropy canaries in redaction fixtures (fix gitleaks)
Sep 1, 2026
5d9db51
test(base): stub getClientRects/getBoundingClientRect on Range + Text…
Sep 1, 2026
e434546
fix(mcp): unify connector/expert redaction + fail-closed multi-server…
Sep 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .i18n/scan-config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"ignoredFiles": [
{
"path": "packages/dmworkmcp/src/api/expertWire.ts",
"reason": "parseTeamAgentsMarkdown is the inverse of the backend's teamAgentsMarkdown renderer; the Chinese section markers (协作方式/策略/依赖/权限, - 阻塞:/- 推荐:) are wire-format tokens that must byte-match the backend, not translatable UI copy."
},
{
"path": "packages/dmworkbase/src/Utils/pinYin.ts",
"reason": "Pinyin search dictionary data, not UI copy."
Expand Down Expand Up @@ -80,6 +84,10 @@
"path": "packages/dmworkmcp/src/utils/mcpBotPublishPrompt.ts",
"reason": "Agent prompt template for the MCP marketplace publish flow. Same rationale as dmworkskillmarket/src/utils/botPublishPrompt.ts — generated prompt content pasted into an agent client, must byte-match; not translatable UI chrome."
},
{
"path": "packages/dmworkmcp/src/utils/mcpConnectPrompt.ts",
"reason": "Agent prompt template for the connector 接入 flow (card primary action forwards this to a Bot). Generated prompt content pasted into an agent client, must byte-match; not translatable UI chrome. Same category as mcpBotPublishPrompt.ts / dmworkskillmarket installPrompt.ts."
},
{
"path": "packages/dmworkmcp/src/mock/expertMock.ts",
"reason": "Expert Marketplace demo fixtures (squad/expert names, summaries, roles, strategies, category names). Sample content data returned by the mock service layer, replaced wholesale by the real backend response later; not translatable UI chrome. Same category as dmworkmcp/src/mock/mcpMock.ts."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@

## 预期结果

- 页面显示“专家”类型导航和“共 1 个”
- 页面显示“专家”类型导航与专家列表
- 列表显示“发布负责人”和“官方发布”。
- 点击专家卡片后,详情弹窗显示专家名称、官方发布者和专家指令正文。
- 点击关闭后,详情弹窗消失,列表内容仍可见。

## 反例

- 如果 Experts 市场没有挂载到统一市场路由,页面不会出现专家类型导航或列表摘要
- 如果 Experts 市场没有挂载到统一市场路由,页面不会出现专家类型导航或专家列表
- 如果官方可见性映射错误,专家卡片或详情会显示脱敏前发布者字段,或缺少“官方发布”。
- 如果列表投影没有成功 hydrate 成详情,详情弹窗不会出现专家指令正文,case 应暴露真实详情加载问题。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,17 @@
## 预期结果

- 初始列表显示 2 个专家。
- 输入“发布”后,结果摘要更新为“共 1 个”。
- 列表保留“发布负责人”,隐藏“会议协调专家”。
- 输入“发布”后,列表重新筛选:仅保留“发布负责人”,隐藏“会议协调专家”。

## 反例

- 如果搜索输入没有触发客户端筛选,结果摘要仍显示 2 个专家
- 如果搜索输入没有触发客户端筛选,两个专家仍全部显示
- 如果搜索只匹配名称而没有按页面约定覆盖简介/标签,匹配结果会错误缺失。
- 筛选过程中不应出现加载失败提示或跳转登录页。

## 视觉基准

不建 pixel baseline; 用 `getByRole` + `getByText` 断言搜索框、结果摘要和卡片结构
不建 pixel baseline; 用 `getByRole` + `getByText` 断言搜索框和卡片结构

## 摸清依据

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,18 @@

## 预期结果

- 结果摘要显示“共 101 个”。
- 首批目录中的“发布负责人”正常显示。
- 页面显示“仅显示前 100 项,请用搜索或分类筛选缩小范围”。
- 页面不显示“加载失败,请稍后重试。”。

## 反例

- 如果分页总数没有从响应 envelope 映射,结果摘要会错误显示 100 个或首批数据量。
- 如果超过单页上限没有提示,用户无法知道当前目录并不完整。
- 如果首批响应被误判为错误,页面会显示加载失败提示或重试状态。

## 视觉基准

不建 pixel baseline; 用 `getByRole` + `getByText` 断言结果摘要、截断提示和专家卡片结构
不建 pixel baseline; 用 `getByRole` + `getByText` 断言截断提示和专家卡片结构

## 摸清依据

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@

## 预期结果

- 页面显示 Skills 市场导航和“共 1 个技能”
- 页面显示 Skills 市场搜索框与技能列表
- 列表显示“发布风险雷达”、技能名 `release-risk-radar` 和“官方发布”。
- 点击技能卡片后,详情弹窗显示技能名称、官方发布者和技能说明正文。
- 点击关闭后,详情弹窗消失,列表内容仍可见。

## 反例

- 如果 Skills 市场没有挂载到统一市场路由,页面不会出现 Skills 导航或列表摘要
- 如果 Skills 市场没有挂载到统一市场路由,页面不会出现 Skills 导航或技能列表
- 如果列表响应映射错误,官方技能卡片不会同时出现展示名称、技能名和“官方发布”。
- 如果详情请求或 SKILL.md 请求失败,详情正文不会出现,case 应暴露真实的详情加载问题。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@
## 预期结果

- 初始列表显示 2 个技能。
- 输入“发布”后,结果摘要更新为“共 1 个技能”。
- 列表保留“发布风险雷达”,隐藏“会议纪要整理”。
- 输入“发布”后,列表重新筛选:仅保留“发布风险雷达”,隐藏“会议纪要整理”。

## 反例

- 如果搜索输入没有触发列表刷新,结果摘要仍显示 2 个技能
- 如果搜索输入没有触发列表刷新,两个技能仍全部显示
- 如果服务端筛选结果没有正确映射,匹配技能不会出现,或不匹配技能仍留在列表中。
- 关键词 debounce 期间不应因一次输入显示加载失败或跳转登录页。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@

## 预期结果

- 结果摘要显示“共 2 个技能”。
- 首屏显示“release-risk-radar”,并最终追加显示“meeting-note-cleaner”。
- 首屏技能在加载下一页后仍然可见,两个技能同时保留在列表中。
- 分页过程中不显示“加载失败”。
Expand All @@ -41,7 +40,7 @@

## 视觉基准

不建 pixel baseline; 用 `getByRole` + `getByText` 断言结果摘要和技能卡片结构
不建 pixel baseline; 用 `getByRole` + `getByText` 断言技能卡片结构

## 摸清依据

Expand Down
33 changes: 29 additions & 4 deletions apps/web/e2e-kit/msw-handlers/c38-mcp-list-retry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { Page } from "@playwright/test";

// Unified plugin surface: the MCP market fetches GET /plugins (plugin_type=
// connector) + GET /plugin_categories, not the legacy /mcps + /mcp_categories.
// The list 500s until the spec sets __c38RetryRequested, then returns one
// plugin-wire row that mapListItem projects to the McpListItem the spec asserts.
export async function registerC38McpListRetry(page: Page): Promise<void> {
function install() {
type Msw = { worker: { use: (...handlers: unknown[]) => void }; http: { get: (path: string, resolver: () => unknown) => unknown }; HttpResponse: { json: (body: unknown, init?: unknown) => unknown } };
Expand All @@ -9,16 +13,37 @@ export async function registerC38McpListRetry(page: Page): Promise<void> {
return false;
}
if (win.__c38Installed) return true;
const plugin = {
plugin_id: "retryable-search",
plugin_name: "Retryable Search MCP",
plugin_type: "connector",
category_id: "search-cat",
tags: ["search"],
publisher: "Octo Community",
owner_id: "space-e2e",
visibility: "public",
creator_name: "Alice",
created_by_type: "human",
icon_url: "",
tool_count: 1,
view_count: 3,
install_count: 1,
download_count: 0,
current_version: "1.0.0",
manifest_json: { name: "retryable-search", description: "Retryable search", labels: ["search"] },
created_at: "2026-08-26T00:00:00Z",
updated_at: "2026-08-26T00:00:00Z",
};
const list = () => {
if (!win.__c38RetryRequested) {
return win.__msw!.HttpResponse.json({ message: "temporary failure" }, { status: 500 });
}
return win.__msw!.HttpResponse.json({ data: [{ mcp_id: "retryable-search", name: "Retryable Search MCP", slogan: "Retryable search", category: "search", icon: "🔎", tags: ["search"], tool_count: 1, visibility: "public", source: "space", creator_name: "Alice", created_by_type: "human", match_reasons: [], updated_at: "2026-08-26T00:00:00Z" }], pagination: { total: 1, page: 1, page_size: 20 } });
return win.__msw!.HttpResponse.json({ data: [plugin], pagination: { total: 1, page: 1, page_size: 20 } });
};
const categories = () => win.__msw!.HttpResponse.json({ data: [{ key: "search", count: 1 }] });
const categories = () => win.__msw!.HttpResponse.json({ data: [{ category_id: "search-cat", name: "search", sort_order: 0, plugin_count: 1 }] });
win.__msw.worker.use(
win.__msw.http.get("*/market/api/v1/mcps", list),
win.__msw.http.get("*/market/api/v1/mcp_categories", categories),
win.__msw.http.get("*/market/api/v1/plugins", list),
win.__msw.http.get("*/market/api/v1/plugin_categories", categories),
);
win.__c38Installed = true;
return true;
Expand Down
8 changes: 6 additions & 2 deletions apps/web/e2e-kit/msw-handlers/c39-mcp-probe-failure.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import type { Page } from "@playwright/test";

// Unified plugin surface for the list/categories the market page loads (empty
// here — this spec exercises the create wizard's probe path). Probing stays on
// the intentionally-retained legacy tool endpoint (/mcps/_probe), so those mocks
// are unchanged.
export async function registerC39McpProbeFailure(page: Page): Promise<void> {
function install() {
type Msw = {
Expand Down Expand Up @@ -38,8 +42,8 @@ export async function registerC39McpProbeFailure(page: Page): Promise<void> {
},
});
win.__msw.worker.use(
win.__msw.http.get("*/market/api/v1/mcps", list),
win.__msw.http.get("*/market/api/v1/mcp_categories", categories),
win.__msw.http.get("*/market/api/v1/plugins", list),
win.__msw.http.get("*/market/api/v1/plugin_categories", categories),
win.__msw.http.post("*/market/api/v1/mcps/_probe", probeFailure),
win.__msw.http.post("*/market/api/v1/mcps/probe", probeFailure),
win.__msw.http.post("*/api/v1/mcps/_probe", probeFailure),
Expand Down
61 changes: 40 additions & 21 deletions apps/web/e2e-kit/msw-handlers/c40-mcp-delete-failure.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import type { Page } from "@playwright/test";

// Unified plugin surface: list/mine both go through GET /plugins (mode=mine for
// the 我的 view), categories through GET /plugin_categories, and delete through
// POST /plugins/delete. The delete resolves 500 so the confirm modal keeps the
// row and shows 删除失败. The item is plugin-wire shaped (icon "🧪" + creator
// "E2E Tester") so the card name matches /^🧪 Delete Failure MCP E2E/.
export async function registerC40McpDeleteFailure(page: Page): Promise<void> {
function install() {
type Msw = {
worker: { use: (...handlers: unknown[]) => void };
http: {
get: (path: string, resolver: () => unknown) => unknown;
delete: (path: string, resolver: () => unknown) => unknown;
get: (path: string, resolver: (info: { request: { url: string } }) => unknown) => unknown;
post: (path: string, resolver: () => unknown) => unknown;
};
HttpResponse: { json: (body: unknown, init?: unknown) => unknown };
};
Expand All @@ -25,35 +30,49 @@ export async function registerC40McpDeleteFailure(page: Page): Promise<void> {
}
if (win.__c40Installed) return true;
const item = {
mcp_id: "delete-failure-mcp",
name: "Delete Failure MCP",
slogan: "A connector that cannot be deleted in this scenario.",
category: "dev",
icon: "🧪",
plugin_id: "delete-failure-mcp",
plugin_name: "Delete Failure MCP",
plugin_type: "connector",
category_id: "dev-cat",
tags: ["e2e"],
tool_count: 1,
publisher: "E2E Tester",
owner_id: "space-e2e",
visibility: "public",
source: "mine",
creator_name: "E2E Tester",
created_by_type: "human",
transport: "streamable-http",
match_reasons: [],
icon_url: "🧪",
tool_count: 1,
view_count: 0,
install_count: 0,
download_count: 0,
current_version: "1.0.0",
manifest_json: {
name: "delete-failure-mcp",
description: "A connector that cannot be deleted in this scenario.",
labels: ["e2e"],
},
created_at: "2026-08-26T00:00:00Z",
updated_at: "2026-08-26T00:00:00Z",
};
win.__msw.worker.use(
win.__msw.http.get("*/market/api/v1/mcps", () =>
win.__msw!.HttpResponse.json({ data: [], pagination: { total: 0, page: 1, page_size: 20 } }),
),
win.__msw.http.get("*/market/api/v1/mcps/mine", () =>
// The connector 我的 view sends mode=mine + plugin_type=connector.
win.__msw.http.get("*/market/api/v1/plugins", ({ request }) => {
const params = new URL(request.url).searchParams;
const mine =
params.get("mode") === "mine" &&
params.get("plugin_type") === "connector";
const data = mine ? [item] : [];
return win.__msw!.HttpResponse.json({
data,
pagination: { total: data.length, page: 1, page_size: 20 },
});
}),
win.__msw.http.get("*/market/api/v1/plugin_categories", () =>
win.__msw!.HttpResponse.json({
data: [item],
pagination: { total: 1, page: 1, page_size: 20 },
data: [{ category_id: "dev-cat", name: "dev", sort_order: 0, plugin_count: 1 }],
}),
),
win.__msw.http.get("*/market/api/v1/mcp_categories", () =>
win.__msw!.HttpResponse.json({ data: [{ key: "dev", count: 1 }] }),
),
win.__msw.http.delete("*/market/api/v1/mcps/delete-failure-mcp", () =>
win.__msw.http.post("*/market/api/v1/plugins/delete", () =>
win.__msw!.HttpResponse.json(
{ error: { message: "删除失败" } },
{ status: 500 },
Expand Down
65 changes: 44 additions & 21 deletions apps/web/e2e-kit/msw-handlers/expert-market-empty.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { http, HttpResponse } from "msw";

const API_BASE = "/api/v1";
// Unified plugin surface (octo-marketplace). Two plugins load, then the spec
// searches for a keyword that matches neither so the page renders its empty
// state (filtering is client-side).
const API_BASE = "/market/api/v1";

function enabled(): boolean {
try {
Expand All @@ -10,51 +13,71 @@ function enabled(): boolean {
}
}

const experts = [
const plugins = [
{
expert_id: "release-lead",
short_name: "发布",
name: "发布负责人",
summary: "统筹发布检查、风险识别和上线决策。",
category: "研发工具",
plugin_id: "release-lead",
plugin_name: "发布负责人",
plugin_type: "expert" as const,
category_id: "dev-tools",
tags: ["发布", "质量"],
publisher: "Octo Platform",
visibility: "system",
owner_id: "space-e2e",
visibility: "system" as const,
creator_name: "[redacted-admin]",
created_by_type: "human",
created_by_type: "human" as const,
icon_url: "",
view_count: 24,
install_count: 8,
download_count: 0,
current_version: "1.0.0",
manifest_json: {
name: "release-lead",
description: "统筹发布检查、风险识别和上线决策。",
labels: ["发布", "质量"],
},
created_at: "2026-07-10T08:00:00Z",
updated_at: "2026-07-20T08:00:00Z",
},
{
expert_id: "meeting-coordinator",
short_name: "会议",
name: "会议协调专家",
summary: "整理会议议程、决策和后续待办。",
category: "办公提效",
plugin_id: "meeting-coordinator",
plugin_name: "会议协调专家",
plugin_type: "expert" as const,
category_id: "office",
tags: ["会议", "协作"],
publisher: "Octo Community",
visibility: "space",
owner_id: "space-e2e",
visibility: "space" as const,
creator_name: "Alice",
created_by_type: "human",
created_by_type: "human" as const,
icon_url: "",
view_count: 11,
install_count: 3,
download_count: 0,
current_version: "1.0.0",
manifest_json: {
name: "meeting-coordinator",
description: "整理会议议程、决策和后续待办。",
labels: ["会议", "协作"],
},
created_at: "2026-07-11T08:00:00Z",
updated_at: "2026-07-21T08:00:00Z",
},
];

export const expertMarketEmptyHandlers = [
http.get(`*${API_BASE}/experts`, () => {
http.get(`*${API_BASE}/plugins`, () => {
if (!enabled()) return undefined;
return HttpResponse.json({
data: experts,
data: plugins,
pagination: { total: 2, page: 1, page_size: 100 },
});
}),
http.get(`*${API_BASE}/expert_categories`, () => {
http.get(`*${API_BASE}/plugin_categories`, () => {
if (!enabled()) return undefined;
return HttpResponse.json({
data: [
{ expert_category_id: "dev-tools", name: "研发工具", count: 1 },
{ expert_category_id: "office", name: "办公提效", count: 1 },
{ category_id: "dev-tools", name: "研发工具", sort_order: 0, plugin_count: 1 },
{ category_id: "office", name: "办公提效", sort_order: 1, plugin_count: 1 },
],
});
}),
Expand Down
Loading
Loading