vskill 0.4.6 → 0.4.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/dist/commands/add.js +82 -2
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/eval/serve.js +23 -4
- package/dist/commands/eval/serve.js.map +1 -1
- package/dist/commands/update.js +48 -21
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/update.test.js +129 -10
- package/dist/commands/update.test.js.map +1 -1
- package/dist/eval-server/api-routes.js +32 -3
- package/dist/eval-server/api-routes.js.map +1 -1
- package/dist/eval-server/skill-create-routes.js +12 -5
- package/dist/eval-server/skill-create-routes.js.map +1 -1
- package/dist/eval-ui/assets/index-BMwu9gZa.css +1 -0
- package/dist/eval-ui/assets/index-CyNe7XG9.js +75 -0
- package/dist/eval-ui/index.html +2 -2
- package/dist/resolvers/source-resolver.d.ts +36 -0
- package/dist/resolvers/source-resolver.js +63 -0
- package/dist/resolvers/source-resolver.js.map +1 -0
- package/dist/resolvers/source-resolver.test.d.ts +1 -0
- package/dist/resolvers/source-resolver.test.js +104 -0
- package/dist/resolvers/source-resolver.test.js.map +1 -0
- package/dist/updater/source-fetcher.d.ts +10 -0
- package/dist/updater/source-fetcher.js +147 -0
- package/dist/updater/source-fetcher.js.map +1 -0
- package/dist/updater/source-fetcher.test.d.ts +1 -0
- package/dist/updater/source-fetcher.test.js +192 -0
- package/dist/updater/source-fetcher.test.js.map +1 -0
- package/package.json +1 -1
- package/dist/eval-ui/assets/index-BvYYg8yK.js +0 -75
- package/dist/eval-ui/assets/index-WYEaSjlU.css +0 -1
package/dist/eval-ui/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Skill Studio</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-CyNe7XG9.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BMwu9gZa.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type ParsedSource = {
|
|
2
|
+
type: "registry";
|
|
3
|
+
skillName: string;
|
|
4
|
+
} | {
|
|
5
|
+
type: "github";
|
|
6
|
+
owner: string;
|
|
7
|
+
repo: string;
|
|
8
|
+
} | {
|
|
9
|
+
type: "github-plugin";
|
|
10
|
+
owner: string;
|
|
11
|
+
repo: string;
|
|
12
|
+
pluginName: string;
|
|
13
|
+
} | {
|
|
14
|
+
type: "marketplace";
|
|
15
|
+
owner: string;
|
|
16
|
+
repo: string;
|
|
17
|
+
pluginName: string;
|
|
18
|
+
} | {
|
|
19
|
+
type: "local";
|
|
20
|
+
baseName: string;
|
|
21
|
+
} | {
|
|
22
|
+
type: "unknown";
|
|
23
|
+
raw: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Parse a lockfile `source` string into a typed discriminated union.
|
|
27
|
+
*
|
|
28
|
+
* Supported formats:
|
|
29
|
+
* "registry:skillName"
|
|
30
|
+
* "local:baseName"
|
|
31
|
+
* "marketplace:owner/repo#pluginName"
|
|
32
|
+
* "github:owner/repo"
|
|
33
|
+
* "github:owner/repo#plugin:pluginName"
|
|
34
|
+
* "" or unrecognized → { type: "unknown" }
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseSource(source: string): ParsedSource;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Source string parser for vskill lockfile entries.
|
|
3
|
+
// Parses the `source` field into a typed discriminated union for update routing.
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
/**
|
|
6
|
+
* Parse a lockfile `source` string into a typed discriminated union.
|
|
7
|
+
*
|
|
8
|
+
* Supported formats:
|
|
9
|
+
* "registry:skillName"
|
|
10
|
+
* "local:baseName"
|
|
11
|
+
* "marketplace:owner/repo#pluginName"
|
|
12
|
+
* "github:owner/repo"
|
|
13
|
+
* "github:owner/repo#plugin:pluginName"
|
|
14
|
+
* "" or unrecognized → { type: "unknown" }
|
|
15
|
+
*/
|
|
16
|
+
export function parseSource(source) {
|
|
17
|
+
if (source.startsWith("registry:")) {
|
|
18
|
+
return { type: "registry", skillName: source.slice("registry:".length) };
|
|
19
|
+
}
|
|
20
|
+
if (source.startsWith("local:")) {
|
|
21
|
+
return { type: "local", baseName: source.slice("local:".length) };
|
|
22
|
+
}
|
|
23
|
+
if (source.startsWith("marketplace:")) {
|
|
24
|
+
const rest = source.slice("marketplace:".length);
|
|
25
|
+
const hashIdx = rest.indexOf("#");
|
|
26
|
+
if (hashIdx === -1)
|
|
27
|
+
return { type: "unknown", raw: source };
|
|
28
|
+
const ownerRepo = rest.slice(0, hashIdx);
|
|
29
|
+
const pluginName = rest.slice(hashIdx + 1);
|
|
30
|
+
const slashIdx = ownerRepo.indexOf("/");
|
|
31
|
+
if (slashIdx === -1)
|
|
32
|
+
return { type: "unknown", raw: source };
|
|
33
|
+
const owner = ownerRepo.slice(0, slashIdx);
|
|
34
|
+
const repo = ownerRepo.slice(slashIdx + 1);
|
|
35
|
+
if (!owner || !repo || !pluginName)
|
|
36
|
+
return { type: "unknown", raw: source };
|
|
37
|
+
return { type: "marketplace", owner, repo, pluginName };
|
|
38
|
+
}
|
|
39
|
+
if (source.startsWith("github:")) {
|
|
40
|
+
const rest = source.slice("github:".length);
|
|
41
|
+
const hashIdx = rest.indexOf("#");
|
|
42
|
+
const ownerRepo = hashIdx === -1 ? rest : rest.slice(0, hashIdx);
|
|
43
|
+
const slashIdx = ownerRepo.indexOf("/");
|
|
44
|
+
if (slashIdx === -1)
|
|
45
|
+
return { type: "unknown", raw: source };
|
|
46
|
+
const owner = ownerRepo.slice(0, slashIdx);
|
|
47
|
+
const repo = ownerRepo.slice(slashIdx + 1);
|
|
48
|
+
if (!owner || !repo)
|
|
49
|
+
return { type: "unknown", raw: source };
|
|
50
|
+
if (hashIdx !== -1) {
|
|
51
|
+
const fragment = rest.slice(hashIdx + 1);
|
|
52
|
+
if (fragment.startsWith("plugin:")) {
|
|
53
|
+
const pluginName = fragment.slice("plugin:".length);
|
|
54
|
+
return { type: "github-plugin", owner, repo, pluginName };
|
|
55
|
+
}
|
|
56
|
+
// Unknown fragment type
|
|
57
|
+
return { type: "unknown", raw: source };
|
|
58
|
+
}
|
|
59
|
+
return { type: "github", owner, repo };
|
|
60
|
+
}
|
|
61
|
+
return { type: "unknown", raw: source };
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=source-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-resolver.js","sourceRoot":"","sources":["../../src/resolvers/source-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { parseSource } from "./source-resolver.js";
|
|
3
|
+
describe("parseSource", () => {
|
|
4
|
+
// registry: prefix
|
|
5
|
+
it("parses registry: prefix", () => {
|
|
6
|
+
expect(parseSource("registry:architect")).toEqual({
|
|
7
|
+
type: "registry",
|
|
8
|
+
skillName: "architect",
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
it("parses registry: with empty name", () => {
|
|
12
|
+
expect(parseSource("registry:")).toEqual({
|
|
13
|
+
type: "registry",
|
|
14
|
+
skillName: "",
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
// local: prefix
|
|
18
|
+
it("parses local: prefix", () => {
|
|
19
|
+
expect(parseSource("local:specweave")).toEqual({
|
|
20
|
+
type: "local",
|
|
21
|
+
baseName: "specweave",
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
it("parses local: with path", () => {
|
|
25
|
+
expect(parseSource("local:/abs/path/to/plugin")).toEqual({
|
|
26
|
+
type: "local",
|
|
27
|
+
baseName: "/abs/path/to/plugin",
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
// marketplace: prefix
|
|
31
|
+
it("parses marketplace: prefix", () => {
|
|
32
|
+
expect(parseSource("marketplace:anton-abyzov/specweave#sw")).toEqual({
|
|
33
|
+
type: "marketplace",
|
|
34
|
+
owner: "anton-abyzov",
|
|
35
|
+
repo: "specweave",
|
|
36
|
+
pluginName: "sw",
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
it("parses marketplace: with hyphenated plugin name", () => {
|
|
40
|
+
expect(parseSource("marketplace:org/repo#my-plugin")).toEqual({
|
|
41
|
+
type: "marketplace",
|
|
42
|
+
owner: "org",
|
|
43
|
+
repo: "repo",
|
|
44
|
+
pluginName: "my-plugin",
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
// github: flat (no fragment)
|
|
48
|
+
it("parses github: flat repo", () => {
|
|
49
|
+
expect(parseSource("github:coreyhaines31/marketingskills")).toEqual({
|
|
50
|
+
type: "github",
|
|
51
|
+
owner: "coreyhaines31",
|
|
52
|
+
repo: "marketingskills",
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
// github: with #plugin: fragment
|
|
56
|
+
it("parses github: with #plugin: fragment", () => {
|
|
57
|
+
expect(parseSource("github:anton-abyzov/vskill#plugin:frontend")).toEqual({
|
|
58
|
+
type: "github-plugin",
|
|
59
|
+
owner: "anton-abyzov",
|
|
60
|
+
repo: "vskill",
|
|
61
|
+
pluginName: "frontend",
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
it("parses github: with #plugin: fragment for nested plugin name", () => {
|
|
65
|
+
expect(parseSource("github:org/repo#plugin:my-plugin")).toEqual({
|
|
66
|
+
type: "github-plugin",
|
|
67
|
+
owner: "org",
|
|
68
|
+
repo: "repo",
|
|
69
|
+
pluginName: "my-plugin",
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
// unknown / fallback cases
|
|
73
|
+
it("returns unknown for empty string", () => {
|
|
74
|
+
expect(parseSource("")).toEqual({
|
|
75
|
+
type: "unknown",
|
|
76
|
+
raw: "",
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
it("returns unknown for unrecognized prefix", () => {
|
|
80
|
+
expect(parseSource("npm:some-package")).toEqual({
|
|
81
|
+
type: "unknown",
|
|
82
|
+
raw: "npm:some-package",
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
it("returns unknown for garbage string", () => {
|
|
86
|
+
expect(parseSource("garbage-string")).toEqual({
|
|
87
|
+
type: "unknown",
|
|
88
|
+
raw: "garbage-string",
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
it("returns unknown for marketplace: without #", () => {
|
|
92
|
+
expect(parseSource("marketplace:owner/repo")).toEqual({
|
|
93
|
+
type: "unknown",
|
|
94
|
+
raw: "marketplace:owner/repo",
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
it("returns unknown for github: without slash", () => {
|
|
98
|
+
expect(parseSource("github:noslash")).toEqual({
|
|
99
|
+
type: "unknown",
|
|
100
|
+
raw: "github:noslash",
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
//# sourceMappingURL=source-resolver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-resolver.test.js","sourceRoot":"","sources":["../../src/resolvers/source-resolver.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ParsedSource } from "../resolvers/source-resolver.js";
|
|
2
|
+
import type { SkillLockEntry } from "../lockfile/types.js";
|
|
3
|
+
export interface FetchResult {
|
|
4
|
+
/** Skill content. For plugin-dir sources: combined content of all skills. */
|
|
5
|
+
content: string;
|
|
6
|
+
version: string;
|
|
7
|
+
sha: string;
|
|
8
|
+
tier: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function fetchFromSource(parsed: ParsedSource, skillName: string, entry: SkillLockEntry): Promise<FetchResult | null>;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Source-aware skill fetcher for vskill update command.
|
|
3
|
+
// Routes fetch to the correct origin based on ParsedSource type.
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { getDefaultBranch } from "../discovery/github-tree.js";
|
|
7
|
+
import { getPluginSource, getPluginVersion } from "../marketplace/index.js";
|
|
8
|
+
import { getSkill } from "../api/client.js";
|
|
9
|
+
function buildAuthHeader() {
|
|
10
|
+
const token = process.env["GITHUB_TOKEN"];
|
|
11
|
+
if (token)
|
|
12
|
+
return { Authorization: `token ${token}` };
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
async function fetchRaw(url) {
|
|
16
|
+
try {
|
|
17
|
+
const res = await fetch(url, { headers: buildAuthHeader() });
|
|
18
|
+
if (!res.ok)
|
|
19
|
+
return null;
|
|
20
|
+
return await res.text();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function computeSha(content) {
|
|
27
|
+
return createHash("sha256").update(content).digest("hex").slice(0, 12);
|
|
28
|
+
}
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Registry: delegate to getSkill()
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
async function fetchRegistry(skillName, entry) {
|
|
33
|
+
try {
|
|
34
|
+
const remote = await getSkill(skillName);
|
|
35
|
+
if (!remote.content)
|
|
36
|
+
return null;
|
|
37
|
+
const sha = computeSha(remote.content);
|
|
38
|
+
return {
|
|
39
|
+
content: remote.content,
|
|
40
|
+
version: remote.version || entry.version,
|
|
41
|
+
sha,
|
|
42
|
+
tier: remote.tier || entry.tier,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// GitHub flat: fetch SKILL.md from raw.githubusercontent.com
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
async function fetchGitHubFlat(owner, repo, skillName, entry) {
|
|
53
|
+
const branch = await getDefaultBranch(owner, repo);
|
|
54
|
+
const base = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
|
|
55
|
+
// Try skill-specific path first
|
|
56
|
+
let content = await fetchRaw(`${base}/skills/${skillName}/SKILL.md`);
|
|
57
|
+
// Fallback to root SKILL.md
|
|
58
|
+
if (!content) {
|
|
59
|
+
content = await fetchRaw(`${base}/SKILL.md`);
|
|
60
|
+
}
|
|
61
|
+
if (!content)
|
|
62
|
+
return null;
|
|
63
|
+
return {
|
|
64
|
+
content,
|
|
65
|
+
version: entry.version,
|
|
66
|
+
sha: computeSha(content),
|
|
67
|
+
tier: entry.tier,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// GitHub plugin / marketplace: re-fetch via marketplace.json + Trees API
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
async function fetchPlugin(owner, repo, pluginName, entry) {
|
|
74
|
+
const branch = await getDefaultBranch(owner, repo);
|
|
75
|
+
const base = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
|
|
76
|
+
const apiBase = `https://api.github.com/repos/${owner}/${repo}`;
|
|
77
|
+
// 1. Fetch marketplace.json
|
|
78
|
+
const manifestContent = await fetchRaw(`${base}/.claude-plugin/marketplace.json`);
|
|
79
|
+
if (!manifestContent)
|
|
80
|
+
return null;
|
|
81
|
+
// 2. Resolve plugin source path (e.g. "./plugins/frontend")
|
|
82
|
+
const pluginSourcePath = getPluginSource(pluginName, manifestContent);
|
|
83
|
+
const version = getPluginVersion(pluginName, manifestContent) ?? entry.version;
|
|
84
|
+
if (!pluginSourcePath)
|
|
85
|
+
return null;
|
|
86
|
+
// Normalise: "./plugins/frontend" → "plugins/frontend"
|
|
87
|
+
const normalizedPath = pluginSourcePath.replace(/^\.\//, "");
|
|
88
|
+
// 3. Discover all skills under the plugin via Trees API
|
|
89
|
+
let tree = [];
|
|
90
|
+
try {
|
|
91
|
+
const treeRes = await fetch(`${apiBase}/git/trees/${branch}?recursive=1`, { headers: { Accept: "application/vnd.github.v3+json", ...buildAuthHeader() } });
|
|
92
|
+
if (treeRes.ok) {
|
|
93
|
+
const data = (await treeRes.json());
|
|
94
|
+
if (Array.isArray(data?.tree)) {
|
|
95
|
+
tree = data.tree;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
// 4. Collect paths matching plugins/{name}/skills/*/SKILL.md
|
|
103
|
+
const skillPattern = new RegExp(`^${normalizedPath}/skills/([^/]+)/SKILL\\.md$`);
|
|
104
|
+
const skillPaths = tree
|
|
105
|
+
.filter((e) => e.type === "blob" && skillPattern.test(e.path))
|
|
106
|
+
.map((e) => e.path);
|
|
107
|
+
if (skillPaths.length === 0)
|
|
108
|
+
return null;
|
|
109
|
+
// 5. Fetch all skill files in parallel
|
|
110
|
+
const fetched = await Promise.all(skillPaths.map(async (path) => {
|
|
111
|
+
const content = await fetchRaw(`${base}/${path}`);
|
|
112
|
+
return { path, content };
|
|
113
|
+
}));
|
|
114
|
+
const successful = fetched.filter((f) => f.content !== null);
|
|
115
|
+
if (successful.length === 0)
|
|
116
|
+
return null;
|
|
117
|
+
// 6. Combine content (sorted by path for deterministic SHA)
|
|
118
|
+
successful.sort((a, b) => a.path.localeCompare(b.path));
|
|
119
|
+
const combined = successful.map((f) => f.content).join("\n");
|
|
120
|
+
return {
|
|
121
|
+
content: combined,
|
|
122
|
+
version,
|
|
123
|
+
sha: computeSha(combined),
|
|
124
|
+
tier: entry.tier,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// Main dispatch
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
export async function fetchFromSource(parsed, skillName, entry) {
|
|
131
|
+
switch (parsed.type) {
|
|
132
|
+
case "registry":
|
|
133
|
+
return fetchRegistry(parsed.skillName, entry);
|
|
134
|
+
case "github":
|
|
135
|
+
return fetchGitHubFlat(parsed.owner, parsed.repo, skillName, entry);
|
|
136
|
+
case "github-plugin":
|
|
137
|
+
return fetchPlugin(parsed.owner, parsed.repo, parsed.pluginName, entry);
|
|
138
|
+
case "marketplace":
|
|
139
|
+
return fetchPlugin(parsed.owner, parsed.repo, parsed.pluginName, entry);
|
|
140
|
+
case "local":
|
|
141
|
+
// Managed by external tool (e.g. specweave refresh-plugins). Skip silently.
|
|
142
|
+
return null;
|
|
143
|
+
case "unknown":
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=source-fetcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-fetcher.js","sourceRoot":"","sources":["../../src/updater/source-fetcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
// ---------------------------------------------------------------------------
|
|
3
|
+
// Mock getDefaultBranch (GitHub tree discovery)
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
const mockGetDefaultBranch = vi.hoisted(() => vi.fn());
|
|
6
|
+
vi.mock("../discovery/github-tree.js", () => ({
|
|
7
|
+
getDefaultBranch: mockGetDefaultBranch,
|
|
8
|
+
}));
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Mock getSkill (registry)
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
const mockGetSkill = vi.hoisted(() => vi.fn());
|
|
13
|
+
vi.mock("../api/client.js", () => ({
|
|
14
|
+
getSkill: mockGetSkill,
|
|
15
|
+
}));
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Mock marketplace helpers
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
const mockGetPluginSource = vi.hoisted(() => vi.fn());
|
|
20
|
+
const mockGetPluginVersion = vi.hoisted(() => vi.fn());
|
|
21
|
+
vi.mock("../marketplace/index.js", () => ({
|
|
22
|
+
getPluginSource: mockGetPluginSource,
|
|
23
|
+
getPluginVersion: mockGetPluginVersion,
|
|
24
|
+
}));
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Mock global fetch
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
const mockFetch = vi.hoisted(() => vi.fn());
|
|
29
|
+
vi.stubGlobal("fetch", mockFetch);
|
|
30
|
+
import { fetchFromSource } from "./source-fetcher.js";
|
|
31
|
+
const MOCK_LOCK_ENTRY = {
|
|
32
|
+
version: "1.0.0",
|
|
33
|
+
sha: "oldsha12345",
|
|
34
|
+
tier: "VERIFIED",
|
|
35
|
+
installedAt: "2026-01-01T00:00:00Z",
|
|
36
|
+
source: "github:test/repo",
|
|
37
|
+
};
|
|
38
|
+
function mockFetchOk(text) {
|
|
39
|
+
return {
|
|
40
|
+
ok: true,
|
|
41
|
+
status: 200,
|
|
42
|
+
text: async () => text,
|
|
43
|
+
json: async () => JSON.parse(text),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function mockFetchNotFound() {
|
|
47
|
+
return { ok: false, status: 404, text: async () => "", json: async () => ({}) };
|
|
48
|
+
}
|
|
49
|
+
describe("fetchFromSource", () => {
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
vi.clearAllMocks();
|
|
52
|
+
mockGetDefaultBranch.mockResolvedValue("main");
|
|
53
|
+
});
|
|
54
|
+
// ---- registry -------------------------------------------------------
|
|
55
|
+
it("delegates to getSkill for registry source", async () => {
|
|
56
|
+
const parsed = { type: "registry", skillName: "architect" };
|
|
57
|
+
mockGetSkill.mockResolvedValue({
|
|
58
|
+
content: "# Registry skill",
|
|
59
|
+
version: "2.0.0",
|
|
60
|
+
sha: "abc123",
|
|
61
|
+
tier: "VERIFIED",
|
|
62
|
+
});
|
|
63
|
+
const result = await fetchFromSource(parsed, "architect", MOCK_LOCK_ENTRY);
|
|
64
|
+
expect(mockGetSkill).toHaveBeenCalledWith("architect");
|
|
65
|
+
expect(result).not.toBeNull();
|
|
66
|
+
expect(result.content).toBe("# Registry skill");
|
|
67
|
+
expect(result.version).toBe("2.0.0");
|
|
68
|
+
});
|
|
69
|
+
it("returns null when registry fetch fails", async () => {
|
|
70
|
+
const parsed = { type: "registry", skillName: "missing" };
|
|
71
|
+
mockGetSkill.mockRejectedValue(new Error("not found"));
|
|
72
|
+
const result = await fetchFromSource(parsed, "missing", MOCK_LOCK_ENTRY);
|
|
73
|
+
expect(result).toBeNull();
|
|
74
|
+
});
|
|
75
|
+
// ---- github (flat) --------------------------------------------------
|
|
76
|
+
it("fetches SKILL.md from raw.githubusercontent.com for github source", async () => {
|
|
77
|
+
const parsed = { type: "github", owner: "foo", repo: "bar" };
|
|
78
|
+
mockFetch.mockResolvedValue(mockFetchOk("# GitHub flat skill"));
|
|
79
|
+
const result = await fetchFromSource(parsed, "myskill", MOCK_LOCK_ENTRY);
|
|
80
|
+
expect(mockGetDefaultBranch).toHaveBeenCalledWith("foo", "bar");
|
|
81
|
+
expect(mockFetch).toHaveBeenCalled();
|
|
82
|
+
const url = mockFetch.mock.calls[0][0];
|
|
83
|
+
expect(url).toContain("raw.githubusercontent.com/foo/bar/main");
|
|
84
|
+
expect(result).not.toBeNull();
|
|
85
|
+
expect(result.content).toBe("# GitHub flat skill");
|
|
86
|
+
});
|
|
87
|
+
it("falls back to root SKILL.md when skills/{name}/SKILL.md returns 404", async () => {
|
|
88
|
+
const parsed = { type: "github", owner: "foo", repo: "bar" };
|
|
89
|
+
mockFetch
|
|
90
|
+
.mockResolvedValueOnce(mockFetchNotFound()) // skills/myskill/SKILL.md
|
|
91
|
+
.mockResolvedValueOnce(mockFetchOk("# Root skill")); // SKILL.md
|
|
92
|
+
const result = await fetchFromSource(parsed, "myskill", MOCK_LOCK_ENTRY);
|
|
93
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
94
|
+
expect(result.content).toBe("# Root skill");
|
|
95
|
+
});
|
|
96
|
+
it("returns null when both github fetch attempts fail", async () => {
|
|
97
|
+
const parsed = { type: "github", owner: "foo", repo: "bar" };
|
|
98
|
+
mockFetch.mockResolvedValue(mockFetchNotFound());
|
|
99
|
+
const result = await fetchFromSource(parsed, "myskill", MOCK_LOCK_ENTRY);
|
|
100
|
+
expect(result).toBeNull();
|
|
101
|
+
});
|
|
102
|
+
// ---- github-plugin --------------------------------------------------
|
|
103
|
+
it("fetches plugin skills via marketplace.json for github-plugin source", async () => {
|
|
104
|
+
const parsed = {
|
|
105
|
+
type: "github-plugin",
|
|
106
|
+
owner: "org",
|
|
107
|
+
repo: "repo",
|
|
108
|
+
pluginName: "frontend",
|
|
109
|
+
};
|
|
110
|
+
const marketplaceJson = JSON.stringify({
|
|
111
|
+
name: "repo",
|
|
112
|
+
owner: { name: "org" },
|
|
113
|
+
plugins: [{ name: "frontend", source: "./plugins/frontend", version: "1.2.3" }],
|
|
114
|
+
});
|
|
115
|
+
// marketplace.json fetch
|
|
116
|
+
mockFetch.mockResolvedValueOnce(mockFetchOk(marketplaceJson));
|
|
117
|
+
// Trees API for plugin skills
|
|
118
|
+
mockFetch.mockResolvedValueOnce(mockFetchOk(JSON.stringify({
|
|
119
|
+
tree: [
|
|
120
|
+
{ path: "plugins/frontend/skills/nextjs/SKILL.md", type: "blob" },
|
|
121
|
+
{ path: "plugins/frontend/skills/react/SKILL.md", type: "blob" },
|
|
122
|
+
],
|
|
123
|
+
})));
|
|
124
|
+
// Individual skill fetches
|
|
125
|
+
mockFetch.mockResolvedValueOnce(mockFetchOk("# nextjs skill"));
|
|
126
|
+
mockFetch.mockResolvedValueOnce(mockFetchOk("# react skill"));
|
|
127
|
+
mockGetPluginSource.mockReturnValue("./plugins/frontend");
|
|
128
|
+
mockGetPluginVersion.mockReturnValue("1.2.3");
|
|
129
|
+
const result = await fetchFromSource(parsed, "frontend", {
|
|
130
|
+
...MOCK_LOCK_ENTRY,
|
|
131
|
+
pluginDir: true,
|
|
132
|
+
});
|
|
133
|
+
expect(result).not.toBeNull();
|
|
134
|
+
expect(result.version).toBe("1.2.3");
|
|
135
|
+
// Content should contain both skills
|
|
136
|
+
expect(result.content).toContain("# nextjs skill");
|
|
137
|
+
expect(result.content).toContain("# react skill");
|
|
138
|
+
});
|
|
139
|
+
it("returns null when marketplace.json fetch fails for github-plugin", async () => {
|
|
140
|
+
const parsed = {
|
|
141
|
+
type: "github-plugin",
|
|
142
|
+
owner: "org",
|
|
143
|
+
repo: "repo",
|
|
144
|
+
pluginName: "frontend",
|
|
145
|
+
};
|
|
146
|
+
mockFetch.mockResolvedValue(mockFetchNotFound());
|
|
147
|
+
const result = await fetchFromSource(parsed, "frontend", MOCK_LOCK_ENTRY);
|
|
148
|
+
expect(result).toBeNull();
|
|
149
|
+
});
|
|
150
|
+
// ---- marketplace ----------------------------------------------------
|
|
151
|
+
it("handles marketplace source same as github-plugin", async () => {
|
|
152
|
+
const parsed = {
|
|
153
|
+
type: "marketplace",
|
|
154
|
+
owner: "org",
|
|
155
|
+
repo: "repo",
|
|
156
|
+
pluginName: "sw",
|
|
157
|
+
};
|
|
158
|
+
const marketplaceJson = JSON.stringify({
|
|
159
|
+
name: "repo",
|
|
160
|
+
owner: { name: "org" },
|
|
161
|
+
plugins: [{ name: "sw", source: "./plugins/sw", version: "3.0.0" }],
|
|
162
|
+
});
|
|
163
|
+
mockFetch.mockResolvedValueOnce(mockFetchOk(marketplaceJson));
|
|
164
|
+
mockFetch.mockResolvedValueOnce(mockFetchOk(JSON.stringify({
|
|
165
|
+
tree: [{ path: "plugins/sw/skills/commands/SKILL.md", type: "blob" }],
|
|
166
|
+
})));
|
|
167
|
+
mockFetch.mockResolvedValueOnce(mockFetchOk("# sw commands skill"));
|
|
168
|
+
mockGetPluginSource.mockReturnValue("./plugins/sw");
|
|
169
|
+
mockGetPluginVersion.mockReturnValue("3.0.0");
|
|
170
|
+
const result = await fetchFromSource(parsed, "sw", {
|
|
171
|
+
...MOCK_LOCK_ENTRY,
|
|
172
|
+
pluginDir: true,
|
|
173
|
+
});
|
|
174
|
+
expect(result).not.toBeNull();
|
|
175
|
+
expect(result.version).toBe("3.0.0");
|
|
176
|
+
});
|
|
177
|
+
// ---- local ----------------------------------------------------------
|
|
178
|
+
it("returns null for local source (skip with no fetch)", async () => {
|
|
179
|
+
const parsed = { type: "local", baseName: "specweave" };
|
|
180
|
+
const result = await fetchFromSource(parsed, "sw", MOCK_LOCK_ENTRY);
|
|
181
|
+
expect(result).toBeNull();
|
|
182
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
183
|
+
expect(mockGetSkill).not.toHaveBeenCalled();
|
|
184
|
+
});
|
|
185
|
+
// ---- unknown --------------------------------------------------------
|
|
186
|
+
it("returns null for unknown source type", async () => {
|
|
187
|
+
const parsed = { type: "unknown", raw: "garbage" };
|
|
188
|
+
const result = await fetchFromSource(parsed, "skill", MOCK_LOCK_ENTRY);
|
|
189
|
+
expect(result).toBeNull();
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
//# sourceMappingURL=source-fetcher.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-fetcher.test.js","sourceRoot":"","sources":["../../src/updater/source-fetcher.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAE9D,8EAA8E;AAC9E,gDAAgD;AAChD,8EAA8E;AAC9E,MAAM,oBAAoB,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACvD,EAAE,CAAC,IAAI,CAAC,6BAA6B,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5C,gBAAgB,EAAE,oBAAoB;CACvC,CAAC,CAAC,CAAC;AAEJ,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAC9E,MAAM,YAAY,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAC/C,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;IACjC,QAAQ,EAAE,YAAY;CACvB,CAAC,CAAC,CAAC;AAEJ,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAC9E,MAAM,mBAAmB,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACtD,MAAM,oBAAoB,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AACvD,EAAE,CAAC,IAAI,CAAC,yBAAyB,EAAE,GAAG,EAAE,CAAC,CAAC;IACxC,eAAe,EAAE,mBAAmB;IACpC,gBAAgB,EAAE,oBAAoB;CACvC,CAAC,CAAC,CAAC;AAEJ,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAC9E,MAAM,SAAS,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAC5C,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AAElC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtD,MAAM,eAAe,GAAG;IACtB,OAAO,EAAE,OAAO;IAChB,GAAG,EAAE,aAAa;IAClB,IAAI,EAAE,UAAU;IAChB,WAAW,EAAE,sBAAsB;IACnC,MAAM,EAAE,kBAAkB;CAC3B,CAAC;AAEF,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO;QACL,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,GAAG;QACX,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;QACtB,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;KACnC,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAClF,CAAC;AAED,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,oBAAoB,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,wEAAwE;IAExE,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,MAAM,GAAiB,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;QAC1E,YAAY,CAAC,iBAAiB,CAAC;YAC7B,OAAO,EAAE,kBAAkB;YAC3B,OAAO,EAAE,OAAO;YAChB,GAAG,EAAE,QAAQ;YACb,IAAI,EAAE,UAAU;SACjB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,eAAe,CAAC,CAAC;QAE3E,MAAM,CAAC,YAAY,CAAC,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC;QACvD,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACjD,MAAM,CAAC,MAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,MAAM,GAAiB,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;QACxE,YAAY,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;QAEvD,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;QACzE,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,wEAAwE;IAExE,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,MAAM,GAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAC3E,SAAS,CAAC,iBAAiB,CAAC,WAAW,CAAC,qBAAqB,CAAC,CAAC,CAAC;QAEhE,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;QAEzE,MAAM,CAAC,oBAAoB,CAAC,CAAC,oBAAoB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAChE,MAAM,CAAC,SAAS,CAAC,CAAC,gBAAgB,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAW,CAAC;QACjD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,wCAAwC,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,MAAM,GAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAC3E,SAAS;aACN,qBAAqB,CAAC,iBAAiB,EAAE,CAAC,CAAG,0BAA0B;aACvE,qBAAqB,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,WAAW;QAElE,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;QAEzE,MAAM,CAAC,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,MAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,MAAM,MAAM,GAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;QAC3E,SAAS,CAAC,iBAAiB,CAAC,iBAAiB,EAAE,CAAC,CAAC;QAEjD,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;QACzE,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,wEAAwE;IAExE,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,MAAM,GAAiB;YAC3B,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,KAAK;YACZ,IAAI,EAAE,MAAM;YACZ,UAAU,EAAE,UAAU;SACvB,CAAC;QACF,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC;YACrC,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;YACtB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,oBAAoB,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;SAChF,CAAC,CAAC;QAEH,yBAAyB;QACzB,SAAS,CAAC,qBAAqB,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC;QAC9D,8BAA8B;QAC9B,SAAS,CAAC,qBAAqB,CAC7B,WAAW,CACT,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE;gBACJ,EAAE,IAAI,EAAE,yCAAyC,EAAE,IAAI,EAAE,MAAM,EAAE;gBACjE,EAAE,IAAI,EAAE,wCAAwC,EAAE,IAAI,EAAE,MAAM,EAAE;aACjE;SACF,CAAC,CACH,CACF,CAAC;QACF,2BAA2B;QAC3B,SAAS,CAAC,qBAAqB,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC,CAAC;QAC/D,SAAS,CAAC,qBAAqB,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC;QAE9D,mBAAmB,CAAC,eAAe,CAAC,oBAAoB,CAAC,CAAC;QAC1D,oBAAoB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAE9C,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE;YACvD,GAAG,eAAe;YAClB,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtC,qCAAqC;QACrC,MAAM,CAAC,MAAO,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QACpD,MAAM,CAAC,MAAO,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,MAAM,GAAiB;YAC3B,IAAI,EAAE,eAAe;YACrB,KAAK,EAAE,KAAK;YACZ,IAAI,EAAE,MAAM;YACZ,UAAU,EAAE,UAAU;SACvB,CAAC;QACF,SAAS,CAAC,iBAAiB,CAAC,iBAAiB,EAAE,CAAC,CAAC;QAEjD,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC;QAC1E,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,wEAAwE;IAExE,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,MAAM,GAAiB;YAC3B,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE,KAAK;YACZ,IAAI,EAAE,MAAM;YACZ,UAAU,EAAE,IAAI;SACjB,CAAC;QACF,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC;YACrC,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;YACtB,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;SACpE,CAAC,CAAC;QAEH,SAAS,CAAC,qBAAqB,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC,CAAC;QAC9D,SAAS,CAAC,qBAAqB,CAC7B,WAAW,CACT,IAAI,CAAC,SAAS,CAAC;YACb,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,qCAAqC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;SACtE,CAAC,CACH,CACF,CAAC;QACF,SAAS,CAAC,qBAAqB,CAAC,WAAW,CAAC,qBAAqB,CAAC,CAAC,CAAC;QAEpE,mBAAmB,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;QACpD,oBAAoB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAE9C,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE;YACjD,GAAG,eAAe;YAClB,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,wEAAwE;IAExE,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,MAAM,GAAiB,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;QAEtE,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC;QAEpE,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC1B,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACzC,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,wEAAwE;IAExE,EAAE,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACpD,MAAM,MAAM,GAAiB,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;QAEjE,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,eAAe,CAAC,CAAC;QACvE,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5B,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|