crewly 1.20.131 → 1.20.133
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/config/constants.ts +49 -0
- package/config/index.ts +2 -0
- package/config/skills/agent/core/find-skill/SKILL.md +58 -0
- package/config/skills/agent/core/find-skill/execute.sh +67 -0
- package/config/skills/agent/core/find-skill/execute.test.sh +69 -0
- package/config/skills/agent/core/generate-pdf/SKILL.md +24 -29
- package/config/skills/agent/core/generate-pdf/execute.sh +22 -78
- package/config/skills/agent/core/generate-pdf/execute.test.sh +45 -0
- package/config/skills/agent/core/install-skill/SKILL.md +67 -0
- package/config/skills/agent/core/install-skill/execute.sh +80 -0
- package/config/skills/agent/core/install-skill/execute.test.sh +74 -0
- package/config/skills/agent/pdf-tools/SKILL.md +103 -0
- package/config/skills/agent/pdf-tools/execute.sh +301 -0
- package/config/skills/agent/pdf-tools/execute.test.sh +218 -0
- package/config/skills/agent/pdf-tools/instructions.md +14 -0
- package/config/skills/agent/pdf-tools/pdf_tools.py +268 -0
- package/config/skills/agent/pdf-tools/skill.json +103 -0
- package/config/skills/agent/pdf-tools/style.css +143 -0
- package/config/skills/agent/transcribe-audio/SKILL.md +42 -5
- package/config/skills/agent/transcribe-audio/execute.sh +44 -11
- package/config/skills/agent/transcribe-audio/execute.test.sh +90 -0
- package/config/skills/agent/transcribe-audio/install-whisper-cpp.sh +126 -0
- package/config/skills/agent/transcribe-audio/install-whisper-cpp.test.sh +69 -0
- package/config/skills/agent/transcribe-audio/skill.json +67 -6
- package/config/skills/marketplace-registry.ts +8 -1
- package/config/skills/registry.json +337 -3
- package/dist/backend/backend/src/constants.d.ts +71 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +96 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/cloud/cloud-device-pairing.controller.d.ts +56 -0
- package/dist/backend/backend/src/controllers/cloud/cloud-device-pairing.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/cloud/cloud-device-pairing.controller.js +114 -0
- package/dist/backend/backend/src/controllers/cloud/cloud-device-pairing.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/cloud/cloud.controller.d.ts +25 -0
- package/dist/backend/backend/src/controllers/cloud/cloud.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/cloud/cloud.controller.js +73 -55
- package/dist/backend/backend/src/controllers/cloud/cloud.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/cloud/cloud.routes.d.ts +3 -0
- package/dist/backend/backend/src/controllers/cloud/cloud.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/cloud/cloud.routes.js +9 -0
- package/dist/backend/backend/src/controllers/cloud/cloud.routes.js.map +1 -1
- package/dist/backend/backend/src/controllers/skill-setup/skill-setup.controller.d.ts +49 -0
- package/dist/backend/backend/src/controllers/skill-setup/skill-setup.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/skill-setup/skill-setup.controller.js +185 -0
- package/dist/backend/backend/src/controllers/skill-setup/skill-setup.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/skill-setup/skill-setup.routes.d.ts +21 -0
- package/dist/backend/backend/src/controllers/skill-setup/skill-setup.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/skill-setup/skill-setup.routes.js +29 -0
- package/dist/backend/backend/src/controllers/skill-setup/skill-setup.routes.js.map +1 -0
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +5 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts +14 -0
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +33 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-device-pairing.client.d.ts +132 -0
- package/dist/backend/backend/src/services/cloud/cloud-device-pairing.client.d.ts.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-device-pairing.client.js +228 -0
- package/dist/backend/backend/src/services/cloud/cloud-device-pairing.client.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-device-pairing.service.d.ts +115 -0
- package/dist/backend/backend/src/services/cloud/cloud-device-pairing.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-device-pairing.service.js +160 -0
- package/dist/backend/backend/src/services/cloud/cloud-device-pairing.service.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +8 -0
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
- package/dist/backend/backend/src/services/marketplace/marketplace.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/marketplace/marketplace.service.js +11 -3
- package/dist/backend/backend/src/services/marketplace/marketplace.service.js.map +1 -1
- package/dist/backend/backend/src/services/skill-setup/skill-discovery.service.d.ts +281 -0
- package/dist/backend/backend/src/services/skill-setup/skill-discovery.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/skill-setup/skill-discovery.service.js +559 -0
- package/dist/backend/backend/src/services/skill-setup/skill-discovery.service.js.map +1 -0
- package/dist/backend/backend/src/services/skill-setup/skill-install-job.service.d.ts +243 -0
- package/dist/backend/backend/src/services/skill-setup/skill-install-job.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/skill-setup/skill-install-job.service.js +439 -0
- package/dist/backend/backend/src/services/skill-setup/skill-install-job.service.js.map +1 -0
- package/dist/backend/backend/src/services/skill-setup/skill-setup-manifest.d.ts +138 -0
- package/dist/backend/backend/src/services/skill-setup/skill-setup-manifest.d.ts.map +1 -0
- package/dist/backend/backend/src/services/skill-setup/skill-setup-manifest.js +281 -0
- package/dist/backend/backend/src/services/skill-setup/skill-setup-manifest.js.map +1 -0
- package/dist/backend/backend/src/services/skill-setup/skill-setup-runner.service.d.ts +338 -0
- package/dist/backend/backend/src/services/skill-setup/skill-setup-runner.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/skill-setup/skill-setup-runner.service.js +761 -0
- package/dist/backend/backend/src/services/skill-setup/skill-setup-runner.service.js.map +1 -0
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +6 -0
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/types/marketplace.types.d.ts +16 -1
- package/dist/backend/backend/src/types/marketplace.types.d.ts.map +1 -1
- package/dist/backend/backend/src/utils/inbound-file-hint.utils.d.ts +55 -0
- package/dist/backend/backend/src/utils/inbound-file-hint.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/utils/inbound-file-hint.utils.js +78 -0
- package/dist/backend/backend/src/utils/inbound-file-hint.utils.js.map +1 -0
- package/dist/backend/build-info.json +2 -2
- package/dist/backend/config/constants.d.ts +48 -0
- package/dist/backend/config/constants.d.ts.map +1 -1
- package/dist/backend/config/constants.js +48 -0
- package/dist/backend/config/constants.js.map +1 -1
- package/dist/backend/config/index.d.ts +2 -2
- package/dist/backend/config/index.d.ts.map +1 -1
- package/dist/backend/config/index.js +2 -2
- package/dist/backend/config/index.js.map +1 -1
- package/dist/cli/backend/src/constants.d.ts +71 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +96 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts +14 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +33 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-device-pairing.client.d.ts +132 -0
- package/dist/cli/backend/src/services/cloud/cloud-device-pairing.client.d.ts.map +1 -0
- package/dist/cli/backend/src/services/cloud/cloud-device-pairing.client.js +228 -0
- package/dist/cli/backend/src/services/cloud/cloud-device-pairing.client.js.map +1 -0
- package/dist/cli/backend/src/services/marketplace/marketplace.service.d.ts +101 -0
- package/dist/cli/backend/src/services/marketplace/marketplace.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/marketplace/marketplace.service.js +392 -0
- package/dist/cli/backend/src/services/marketplace/marketplace.service.js.map +1 -0
- package/dist/cli/backend/src/services/skill-setup/skill-discovery.service.d.ts +281 -0
- package/dist/cli/backend/src/services/skill-setup/skill-discovery.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/skill-setup/skill-discovery.service.js +559 -0
- package/dist/cli/backend/src/services/skill-setup/skill-discovery.service.js.map +1 -0
- package/dist/cli/backend/src/services/skill-setup/skill-setup-manifest.d.ts +138 -0
- package/dist/cli/backend/src/services/skill-setup/skill-setup-manifest.d.ts.map +1 -0
- package/dist/cli/backend/src/services/skill-setup/skill-setup-manifest.js +281 -0
- package/dist/cli/backend/src/services/skill-setup/skill-setup-manifest.js.map +1 -0
- package/dist/cli/backend/src/services/skill-setup/skill-setup-runner.service.d.ts +338 -0
- package/dist/cli/backend/src/services/skill-setup/skill-setup-runner.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/skill-setup/skill-setup-runner.service.js +761 -0
- package/dist/cli/backend/src/services/skill-setup/skill-setup-runner.service.js.map +1 -0
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +6 -0
- package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/cli/backend/src/types/marketplace.types.d.ts +219 -0
- package/dist/cli/backend/src/types/marketplace.types.d.ts.map +1 -0
- package/dist/cli/backend/src/types/marketplace.types.js +6 -0
- package/dist/cli/backend/src/types/marketplace.types.js.map +1 -0
- package/dist/cli/backend/src/utils/inbound-file-hint.utils.d.ts +55 -0
- package/dist/cli/backend/src/utils/inbound-file-hint.utils.d.ts.map +1 -0
- package/dist/cli/backend/src/utils/inbound-file-hint.utils.js +78 -0
- package/dist/cli/backend/src/utils/inbound-file-hint.utils.js.map +1 -0
- package/dist/cli/backend/src/utils/package-root.d.ts +15 -0
- package/dist/cli/backend/src/utils/package-root.d.ts.map +1 -0
- package/dist/cli/backend/src/utils/package-root.js +38 -0
- package/dist/cli/backend/src/utils/package-root.js.map +1 -0
- package/dist/cli/cli/src/commands/cloud.d.ts +45 -25
- package/dist/cli/cli/src/commands/cloud.d.ts.map +1 -1
- package/dist/cli/cli/src/commands/cloud.js +153 -47
- package/dist/cli/cli/src/commands/cloud.js.map +1 -1
- package/dist/cli/cli/src/commands/install.d.ts +13 -1
- package/dist/cli/cli/src/commands/install.d.ts.map +1 -1
- package/dist/cli/cli/src/commands/install.js +40 -3
- package/dist/cli/cli/src/commands/install.js.map +1 -1
- package/dist/cli/cli/src/commands/onboard-checklist.js +3 -3
- package/dist/cli/cli/src/commands/onboard-checklist.js.map +1 -1
- package/dist/cli/cli/src/commands/skills.d.ts +58 -0
- package/dist/cli/cli/src/commands/skills.d.ts.map +1 -0
- package/dist/cli/cli/src/commands/skills.js +132 -0
- package/dist/cli/cli/src/commands/skills.js.map +1 -0
- package/dist/cli/cli/src/index.js +18 -7
- package/dist/cli/cli/src/index.js.map +1 -1
- package/dist/cli/cli/src/utils/archive-creator.d.ts +14 -0
- package/dist/cli/cli/src/utils/archive-creator.d.ts.map +1 -1
- package/dist/cli/cli/src/utils/archive-creator.js +24 -3
- package/dist/cli/cli/src/utils/archive-creator.js.map +1 -1
- package/dist/cli/cli/src/utils/cloud-submit.d.ts.map +1 -1
- package/dist/cli/cli/src/utils/cloud-submit.js +14 -5
- package/dist/cli/cli/src/utils/cloud-submit.js.map +1 -1
- package/dist/cli/cli/src/utils/package-validator.d.ts +2 -0
- package/dist/cli/cli/src/utils/package-validator.d.ts.map +1 -1
- package/dist/cli/cli/src/utils/package-validator.js +16 -0
- package/dist/cli/cli/src/utils/package-validator.js.map +1 -1
- package/dist/cli/config/constants.d.ts +48 -0
- package/dist/cli/config/constants.d.ts.map +1 -1
- package/dist/cli/config/constants.js +48 -0
- package/dist/cli/config/constants.js.map +1 -1
- package/dist/cli/config/index.d.ts +2 -2
- package/dist/cli/config/index.d.ts.map +1 -1
- package/dist/cli/config/index.js +2 -2
- package/dist/cli/config/index.js.map +1 -1
- package/frontend/dist/assets/{index-5b98bf77.js → index-e6c403b6.js} +265 -265
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketplace Service
|
|
3
|
+
*
|
|
4
|
+
* Handles fetching the marketplace registry from the Crewly webapp API,
|
|
5
|
+
* managing the local installed-items manifest, and enriching items
|
|
6
|
+
* with install status information.
|
|
7
|
+
*
|
|
8
|
+
* @module services/marketplace/marketplace.service
|
|
9
|
+
*/
|
|
10
|
+
import { homedir } from 'os';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
13
|
+
import { MARKETPLACE_CONSTANTS } from '../../constants.js';
|
|
14
|
+
const CREWLY_HOME = path.join(homedir(), '.crewly');
|
|
15
|
+
const MARKETPLACE_DIR = path.join(CREWLY_HOME, MARKETPLACE_CONSTANTS.DIR_NAME);
|
|
16
|
+
const MANIFEST_PATH = path.join(MARKETPLACE_DIR, MARKETPLACE_CONSTANTS.MANIFEST_FILE);
|
|
17
|
+
const LOCAL_REGISTRY_PATH = path.join(MARKETPLACE_DIR, MARKETPLACE_CONSTANTS.LOCAL_REGISTRY_FILE);
|
|
18
|
+
/** Pattern for valid marketplace item IDs: lowercase alphanumeric with hyphens */
|
|
19
|
+
const VALID_ITEM_ID_PATTERN = /^[a-z0-9][a-z0-9\-]*$/;
|
|
20
|
+
let cachedRegistry = null;
|
|
21
|
+
let cacheTimestamp = 0;
|
|
22
|
+
let fetchInFlight = null;
|
|
23
|
+
/**
|
|
24
|
+
* Validates that a marketplace item ID is safe (no path traversal).
|
|
25
|
+
*
|
|
26
|
+
* @param id - The item ID to validate
|
|
27
|
+
* @returns True if the ID matches the allowed pattern
|
|
28
|
+
*/
|
|
29
|
+
function isValidItemId(id) {
|
|
30
|
+
return VALID_ITEM_ID_PATTERN.test(id);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Fetches the marketplace registry from the Crewly webapp API.
|
|
34
|
+
* Uses in-memory caching with a 1-hour TTL. Concurrent callers share
|
|
35
|
+
* the same in-flight request to avoid duplicate network fetches.
|
|
36
|
+
*
|
|
37
|
+
* On network failure, falls back to the previously cached registry
|
|
38
|
+
* if one is available. Throws only when no cached data exists.
|
|
39
|
+
*
|
|
40
|
+
* @param forceRefresh - If true, bypasses the cache and fetches fresh data
|
|
41
|
+
* @returns The marketplace registry containing all available items
|
|
42
|
+
* @throws Error if the fetch fails and no cached registry is available
|
|
43
|
+
*/
|
|
44
|
+
export async function fetchRegistry(forceRefresh = false) {
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
if (!forceRefresh && cachedRegistry && now - cacheTimestamp < MARKETPLACE_CONSTANTS.CACHE_TTL) {
|
|
47
|
+
return cachedRegistry;
|
|
48
|
+
}
|
|
49
|
+
// When forceRefresh is true, wait for any in-flight request to complete
|
|
50
|
+
// before starting a new fetch to avoid returning stale data
|
|
51
|
+
if (forceRefresh && fetchInFlight) {
|
|
52
|
+
await fetchInFlight.catch(() => { });
|
|
53
|
+
// Fall through to start a new fetch
|
|
54
|
+
}
|
|
55
|
+
// Deduplicate concurrent requests
|
|
56
|
+
if (fetchInFlight) {
|
|
57
|
+
return fetchInFlight;
|
|
58
|
+
}
|
|
59
|
+
fetchInFlight = doFetchRegistry(now).finally(() => {
|
|
60
|
+
fetchInFlight = null;
|
|
61
|
+
});
|
|
62
|
+
return fetchInFlight;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Internal fetch implementation. Separated to enable in-flight deduplication.
|
|
66
|
+
*
|
|
67
|
+
* Fetches from two sources in parallel:
|
|
68
|
+
* 1. Public registry -- GitHub raw content (config/skills/registry.json in crewly repo)
|
|
69
|
+
* 2. Premium registry -- crewlyai.com/api/registry/skills (private/paid skills)
|
|
70
|
+
*
|
|
71
|
+
* Results are merged with local registry items (locally published skills).
|
|
72
|
+
*
|
|
73
|
+
* When both remote sources fail and no local items exist, the empty registry
|
|
74
|
+
* is returned but NOT cached so the next call will retry immediately.
|
|
75
|
+
*/
|
|
76
|
+
async function doFetchRegistry(now) {
|
|
77
|
+
// Fetch public and premium registries in parallel
|
|
78
|
+
const [publicResult, premiumResult] = await Promise.allSettled([
|
|
79
|
+
fetchPublicRegistry(),
|
|
80
|
+
fetchPremiumRegistry(),
|
|
81
|
+
]);
|
|
82
|
+
const publicItems = publicResult.status === 'fulfilled' ? publicResult.value : [];
|
|
83
|
+
const premiumItems = premiumResult.status === 'fulfilled' ? premiumResult.value : [];
|
|
84
|
+
// If both failed and we have a cache, return cache
|
|
85
|
+
if (publicItems.length === 0 && premiumItems.length === 0 && cachedRegistry) {
|
|
86
|
+
return cachedRegistry;
|
|
87
|
+
}
|
|
88
|
+
// Merge: public + premium (premium overwrites by ID if conflict)
|
|
89
|
+
const mergedMap = new Map();
|
|
90
|
+
for (const item of publicItems) {
|
|
91
|
+
mergedMap.set(item.id, { ...item, registrySource: 'public' });
|
|
92
|
+
}
|
|
93
|
+
for (const item of premiumItems) {
|
|
94
|
+
// Keep the public entry a premium one replaces: premium archives can be
|
|
95
|
+
// missing from the CDN while the public copy of the same skill installs.
|
|
96
|
+
const replaced = mergedMap.get(item.id);
|
|
97
|
+
const sameSource = replaced && (replaced.assets.archive ?? replaced.assets.model) === (item.assets.archive ?? item.assets.model);
|
|
98
|
+
mergedMap.set(item.id, {
|
|
99
|
+
...item,
|
|
100
|
+
registrySource: 'premium',
|
|
101
|
+
...(replaced && !sameSource ? { fallback: replaced } : {}),
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Merge locally published skills
|
|
105
|
+
const localItems = await loadLocalRegistry();
|
|
106
|
+
for (const item of localItems) {
|
|
107
|
+
if (!mergedMap.has(item.id)) {
|
|
108
|
+
mergedMap.set(item.id, { ...item, registrySource: 'local' });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const registry = {
|
|
112
|
+
schemaVersion: MARKETPLACE_CONSTANTS.SCHEMA_VERSION,
|
|
113
|
+
lastUpdated: new Date().toISOString(),
|
|
114
|
+
cdnBaseUrl: MARKETPLACE_CONSTANTS.PUBLIC_CDN_BASE,
|
|
115
|
+
items: Array.from(mergedMap.values()),
|
|
116
|
+
};
|
|
117
|
+
// Only cache non-empty registries so that next call retries on total failure
|
|
118
|
+
if (mergedMap.size > 0) {
|
|
119
|
+
cachedRegistry = registry;
|
|
120
|
+
cacheTimestamp = now;
|
|
121
|
+
}
|
|
122
|
+
return registry;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Fetches items from the public registry (GitHub raw content).
|
|
126
|
+
*
|
|
127
|
+
* @returns Array of public marketplace items, empty on failure
|
|
128
|
+
*/
|
|
129
|
+
async function fetchPublicRegistry() {
|
|
130
|
+
try {
|
|
131
|
+
const res = await fetch(MARKETPLACE_CONSTANTS.PUBLIC_REGISTRY_URL, {
|
|
132
|
+
signal: AbortSignal.timeout(MARKETPLACE_CONSTANTS.REGISTRY_FETCH_TIMEOUT),
|
|
133
|
+
});
|
|
134
|
+
if (res.ok) {
|
|
135
|
+
const data = (await res.json());
|
|
136
|
+
return data.items || [];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Public registry unavailable -- continue with empty
|
|
141
|
+
}
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Fetches items from the premium registry (crewlyai.com).
|
|
146
|
+
*
|
|
147
|
+
* @returns Array of premium marketplace items with premium flag, empty on failure
|
|
148
|
+
*/
|
|
149
|
+
async function fetchPremiumRegistry() {
|
|
150
|
+
try {
|
|
151
|
+
const url = `${MARKETPLACE_CONSTANTS.PREMIUM_BASE_URL}${MARKETPLACE_CONSTANTS.PREMIUM_REGISTRY_ENDPOINT}`;
|
|
152
|
+
const res = await fetch(url, {
|
|
153
|
+
signal: AbortSignal.timeout(MARKETPLACE_CONSTANTS.REGISTRY_FETCH_TIMEOUT),
|
|
154
|
+
});
|
|
155
|
+
if (res.ok) {
|
|
156
|
+
const data = (await res.json());
|
|
157
|
+
return (data.items || []).map((item) => ({
|
|
158
|
+
...item,
|
|
159
|
+
metadata: { ...item.metadata, premium: true },
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// Premium registry unavailable -- continue without premium skills
|
|
165
|
+
}
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Loads locally published marketplace items from the local registry file.
|
|
170
|
+
*
|
|
171
|
+
* These are skills that were submitted and approved through the local
|
|
172
|
+
* submission workflow.
|
|
173
|
+
*
|
|
174
|
+
* @returns Array of locally published marketplace items
|
|
175
|
+
*/
|
|
176
|
+
async function loadLocalRegistry() {
|
|
177
|
+
try {
|
|
178
|
+
const data = await readFile(LOCAL_REGISTRY_PATH, 'utf-8');
|
|
179
|
+
const registry = JSON.parse(data);
|
|
180
|
+
if (!Array.isArray(registry.items)) {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
return registry.items;
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return [];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Loads the locally installed items manifest from disk.
|
|
191
|
+
*
|
|
192
|
+
* Returns an empty manifest (schemaVersion 1, no items) if the
|
|
193
|
+
* manifest file does not exist or cannot be parsed.
|
|
194
|
+
*
|
|
195
|
+
* @returns The installed items manifest
|
|
196
|
+
*/
|
|
197
|
+
export async function loadManifest() {
|
|
198
|
+
try {
|
|
199
|
+
const data = await readFile(MANIFEST_PATH, 'utf-8');
|
|
200
|
+
return JSON.parse(data);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return { schemaVersion: 1, items: [] };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Saves the installed items manifest to disk.
|
|
208
|
+
*
|
|
209
|
+
* Creates the marketplace directory if it does not exist.
|
|
210
|
+
*
|
|
211
|
+
* @param manifest - The manifest to persist
|
|
212
|
+
*/
|
|
213
|
+
export async function saveManifest(manifest) {
|
|
214
|
+
await mkdir(MARKETPLACE_DIR, { recursive: true });
|
|
215
|
+
await writeFile(MANIFEST_PATH, JSON.stringify(manifest, null, 2) + '\n');
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Returns a single marketplace item by ID, enriched with install status.
|
|
219
|
+
*
|
|
220
|
+
* @param id - The marketplace item ID to look up
|
|
221
|
+
* @returns The item with install status, or null if not found in the registry
|
|
222
|
+
*/
|
|
223
|
+
export async function getItem(id) {
|
|
224
|
+
const registry = await fetchRegistry();
|
|
225
|
+
const item = registry.items.find((i) => i.id === id);
|
|
226
|
+
if (!item)
|
|
227
|
+
return null;
|
|
228
|
+
const manifest = await loadManifest();
|
|
229
|
+
const installedMap = new Map(manifest.items.map((r) => [r.id, r]));
|
|
230
|
+
return enrichWithStatusFromMap(item, installedMap);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Lists marketplace items with optional filtering, enriched with install status.
|
|
234
|
+
*
|
|
235
|
+
* Supports filtering by type, category, and free-text search (matches name,
|
|
236
|
+
* description, and tags). Results are sorted by the specified sort option,
|
|
237
|
+
* defaulting to most popular (highest downloads).
|
|
238
|
+
*
|
|
239
|
+
* @param filter - Optional filter and sort criteria
|
|
240
|
+
* @returns Filtered and sorted list of items with install status
|
|
241
|
+
*/
|
|
242
|
+
export async function listItems(filter) {
|
|
243
|
+
const registry = await fetchRegistry();
|
|
244
|
+
// Shallow copy to avoid mutating the cached registry's items array via .sort()
|
|
245
|
+
let items = [...registry.items];
|
|
246
|
+
if (filter?.type) {
|
|
247
|
+
items = items.filter((i) => i.type === filter.type);
|
|
248
|
+
}
|
|
249
|
+
if (filter?.category) {
|
|
250
|
+
items = items.filter((i) => i.category === filter.category);
|
|
251
|
+
}
|
|
252
|
+
if (filter?.search) {
|
|
253
|
+
const q = filter.search.toLowerCase();
|
|
254
|
+
items = items.filter((i) => i.name.toLowerCase().includes(q) ||
|
|
255
|
+
i.description.toLowerCase().includes(q) ||
|
|
256
|
+
i.tags.some((t) => t.toLowerCase().includes(q)));
|
|
257
|
+
}
|
|
258
|
+
switch (filter?.sortBy) {
|
|
259
|
+
case 'popular':
|
|
260
|
+
items.sort((a, b) => b.downloads - a.downloads);
|
|
261
|
+
break;
|
|
262
|
+
case 'rating':
|
|
263
|
+
items.sort((a, b) => b.rating - a.rating);
|
|
264
|
+
break;
|
|
265
|
+
case 'newest':
|
|
266
|
+
items.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
267
|
+
break;
|
|
268
|
+
default:
|
|
269
|
+
items.sort((a, b) => b.downloads - a.downloads);
|
|
270
|
+
}
|
|
271
|
+
const manifest = await loadManifest();
|
|
272
|
+
// Use Map for O(n+m) enrichment instead of O(n*m) linear scan per item
|
|
273
|
+
const installedMap = new Map(manifest.items.map((r) => [r.id, r]));
|
|
274
|
+
return items.map((i) => enrichWithStatusFromMap(i, installedMap));
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Returns marketplace items that have updates available.
|
|
278
|
+
*
|
|
279
|
+
* An item has an update available when the installed version differs
|
|
280
|
+
* from the latest version in the registry.
|
|
281
|
+
*
|
|
282
|
+
* @returns Items with installStatus 'update_available'
|
|
283
|
+
*/
|
|
284
|
+
export async function getUpdatableItems() {
|
|
285
|
+
const all = await listItems();
|
|
286
|
+
return all.filter((i) => i.installStatus === 'update_available');
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Returns locally installed items from the manifest.
|
|
290
|
+
*
|
|
291
|
+
* @returns Array of installed item records
|
|
292
|
+
*/
|
|
293
|
+
export async function getInstalledItems() {
|
|
294
|
+
const manifest = await loadManifest();
|
|
295
|
+
return manifest.items;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Searches marketplace items by query string.
|
|
299
|
+
*
|
|
300
|
+
* Convenience wrapper around listItems with a search filter.
|
|
301
|
+
*
|
|
302
|
+
* @param query - Free-text search query
|
|
303
|
+
* @returns Matching items with install status
|
|
304
|
+
*/
|
|
305
|
+
export async function searchItems(query) {
|
|
306
|
+
return listItems({ search: query });
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Enriches a marketplace item with its local install status using a pre-built Map.
|
|
310
|
+
*
|
|
311
|
+
* @param item - The marketplace item from the registry
|
|
312
|
+
* @param installedMap - Map of installed item ID to record for O(1) lookup
|
|
313
|
+
* @returns The item enriched with installStatus and optional installedVersion
|
|
314
|
+
*/
|
|
315
|
+
function enrichWithStatusFromMap(item, installedMap) {
|
|
316
|
+
const installed = installedMap.get(item.id);
|
|
317
|
+
if (!installed) {
|
|
318
|
+
return { ...item, installStatus: 'not_installed' };
|
|
319
|
+
}
|
|
320
|
+
if (installed.version !== item.version) {
|
|
321
|
+
return { ...item, installStatus: 'update_available', installedVersion: installed.version };
|
|
322
|
+
}
|
|
323
|
+
return { ...item, installStatus: 'installed', installedVersion: installed.version };
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Returns the local install path for a marketplace item.
|
|
327
|
+
*
|
|
328
|
+
* Maps item types to directory names:
|
|
329
|
+
* - 'skill' -> 'skills'
|
|
330
|
+
* - 'model' -> 'models'
|
|
331
|
+
* - 'role' -> 'roles'
|
|
332
|
+
*
|
|
333
|
+
* @param type - The marketplace item type
|
|
334
|
+
* @param id - The marketplace item ID
|
|
335
|
+
* @returns Absolute path to the item's install directory
|
|
336
|
+
* @throws Error if the id contains path traversal characters
|
|
337
|
+
*/
|
|
338
|
+
const TYPE_DIR_MAP = {
|
|
339
|
+
skill: 'skills',
|
|
340
|
+
model: 'models',
|
|
341
|
+
role: 'roles',
|
|
342
|
+
mcp_tool: 'mcp-tools',
|
|
343
|
+
};
|
|
344
|
+
export function getInstallPath(type, id) {
|
|
345
|
+
if (!isValidItemId(id)) {
|
|
346
|
+
throw new Error(`Invalid marketplace item ID "${id}": must match ${VALID_ITEM_ID_PATTERN}`);
|
|
347
|
+
}
|
|
348
|
+
const typeDir = TYPE_DIR_MAP[type];
|
|
349
|
+
return path.join(MARKETPLACE_DIR, typeDir, id);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Retrieves the README.md content for a marketplace item.
|
|
353
|
+
*
|
|
354
|
+
* Looks for a README.md file in the item's install directory. Tries multiple
|
|
355
|
+
* filenames (README.md, readme.md, SKILL.md) for flexibility.
|
|
356
|
+
*
|
|
357
|
+
* @param id - The marketplace item ID
|
|
358
|
+
* @returns The README content as a string, or null if not found
|
|
359
|
+
* @throws Error if the item ID is invalid (path traversal protection)
|
|
360
|
+
*/
|
|
361
|
+
export async function getItemReadme(id) {
|
|
362
|
+
if (!isValidItemId(id)) {
|
|
363
|
+
throw new Error(`Invalid marketplace item ID "${id}": must match ${VALID_ITEM_ID_PATTERN}`);
|
|
364
|
+
}
|
|
365
|
+
// Try skill type first (most common), then other types
|
|
366
|
+
const types = ['skill', 'role', 'mcp_tool', 'model'];
|
|
367
|
+
const readmeNames = ['README.md', 'readme.md', 'SKILL.md'];
|
|
368
|
+
for (const type of types) {
|
|
369
|
+
const installDir = getInstallPath(type, id);
|
|
370
|
+
for (const name of readmeNames) {
|
|
371
|
+
try {
|
|
372
|
+
const content = await readFile(path.join(installDir, name), 'utf-8');
|
|
373
|
+
return content;
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
// File doesn't exist for this type/name combo — try next
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Resets the in-memory registry cache.
|
|
384
|
+
*
|
|
385
|
+
* Used primarily for testing to ensure a clean state between test runs.
|
|
386
|
+
*/
|
|
387
|
+
export function resetRegistryCache() {
|
|
388
|
+
cachedRegistry = null;
|
|
389
|
+
cacheTimestamp = 0;
|
|
390
|
+
fetchInFlight = null;
|
|
391
|
+
}
|
|
392
|
+
//# sourceMappingURL=marketplace.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"marketplace.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/marketplace/marketplace.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill discovery — find a skill for a capability, locally or in the marketplace.
|
|
3
|
+
*
|
|
4
|
+
* Backs `find-skill` and `install-skill` (specs/skill-auto-install.md). It
|
|
5
|
+
* merges three sources into one candidate list:
|
|
6
|
+
*
|
|
7
|
+
* 1. **Bundled** skills shipped in the npm package (`config/skills/agent/**`).
|
|
8
|
+
* They are present on every install, so `find-skill` works for them even
|
|
9
|
+
* when the registry has no entry or the network is down.
|
|
10
|
+
* 2. **Installed** marketplace skills (`~/.crewly/marketplace/skills/<id>`).
|
|
11
|
+
* 3. **Registry** entries (public GitHub registry + crewlyai.com), fetched
|
|
12
|
+
* with the existing marketplace service (cached, deduplicated).
|
|
13
|
+
*
|
|
14
|
+
* ## What "official" means
|
|
15
|
+
*
|
|
16
|
+
* A skill is official — and may be installed by an agent without asking — when
|
|
17
|
+
* it is **bundled with Crewly**, or it is listed in an **official registry**
|
|
18
|
+
* (the public GitHub registry or crewlyai.com, both curated by Crewly
|
|
19
|
+
* maintainers) with `author` "Crewly Team" / "crewly" or `metadata.verified:
|
|
20
|
+
* true`. Entries from the machine-local registry (`local-registry.json`,
|
|
21
|
+
* written by local publishing) are never official, whatever author they claim.
|
|
22
|
+
* A marketplace skill installed earlier counts as official only when the
|
|
23
|
+
* registry still lists it as official.
|
|
24
|
+
*
|
|
25
|
+
* @module services/skill-setup/skill-discovery.service
|
|
26
|
+
*/
|
|
27
|
+
import type { MarketplaceItem, MarketplaceRegistry, InstalledItemsManifest } from '../../types/marketplace.types.js';
|
|
28
|
+
import { type SkillSetupManifest } from './skill-setup-manifest.js';
|
|
29
|
+
import { type SkillSetupRunner } from './skill-setup-runner.service.js';
|
|
30
|
+
/** Where a candidate was found. */
|
|
31
|
+
export type SkillSource = 'bundled' | 'installed' | 'registry';
|
|
32
|
+
/** Setup state reported for a candidate. */
|
|
33
|
+
export interface CandidateSetup {
|
|
34
|
+
/** The skill declares a setup block */
|
|
35
|
+
declared: boolean;
|
|
36
|
+
/** Minutes a first-time setup takes (quoted to the user) */
|
|
37
|
+
estimatedMinutes?: number;
|
|
38
|
+
/** Result of a check-only probe (undefined when not probed) */
|
|
39
|
+
satisfied?: boolean;
|
|
40
|
+
/** Steps that are missing (check-only probe) */
|
|
41
|
+
missing?: string[];
|
|
42
|
+
}
|
|
43
|
+
/** One skill that could provide a capability. */
|
|
44
|
+
export interface SkillCandidate {
|
|
45
|
+
/** The id `install-skill --id` takes */
|
|
46
|
+
id: string;
|
|
47
|
+
name: string;
|
|
48
|
+
description: string;
|
|
49
|
+
tags: string[];
|
|
50
|
+
triggers: string[];
|
|
51
|
+
source: SkillSource;
|
|
52
|
+
official: boolean;
|
|
53
|
+
/** Why it is (or is not) official, in words */
|
|
54
|
+
officialReason: string;
|
|
55
|
+
author?: string;
|
|
56
|
+
version?: string;
|
|
57
|
+
/** The skill's files are on this machine */
|
|
58
|
+
installed: boolean;
|
|
59
|
+
/** Installed and its setup (if any) is satisfied — usable right now */
|
|
60
|
+
ready?: boolean;
|
|
61
|
+
/** Local skill directory */
|
|
62
|
+
skillDir?: string;
|
|
63
|
+
/** Local entry point (`<skillDir>/execute.sh`) */
|
|
64
|
+
executePath?: string;
|
|
65
|
+
/** Registry id when it differs from `id` */
|
|
66
|
+
registryId?: string;
|
|
67
|
+
setup: CandidateSetup;
|
|
68
|
+
/** Ranking score (higher is better) */
|
|
69
|
+
score: number;
|
|
70
|
+
}
|
|
71
|
+
/** A candidate plus the internals install-skill needs. */
|
|
72
|
+
export interface ResolvedSkill extends SkillCandidate {
|
|
73
|
+
/** Parsed, validated setup manifest (local copy preferred, registry fallback) */
|
|
74
|
+
manifest?: SkillSetupManifest;
|
|
75
|
+
/** Setup block that failed validation (message) */
|
|
76
|
+
manifestError?: string;
|
|
77
|
+
/** The registry entry, when there is one */
|
|
78
|
+
registryItem?: MarketplaceItem;
|
|
79
|
+
}
|
|
80
|
+
/** Injectable dependencies. */
|
|
81
|
+
export interface SkillDiscoveryDeps {
|
|
82
|
+
/** Root of bundled agent skills (default: `<package>/config/skills/agent`) */
|
|
83
|
+
bundledRoot?: string;
|
|
84
|
+
/** Directory of marketplace-installed skills */
|
|
85
|
+
installedRoot?: string;
|
|
86
|
+
fetchRegistry?: () => Promise<MarketplaceRegistry>;
|
|
87
|
+
loadManifest?: () => Promise<InstalledItemsManifest>;
|
|
88
|
+
runner?: SkillSetupRunner;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Split a query into lower-case tokens, dropping stopwords.
|
|
92
|
+
*
|
|
93
|
+
* @param text - Free text
|
|
94
|
+
* @returns Tokens (CJK runs stay whole)
|
|
95
|
+
*/
|
|
96
|
+
export declare function tokenize(text: string): string[];
|
|
97
|
+
/**
|
|
98
|
+
* Expand tokens with synonyms (QUERY_SYNONYMS). CJK tokens also match a
|
|
99
|
+
* synonym key they contain ("语音消息" contains "语音").
|
|
100
|
+
*
|
|
101
|
+
* @param tokens - Query tokens
|
|
102
|
+
* @returns Original tokens (weight 1) and synonyms (weight 0.6), deduplicated
|
|
103
|
+
*/
|
|
104
|
+
export declare function expandTokens(tokens: string[]): Array<{
|
|
105
|
+
token: string;
|
|
106
|
+
weight: number;
|
|
107
|
+
}>;
|
|
108
|
+
/** The text fields a candidate is scored on. */
|
|
109
|
+
export interface ScoredDoc {
|
|
110
|
+
id: string;
|
|
111
|
+
name: string;
|
|
112
|
+
tags: string[];
|
|
113
|
+
triggers: string[];
|
|
114
|
+
description: string;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Score how well a skill matches a query.
|
|
118
|
+
*
|
|
119
|
+
* Each query token (and synonym, at reduced weight) takes its best match:
|
|
120
|
+
* exact id/name 10, exact tag 6, id/name contains 5, trigger contains 3,
|
|
121
|
+
* tag overlap 3, description contains 1.5. The whole query appearing in a
|
|
122
|
+
* trigger or the description adds 4.
|
|
123
|
+
*
|
|
124
|
+
* @param query - Free-text need ("transcribe a voice message")
|
|
125
|
+
* @param doc - Candidate fields
|
|
126
|
+
* @returns Score (0 = no match)
|
|
127
|
+
*/
|
|
128
|
+
export declare function scoreSkill(query: string, doc: ScoredDoc): number;
|
|
129
|
+
/**
|
|
130
|
+
* Whether a registry entry is official (see module docs).
|
|
131
|
+
*
|
|
132
|
+
* @param item - Registry entry (with registrySource set by fetchRegistry)
|
|
133
|
+
* @returns `{ official, reason }`
|
|
134
|
+
*/
|
|
135
|
+
export declare function registryOfficial(item: MarketplaceItem): {
|
|
136
|
+
official: boolean;
|
|
137
|
+
reason: string;
|
|
138
|
+
};
|
|
139
|
+
/** Finds and describes skills. */
|
|
140
|
+
export declare class SkillDiscoveryService {
|
|
141
|
+
private readonly bundledRoot;
|
|
142
|
+
private readonly installedRoot;
|
|
143
|
+
private readonly fetchRegistryFn;
|
|
144
|
+
private readonly loadManifestFn;
|
|
145
|
+
private readonly runner;
|
|
146
|
+
/**
|
|
147
|
+
* @param deps - Injectable dependencies (all optional)
|
|
148
|
+
*/
|
|
149
|
+
constructor(deps?: SkillDiscoveryDeps);
|
|
150
|
+
/**
|
|
151
|
+
* `<package root>/config/skills/agent`, or null when the package root cannot be found.
|
|
152
|
+
*
|
|
153
|
+
* @returns Absolute path or null
|
|
154
|
+
*/
|
|
155
|
+
static defaultBundledRoot(): string | null;
|
|
156
|
+
/**
|
|
157
|
+
* Where to start looking for the package root.
|
|
158
|
+
*
|
|
159
|
+
* The compiled backend and CLI are ES modules, where `__dirname` does not
|
|
160
|
+
* exist (a bare reference throws); under ts-jest it does. The entry script
|
|
161
|
+
* (`process.argv[1]`, realpath'd through the global `bin` symlink) is
|
|
162
|
+
* always inside the package at runtime, so it is the anchor that works
|
|
163
|
+
* for `crewly` and the backend alike; cwd is the last resort.
|
|
164
|
+
*
|
|
165
|
+
* @returns Candidate start directories, most reliable first
|
|
166
|
+
*/
|
|
167
|
+
static packageSearchStarts(): string[];
|
|
168
|
+
/**
|
|
169
|
+
* Find skills for a need, best first.
|
|
170
|
+
*
|
|
171
|
+
* @param query - What the agent needs ("transcribe voice message", "read pdf")
|
|
172
|
+
* @param options - `limit` (default FIND_MAX_RESULTS); `probe` runs check-only setup on the top few (default true)
|
|
173
|
+
* @returns Ranked candidates (score > 0) and whether the registry was reachable
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* const { candidates } = await discovery.find('transcribe a voice message');
|
|
178
|
+
* candidates[0].id; // 'transcribe-audio'
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
find(query: string, options?: {
|
|
182
|
+
limit?: number;
|
|
183
|
+
probe?: boolean;
|
|
184
|
+
}): Promise<{
|
|
185
|
+
candidates: SkillCandidate[];
|
|
186
|
+
registryAvailable: boolean;
|
|
187
|
+
}>;
|
|
188
|
+
/**
|
|
189
|
+
* Resolve one skill by id (local id, directory name or registry id).
|
|
190
|
+
*
|
|
191
|
+
* @param id - Skill id
|
|
192
|
+
* @returns The skill, or null when neither bundled, installed nor in the registry
|
|
193
|
+
*/
|
|
194
|
+
resolve(id: string): Promise<ResolvedSkill | null>;
|
|
195
|
+
/**
|
|
196
|
+
* Resolve a skill that is on this machine (bundled or installed) without
|
|
197
|
+
* touching the network — for `crewly skills setup` offline.
|
|
198
|
+
*
|
|
199
|
+
* Official status of an installed marketplace skill needs the registry, so
|
|
200
|
+
* here it is reported as not official; bundled skills are official.
|
|
201
|
+
*
|
|
202
|
+
* @param id - Skill id or directory name
|
|
203
|
+
* @returns The skill, or null when it is not on this machine
|
|
204
|
+
*/
|
|
205
|
+
resolveLocal(id: string): Promise<ResolvedSkill | null>;
|
|
206
|
+
/**
|
|
207
|
+
* Pick a candidate by id, registry id or directory name.
|
|
208
|
+
*
|
|
209
|
+
* @param id - Wanted id
|
|
210
|
+
* @param all - Candidates
|
|
211
|
+
* @returns The match or null
|
|
212
|
+
*/
|
|
213
|
+
private pick;
|
|
214
|
+
/**
|
|
215
|
+
* Run a check-only setup probe and record the result on the candidate.
|
|
216
|
+
*
|
|
217
|
+
* @param c - Candidate (mutated)
|
|
218
|
+
* @returns The same candidate
|
|
219
|
+
*/
|
|
220
|
+
probe(c: ResolvedSkill): Promise<ResolvedSkill>;
|
|
221
|
+
/**
|
|
222
|
+
* Strip internals for API output.
|
|
223
|
+
*
|
|
224
|
+
* @param c - Resolved candidate
|
|
225
|
+
* @returns Public candidate
|
|
226
|
+
*/
|
|
227
|
+
publicView(c: ResolvedSkill): SkillCandidate;
|
|
228
|
+
/**
|
|
229
|
+
* Build every candidate from all sources.
|
|
230
|
+
*
|
|
231
|
+
* @param options - `registry: false` skips the registry fetch
|
|
232
|
+
* @returns Candidates and registry reachability
|
|
233
|
+
*/
|
|
234
|
+
private collect;
|
|
235
|
+
/**
|
|
236
|
+
* Candidate for a local (bundled or installed) skill.
|
|
237
|
+
*
|
|
238
|
+
* @param local - Local skill
|
|
239
|
+
* @param item - Matching registry entry, if any
|
|
240
|
+
* @returns Candidate
|
|
241
|
+
*/
|
|
242
|
+
private fromLocal;
|
|
243
|
+
/**
|
|
244
|
+
* Candidate for a registry-only skill.
|
|
245
|
+
*
|
|
246
|
+
* @param item - Registry entry
|
|
247
|
+
* @param installed - The marketplace manifest lists it (files may still be missing)
|
|
248
|
+
* @returns Candidate
|
|
249
|
+
*/
|
|
250
|
+
private fromRegistry;
|
|
251
|
+
/**
|
|
252
|
+
* Validate a raw setup block and record it on the candidate.
|
|
253
|
+
*
|
|
254
|
+
* @param c - Candidate (mutated)
|
|
255
|
+
* @param raw - Raw `setup` value
|
|
256
|
+
*/
|
|
257
|
+
private attachSetup;
|
|
258
|
+
/**
|
|
259
|
+
* Scan a skills root: direct skill dirs, and one level of category dirs.
|
|
260
|
+
*
|
|
261
|
+
* @param root - Directory
|
|
262
|
+
* @param source - Label for what is found
|
|
263
|
+
* @returns Local skills
|
|
264
|
+
*/
|
|
265
|
+
private scanRoot;
|
|
266
|
+
/**
|
|
267
|
+
* Read one skill directory (SKILL.md frontmatter wins over skill.json, as in the registry builder).
|
|
268
|
+
*
|
|
269
|
+
* @param dir - Skill directory
|
|
270
|
+
* @param source - Label
|
|
271
|
+
* @returns The skill, or null when the directory has no manifest
|
|
272
|
+
*/
|
|
273
|
+
private readLocal;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* The process-wide discovery service.
|
|
277
|
+
*
|
|
278
|
+
* @returns The service
|
|
279
|
+
*/
|
|
280
|
+
export declare function getSkillDiscoveryService(): SkillDiscoveryService;
|
|
281
|
+
//# sourceMappingURL=skill-discovery.service.d.ts.map
|