crewly 1.20.114 → 1.20.116
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 +1 -0
- package/config/skills/marketplace-registry.ts +256 -0
- package/config/skills/registry.json +441 -283
- package/dist/backend/backend/src/constants.d.ts +18 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +18 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +4 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +27 -0
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +60 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/request.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/request.service.js +7 -0
- package/dist/backend/backend/src/services/v3/request.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-channel-hooks.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-channel-hooks.js +3 -2
- package/dist/backend/backend/src/services/v3/ticket-channel-hooks.js.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-intake.service.d.ts +12 -0
- package/dist/backend/backend/src/services/v3/ticket-intake.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-intake.service.js +38 -5
- package/dist/backend/backend/src/services/v3/ticket-intake.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-review.service.d.ts +14 -0
- package/dist/backend/backend/src/services/v3/ticket-review.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-review.service.js +48 -5
- package/dist/backend/backend/src/services/v3/ticket-review.service.js.map +1 -1
- package/dist/backend/backend/src/types/v2/request.types.d.ts +6 -0
- package/dist/backend/backend/src/types/v2/request.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/v2/request.types.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +18 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +18 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +27 -0
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +60 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/cli/backend/src/services/v3/request.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/request.service.js +7 -0
- package/dist/cli/backend/src/services/v3/request.service.js.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-channel-hooks.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-channel-hooks.js +3 -2
- package/dist/cli/backend/src/services/v3/ticket-channel-hooks.js.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-intake.service.d.ts +12 -0
- package/dist/cli/backend/src/services/v3/ticket-intake.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-intake.service.js +38 -5
- package/dist/cli/backend/src/services/v3/ticket-intake.service.js.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-review.service.d.ts +14 -0
- package/dist/cli/backend/src/services/v3/ticket-review.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-review.service.js +48 -5
- package/dist/cli/backend/src/services/v3/ticket-review.service.js.map +1 -1
- package/dist/cli/backend/src/types/v2/request.types.d.ts +6 -0
- package/dist/cli/backend/src/types/v2/request.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/v2/request.types.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ The `init` command walks you through provider selection, installs agent skills,
|
|
|
38
38
|
- **Node.js 22 or later** (with npm). Node 20 is end-of-life and its `better-sqlite3` has no prebuilt binary.
|
|
39
39
|
- **No C/C++ toolchain on macOS (x64/arm64) or glibc Linux (x64/arm64).** The native modules install from prebuilt binaries there. You do need `python3`, `make` and `g++` (Xcode Command Line Tools on macOS) in these cases:
|
|
40
40
|
- Alpine/musl, glibc older than 2.28, or other architectures, where `node-pty` compiles from source
|
|
41
|
+
- a network that blocks the prebuilt download: `better-sqlite3` fetches its binary from GitHub at install time and compiles from source if it cannot
|
|
41
42
|
|
|
42
43
|
On Debian/Ubuntu, install the toolchain with `sudo apt-get install -y python3 make g++`. `crewly doctor` tells you whether you need it.
|
|
43
44
|
- **jq** (`brew install jq` on macOS, `sudo apt-get install -y jq` on Debian/Ubuntu). Agent skills use it, and `crewly init` stops if it is missing.
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds the public marketplace registry (config/skills/registry.json) from
|
|
3
|
+
* the skill directories under config/skills/agent/marketplace/.
|
|
4
|
+
*
|
|
5
|
+
* Pure, filesystem-reading logic shared by scripts/generate-registry.ts (which
|
|
6
|
+
* writes the file) and the registry guard test (which checks the committed file
|
|
7
|
+
* is up to date). It lives under config/ so jest runs its tests.
|
|
8
|
+
*
|
|
9
|
+
* @module config/skills/marketplace-registry
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import YAML from 'yaml';
|
|
15
|
+
import { MARKETPLACE_CONSTANTS } from '../constants.js';
|
|
16
|
+
|
|
17
|
+
/** Repo-relative directory holding marketplace skills. */
|
|
18
|
+
export const MARKETPLACE_SKILLS_REL_DIR = 'config/skills/agent/marketplace';
|
|
19
|
+
|
|
20
|
+
/** Manifest fields the registry reads, from SKILL.md frontmatter or skill.json. */
|
|
21
|
+
export interface SkillManifest {
|
|
22
|
+
id?: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
version?: string;
|
|
26
|
+
category?: string;
|
|
27
|
+
author?: string;
|
|
28
|
+
license?: string;
|
|
29
|
+
tags?: string[];
|
|
30
|
+
skillType?: string;
|
|
31
|
+
assignableRoles?: string[];
|
|
32
|
+
triggers?: string[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** One public registry entry. */
|
|
36
|
+
export interface RegistryItem {
|
|
37
|
+
id: string;
|
|
38
|
+
type: 'skill';
|
|
39
|
+
name: string;
|
|
40
|
+
description: string;
|
|
41
|
+
author: string;
|
|
42
|
+
version: string;
|
|
43
|
+
category: string;
|
|
44
|
+
tags: string[];
|
|
45
|
+
license: string;
|
|
46
|
+
downloads: number;
|
|
47
|
+
rating: number;
|
|
48
|
+
createdAt: string;
|
|
49
|
+
updatedAt: string;
|
|
50
|
+
source: string;
|
|
51
|
+
assets: { archive: string; checksum: string; sizeBytes: number };
|
|
52
|
+
metadata: { skillType?: string; assignableRoles?: string[]; triggers?: string[]; files: string[] };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The public registry document. */
|
|
56
|
+
export interface Registry {
|
|
57
|
+
schemaVersion: number;
|
|
58
|
+
lastUpdated: string;
|
|
59
|
+
cdnBaseUrl: string;
|
|
60
|
+
source: string;
|
|
61
|
+
items: RegistryItem[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** A marketplace directory that could not be listed, and why. */
|
|
65
|
+
export interface SkippedDir {
|
|
66
|
+
dir: string;
|
|
67
|
+
reason: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Parse the YAML frontmatter of a SKILL.md file.
|
|
72
|
+
*
|
|
73
|
+
* @param markdown - SKILL.md contents
|
|
74
|
+
* @returns The frontmatter object, or null when the file has none
|
|
75
|
+
*/
|
|
76
|
+
export function parseFrontmatter(markdown: string): Record<string, unknown> | null {
|
|
77
|
+
const match = markdown.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);
|
|
78
|
+
if (!match) return null;
|
|
79
|
+
const parsed = YAML.parse(match[1]) as unknown;
|
|
80
|
+
return parsed && typeof parsed === 'object' ? (parsed as Record<string, unknown>) : null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Read a skill directory's manifest. SKILL.md frontmatter wins field by field
|
|
85
|
+
* (the CLI installs SKILL.md first); skill.json fills anything it lacks.
|
|
86
|
+
*
|
|
87
|
+
* @param skillDir - Absolute skill directory
|
|
88
|
+
* @returns The merged manifest, or null when the directory has neither file
|
|
89
|
+
*/
|
|
90
|
+
export function readSkillManifest(skillDir: string): SkillManifest | null {
|
|
91
|
+
const mdPath = path.join(skillDir, 'SKILL.md');
|
|
92
|
+
const jsonPath = path.join(skillDir, 'skill.json');
|
|
93
|
+
const fromMd = existsSync(mdPath) ? parseFrontmatter(readFileSync(mdPath, 'utf-8')) : null;
|
|
94
|
+
const fromJson = existsSync(jsonPath) ? (JSON.parse(readFileSync(jsonPath, 'utf-8')) as Record<string, unknown>) : null;
|
|
95
|
+
if (!fromMd && !fromJson) return null;
|
|
96
|
+
return { ...(fromJson ?? {}), ...(fromMd ?? {}) } as SkillManifest;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Files a skill must not ship: tests (`*.test.*`), test fixtures (`mock-*`)
|
|
101
|
+
* and packaging hints (`.crewlyignore`).
|
|
102
|
+
*/
|
|
103
|
+
export const SKILL_FILE_EXCLUDES: ReadonlyArray<RegExp> = [/\.test\./, /^mock-/, /^\.crewlyignore$/];
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The files the CLI should download for a skill: every regular file directly
|
|
107
|
+
* in the directory, minus SKILL_FILE_EXCLUDES, sorted.
|
|
108
|
+
*
|
|
109
|
+
* The CLI fetches `metadata.files` (plus SKILL.md and skill.json) from GitHub
|
|
110
|
+
* raw content; without the list it fetches only SKILL.md, execute.sh,
|
|
111
|
+
* skill.json and instructions.md, so e.g. nano-banana-image's generate.sh was
|
|
112
|
+
* never installed.
|
|
113
|
+
*
|
|
114
|
+
* Deliberately FLAT: files in subdirectories (e.g. remotion-video/templates/)
|
|
115
|
+
* are not listed. The shipped CLI writes each listed file without creating
|
|
116
|
+
* parent directories, so a nested path would fail the whole install on every
|
|
117
|
+
* CLI released so far. List them only after the CLI creates parent dirs AND
|
|
118
|
+
* the older CLIs are no longer in use.
|
|
119
|
+
*
|
|
120
|
+
* @param skillDir - Absolute skill directory
|
|
121
|
+
* @returns File names relative to the skill directory
|
|
122
|
+
*/
|
|
123
|
+
export function listSkillFiles(skillDir: string): string[] {
|
|
124
|
+
return readdirSync(skillDir, { withFileTypes: true })
|
|
125
|
+
.filter((e) => e.isFile() && !SKILL_FILE_EXCLUDES.some((re) => re.test(e.name)))
|
|
126
|
+
.map((e) => e.name)
|
|
127
|
+
.sort((a, b) => a.localeCompare(b));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Total size in bytes of the regular files directly inside a directory. */
|
|
131
|
+
function directorySize(dir: string): number {
|
|
132
|
+
return readdirSync(dir)
|
|
133
|
+
.map((f) => statSync(path.join(dir, f)))
|
|
134
|
+
.filter((s) => s.isFile())
|
|
135
|
+
.reduce((sum, s) => sum + s.size, 0);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** The fields derived from the skill's files (everything except dates and counters). */
|
|
139
|
+
function contentOf(item: RegistryItem): string {
|
|
140
|
+
return JSON.stringify({ ...item, createdAt: undefined, updatedAt: undefined, downloads: undefined, rating: undefined });
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Build the registry from the marketplace directories.
|
|
145
|
+
*
|
|
146
|
+
* Ids are stable: a skill the registry already lists (matched by source path)
|
|
147
|
+
* keeps its published id, because users and published docs install it by that
|
|
148
|
+
* id (e.g. `crewly install agent-send-pdf-to-slack`). A new skill gets its
|
|
149
|
+
* directory name, the id the CLI installs under. The same id must be used by
|
|
150
|
+
* crewlyai.com's registry, or the CLI cannot pair the two entries.
|
|
151
|
+
* Name, description and version come from the manifest.
|
|
152
|
+
*
|
|
153
|
+
* Skills outside the marketplace directory (e.g. config/skills/agent/browse-stealth)
|
|
154
|
+
* are listed only when the committed registry already lists them: their entry
|
|
155
|
+
* is rebuilt from the same source directory, or reported as skipped when that
|
|
156
|
+
* directory is gone. Items are sorted by id.
|
|
157
|
+
*
|
|
158
|
+
* Entries that already exist (matched by source path) keep createdAt,
|
|
159
|
+
* downloads and rating; updatedAt changes only when the entry's content
|
|
160
|
+
* changes, and lastUpdated only when any entry changes. Re-running with no
|
|
161
|
+
* skill changes therefore reproduces the committed file byte for byte.
|
|
162
|
+
*
|
|
163
|
+
* @param repoRoot - Repository root
|
|
164
|
+
* @param previous - The currently committed registry, if any
|
|
165
|
+
* @param now - ISO timestamp for new or changed entries
|
|
166
|
+
* @returns The registry and the directories that could not be listed
|
|
167
|
+
*/
|
|
168
|
+
export function buildRegistry(
|
|
169
|
+
repoRoot: string,
|
|
170
|
+
previous: Registry | null,
|
|
171
|
+
now: string
|
|
172
|
+
): { registry: Registry; skipped: SkippedDir[] } {
|
|
173
|
+
const baseDir = path.join(repoRoot, MARKETPLACE_SKILLS_REL_DIR);
|
|
174
|
+
const previousBySource = new Map((previous?.items ?? []).map((i) => [i.source, i]));
|
|
175
|
+
const skipped: SkippedDir[] = [];
|
|
176
|
+
const items: RegistryItem[] = [];
|
|
177
|
+
|
|
178
|
+
const dirs = readdirSync(baseDir, { withFileTypes: true })
|
|
179
|
+
.filter((e) => e.isDirectory())
|
|
180
|
+
.map((e) => e.name)
|
|
181
|
+
.sort((a, b) => a.localeCompare(b));
|
|
182
|
+
|
|
183
|
+
// Marketplace directories, plus sources outside it that the registry already lists.
|
|
184
|
+
const sources = dirs.map((dir) => `${MARKETPLACE_SKILLS_REL_DIR}/${dir}`);
|
|
185
|
+
for (const prior of previous?.items ?? []) {
|
|
186
|
+
if (!prior.source.startsWith(`${MARKETPLACE_SKILLS_REL_DIR}/`) && !sources.includes(prior.source)) {
|
|
187
|
+
sources.push(prior.source);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
for (const source of sources) {
|
|
192
|
+
const dir = path.basename(source);
|
|
193
|
+
const skillDir = path.join(repoRoot, source);
|
|
194
|
+
if (!existsSync(skillDir)) {
|
|
195
|
+
skipped.push({ dir: source, reason: 'listed in the registry but the directory no longer exists' });
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const manifest = readSkillManifest(skillDir);
|
|
199
|
+
if (!manifest) {
|
|
200
|
+
skipped.push({ dir: source, reason: 'no SKILL.md frontmatter or skill.json' });
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (!manifest.name) {
|
|
204
|
+
skipped.push({ dir: source, reason: 'manifest has no name' });
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const prior = previousBySource.get(source);
|
|
208
|
+
const draft: RegistryItem = {
|
|
209
|
+
id: prior?.id ?? dir,
|
|
210
|
+
type: 'skill',
|
|
211
|
+
name: manifest.name,
|
|
212
|
+
description: manifest.description ?? '',
|
|
213
|
+
author: manifest.author || 'Crewly Team',
|
|
214
|
+
version: manifest.version || '1.0.0',
|
|
215
|
+
category: (manifest.category && MARKETPLACE_CONSTANTS.CATEGORY_MAP[manifest.category]) || 'development',
|
|
216
|
+
tags: manifest.tags ?? [],
|
|
217
|
+
license: manifest.license || 'MIT',
|
|
218
|
+
downloads: 0,
|
|
219
|
+
rating: 0,
|
|
220
|
+
createdAt: now,
|
|
221
|
+
updatedAt: now,
|
|
222
|
+
source,
|
|
223
|
+
assets: { archive: source, checksum: '', sizeBytes: directorySize(skillDir) },
|
|
224
|
+
metadata: {
|
|
225
|
+
skillType: manifest.skillType,
|
|
226
|
+
assignableRoles: manifest.assignableRoles,
|
|
227
|
+
triggers: manifest.triggers,
|
|
228
|
+
files: listSkillFiles(skillDir),
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
if (prior) {
|
|
232
|
+
draft.createdAt = prior.createdAt;
|
|
233
|
+
draft.downloads = prior.downloads;
|
|
234
|
+
draft.rating = prior.rating;
|
|
235
|
+
if (contentOf(prior) === contentOf(draft)) draft.updatedAt = prior.updatedAt;
|
|
236
|
+
}
|
|
237
|
+
items.push(draft);
|
|
238
|
+
}
|
|
239
|
+
items.sort((a, b) => a.id.localeCompare(b.id));
|
|
240
|
+
|
|
241
|
+
const unchanged =
|
|
242
|
+
previous !== null &&
|
|
243
|
+
previous.items.length === items.length &&
|
|
244
|
+
previous.items.every((p, i) => JSON.stringify(p) === JSON.stringify(items[i]));
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
registry: {
|
|
248
|
+
schemaVersion: MARKETPLACE_CONSTANTS.SCHEMA_VERSION,
|
|
249
|
+
lastUpdated: unchanged ? previous.lastUpdated : now,
|
|
250
|
+
cdnBaseUrl: MARKETPLACE_CONSTANTS.PUBLIC_CDN_BASE,
|
|
251
|
+
source: 'github',
|
|
252
|
+
items,
|
|
253
|
+
},
|
|
254
|
+
skipped,
|
|
255
|
+
};
|
|
256
|
+
}
|