@chatcode/cco-market 1.45.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -0
  3. package/README.zh.md +143 -0
  4. package/UPDATE-API-V1.md +132 -0
  5. package/client/client.js +11534 -0
  6. package/cordis.patch.yml +5 -0
  7. package/lib/accelerate.js +184 -0
  8. package/lib/agents.js +36 -0
  9. package/lib/backup.js +572 -0
  10. package/lib/catalog-local-match.js +126 -0
  11. package/lib/catalog-npm.js +111 -0
  12. package/lib/changelog.js +242 -0
  13. package/lib/channels.js +64 -0
  14. package/lib/check.js +1067 -0
  15. package/lib/compatibility.js +198 -0
  16. package/lib/diagnostics.js +49 -0
  17. package/lib/discovery-compatibility.js +260 -0
  18. package/lib/dsh-cli.js +949 -0
  19. package/lib/dsh-install.js +108 -0
  20. package/lib/gist.js +352 -0
  21. package/lib/groups.js +97 -0
  22. package/lib/home-paths.js +42 -0
  23. package/lib/hot.js +519 -0
  24. package/lib/http.js +39 -0
  25. package/lib/index.js +97 -0
  26. package/lib/install.js +387 -0
  27. package/lib/log.js +197 -0
  28. package/lib/ndjson.js +154 -0
  29. package/lib/net.js +98 -0
  30. package/lib/order.js +284 -0
  31. package/lib/patch.js +521 -0
  32. package/lib/pnpm-compat.js +490 -0
  33. package/lib/presets.js +278 -0
  34. package/lib/profile.js +911 -0
  35. package/lib/region-probe.js +93 -0
  36. package/lib/regions.js +253 -0
  37. package/lib/registry.js +204 -0
  38. package/lib/restart.js +373 -0
  39. package/lib/routes.js +4372 -0
  40. package/lib/settings.js +109 -0
  41. package/lib/skill-market/api.js +119 -0
  42. package/lib/skill-market/config.js +29 -0
  43. package/lib/skill-market/installer.js +352 -0
  44. package/lib/skill-market/runtime.js +13 -0
  45. package/lib/skill-market/tui.js +119 -0
  46. package/lib/skill-market/types.js +1 -0
  47. package/lib/skill-market/web-routes.js +123 -0
  48. package/lib/snapshot.js +500 -0
  49. package/lib/source-migration.js +54 -0
  50. package/lib/sources.js +596 -0
  51. package/lib/store.js +91 -0
  52. package/lib/themes.js +102 -0
  53. package/lib/trial.js +116 -0
  54. package/lib/types/accelerate.d.ts +85 -0
  55. package/lib/types/agents.d.ts +22 -0
  56. package/lib/types/backup.d.ts +131 -0
  57. package/lib/types/catalog-local-match.d.ts +50 -0
  58. package/lib/types/catalog-npm.d.ts +47 -0
  59. package/lib/types/changelog.d.ts +102 -0
  60. package/lib/types/channels.d.ts +56 -0
  61. package/lib/types/check.d.ts +266 -0
  62. package/lib/types/compatibility.d.ts +64 -0
  63. package/lib/types/diagnostics.d.ts +29 -0
  64. package/lib/types/discovery-compatibility.d.ts +80 -0
  65. package/lib/types/dsh-cli.d.ts +324 -0
  66. package/lib/types/dsh-install.d.ts +46 -0
  67. package/lib/types/gist.d.ts +53 -0
  68. package/lib/types/groups.d.ts +34 -0
  69. package/lib/types/home-paths.d.ts +16 -0
  70. package/lib/types/hot.d.ts +221 -0
  71. package/lib/types/http.d.ts +12 -0
  72. package/lib/types/index.d.ts +14 -0
  73. package/lib/types/install.d.ts +172 -0
  74. package/lib/types/log.d.ts +43 -0
  75. package/lib/types/ndjson.d.ts +52 -0
  76. package/lib/types/net.d.ts +58 -0
  77. package/lib/types/order.d.ts +100 -0
  78. package/lib/types/patch.d.ts +129 -0
  79. package/lib/types/pnpm-compat.d.ts +93 -0
  80. package/lib/types/presets.d.ts +90 -0
  81. package/lib/types/profile.d.ts +253 -0
  82. package/lib/types/region-probe.d.ts +50 -0
  83. package/lib/types/regions.d.ts +122 -0
  84. package/lib/types/registry.d.ts +86 -0
  85. package/lib/types/restart.d.ts +194 -0
  86. package/lib/types/routes.d.ts +67 -0
  87. package/lib/types/settings.d.ts +78 -0
  88. package/lib/types/skill-market/api.d.ts +10 -0
  89. package/lib/types/skill-market/config.d.ts +3 -0
  90. package/lib/types/skill-market/installer.d.ts +23 -0
  91. package/lib/types/skill-market/runtime.d.ts +10 -0
  92. package/lib/types/skill-market/tui.d.ts +4 -0
  93. package/lib/types/skill-market/types.d.ts +71 -0
  94. package/lib/types/skill-market/web-routes.d.ts +19 -0
  95. package/lib/types/snapshot.d.ts +93 -0
  96. package/lib/types/source-migration.d.ts +11 -0
  97. package/lib/types/sources.d.ts +216 -0
  98. package/lib/types/store.d.ts +30 -0
  99. package/lib/types/themes.d.ts +40 -0
  100. package/lib/types/trial.d.ts +61 -0
  101. package/lib/types/update-api-v1.d.ts +66 -0
  102. package/lib/types/updates.d.ts +123 -0
  103. package/lib/types/verify.d.ts +139 -0
  104. package/lib/update-api-v1.js +215 -0
  105. package/lib/updates.js +361 -0
  106. package/lib/verify.js +453 -0
  107. package/package.json +121 -0
  108. package/src/accelerate.ts +213 -0
  109. package/src/agents.ts +43 -0
  110. package/src/backup.ts +583 -0
  111. package/src/catalog-local-match.ts +144 -0
  112. package/src/catalog-npm.ts +120 -0
  113. package/src/changelog.ts +282 -0
  114. package/src/channels.ts +70 -0
  115. package/src/check.ts +1239 -0
  116. package/src/client/CommentsModal.tsx +119 -0
  117. package/src/client/Diagnostics.tsx +907 -0
  118. package/src/client/ErrorBoundary.tsx +111 -0
  119. package/src/client/InstallToast.tsx +31 -0
  120. package/src/client/Market.module.css +830 -0
  121. package/src/client/MarketSection.tsx +5301 -0
  122. package/src/client/OperationsPanel.tsx +365 -0
  123. package/src/client/SettingsCard.tsx +621 -0
  124. package/src/client/SkillMarket.module.css +21 -0
  125. package/src/client/SkillMarketSection.tsx +162 -0
  126. package/src/client/comments.ts +54 -0
  127. package/src/client/globals.d.ts +13 -0
  128. package/src/client/index.ts +180 -0
  129. package/src/client/locales.ts +1112 -0
  130. package/src/client/market-data.ts +1321 -0
  131. package/src/client/operations.ts +201 -0
  132. package/src/client/preset-panel.tsx +263 -0
  133. package/src/client/primitives.d.ts +140 -0
  134. package/src/client/self-check.ts +147 -0
  135. package/src/client/snapshot-panel.tsx +244 -0
  136. package/src/compatibility.ts +237 -0
  137. package/src/diagnostics.ts +84 -0
  138. package/src/discovery-compatibility.ts +315 -0
  139. package/src/dsh-cli.ts +1126 -0
  140. package/src/dsh-install.ts +118 -0
  141. package/src/gist.ts +362 -0
  142. package/src/groups.ts +111 -0
  143. package/src/home-paths.ts +53 -0
  144. package/src/hot.ts +628 -0
  145. package/src/http.ts +41 -0
  146. package/src/index.ts +128 -0
  147. package/src/install.ts +420 -0
  148. package/src/log.ts +206 -0
  149. package/src/ndjson.ts +185 -0
  150. package/src/net.ts +106 -0
  151. package/src/order.ts +303 -0
  152. package/src/patch.ts +522 -0
  153. package/src/pnpm-compat.ts +527 -0
  154. package/src/presets.ts +344 -0
  155. package/src/profile.ts +940 -0
  156. package/src/region-probe.ts +97 -0
  157. package/src/regions.ts +310 -0
  158. package/src/registry.ts +250 -0
  159. package/src/restart.ts +396 -0
  160. package/src/routes.ts +4506 -0
  161. package/src/settings.ts +141 -0
  162. package/src/skill-market/api.ts +130 -0
  163. package/src/skill-market/config.ts +32 -0
  164. package/src/skill-market/installer.ts +337 -0
  165. package/src/skill-market/runtime.ts +14 -0
  166. package/src/skill-market/tui.ts +157 -0
  167. package/src/skill-market/types.ts +80 -0
  168. package/src/skill-market/web-routes.ts +132 -0
  169. package/src/snapshot.ts +532 -0
  170. package/src/source-migration.ts +61 -0
  171. package/src/sources.ts +565 -0
  172. package/src/store.ts +89 -0
  173. package/src/themes.ts +125 -0
  174. package/src/trial.ts +156 -0
  175. package/src/update-api-v1.ts +277 -0
  176. package/src/updates.ts +400 -0
  177. package/src/verify.ts +492 -0
@@ -0,0 +1,119 @@
1
+ /** Register `/skill-market` and its full-screen terminal scene together. */
2
+ export function installSkillMarketTui(ctx, runtime) {
3
+ ctx.inject(['commands', 'tuiScenes'], (scopedContext) => {
4
+ const scoped = scopedContext;
5
+ scoped.effect(() => {
6
+ const scene = createSkillMarketScene(runtime);
7
+ const disposeScene = scoped.tuiScenes.register({ id: 'skill-market', title: '技能市场', component: scene }, scoped);
8
+ const disposeCommand = scoped.commands.register({
9
+ name: 'skill-market',
10
+ description: '浏览并安装技能市场中的技能',
11
+ recordInput: false,
12
+ handler: async ({ rawInput }) => {
13
+ if (rawInput.trim().length > 0)
14
+ return { kind: 'error', text: '用法:/skill-market' };
15
+ if (!scoped.tuiScenes.open('skill-market'))
16
+ return { kind: 'error', text: '技能市场界面当前不可用' };
17
+ return { kind: 'success' };
18
+ },
19
+ });
20
+ return () => {
21
+ disposeCommand();
22
+ disposeScene();
23
+ };
24
+ }, 'dsh-market: skill market terminal scene');
25
+ });
26
+ }
27
+ function createSkillMarketScene(runtime) {
28
+ return function SkillMarketScene({ React: HostReact, ui, channel, close }) {
29
+ const { Box, Text } = ui;
30
+ const [items, setItems] = HostReact.useState([]);
31
+ const [query, setQuery] = HostReact.useState('');
32
+ const [selected, setSelected] = HostReact.useState(0);
33
+ const [page, setPage] = HostReact.useState(1);
34
+ const [total, setTotal] = HostReact.useState(0);
35
+ const [installedNames, setInstalledNames] = HostReact.useState(() => new Set());
36
+ const [loading, setLoading] = HostReact.useState(true);
37
+ const [error, setError] = HostReact.useState();
38
+ const [location, setLocation] = HostReact.useState();
39
+ const [installing, setInstalling] = HostReact.useState(false);
40
+ const [notice, setNotice] = HostReact.useState();
41
+ const loadPage = HostReact.useCallback(async (nextPage, search) => {
42
+ setLoading(true);
43
+ setError(undefined);
44
+ try {
45
+ const [result, installed] = await Promise.all([
46
+ runtime.api.list({ page: nextPage, pageSize: 10, search: search || undefined, sort: 'latest' }),
47
+ runtime.installer.installed(channel.cwd),
48
+ ]);
49
+ setItems(result.items);
50
+ setTotal(result.total);
51
+ setInstalledNames(new Set(installed.map(entry => entry.name)));
52
+ setSelected(0);
53
+ }
54
+ catch (cause) {
55
+ setError(cause instanceof Error ? cause.message : String(cause));
56
+ }
57
+ finally {
58
+ setLoading(false);
59
+ }
60
+ }, [channel.cwd]);
61
+ HostReact.useEffect(() => {
62
+ const timer = setTimeout(() => { void loadPage(page, query); }, 250);
63
+ return () => clearTimeout(timer);
64
+ }, [loadPage, page, query]);
65
+ ui.useInput((input, key) => {
66
+ if (installing)
67
+ return;
68
+ if (key.escape) {
69
+ if (location !== undefined)
70
+ setLocation(undefined);
71
+ else
72
+ close();
73
+ return;
74
+ }
75
+ const item = items[selected];
76
+ if (location !== undefined) {
77
+ if ((input === 'p' || input === 'u') && item !== undefined) {
78
+ const chosen = input === 'p' ? 'project' : 'user';
79
+ setInstalling(true);
80
+ setNotice(undefined);
81
+ void runtime.installer.install({ id: item.id, name: item.name, version: item.version, location: chosen, cwd: channel.cwd })
82
+ .then(result => {
83
+ setInstalledNames(current => new Set(current).add(item.name));
84
+ setNotice(`已安装到 ${result.installPath}`);
85
+ })
86
+ .catch(cause => setError(cause instanceof Error ? cause.message : String(cause)))
87
+ .finally(() => { setInstalling(false); setLocation(undefined); });
88
+ }
89
+ return;
90
+ }
91
+ if (key.upArrow)
92
+ setSelected(index => Math.max(0, index - 1));
93
+ else if (key.downArrow)
94
+ setSelected(index => Math.min(items.length - 1, index + 1));
95
+ else if (key.leftArrow && page > 1)
96
+ setPage(value => value - 1);
97
+ else if (key.rightArrow && page * 10 < total)
98
+ setPage(value => value + 1);
99
+ else if (key.return && item !== undefined)
100
+ setLocation('project');
101
+ else if (key.backspace) {
102
+ setPage(1);
103
+ setQuery(value => value.slice(0, -1));
104
+ }
105
+ else if (input.length === 1 && !/[\u0000-\u001f\u007f]/u.test(input)) {
106
+ setPage(1);
107
+ setQuery(value => value + input);
108
+ }
109
+ });
110
+ const h = HostReact.createElement;
111
+ const item = items[selected];
112
+ const pages = Math.max(1, Math.ceil(total / 10));
113
+ return h(Box, { flexDirection: 'column', paddingX: 1, paddingY: 1 }, h(Text, { bold: true, color: 'cyan' }, '技能市场'), h(Text, { dimColor: true }, `搜索:${query || '(直接输入关键词)'} 第 ${page}/${pages} 页,共 ${total} 个技能`), h(Text, { dimColor: true }, '↑↓ 选择 · ←→ 翻页 · Enter 安装 · Esc 返回'), h(Box, { marginTop: 1, flexDirection: 'column' }, loading ? h(Text, { color: 'yellow' }, '正在加载…') : null, error ? h(Text, { color: 'red' }, `错误:${error}`) : null, !loading && error === undefined && items.length === 0 ? h(Text, { dimColor: true }, '没有找到技能') : null, ...items.map((entry, index) => h(Text, {
114
+ key: String(entry.id),
115
+ bold: index === selected,
116
+ color: index === selected ? 'cyan' : undefined,
117
+ }, `${index === selected ? '▸' : ' '} ${entry.name} ${entry.version ? `v${entry.version}` : ''}${installedNames.has(entry.name) ? ' [已安装]' : ''}`))), item === undefined ? null : h(Box, { marginTop: 1, flexDirection: 'column' }, h(Text, { bold: true }, `${item.name}${item.author || item.creatorName ? ` · ${item.author || item.creatorName}` : ''}`), h(Text, { dimColor: true }, item.description || '暂无简介')), location === undefined ? null : h(Box, { marginTop: 1, flexDirection: 'column' }, h(Text, { color: 'yellow' }, `安装 ${item?.name ?? ''}`), h(Text, null, '按 P 安装到当前项目 .chatcode-cli/skills,按 U 安装到个人 CHATCODE_CLI_HOME/skills,Esc 取消')), installing ? h(Text, { color: 'yellow' }, '正在下载并验证技能包…') : null, notice ? h(Text, { color: 'green' }, notice) : null);
118
+ };
119
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,123 @@
1
+ import { readJsonBody, sameOrigin, sendJson } from '../http.js';
2
+ /** Mount local-session skill catalog and installation routes. */
3
+ export function mountSkillMarketRoutes(host, runtime) {
4
+ const routes = [
5
+ host.webServer.register({ kind: 'exact', path: '/dsh-market/skills', handler: async (request, response) => {
6
+ if (request.method !== 'GET')
7
+ return methodNotAllowed(response, 'GET');
8
+ try {
9
+ const url = requestUrl(request);
10
+ const query = {
11
+ search: optionalParam(url, 'search'),
12
+ category: optionalParam(url, 'category'),
13
+ tag: optionalParam(url, 'tag'),
14
+ sort: sortParam(url),
15
+ page: integerParam(url, 'page'),
16
+ pageSize: integerParam(url, 'pageSize'),
17
+ };
18
+ sendJson(response, 200, await runtime.api.list(query));
19
+ }
20
+ catch (error) {
21
+ sendRouteError(host, response, error);
22
+ }
23
+ } }),
24
+ host.webServer.register({ kind: 'exact', path: '/dsh-market/skills/installed', handler: async (request, response) => {
25
+ if (request.method !== 'GET')
26
+ return methodNotAllowed(response, 'GET');
27
+ try {
28
+ const cwd = sessionCwd(host, requestUrl(request).searchParams.get('sessionId'));
29
+ sendJson(response, 200, { items: await runtime.installer.installed(cwd) });
30
+ }
31
+ catch (error) {
32
+ sendRouteError(host, response, error);
33
+ }
34
+ } }),
35
+ host.webServer.register({ kind: 'exact', path: '/dsh-market/skills/install', handler: async (request, response) => {
36
+ if (request.method !== 'POST')
37
+ return methodNotAllowed(response, 'POST');
38
+ if (!sameOrigin(request)) {
39
+ sendJson(response, 403, { error: 'origin rejected' });
40
+ return;
41
+ }
42
+ try {
43
+ const body = installBody(await readJsonBody(request));
44
+ const cwd = sessionCwd(host, body.sessionId);
45
+ const result = await runtime.installer.install({ ...body, cwd });
46
+ sendJson(response, 200, result);
47
+ }
48
+ catch (error) {
49
+ sendRouteError(host, response, error);
50
+ }
51
+ } }),
52
+ ];
53
+ return () => {
54
+ for (const dispose of routes.reverse())
55
+ dispose();
56
+ };
57
+ }
58
+ function requestUrl(request) {
59
+ return new URL(request.url ?? '/', `http://${request.headers.host ?? 'localhost'}`);
60
+ }
61
+ function sessionCwd(host, sessionId) {
62
+ if (sessionId === null || sessionId.trim().length === 0)
63
+ throw new Error('请选择一个本地会话后再安装技能');
64
+ const session = host.sessions.get(sessionId);
65
+ if (session === undefined)
66
+ throw new Error('会话不存在或已结束');
67
+ const cwd = session.header.cwd;
68
+ if (cwd === undefined)
69
+ throw new Error('该会话没有本地工作目录');
70
+ return cwd;
71
+ }
72
+ function installBody(value) {
73
+ if (typeof value !== 'object' || value === null)
74
+ throw new Error('安装请求必须是 JSON 对象');
75
+ const body = value;
76
+ if (!Number.isSafeInteger(body.id) || body.id <= 0)
77
+ throw new Error('安装请求缺少有效的技能 id');
78
+ if (typeof body.name !== 'string' || body.name.length === 0)
79
+ throw new Error('安装请求缺少技能名称');
80
+ if (body.location !== 'project' && body.location !== 'user')
81
+ throw new Error('安装位置必须是 project 或 user');
82
+ if (typeof body.sessionId !== 'string' || body.sessionId.length === 0)
83
+ throw new Error('安装请求缺少 sessionId');
84
+ if (body.version !== undefined && typeof body.version !== 'string')
85
+ throw new Error('技能版本必须是字符串');
86
+ return {
87
+ id: body.id,
88
+ name: body.name,
89
+ location: body.location,
90
+ sessionId: body.sessionId,
91
+ version: body.version,
92
+ };
93
+ }
94
+ function optionalParam(url, name) {
95
+ const value = url.searchParams.get(name)?.trim();
96
+ return value ? value : undefined;
97
+ }
98
+ function integerParam(url, name) {
99
+ const value = url.searchParams.get(name);
100
+ if (value === null)
101
+ return undefined;
102
+ const number = Number(value);
103
+ if (!Number.isSafeInteger(number) || number <= 0)
104
+ throw new Error(`${name} 必须是正整数`);
105
+ return number;
106
+ }
107
+ function sortParam(url) {
108
+ const value = optionalParam(url, 'sort');
109
+ if (value === undefined)
110
+ return undefined;
111
+ if (value !== 'latest' && value !== 'downloads' && value !== 'name')
112
+ throw new Error('sort 参数无效');
113
+ return value;
114
+ }
115
+ function methodNotAllowed(response, allowed) {
116
+ response.setHeader('allow', allowed);
117
+ sendJson(response, 405, { error: 'method not allowed' });
118
+ }
119
+ function sendRouteError(host, response, error) {
120
+ const message = error instanceof Error ? error.message : String(error);
121
+ host.logger?.warn(`[ChatCode CLI Market] skill market request failed: ${message}`);
122
+ sendJson(response, 400, { error: message });
123
+ }