dsh-skill-hub 0.3.7 → 0.3.8
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.
- package/README.md +40 -50
- package/README.zh.md +38 -48
- package/lib/client.js +2209 -1820
- package/lib/client.js.map +1 -1
- package/lib/index.js +653 -419
- package/lib/types/client/SkillHubSettingsCard.d.ts +1 -0
- package/lib/types/client/api.d.ts +12 -6
- package/lib/types/client/helpers.d.ts +0 -1
- package/lib/types/client/locales/common.d.ts +52 -0
- package/lib/types/client/locales/detail.d.ts +16 -0
- package/lib/types/client/locales/market.d.ts +73 -0
- package/lib/types/client/locales/settings.d.ts +41 -0
- package/lib/types/client/locales/skills.d.ts +65 -0
- package/lib/types/client/locales/sources.d.ts +35 -0
- package/lib/types/client/locales.d.ts +124 -118
- package/lib/types/client/panel/MarketSourceRow.d.ts +12 -0
- package/lib/types/client/panel/MarketView.d.ts +1 -0
- package/lib/types/client/panel/RepoScanCard.d.ts +10 -0
- package/lib/types/client/panel/hooks/shared.d.ts +72 -0
- package/lib/types/client/panel/hooks/useCatalogFlow.d.ts +62 -0
- package/lib/types/client/panel/hooks/useGroupFlow.d.ts +41 -0
- package/lib/types/client/panel/hooks/useMarketFlow.d.ts +73 -0
- package/lib/types/client/panel/hooks/useMetaFlow.d.ts +18 -0
- package/lib/types/client/panel/hooks/useSourceFlow.d.ts +44 -0
- package/lib/types/client/panel/market-format.d.ts +6 -0
- package/lib/types/client/panel/useSkillHub.d.ts +45 -73
- package/lib/types/client/settings-card.d.ts +20 -0
- package/lib/types/client/settings-form.d.ts +6 -0
- package/lib/types/protocol/api.d.ts +38 -0
- package/lib/types/protocol/catalog.d.ts +155 -0
- package/lib/types/protocol/config.d.ts +113 -0
- package/lib/types/protocol/groups.d.ts +95 -0
- package/lib/types/protocol/market.d.ts +94 -0
- package/lib/types/protocol/repo.d.ts +107 -0
- package/lib/types/protocol/sources.d.ts +139 -0
- package/lib/types/protocol/stats.d.ts +46 -0
- package/lib/types/protocol.d.ts +13 -743
- package/lib/types/repo/github-client.d.ts +42 -0
- package/lib/types/repo.d.ts +6 -17
- package/lib/types/routes/catalog.d.ts +7 -0
- package/lib/types/routes/config.d.ts +7 -0
- package/lib/types/routes/groups.d.ts +7 -0
- package/lib/types/routes/helpers.d.ts +145 -0
- package/lib/types/routes/market.d.ts +8 -0
- package/lib/types/routes/repo-import.d.ts +7 -0
- package/lib/types/routes/route-state.d.ts +55 -0
- package/lib/types/routes/sources.d.ts +8 -0
- package/lib/types/routes.d.ts +5 -34
- package/lib/types/stats.d.ts +3 -2
- package/lib/types/store/errors.d.ts +10 -0
- package/lib/types/store/migrate.d.ts +44 -0
- package/lib/types/store/paths.d.ts +32 -0
- package/lib/types/store/store.d.ts +133 -0
- package/lib/types/store.d.ts +7 -146
- package/package.json +28 -27
- package/src/client/SkillHubSettingsCard.tsx +17 -3
- package/src/client/api.ts +101 -195
- package/src/client/helpers.ts +10 -1
- package/src/client/locales/common.ts +102 -0
- package/src/client/locales/detail.ts +30 -0
- package/src/client/locales/market.ts +144 -0
- package/src/client/locales/settings.ts +80 -0
- package/src/client/locales/skills.ts +128 -0
- package/src/client/locales/sources.ts +68 -0
- package/src/client/locales.ts +26 -512
- package/src/client/panel/MarketSourceRow.tsx +108 -0
- package/src/client/panel/MarketView.tsx +12 -253
- package/src/client/panel/RepoScanCard.tsx +171 -0
- package/src/client/panel/hooks/shared.ts +81 -0
- package/src/client/panel/hooks/useCatalogFlow.ts +211 -0
- package/src/client/panel/hooks/useGroupFlow.ts +240 -0
- package/src/client/panel/hooks/useMarketFlow.ts +421 -0
- package/src/client/panel/hooks/useMetaFlow.ts +58 -0
- package/src/client/panel/hooks/useSourceFlow.ts +193 -0
- package/src/client/panel/market-format.ts +22 -0
- package/src/client/panel/useSkillHub.ts +141 -1032
- package/src/client/settings-card.tsx +51 -0
- package/src/client/settings-form.ts +18 -0
- package/src/index.ts +14 -4
- package/src/protocol/api.ts +38 -0
- package/src/protocol/catalog.ts +168 -0
- package/src/protocol/config.ts +145 -0
- package/src/protocol/groups.ts +106 -0
- package/src/protocol/market.ts +100 -0
- package/src/protocol/repo.ts +104 -0
- package/src/protocol/sources.ts +146 -0
- package/src/protocol/stats.ts +42 -0
- package/src/protocol.ts +83 -809
- package/src/repo/github-client.ts +131 -0
- package/src/repo.test.ts +24 -1
- package/src/repo.ts +50 -170
- package/src/routes/catalog.ts +309 -0
- package/src/routes/config.ts +119 -0
- package/src/routes/groups.ts +133 -0
- package/src/routes/helpers.ts +489 -0
- package/src/routes/market.ts +255 -0
- package/src/routes/repo-import.ts +267 -0
- package/src/routes/route-state.ts +89 -0
- package/src/routes/sources.ts +318 -0
- package/src/routes.test.ts +20 -0
- package/src/routes.ts +21 -1699
- package/src/stats.test.ts +58 -3
- package/src/stats.ts +45 -16
- package/src/store/errors.ts +14 -0
- package/src/store/migrate.ts +272 -0
- package/src/store/paths.ts +43 -0
- package/src/store/store.ts +532 -0
- package/src/store.test.ts +18 -0
- package/src/store.ts +7 -737
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub API 统一请求层:token 管理、鉴权头、ETag 缓存、错误映射、
|
|
3
|
+
* 带缓存的 JSON GET。从 repo.ts 抽出,原 6 处重复的 try/catch +
|
|
4
|
+
* headers + json 解析收敛到 fetchJson / fetchJsonCached 两个入口。
|
|
5
|
+
*/
|
|
6
|
+
/** Fetch failure carrying a useful HTTP status for the route layer. */
|
|
7
|
+
export declare class RepoFetchError extends Error {
|
|
8
|
+
readonly status: number;
|
|
9
|
+
constructor(message: string, status?: number);
|
|
10
|
+
}
|
|
11
|
+
/** Override the GitHub token at runtime ('' falls back to the env var). */
|
|
12
|
+
export declare function setGithubToken(token: string | undefined): void;
|
|
13
|
+
/** Authorization header for api.github.com / raw.githubusercontent.com calls. */
|
|
14
|
+
export declare function githubAuthHeaders(): Record<string, string>;
|
|
15
|
+
/** JSON API 默认请求头(含鉴权)。 */
|
|
16
|
+
export declare function apiHeaders(): Record<string, string>;
|
|
17
|
+
export declare function clearEtagCache(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Build a RepoFetchError; when the response shows an exhausted rate limit,
|
|
20
|
+
* report the reset time instead of a bare HTTP status.
|
|
21
|
+
*/
|
|
22
|
+
export declare function fetchError(context: string, response: Response, fallbackStatus?: number): RepoFetchError;
|
|
23
|
+
/** 是否限流/中断类错误(调用方可直接透出,不再包装)。 */
|
|
24
|
+
export declare function isAbortError(error: unknown): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* 不带缓存的统一 GET+JSON:网络异常、非 2xx、坏 JSON 全部映射为
|
|
27
|
+
* RepoFetchError,调用方无需重复写 try/catch。
|
|
28
|
+
*/
|
|
29
|
+
export declare function fetchJson(url: string, fetchImpl: typeof fetch, context: string, headers?: Record<string, string>): Promise<{
|
|
30
|
+
json: unknown;
|
|
31
|
+
response: Response;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Load repo metadata and the recursive git tree at the given ref. The meta
|
|
35
|
+
* request always resolves the default branch (used when ref is absent); the
|
|
36
|
+
* tree is fetched at `ref ?? default_branch` so an explicit branch/tag scans
|
|
37
|
+
* the exact same content a later download would fetch.
|
|
38
|
+
*/
|
|
39
|
+
export declare function fetchJsonCached(url: string, fetchImpl: typeof fetch, context: string): Promise<{
|
|
40
|
+
json: unknown;
|
|
41
|
+
response: Response;
|
|
42
|
+
}>;
|
package/lib/types/repo.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* `workflows/**`) is treated as a skill root. No hard-coded allowlist.
|
|
8
8
|
*/
|
|
9
9
|
import type { RepoRoot, RepoSkillEntry } from './protocol.ts';
|
|
10
|
+
export { RepoFetchError, clearEtagCache, fetchError, fetchJson, fetchJsonCached, githubAuthHeaders, isAbortError, setGithubToken } from './repo/github-client.ts';
|
|
10
11
|
/** Preferred display order for known roots; unknown roots sort alphabetically after these. */
|
|
11
12
|
export declare const REPO_ROOTS: readonly RepoRoot[];
|
|
12
13
|
/** Parsed GitHub repository reference. */
|
|
@@ -27,21 +28,6 @@ export interface RepoFile {
|
|
|
27
28
|
path: string;
|
|
28
29
|
size: number;
|
|
29
30
|
}
|
|
30
|
-
/** Fetch failure carrying a useful HTTP status for the route layer. */
|
|
31
|
-
export declare class RepoFetchError extends Error {
|
|
32
|
-
readonly status: number;
|
|
33
|
-
constructor(message: string, status?: number);
|
|
34
|
-
}
|
|
35
|
-
/** Override the GitHub token at runtime ('' falls back to the env var). */
|
|
36
|
-
export declare function setGithubToken(token: string | undefined): void;
|
|
37
|
-
/** Authorization header for api.github.com / raw.githubusercontent.com calls. */
|
|
38
|
-
export declare function githubAuthHeaders(): Record<string, string>;
|
|
39
|
-
export declare function clearEtagCache(): void;
|
|
40
|
-
/**
|
|
41
|
-
* Build a RepoFetchError; when the response shows an exhausted rate limit,
|
|
42
|
-
* report the reset time instead of a bare HTTP status.
|
|
43
|
-
*/
|
|
44
|
-
export declare function fetchError(context: string, response: Response, fallbackStatus?: number): RepoFetchError;
|
|
45
31
|
/** `owner/repo` slug for a parsed reference. */
|
|
46
32
|
export declare function repoSlug(ref: RepoRef): string;
|
|
47
33
|
/**
|
|
@@ -62,8 +48,6 @@ export declare function originForRoot(repo: string, rootsPresent: ReadonlySet<st
|
|
|
62
48
|
export declare function discoverRepoEntries(tree: readonly RepoTreeItem[], repo: string, existingNames?: ReadonlySet<string>): RepoSkillEntry[];
|
|
63
49
|
/** Run an async worker over items with a bounded concurrency. */
|
|
64
50
|
export declare function mapConcurrent<T, R>(items: readonly T[], limit: number, worker: (item: T, index: number) => Promise<R>): Promise<R[]>;
|
|
65
|
-
/** Whether an error is an abort (AbortError / signal). */
|
|
66
|
-
export declare function isAbortError(error: unknown): boolean;
|
|
67
51
|
/**
|
|
68
52
|
* Download one GitHub file as a buffer. Tries raw.githubusercontent.com
|
|
69
53
|
* first (no API quota); on any failure falls back to the api.github.com
|
|
@@ -90,6 +74,11 @@ export declare function cleanupLeftoverImportDirs(targetRoot: string): Promise<n
|
|
|
90
74
|
* repo has no releases (GitHub's /releases/latest skips prereleases/drafts).
|
|
91
75
|
*/
|
|
92
76
|
export declare function getLatestReleaseTag(repo: string, fetchImpl?: typeof fetch): Promise<string | undefined>;
|
|
77
|
+
/** Stars + release-asset downloads of a repo (two GitHub API requests). */
|
|
78
|
+
export declare function getRepoStats(repo: string, fetchImpl?: typeof fetch): Promise<{
|
|
79
|
+
stars: number;
|
|
80
|
+
downloads: number;
|
|
81
|
+
}>;
|
|
93
82
|
/** Release tags of a repo, newest first (skips drafts, keeps prereleases). */
|
|
94
83
|
export declare function listRepoReleases(repo: string, fetchImpl?: typeof fetch): Promise<string[]>;
|
|
95
84
|
/** Branch names of a repo, default branch first (one GitHub API request). */
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 目录域路由:catalog / skill 详情 / skill 删除 / toggle / toggle-batch /
|
|
3
|
+
* create / stats。从 routes.ts 原样搬出,handler 逻辑不变。
|
|
4
|
+
*/
|
|
5
|
+
import { type RouteSpec, type SkillHubRouteDeps } from './helpers.ts';
|
|
6
|
+
/** 目录域全部路由 spec(由 routes.ts 经 createRoute 包上统一围栏)。 */
|
|
7
|
+
export declare function catalogRoutes(deps: SkillHubRouteDeps): RouteSpec[];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置域路由:config(总开关开着也可用,设置卡自救入口)/ update 自身更新
|
|
3
|
+
* 检查 / diagnosticFix。从 routes.ts 原样搬出,handler 逻辑不变。
|
|
4
|
+
*/
|
|
5
|
+
import { type RouteSpec, type SkillHubRouteDeps } from './helpers.ts';
|
|
6
|
+
/** 配置域全部路由 spec(由 routes.ts 经 createRoute 包上统一围栏)。 */
|
|
7
|
+
export declare function configRoutes(deps: SkillHubRouteDeps): RouteSpec[];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 分组域路由:groups / tag 新建重命名 / tag 删除 / tag 成员 / tag 重排 /
|
|
3
|
+
* collection 重排 / source-group 重排。从 routes.ts 原样搬出,handler 逻辑不变。
|
|
4
|
+
*/
|
|
5
|
+
import { type RouteSpec, type SkillHubRouteDeps } from './helpers.ts';
|
|
6
|
+
/** 分组域全部路由 spec(由 routes.ts 经 createRoute 包上统一围栏)。 */
|
|
7
|
+
export declare function groupRoutes(deps: SkillHubRouteDeps): RouteSpec[];
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* routes 共享层:请求围栏(回环信任/方法/总开关/JSON 体)、统一错误映射、
|
|
3
|
+
* 可写技能解析、目录/分组数据装配。handler 只写业务,从这里 import。
|
|
4
|
+
* 从 routes.ts 原样搬出,行为不变。
|
|
5
|
+
*/
|
|
6
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
7
|
+
import type { WebRoute } from '@deepseek-ai/dsh-host-webserver';
|
|
8
|
+
import type { SkillDefinition, SkillSummary } from '@deepseek-ai/dsh-skill';
|
|
9
|
+
import { type CatalogResponse, type GroupsResponse, type HubConfig, type SkillDetail, type WritableRoot } from '../protocol.ts';
|
|
10
|
+
import { type SkillHubStore } from '../store.ts';
|
|
11
|
+
/** Cap on JSON request bodies (toggle/create payloads are tiny). */
|
|
12
|
+
export declare const MAX_JSON_BODY_BYTES: number;
|
|
13
|
+
/** Loopback literal check plus browser same-origin markers. */
|
|
14
|
+
export declare function isLoopbackRequest(request: IncomingMessage): boolean;
|
|
15
|
+
/** One JSON response. */
|
|
16
|
+
export declare function writeJson(res: ServerResponse, status: number, body: unknown): void;
|
|
17
|
+
/** One JSON error response. */
|
|
18
|
+
export declare function writeError(res: ServerResponse, status: number, error: unknown): void;
|
|
19
|
+
/** HTTP status per store business-rule kind (user error, not server fault). */
|
|
20
|
+
export declare const STORE_ERROR_STATUS: {
|
|
21
|
+
readonly validation: 400;
|
|
22
|
+
readonly 'not-found': 404;
|
|
23
|
+
readonly conflict: 409;
|
|
24
|
+
};
|
|
25
|
+
/** Map known error types onto HTTP statuses: repo fetch + store business rules. */
|
|
26
|
+
export declare function writeRouteError(res: ServerResponse, error: unknown): void;
|
|
27
|
+
/** Read a JSON request body (undefined when too large or unparseable). */
|
|
28
|
+
export declare function readJsonBody(req: IncomingMessage): Promise<Record<string, unknown> | undefined>;
|
|
29
|
+
/** URL query helper (first value, decoded). */
|
|
30
|
+
export declare function queryParam(url: URL, name: string): string | undefined;
|
|
31
|
+
/** Async existence check (import/sync use it on the user root). */
|
|
32
|
+
export declare function pathExists(path: string): Promise<boolean>;
|
|
33
|
+
/** Sources the hub may toggle (the user-level filesystem roots). */
|
|
34
|
+
export declare function isWritableSource(source: string): source is WritableRoot;
|
|
35
|
+
/** Lookup options the hub forwards to the registry (cwd selects project roots). */
|
|
36
|
+
export interface SkillLookupLike {
|
|
37
|
+
cwd?: string;
|
|
38
|
+
}
|
|
39
|
+
/** Route family dependencies (narrow structural view of ctx.skills for tests). */
|
|
40
|
+
export interface SkillHubRouteDeps {
|
|
41
|
+
skills: {
|
|
42
|
+
snapshot(options?: SkillLookupLike): Promise<{
|
|
43
|
+
skills: SkillSummary[];
|
|
44
|
+
complete: boolean;
|
|
45
|
+
}>;
|
|
46
|
+
get(name: string, options?: SkillLookupLike): Promise<SkillDefinition | undefined>;
|
|
47
|
+
};
|
|
48
|
+
store: SkillHubStore;
|
|
49
|
+
/** DSH home override (tests isolate the writable roots; defaults to ~/.dsh). */
|
|
50
|
+
home?: string;
|
|
51
|
+
/** Invalidate the registry catalog cache after hub-driven mutations. */
|
|
52
|
+
invalidate?: () => void;
|
|
53
|
+
/** Optional invocation-count reader; absent means the stats route reports unavailable. */
|
|
54
|
+
stats?: import('../stats.ts').SkillStatsReader;
|
|
55
|
+
/** Resolves the current plugin config; business routes honour the master switch. */
|
|
56
|
+
config?: () => HubConfig;
|
|
57
|
+
/** Resolves the raw saved config layer (fields the user explicitly overrode). */
|
|
58
|
+
saved?: () => Partial<HubConfig>;
|
|
59
|
+
/** Persist a config patch and re-sync plugin surfaces; resolves with the fresh config. */
|
|
60
|
+
updateConfig?: (patch: Partial<HubConfig>) => Promise<HubConfig>;
|
|
61
|
+
}
|
|
62
|
+
/** The resolved hub config a route sees (the shared resolver fills defaults). */
|
|
63
|
+
export declare function configOf(deps: SkillHubRouteDeps): HubConfig;
|
|
64
|
+
/** The raw saved config layer a route reports (empty when the owner omits it). */
|
|
65
|
+
export declare function savedOf(deps: SkillHubRouteDeps): Partial<HubConfig>;
|
|
66
|
+
/** Refuse business routes while the master switch is off (the config route stays up). */
|
|
67
|
+
export declare function disabledGate(deps: SkillHubRouteDeps, res: ServerResponse): boolean;
|
|
68
|
+
/** Resolve the home used for writable-root operations. */
|
|
69
|
+
export declare function homeOf(deps: SkillHubRouteDeps): string;
|
|
70
|
+
/** A skill resolved as a hub-writable user-level file. */
|
|
71
|
+
export interface WritableSkill {
|
|
72
|
+
skill: SkillDefinition;
|
|
73
|
+
/** Absolute discovery-file path (guaranteed present by the resolver). */
|
|
74
|
+
path: string;
|
|
75
|
+
/** The writable root containing the file. */
|
|
76
|
+
root: WritableRoot;
|
|
77
|
+
}
|
|
78
|
+
/** A refusal with the exact HTTP error the caller should answer with. */
|
|
79
|
+
export interface WritableSkillRefusal {
|
|
80
|
+
status: number;
|
|
81
|
+
error: string;
|
|
82
|
+
}
|
|
83
|
+
export type WritableSkillResult = {
|
|
84
|
+
ok: true;
|
|
85
|
+
} & WritableSkill | {
|
|
86
|
+
ok: false;
|
|
87
|
+
} & WritableSkillRefusal;
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a skill by name as something the hub may write, or the exact
|
|
90
|
+
* refusal the caller should answer with. Owns the guard sequence shared by
|
|
91
|
+
* toggle, toggle-batch, and skill/delete: registry lookup, writable source,
|
|
92
|
+
* writable file, and path containment inside a user root.
|
|
93
|
+
*/
|
|
94
|
+
export declare function resolveWritableSkill(deps: SkillHubRouteDeps, name: string, cwd?: string): Promise<WritableSkillResult>;
|
|
95
|
+
/** 系统集合组 + 用户 tag + origin 映射(groups 路由的数据源)。 */
|
|
96
|
+
export declare function buildGroups(deps: SkillHubRouteDeps): Promise<GroupsResponse>;
|
|
97
|
+
/** 目录中存在的技能名集合(tag 成员校验用):启用目录 ∪ 已禁用名单,避免成员因禁用而丢失。 */
|
|
98
|
+
export declare function knownSkillNames(deps: SkillHubRouteDeps): Promise<Set<string>>;
|
|
99
|
+
/** 已知工作区条目(dsh 的 workspace.json 表)。 */
|
|
100
|
+
export interface WorkspaceEntry {
|
|
101
|
+
path: string;
|
|
102
|
+
title: string;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 读取 dsh 的已知工作区清单(~/.dsh/storages/workspace.json 的
|
|
106
|
+
* tables.workspaces 表)。面板默认视图据此合并所有工作区的项目技能;
|
|
107
|
+
* 文件缺失/损坏时返回空清单(回退为仅用户级视图)。
|
|
108
|
+
*/
|
|
109
|
+
export declare function workspaceEntries(home: string): Promise<WorkspaceEntry[]>;
|
|
110
|
+
/**
|
|
111
|
+
* Build the full catalog response (shared by catalog/toggle/create handlers).
|
|
112
|
+
* 显式 cwd 只看该工作区;否则合并所有已知工作区(workspace.json)的项目技能
|
|
113
|
+
* + 用户级技能,同名技能先到先得;没有任何工作区时回退为仅用户级视图。
|
|
114
|
+
*/
|
|
115
|
+
export declare function buildCatalog(deps: SkillHubRouteDeps, cwd?: string): Promise<CatalogResponse>;
|
|
116
|
+
/** Map a loaded definition onto the wire shape. */
|
|
117
|
+
export declare function toDetail(skill: SkillDefinition): SkillDetail;
|
|
118
|
+
/** What a route handler receives: fences already passed, URL/body prepared. */
|
|
119
|
+
export interface RouteContext {
|
|
120
|
+
req: IncomingMessage;
|
|
121
|
+
res: ServerResponse;
|
|
122
|
+
/** Request URL parsed against a localhost base (query reading for GET). */
|
|
123
|
+
url: URL;
|
|
124
|
+
/** Parsed JSON body; empty object for requests without one. */
|
|
125
|
+
body: Record<string, unknown>;
|
|
126
|
+
}
|
|
127
|
+
export type RouteHandler = (context: RouteContext) => Promise<void>;
|
|
128
|
+
/** One declarative route: path + accepted methods + the business handler. */
|
|
129
|
+
export interface RouteSpec {
|
|
130
|
+
path: string;
|
|
131
|
+
/** Accepted HTTP methods; anything else answers 405. */
|
|
132
|
+
methods: readonly ('GET' | 'POST')[];
|
|
133
|
+
/** POST requests must carry a JSON body (400 when missing/unparseable). */
|
|
134
|
+
jsonBody?: boolean;
|
|
135
|
+
/** Skip the master-switch gate (the config route stays up while disabled). */
|
|
136
|
+
skipGate?: boolean;
|
|
137
|
+
handler: RouteHandler;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Wrap one handler in the shared request fences. Order matches the
|
|
141
|
+
* original per-handler prologue: loopback trust → HTTP method → master
|
|
142
|
+
* switch → JSON body. The outer catch maps errors onto the JSON error
|
|
143
|
+
* body (preserving RepoFetchError status codes).
|
|
144
|
+
*/
|
|
145
|
+
export declare function createRoute(deps: SkillHubRouteDeps, spec: RouteSpec): WebRoute;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 市场域路由:market 列表 / source 增删 / ref 锁定 / versions 选择器 /
|
|
3
|
+
* check 更新检查 / stats 星标统计 / sync 对齐版本。从 routes.ts 原样搬出,
|
|
4
|
+
* handler 逻辑不变。
|
|
5
|
+
*/
|
|
6
|
+
import { type RouteSpec, type SkillHubRouteDeps } from './helpers.ts';
|
|
7
|
+
/** 市场域全部路由 spec(由 routes.ts 经 createRoute 包上统一围栏)。 */
|
|
8
|
+
export declare function marketRoutes(deps: SkillHubRouteDeps): RouteSpec[];
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 仓库导入域路由:repo 发现 / repoImport 异步导入任务 / progress 轮询 /
|
|
3
|
+
* cancel 取消。从 routes.ts 原样搬出,handler 逻辑不变。
|
|
4
|
+
*/
|
|
5
|
+
import { type RouteSpec, type SkillHubRouteDeps } from './helpers.ts';
|
|
6
|
+
/** 仓库导入域全部路由 spec(由 routes.ts 经 createRoute 包上统一围栏)。 */
|
|
7
|
+
export declare function repoImportRoutes(deps: SkillHubRouteDeps): RouteSpec[];
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* routes 共享状态:各类节流表、市场统计内存缓存、异步导入任务表。
|
|
3
|
+
* 从 routes.ts 原样搬出,行为不变;各域 handler 从这里 import。
|
|
4
|
+
*/
|
|
5
|
+
import type { SkillHubRouteDeps } from './helpers.ts';
|
|
6
|
+
/** Minimum interval between network update checks per source (GitHub rate limits). */
|
|
7
|
+
export declare const MIN_CHECK_INTERVAL_MS: number;
|
|
8
|
+
/** Last successful check time per repo (in-memory throttle for GitHub rate limits). */
|
|
9
|
+
export declare const lastSourceCheck: Map<string, number>;
|
|
10
|
+
/** Last successful market update check per repo (same throttle). */
|
|
11
|
+
export declare const lastMarketCheck: Map<string, number>;
|
|
12
|
+
/** Market stats (stars/downloads) throttle: hourly, plus a fallback cache. */
|
|
13
|
+
export declare const lastMarketStats: Map<string, number>;
|
|
14
|
+
export declare const marketStatsCache: Map<string, {
|
|
15
|
+
stars: number;
|
|
16
|
+
downloads: number;
|
|
17
|
+
}>;
|
|
18
|
+
export declare const MARKET_STATS_TTL_MS: number;
|
|
19
|
+
export declare function seedMarketStats(deps: SkillHubRouteDeps): Promise<void>;
|
|
20
|
+
/** Async import job (B方案:jobId + 轮询,选项2后台继续) */
|
|
21
|
+
export interface ImportJob {
|
|
22
|
+
jobId: string;
|
|
23
|
+
repo: string;
|
|
24
|
+
ref: string;
|
|
25
|
+
total: number;
|
|
26
|
+
done: number;
|
|
27
|
+
current?: string;
|
|
28
|
+
currentFile?: string;
|
|
29
|
+
totalBytes: number;
|
|
30
|
+
downloadedBytes: number;
|
|
31
|
+
startTime: number;
|
|
32
|
+
imported: Array<{
|
|
33
|
+
name: string;
|
|
34
|
+
origin: string;
|
|
35
|
+
path: string;
|
|
36
|
+
}>;
|
|
37
|
+
skipped: Array<{
|
|
38
|
+
name: string;
|
|
39
|
+
reason: 'exists';
|
|
40
|
+
}>;
|
|
41
|
+
failed: Array<{
|
|
42
|
+
name: string;
|
|
43
|
+
error: string;
|
|
44
|
+
}>;
|
|
45
|
+
status: 'running' | 'done' | 'cancelled' | 'error';
|
|
46
|
+
error?: string;
|
|
47
|
+
controller: AbortController;
|
|
48
|
+
createdAt: number;
|
|
49
|
+
}
|
|
50
|
+
export declare const importJobs: Map<string, ImportJob>;
|
|
51
|
+
export declare const IMPORT_JOB_TTL_MS: number;
|
|
52
|
+
export declare const IMPORT_JOB_MAX = 100;
|
|
53
|
+
export declare function gcImportJobs(): void;
|
|
54
|
+
/** Replace one skill directory with a fresh download; restores the old dir on failure. */
|
|
55
|
+
export declare function replaceSkillDir(targetDir: string, download: () => Promise<void>): Promise<void>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 来源跟踪域路由:sources 列表 / check 上游更新 / sync 同步 / delete 跟进
|
|
3
|
+
* 删除 / restore 恢复 / trash/clear 清空回收站。从 routes.ts 原样搬出,
|
|
4
|
+
* handler 逻辑不变。
|
|
5
|
+
*/
|
|
6
|
+
import { type RouteSpec, type SkillHubRouteDeps } from './helpers.ts';
|
|
7
|
+
/** 来源跟踪域全部路由 spec(由 routes.ts 经 createRoute 包上统一围栏)。 */
|
|
8
|
+
export declare function sourceRoutes(deps: SkillHubRouteDeps): RouteSpec[];
|
package/lib/types/routes.d.ts
CHANGED
|
@@ -7,42 +7,13 @@
|
|
|
7
7
|
* loopback-only trust fence — these endpoints rename files under the user's
|
|
8
8
|
* skill roots, so LAN-exposed dsh web deployments must not serve them.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* 按域拆分后的聚合入口:各域 handler 在 ./routes/<domain>.ts,共享围栏在
|
|
11
|
+
* ./routes/helpers.ts,节流/任务状态在 ./routes/route-state.ts。
|
|
12
|
+
* 老 `from './routes.ts'`(makeRoutes + SkillHubRouteDeps)保持可用。
|
|
13
13
|
*/
|
|
14
14
|
import type { WebRoute } from '@deepseek-ai/dsh-host-webserver';
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
import { type SkillHubStore } from './store.ts';
|
|
18
|
-
import type { SkillStatsReader } from './stats.ts';
|
|
19
|
-
/** Lookup options the hub forwards to the registry (cwd selects project roots). */
|
|
20
|
-
export interface SkillLookupLike {
|
|
21
|
-
cwd?: string;
|
|
22
|
-
}
|
|
23
|
-
/** Route family dependencies (narrow structural view of ctx.skills for tests). */
|
|
24
|
-
export interface SkillHubRouteDeps {
|
|
25
|
-
skills: {
|
|
26
|
-
snapshot(options?: SkillLookupLike): Promise<{
|
|
27
|
-
skills: SkillSummary[];
|
|
28
|
-
complete: boolean;
|
|
29
|
-
}>;
|
|
30
|
-
get(name: string, options?: SkillLookupLike): Promise<SkillDefinition | undefined>;
|
|
31
|
-
};
|
|
32
|
-
store: SkillHubStore;
|
|
33
|
-
/** DSH home override (tests isolate the writable roots; defaults to ~/.dsh). */
|
|
34
|
-
home?: string;
|
|
35
|
-
/** Invalidate the registry catalog cache after hub-driven mutations. */
|
|
36
|
-
invalidate?: () => void;
|
|
37
|
-
/** Optional invocation-count reader; absent means the stats route reports unavailable. */
|
|
38
|
-
stats?: SkillStatsReader;
|
|
39
|
-
/** Resolves the current plugin config; business routes honour the master switch. */
|
|
40
|
-
config?: () => HubConfig;
|
|
41
|
-
/** Resolves the raw saved config layer (fields the user explicitly overrode). */
|
|
42
|
-
saved?: () => Partial<HubConfig>;
|
|
43
|
-
/** Persist a config patch and re-sync plugin surfaces; resolves with the fresh config. */
|
|
44
|
-
updateConfig?: (patch: Partial<HubConfig>) => Promise<HubConfig>;
|
|
45
|
-
}
|
|
15
|
+
import { type SkillHubRouteDeps } from './routes/helpers.ts';
|
|
16
|
+
export type { SkillHubRouteDeps, SkillLookupLike } from './routes/helpers.ts';
|
|
46
17
|
/**
|
|
47
18
|
* Build every /api/skill-hub route.
|
|
48
19
|
* @param deps - skill registry view + sidecar store.
|
package/lib/types/stats.d.ts
CHANGED
|
@@ -77,8 +77,9 @@ export interface SkillStatsReaderOptions {
|
|
|
77
77
|
ttlMs?: number | (() => number);
|
|
78
78
|
/** Rolling window in days; a getter reads the live config each scan. 0 = all history. */
|
|
79
79
|
windowDays?: () => number;
|
|
80
|
-
/** Called after
|
|
81
|
-
*
|
|
80
|
+
/** Called after every completed scan (full or incremental) so the host can
|
|
81
|
+
* persist the checkpoint including the fresh totals. Cadence follows the
|
|
82
|
+
* scan TTL (minutes, not days) — the payload is tiny and writes are atomic. */
|
|
82
83
|
onCheckpoint?: (checkpoint: SkillStatsCheckpoint) => void;
|
|
83
84
|
}
|
|
84
85
|
/**
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Business-rule failure the routes layer can map onto a 4xx status instead
|
|
3
|
+
* of a blanket 500: user input is invalid (validation → 400), the target
|
|
4
|
+
* does not exist (not-found → 404), or the operation conflicts with an
|
|
5
|
+
* invariant (conflict → 409).
|
|
6
|
+
*/
|
|
7
|
+
export declare class StoreError extends Error {
|
|
8
|
+
readonly kind: 'validation' | 'not-found' | 'conflict';
|
|
9
|
+
constructor(kind: StoreError['kind'], message: string);
|
|
10
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { DisabledSkill, HubConfig, MarketSourceRecord, MarketStatsSnapshot, SkillStatsCheckpoint, SkillTag, SourceRecord, TrashEntry } from '../protocol.ts';
|
|
2
|
+
/** Normalized raw sidecar document after schema migration (fields still unvalidated). */
|
|
3
|
+
export interface MigratedStore {
|
|
4
|
+
version: number;
|
|
5
|
+
disabled: unknown;
|
|
6
|
+
config?: unknown;
|
|
7
|
+
tags?: unknown;
|
|
8
|
+
sources?: unknown;
|
|
9
|
+
marketSources?: unknown;
|
|
10
|
+
trash?: unknown;
|
|
11
|
+
skillStats?: unknown;
|
|
12
|
+
marketStats?: unknown;
|
|
13
|
+
collectionOrder?: unknown;
|
|
14
|
+
sourceGroupOrder?: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Normalize an arbitrary parsed sidecar document to the current schema.
|
|
18
|
+
* v1 to v2: scenes are dropped (the one-click role merged into group
|
|
19
|
+
* switches), and origins are migrated into source records (repo = origin
|
|
20
|
+
* value, commit snapshot empty — the first update check backfills it).
|
|
21
|
+
* v2 to v3: market sources become records ({ repo, ref?, commitSha? })
|
|
22
|
+
* instead of bare repo slugs, so a source can pin a release/branch version.
|
|
23
|
+
* Returns null when the file claims a newer schema than this plugin
|
|
24
|
+
* understands, so the caller starts empty instead of risking data loss.
|
|
25
|
+
*/
|
|
26
|
+
export declare function migrateStore(parsed: unknown): MigratedStore | null;
|
|
27
|
+
/** Validated in-memory state hydrated from a migrated document. */
|
|
28
|
+
export interface HydratedState {
|
|
29
|
+
entries: Map<string, DisabledSkill>;
|
|
30
|
+
config: Partial<HubConfig>;
|
|
31
|
+
tagsById: Map<string, SkillTag>;
|
|
32
|
+
sourcesByRepo: Map<string, SourceRecord>;
|
|
33
|
+
marketSources: MarketSourceRecord[];
|
|
34
|
+
trashByName: Map<string, TrashEntry>;
|
|
35
|
+
skillStats: SkillStatsCheckpoint | undefined;
|
|
36
|
+
marketStats: MarketStatsSnapshot | undefined;
|
|
37
|
+
collectionOrder: string[];
|
|
38
|
+
sourceGroupOrder: string[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 把迁移后的原始文档校验为内存状态:坏条目丢弃、坏桶降级为空,
|
|
42
|
+
* 绝不因一条脏数据丢掉整个 sidecar(与原 ensureLoaded 内联逻辑一致)。
|
|
43
|
+
*/
|
|
44
|
+
export declare function hydrateMigratedState(migrated: MigratedStore): HydratedState;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { DisabledSkill, HubConfig, MarketSourceRecord, MarketStatsSnapshot, SkillStatsCheckpoint, SkillTag, SourceRecord, TrashEntry } from '../protocol.ts';
|
|
2
|
+
/** 默认场景名(系统预置的兜底场景,新技能自动归入)。 */
|
|
3
|
+
export declare const DEFAULT_SCENE_NAME = "\u901A\u7528";
|
|
4
|
+
/** Wire shape persisted on disk. */
|
|
5
|
+
export interface StoreFile {
|
|
6
|
+
version: number;
|
|
7
|
+
disabled: DisabledSkill[];
|
|
8
|
+
/** Runtime configuration edited from the web settings card (hub-owned, not settings-service). */
|
|
9
|
+
config?: Partial<HubConfig>;
|
|
10
|
+
/** User-defined tag groups (pure organization; skill files untouched). */
|
|
11
|
+
tags?: SkillTag[];
|
|
12
|
+
/** Upstream source tracking records (repo + commit snapshot). */
|
|
13
|
+
sources?: SourceRecord[];
|
|
14
|
+
/** User-added market sources (owner/repo slugs + optional pinned ref). */
|
|
15
|
+
marketSources?: MarketSourceRecord[];
|
|
16
|
+
/** Trashed skills (removed after upstream deletion, restorable). */
|
|
17
|
+
trash?: TrashEntry[];
|
|
18
|
+
/** Usage-statistics incremental-scan checkpoint (frozen watermark + totals). */
|
|
19
|
+
skillStats?: SkillStatsCheckpoint;
|
|
20
|
+
/** Market-stats snapshot (stars/downloads per repo, hourly TTL). */
|
|
21
|
+
marketStats?: MarketStatsSnapshot;
|
|
22
|
+
/** Drag-reorder: collection name order for 来源分组 */
|
|
23
|
+
collectionOrder?: string[];
|
|
24
|
+
/** Drag-reorder: 来源顶层分组整体顺序(project / col:xxx / uncategorized-source) */
|
|
25
|
+
sourceGroupOrder?: string[];
|
|
26
|
+
}
|
|
27
|
+
/** Resolve the DSH home directory (the filesystem provider's user-dsh root base). */
|
|
28
|
+
export declare function dshHome(): string;
|
|
29
|
+
/** Resolve the sidecar state path (injectable in tests). */
|
|
30
|
+
export declare function statePath(home?: string): string;
|
|
31
|
+
/** Current sidecar schema version. Bump on breaking shape changes and add a migration below. */
|
|
32
|
+
export declare const STORE_VERSION = 4;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { DisabledSkill, HubConfig, MarketSourceRecord, MarketStatsSnapshot, SkillStatsCheckpoint, SkillTag, SourceRecord, TrashEntry } from '../protocol.ts';
|
|
2
|
+
/** Sidecar state owner. */
|
|
3
|
+
export declare class SkillHubStore {
|
|
4
|
+
private readonly file;
|
|
5
|
+
private entries;
|
|
6
|
+
private config;
|
|
7
|
+
private tagsById;
|
|
8
|
+
private sourcesByRepo;
|
|
9
|
+
private marketSources;
|
|
10
|
+
private trashByName;
|
|
11
|
+
private skillStats;
|
|
12
|
+
private marketStats;
|
|
13
|
+
private collectionOrder;
|
|
14
|
+
private sourceGroupOrder;
|
|
15
|
+
private loaded;
|
|
16
|
+
/** Serializes persist runs: concurrent mutators must not let an earlier
|
|
17
|
+
* snapshot overwrite a later one (rename is atomic, ordering is not). */
|
|
18
|
+
private writeChain;
|
|
19
|
+
constructor(file?: string);
|
|
20
|
+
private ensureLoaded;
|
|
21
|
+
/**
|
|
22
|
+
* 保证存在默认场景:没有任何 default 标记的 tag 时创建「通用」。
|
|
23
|
+
* 新技能创建后自动归入它;用户可改名,但默认场景不可删除。
|
|
24
|
+
*/
|
|
25
|
+
private ensureDefaultTag;
|
|
26
|
+
listDisabled(): Promise<DisabledSkill[]>;
|
|
27
|
+
getDisabled(name: string): Promise<DisabledSkill | undefined>;
|
|
28
|
+
addDisabled(entry: DisabledSkill): Promise<void>;
|
|
29
|
+
removeDisabled(name: string): Promise<void>;
|
|
30
|
+
/** The hub's saved runtime configuration (fields absent here mean "not overridden"). */
|
|
31
|
+
getConfig(): Promise<Partial<HubConfig>>;
|
|
32
|
+
/**
|
|
33
|
+
* Persist a runtime-config patch, merged over the saved values. A field
|
|
34
|
+
* whose patch value is undefined is removed from the saved layer, so the
|
|
35
|
+
* setting re-inherits its default (the web card's "reset" path).
|
|
36
|
+
*/
|
|
37
|
+
setConfig(config: Partial<HubConfig>): Promise<void>;
|
|
38
|
+
/** All user-defined tag groups, in creation order. */
|
|
39
|
+
listTags(): Promise<SkillTag[]>;
|
|
40
|
+
/** One tag by id (undefined when absent). */
|
|
41
|
+
getTag(id: string): Promise<SkillTag | undefined>;
|
|
42
|
+
/**
|
|
43
|
+
* Create (no id) or rename (with id) a tag. Returns the saved tag.
|
|
44
|
+
* Creating assigns a fresh UUID; renaming keeps members.
|
|
45
|
+
*/
|
|
46
|
+
saveTag(input: {
|
|
47
|
+
id?: string;
|
|
48
|
+
name: string;
|
|
49
|
+
}): Promise<SkillTag>;
|
|
50
|
+
/** Delete a tag by id (no-op when absent). The default scene cannot be deleted. */
|
|
51
|
+
deleteTag(id: string): Promise<void>;
|
|
52
|
+
/** The default scene (「通用」), guaranteed to exist after ensureLoaded. */
|
|
53
|
+
getDefaultTag(): Promise<SkillTag | undefined>;
|
|
54
|
+
/** Append one skill name to a tag (deduplicated; no-op when already a member). */
|
|
55
|
+
addSkillToTag(id: string, name: string): Promise<SkillTag | undefined>;
|
|
56
|
+
/**
|
|
57
|
+
* Replace a tag's member list wholesale (idempotent). Deduplicates and
|
|
58
|
+
* drops blank names; unknown skill names are kept (they may arrive later),
|
|
59
|
+
* the routes layer filters against the live catalog before persisting.
|
|
60
|
+
*/
|
|
61
|
+
setTagMembers(id: string, skillNames: readonly string[]): Promise<SkillTag | undefined>;
|
|
62
|
+
/** Remove one skill from every tag group (used when the skill is deleted). */
|
|
63
|
+
removeSkillFromTags(name: string): Promise<void>;
|
|
64
|
+
/** Reorder tag groups by orderedIds (drag-and-drop). */
|
|
65
|
+
reorderTags(orderedIds: string[]): Promise<SkillTag[]>;
|
|
66
|
+
/** Collection order for 来源分组拖拽 */
|
|
67
|
+
getCollectionOrder(): Promise<string[]>;
|
|
68
|
+
/** Reorder collections by orderedNames (drag-and-drop). */
|
|
69
|
+
reorderCollections(orderedNames: string[]): Promise<string[]>;
|
|
70
|
+
/** Source top-level group order for 来源分组(project / col:xxx / personal) */
|
|
71
|
+
getSourceGroupOrder(): Promise<string[]>;
|
|
72
|
+
/** Reorder source top-level groups by orderedKeys */
|
|
73
|
+
reorderSourceGroups(orderedKeys: string[]): Promise<string[]>;
|
|
74
|
+
/** All source records, sorted by repo. */
|
|
75
|
+
listSources(): Promise<SourceRecord[]>;
|
|
76
|
+
/** One source record by repo (undefined when absent). */
|
|
77
|
+
getSource(repo: string): Promise<SourceRecord | undefined>;
|
|
78
|
+
/** The source record that tracks a skill name (undefined when untracked). */
|
|
79
|
+
getSourceForSkill(name: string): Promise<SourceRecord | undefined>;
|
|
80
|
+
/** Remove a source record entirely (no-op when absent). */
|
|
81
|
+
deleteSource(repo: string): Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Upsert one skill into a source record. When the repo has no record yet a
|
|
84
|
+
* new one is created (root + commit snapshot from the caller).
|
|
85
|
+
*/
|
|
86
|
+
addSourceSkill(repo: string, root: string, commitSha: string, ref: string | undefined, skillName: string): Promise<void>;
|
|
87
|
+
/** Replace a source's skill list (used after sync/confirm-delete). */
|
|
88
|
+
setSourceSkills(repo: string, skills: readonly string[]): Promise<SourceRecord | undefined>;
|
|
89
|
+
/** Remove one skill from every source record (used when the skill is deleted). */
|
|
90
|
+
removeSkillFromSources(name: string): Promise<void>;
|
|
91
|
+
/** Update a source's commit snapshot. */
|
|
92
|
+
setSourceCommit(repo: string, commitSha: string): Promise<void>;
|
|
93
|
+
/** Update a source's pinned ref (release tag / branch) when the market syncs. */
|
|
94
|
+
setSourceRef(repo: string, ref: string): Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Merge per-path manifest entries into a source (incremental imports).
|
|
97
|
+
* When `dir` is given, every baseline path under that skill directory is
|
|
98
|
+
* dropped first, so files the upstream removed never linger in the
|
|
99
|
+
* baseline and skew later update diffs.
|
|
100
|
+
*/
|
|
101
|
+
mergeSourceManifest(repo: string, manifest: Record<string, number>, dir?: string): Promise<void>;
|
|
102
|
+
/** skillName → collection name for every recorded origin (derived from sources). */
|
|
103
|
+
listOrigins(): Promise<Record<string, string>>;
|
|
104
|
+
/** The user's market sources, in addition order. */
|
|
105
|
+
listMarketSources(): Promise<MarketSourceRecord[]>;
|
|
106
|
+
/** One market source by repo (undefined when absent). */
|
|
107
|
+
getMarketSource(repo: string): Promise<MarketSourceRecord | undefined>;
|
|
108
|
+
/** Add a repo (deduplicated), optionally with a pinned ref. Returns the fresh list. */
|
|
109
|
+
addMarketSource(repo: string, ref?: string): Promise<MarketSourceRecord[]>;
|
|
110
|
+
/** Remove a repo (no-op when absent). Returns the fresh list. */
|
|
111
|
+
removeMarketSource(repo: string): Promise<MarketSourceRecord[]>;
|
|
112
|
+
/** Pin a market source to an explicit ref (branch/tag). Returns the record. */
|
|
113
|
+
setMarketSourceRef(repo: string, ref: string): Promise<MarketSourceRecord | undefined>;
|
|
114
|
+
/** Record the commit a market source's pinned ref resolved to (update baseline). */
|
|
115
|
+
setMarketSourceCommit(repo: string, commitSha: string): Promise<void>;
|
|
116
|
+
/** All trashed skills, newest first. */
|
|
117
|
+
listTrash(): Promise<TrashEntry[]>;
|
|
118
|
+
/** One trash entry by skill name (undefined when absent). */
|
|
119
|
+
getTrash(name: string): Promise<TrashEntry | undefined>;
|
|
120
|
+
/** Record a trashed skill. */
|
|
121
|
+
addTrash(entry: TrashEntry): Promise<void>;
|
|
122
|
+
/** Remove a trash record (after restore). */
|
|
123
|
+
removeTrash(name: string): Promise<void>;
|
|
124
|
+
/** The persisted usage-statistics checkpoint (undefined until first saved). */
|
|
125
|
+
getSkillStatsState(): Promise<SkillStatsCheckpoint | undefined>;
|
|
126
|
+
/** Persist a usage-statistics checkpoint (after every completed scan; cadence follows the scan TTL). */
|
|
127
|
+
saveSkillStatsState(state: SkillStatsCheckpoint): Promise<void>;
|
|
128
|
+
/** The persisted market-stats snapshot (undefined until first saved). */
|
|
129
|
+
getMarketStatsState(): Promise<MarketStatsSnapshot | undefined>;
|
|
130
|
+
/** Persist a market-stats snapshot (written when a refresh fetched anything new). */
|
|
131
|
+
saveMarketStatsState(state: MarketStatsSnapshot): Promise<void>;
|
|
132
|
+
private persist;
|
|
133
|
+
}
|