@sentry/junior 0.203.0 → 0.205.0
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/agent-D4nu8MQV.js +17521 -0
- package/dist/agent-hooks-DXHpTJ4m.js +4042 -0
- package/dist/agent-runner-C04colea.js +678 -0
- package/dist/api/schema.d.ts +2736 -2639
- package/dist/api/schema.js +4 -169
- package/dist/api.d.ts +23 -17
- package/dist/api.js +3881 -5326
- package/dist/app-Cb776lL8.d.ts +247 -0
- package/dist/app.d.ts +4 -520
- package/dist/app.js +9524 -11875
- package/dist/auth-pause-state-C2P3guS1.js +168 -0
- package/dist/catalog-runtime-CXw3asJD.js +7 -0
- package/dist/checkpoint-CrViJ8Ps.js +2450 -0
- package/dist/cli/chat.d.ts +20 -0
- package/dist/cli/chat.js +229 -336
- package/dist/cli/check.d.ts +8 -0
- package/dist/cli/check.js +513 -806
- package/dist/cli/env.d.ts +13 -0
- package/dist/cli/env.js +63 -12
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.js +90 -159
- package/dist/cli/main.d.ts +6 -0
- package/dist/cli/main.js +43 -64
- package/dist/cli/plugins.d.ts +13 -0
- package/dist/cli/plugins.js +148 -227
- package/dist/cli/run.d.ts +16 -0
- package/dist/cli/run.js +52 -8
- package/dist/cli/snapshot-create.d.ts +4 -0
- package/dist/cli/snapshot-create.js +71 -125
- package/dist/cli/upgrade.d.ts +15 -0
- package/dist/cli/upgrade.js +232 -335
- package/dist/config-BOtJQ7Pc.js +941 -0
- package/dist/context-B45_WqVX.js +298 -0
- package/dist/conversation-privacy-BZRA6Pes.js +182 -0
- package/dist/conversation-work-C6y3T5Hx.js +51 -0
- package/dist/credential-sync-Bep34IB_.js +2661 -0
- package/dist/db-BeyXKJOI.d.ts +5 -0
- package/dist/destinations-BeBiJeEM.js +29 -0
- package/dist/execution-stats-B6lgWxCK.js +373 -0
- package/dist/executor-BlUhFL7_.js +721 -0
- package/dist/experimental-CIXf7Uye.js +46 -0
- package/dist/history-ClE4toxe.d.ts +32 -0
- package/dist/history-CqTuSAdI.js +441 -0
- package/dist/instrumentation.d.ts +3 -3
- package/dist/instrumentation.js +130 -176
- package/dist/job-queue-DLMqIK74.js +63 -0
- package/dist/logging-BMJzdDlP.js +1210 -0
- package/dist/model-profile-CZmVoGdX.js +49 -0
- package/dist/nitro.d.ts +31 -36
- package/dist/nitro.js +275 -456
- package/dist/oauth-callback-server-WwG5_Zgm.js +130 -0
- package/dist/oauth-relay-CdCnu80W.js +62 -0
- package/dist/package-discovery-7a9YqLhE.js +455 -0
- package/dist/pending-auth-CUbXq06G.js +185 -0
- package/dist/plugin-module-BKGOXca2.js +81 -0
- package/dist/plugins-BI9A1vKA.d.ts +95 -0
- package/dist/plugins-egKWjgue.js +99 -0
- package/dist/private-trace-result-BLvBjy_e.js +16 -0
- package/dist/profile-T0tXE4zu.js +861 -0
- package/dist/registry-BA0__8ai.js +1419 -0
- package/dist/reporting-schema-DosTN__Q.js +105 -0
- package/dist/resolve-bvD1ORdv.js +546 -0
- package/dist/rolldown-runtime-B4FMCO8f.js +28 -0
- package/dist/runner-BXYxfIUL.js +392 -0
- package/dist/sandbox-egress-signals-ngEPjSEW.js +46 -0
- package/dist/schema-Rn8-XEaN.js +1140 -0
- package/dist/sentry-CD3SdJl-.js +26 -0
- package/dist/skills-DJOlYdBs.js +316 -0
- package/dist/spawn-h0R1bTEo.js +29 -0
- package/dist/stats-3YZn-Swi.js +2648 -0
- package/dist/store-C5qxF2ac.d.ts +208 -0
- package/dist/task-queue-CoXwd2eq.js +136 -0
- package/dist/task-runner-B2_mJZj0.js +639 -0
- package/dist/thread-state-CqZ1m-es.js +431 -0
- package/dist/turn-execution-fI6PZ0iI.js +47 -0
- package/dist/turn-lifecycle-CcnwrOcp.js +54 -0
- package/dist/usage-B5SfMb3H.js +296 -0
- package/dist/usage-schema-CKaDkH_j.js +20 -0
- package/dist/validation-DfWcvhuS.js +40 -0
- package/dist/vercel-queue-DIkVcfK4.js +167 -0
- package/dist/vercel.d.ts +6 -6
- package/dist/vercel.js +14 -6
- package/dist/version-CnXZ614E.d.ts +5 -0
- package/dist/version-oD-oPrbO.js +8 -0
- package/dist/version.d.ts +2 -4
- package/dist/version.js +2 -6
- package/dist/web-authorization-Crbzlmzo.js +76 -0
- package/dist/web-input-BOuq3oKK.js +494 -0
- package/dist/work-B91LQKHr.js +509 -0
- package/dist/worker-BcRRTe6k.js +625 -0
- package/package.json +9 -9
- package/dist/agent-hooks-GB2VPTNA.js +0 -51
- package/dist/catalog-runtime-UBLIWD3O.js +0 -14
- package/dist/chunk-2LYHWMR4.js +0 -46
- package/dist/chunk-352OWAZH.js +0 -320
- package/dist/chunk-3Y47VMHV.js +0 -6
- package/dist/chunk-3YJSKOWF.js +0 -164
- package/dist/chunk-4KSSKTQ5.js +0 -229
- package/dist/chunk-4UO6FK4G.js +0 -64
- package/dist/chunk-5AHX53NQ.js +0 -717
- package/dist/chunk-5J3E5FWA.js +0 -700
- package/dist/chunk-5PDAG6O3.js +0 -206
- package/dist/chunk-6Y5B5PEX.js +0 -80
- package/dist/chunk-7GWQ5S5R.js +0 -2086
- package/dist/chunk-7H2VZTWN.js +0 -103
- package/dist/chunk-7NL7T6VU.js +0 -406
- package/dist/chunk-A5L27LE4.js +0 -387
- package/dist/chunk-ADIAWL34.js +0 -81
- package/dist/chunk-ASTEMBKJ.js +0 -519
- package/dist/chunk-CEMMLV5L.js +0 -98
- package/dist/chunk-CM5HNV2W.js +0 -131
- package/dist/chunk-CRTRBBRI.js +0 -105
- package/dist/chunk-DA2KGZZI.js +0 -13
- package/dist/chunk-DIB4GX3G.js +0 -8
- package/dist/chunk-DKIJ2RP5.js +0 -68
- package/dist/chunk-DWPN6SCH.js +0 -172
- package/dist/chunk-EDKUFGPL.js +0 -64
- package/dist/chunk-ENY6SKIU.js +0 -1228
- package/dist/chunk-F4MGCZOX.js +0 -2943
- package/dist/chunk-FA6POGLH.js +0 -70
- package/dist/chunk-FSUR7GTG.js +0 -20475
- package/dist/chunk-GKEOHJL5.js +0 -1856
- package/dist/chunk-GMCB6W5Y.js +0 -333
- package/dist/chunk-IE2VPG7O.js +0 -1135
- package/dist/chunk-IZ5B5IHQ.js +0 -3656
- package/dist/chunk-JAL3CH4G.js +0 -27
- package/dist/chunk-KIIEUXD3.js +0 -1764
- package/dist/chunk-KXU4NIL7.js +0 -692
- package/dist/chunk-MTM5IFCY.js +0 -1190
- package/dist/chunk-N7TMSOZH.js +0 -235
- package/dist/chunk-NCEBK6PH.js +0 -36
- package/dist/chunk-NHJU2ZAL.js +0 -56
- package/dist/chunk-P6KTRNVC.js +0 -321
- package/dist/chunk-PDO5BLNM.js +0 -22
- package/dist/chunk-PDRNLLFT.js +0 -3295
- package/dist/chunk-PEQXYSSE.js +0 -45
- package/dist/chunk-PKEX2AQX.js +0 -198
- package/dist/chunk-PSJCGIGO.js +0 -1163
- package/dist/chunk-PZUSUL55.js +0 -506
- package/dist/chunk-R45M4TUS.js +0 -37
- package/dist/chunk-RC745GGZ.js +0 -122
- package/dist/chunk-SP3QKZXJ.js +0 -820
- package/dist/chunk-TXKUKMVO.js +0 -417
- package/dist/chunk-UE4GPWSD.js +0 -931
- package/dist/chunk-UIE3R5XU.js +0 -17
- package/dist/chunk-UXPG6ZIN.js +0 -84
- package/dist/chunk-VFUK3X5B.js +0 -50
- package/dist/chunk-VNMJWREK.js +0 -787
- package/dist/chunk-VZ7CSOX2.js +0 -706
- package/dist/chunk-WCNQJKFT.js +0 -10
- package/dist/chunk-WDZR3QQM.js +0 -135
- package/dist/chunk-WLYVJCHV.js +0 -1433
- package/dist/chunk-WS7NAFTM.js +0 -1520
- package/dist/chunk-X6GDDWQH.js +0 -60
- package/dist/chunk-XPDEYNQI.js +0 -102
- package/dist/chunk-XRW44VGM.js +0 -58
- package/dist/chunk-Y2CM7HXH.js +0 -111
- package/dist/chunk-Y7X25LFY.js +0 -15
- package/dist/conversation-work-VIOQ5ZF3.js +0 -54
- package/dist/db-NGDJV7YH.js +0 -26
- package/dist/discovery-DWVDM7UK.js +0 -44
- package/dist/experimental-P5FZM4CU.js +0 -12
- package/dist/oauth-callback-server-YFY7YEGK.js +0 -243
- package/dist/oauth-relay-5XPVRPYB.js +0 -8
- package/dist/plugins-YTYJMMJF.js +0 -14
- package/dist/runner-ORTNSB2Q.js +0 -522
- package/dist/sandbox-egress-signals-FIL7EGPR.js +0 -73
- package/dist/sentry-MB3NL3IR.js +0 -42
- package/dist/skills-H376WQOH.js +0 -27
- package/dist/spawn-T43WHLOS.js +0 -42
- package/dist/task-runner-LBZWF32V.js +0 -36
- package/dist/validation-REJLRKI6.js +0 -17
- package/dist/work-3KJ3RE76.js +0 -51
- package/dist/worker-QFEBU72E.js +0 -24
package/dist/cli/check.js
CHANGED
|
@@ -1,886 +1,593 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
} from "../
|
|
4
|
-
import "
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import "../chunk-TXKUKMVO.js";
|
|
13
|
-
import {
|
|
14
|
-
JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE,
|
|
15
|
-
JUNIOR_HEARTBEAT_ROUTE,
|
|
16
|
-
LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION
|
|
17
|
-
} from "../chunk-WS7NAFTM.js";
|
|
18
|
-
import "../chunk-PEQXYSSE.js";
|
|
19
|
-
import "../chunk-WDZR3QQM.js";
|
|
20
|
-
|
|
21
|
-
// src/cli/check.ts
|
|
22
|
-
import fs from "fs/promises";
|
|
23
|
-
import path from "path";
|
|
24
|
-
import { pathToFileURL } from "url";
|
|
25
|
-
var DEFAULT_IO = {
|
|
26
|
-
info: console.log,
|
|
27
|
-
warn: console.warn,
|
|
28
|
-
error: console.error
|
|
1
|
+
import { E as JUNIOR_HEARTBEAT_ROUTE, j as LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION, w as JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE } from "../logging-BMJzdDlP.js";
|
|
2
|
+
import { d as parseInlinePluginManifest, f as parsePluginManifest } from "../registry-BA0__8ai.js";
|
|
3
|
+
import { i as parseSkillFile } from "../skills-DJOlYdBs.js";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
//#region src/cli/check.ts
|
|
8
|
+
const DEFAULT_IO = {
|
|
9
|
+
info: console.log,
|
|
10
|
+
warn: console.warn,
|
|
11
|
+
error: console.error
|
|
29
12
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
13
|
+
const ANSI = {
|
|
14
|
+
reset: "\x1B[0m",
|
|
15
|
+
bold: "\x1B[1m",
|
|
16
|
+
dim: "\x1B[2m",
|
|
17
|
+
green: "\x1B[32m",
|
|
18
|
+
yellow: "\x1B[33m",
|
|
19
|
+
red: "\x1B[31m",
|
|
20
|
+
cyan: "\x1B[36m"
|
|
38
21
|
};
|
|
39
22
|
function supportsColor(stream) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
return Boolean(stream?.isTTY);
|
|
47
|
-
}
|
|
48
|
-
var COLOR_ENABLED = supportsColor(process.stdout) || supportsColor(process.stderr);
|
|
23
|
+
if (process.env.NO_COLOR !== void 0) return false;
|
|
24
|
+
if (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== "0") return true;
|
|
25
|
+
return Boolean(stream?.isTTY);
|
|
26
|
+
}
|
|
27
|
+
const COLOR_ENABLED = supportsColor(process.stdout) || supportsColor(process.stderr);
|
|
49
28
|
function color(text, ...codes) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
return `${codes.join("")}${text}${ANSI.reset}`;
|
|
29
|
+
if (!COLOR_ENABLED || codes.length === 0) return text;
|
|
30
|
+
return `${codes.join("")}${text}${ANSI.reset}`;
|
|
54
31
|
}
|
|
55
32
|
function contentRoot(rootDir, subdir) {
|
|
56
|
-
|
|
33
|
+
return path.resolve(rootDir, "app", subdir);
|
|
57
34
|
}
|
|
58
35
|
async function pathIsDirectory(targetPath) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
36
|
+
try {
|
|
37
|
+
return (await fs.stat(targetPath)).isDirectory();
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
64
41
|
}
|
|
65
42
|
async function pathIsFile(targetPath) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
43
|
+
try {
|
|
44
|
+
return (await fs.stat(targetPath)).isFile();
|
|
45
|
+
} catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
71
48
|
}
|
|
72
49
|
async function readJsonFile(targetPath) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(await fs.readFile(targetPath, "utf8"));
|
|
52
|
+
} catch {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
78
55
|
}
|
|
79
56
|
async function readTextFile(targetPath) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
57
|
+
try {
|
|
58
|
+
return await fs.readFile(targetPath, "utf8");
|
|
59
|
+
} catch {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
85
62
|
}
|
|
86
63
|
async function readRootPackageJson(rootDir) {
|
|
87
|
-
|
|
64
|
+
return await readJsonFile(path.join(rootDir, "package.json"));
|
|
88
65
|
}
|
|
89
66
|
function packageInstallDir(rootDir, packageName) {
|
|
90
|
-
|
|
67
|
+
return path.join(rootDir, "node_modules", ...packageName.split("/"));
|
|
91
68
|
}
|
|
92
69
|
function declaredPackages(pkg) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
packages.set(name, spec);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return [...packages.entries()].map(([name, spec]) => ({ name, spec })).sort((left, right) => left.name.localeCompare(right.name));
|
|
70
|
+
if (!pkg) return [];
|
|
71
|
+
const packages = /* @__PURE__ */ new Map();
|
|
72
|
+
for (const deps of [
|
|
73
|
+
pkg.dependencies,
|
|
74
|
+
pkg.optionalDependencies,
|
|
75
|
+
pkg.devDependencies
|
|
76
|
+
]) for (const [name, spec] of Object.entries(deps ?? {})) if (!packages.has(name)) packages.set(name, spec);
|
|
77
|
+
return [...packages.entries()].map(([name, spec]) => ({
|
|
78
|
+
name,
|
|
79
|
+
spec
|
|
80
|
+
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
109
81
|
}
|
|
110
82
|
async function readInstalledPackageVersion(rootDir, packageName) {
|
|
111
|
-
|
|
112
|
-
path.join(packageInstallDir(rootDir, packageName), "package.json")
|
|
113
|
-
))?.version;
|
|
83
|
+
return (await readJsonFile(path.join(packageInstallDir(rootDir, packageName), "package.json")))?.version;
|
|
114
84
|
}
|
|
115
85
|
async function packageHasPluginContent(rootDir, packageName) {
|
|
116
|
-
|
|
117
|
-
|
|
86
|
+
const packageDir = packageInstallDir(rootDir, packageName);
|
|
87
|
+
return await pathIsFile(path.join(packageDir, "plugin.yaml")) || await pathIsDirectory(path.join(packageDir, "plugins")) || await pathIsDirectory(path.join(packageDir, "skills"));
|
|
118
88
|
}
|
|
119
89
|
function comparableVersion(version) {
|
|
120
|
-
|
|
90
|
+
return version?.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
|
|
121
91
|
}
|
|
122
92
|
async function collectPackageWarnings(rootDir, packages) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
return warnings;
|
|
135
|
-
}
|
|
136
|
-
for (const declaredPackage of packages) {
|
|
137
|
-
if (!declaredPackage.name.startsWith("@sentry/junior-")) {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
if (!await packageHasPluginContent(rootDir, declaredPackage.name)) {
|
|
141
|
-
continue;
|
|
142
|
-
}
|
|
143
|
-
const pluginVersion = comparableVersion(
|
|
144
|
-
await readInstalledPackageVersion(rootDir, declaredPackage.name) ?? declaredPackage.spec
|
|
145
|
-
);
|
|
146
|
-
if (pluginVersion && pluginVersion !== coreVersion) {
|
|
147
|
-
warnings.push(
|
|
148
|
-
`${path.join(rootDir, "package.json")}: ${declaredPackage.name} version ${pluginVersion} does not match @sentry/junior version ${coreVersion}`
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return warnings;
|
|
93
|
+
const warnings = [];
|
|
94
|
+
const packageSpecByName = new Map(packages.map((declaredPackage) => [declaredPackage.name, declaredPackage.spec]));
|
|
95
|
+
const coreVersion = comparableVersion(await readInstalledPackageVersion(rootDir, "@sentry/junior") ?? packageSpecByName.get("@sentry/junior"));
|
|
96
|
+
if (!coreVersion) return warnings;
|
|
97
|
+
for (const declaredPackage of packages) {
|
|
98
|
+
if (!declaredPackage.name.startsWith("@sentry/junior-")) continue;
|
|
99
|
+
if (!await packageHasPluginContent(rootDir, declaredPackage.name)) continue;
|
|
100
|
+
const pluginVersion = comparableVersion(await readInstalledPackageVersion(rootDir, declaredPackage.name) ?? declaredPackage.spec);
|
|
101
|
+
if (pluginVersion && pluginVersion !== coreVersion) warnings.push(`${path.join(rootDir, "package.json")}: ${declaredPackage.name} version ${pluginVersion} does not match @sentry/junior version ${coreVersion}`);
|
|
102
|
+
}
|
|
103
|
+
return warnings;
|
|
153
104
|
}
|
|
154
105
|
async function validateSkillDirectory(skillDir, duplicateNames) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
106
|
+
const skillFile = path.join(skillDir, "SKILL.md");
|
|
107
|
+
const errors = [];
|
|
108
|
+
const warnings = [];
|
|
109
|
+
let raw;
|
|
110
|
+
try {
|
|
111
|
+
raw = await fs.readFile(skillFile, "utf8");
|
|
112
|
+
} catch {
|
|
113
|
+
errors.push(`${skillFile}: missing SKILL.md`);
|
|
114
|
+
return {
|
|
115
|
+
skillFile,
|
|
116
|
+
errors,
|
|
117
|
+
warnings
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const parsed = parseSkillFile(raw, path.basename(skillDir));
|
|
121
|
+
if (!parsed.ok) {
|
|
122
|
+
errors.push(`${skillFile}: ${parsed.error}`);
|
|
123
|
+
return {
|
|
124
|
+
skillFile,
|
|
125
|
+
errors,
|
|
126
|
+
warnings
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const name = parsed.skill.name;
|
|
130
|
+
const firstSeen = duplicateNames.get(name);
|
|
131
|
+
if (firstSeen) errors.push(`${skillFile}: duplicate skill name "${name}" (already defined in ${firstSeen})`);
|
|
132
|
+
else duplicateNames.set(name, skillFile);
|
|
133
|
+
if (!parsed.skill.body) warnings.push(`${skillFile}: no skill instructions after frontmatter`);
|
|
134
|
+
if (/\b(?:searchTools|searchMcpTools|useTool|callMcpTool|available_tools)\b|<active-mcp-(?:tools|catalogs)>/.test(parsed.skill.body)) errors.push(`${skillFile}: skill instructions must not hardcode harness tool-discovery or MCP dispatcher mechanics`);
|
|
135
|
+
return {
|
|
136
|
+
skillFile,
|
|
137
|
+
skill: parsed.skill,
|
|
138
|
+
errors,
|
|
139
|
+
warnings
|
|
140
|
+
};
|
|
190
141
|
}
|
|
191
142
|
async function validatePluginDirectory(pluginDir, duplicatePluginNames, duplicateProviderDomains) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
} catch (error) {
|
|
203
|
-
return {
|
|
204
|
-
manifestPath,
|
|
205
|
-
errors: [
|
|
206
|
-
`${manifestPath}: ${error instanceof Error ? error.message : String(error)}`
|
|
207
|
-
]
|
|
208
|
-
};
|
|
209
|
-
}
|
|
143
|
+
const manifestPath = path.join(pluginDir, "plugin.yaml");
|
|
144
|
+
try {
|
|
145
|
+
const raw = await fs.readFile(manifestPath, "utf8");
|
|
146
|
+
return validatePluginManifest(manifestPath, parsePluginManifest(raw, pluginDir), duplicatePluginNames, duplicateProviderDomains);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
return {
|
|
149
|
+
manifestPath,
|
|
150
|
+
errors: [`${manifestPath}: ${error instanceof Error ? error.message : String(error)}`]
|
|
151
|
+
};
|
|
152
|
+
}
|
|
210
153
|
}
|
|
211
154
|
function validatePluginManifest(manifestPath, manifest, duplicatePluginNames, duplicateProviderDomains) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
if (errors.length > 0) {
|
|
234
|
-
return { manifestPath, manifest, errors };
|
|
235
|
-
}
|
|
236
|
-
duplicatePluginNames.set(manifest.name, manifestPath);
|
|
237
|
-
for (const domain of domains) {
|
|
238
|
-
duplicateProviderDomains.set(domain, manifestPath);
|
|
239
|
-
}
|
|
240
|
-
return { manifestPath, manifest, errors: [] };
|
|
155
|
+
const errors = [];
|
|
156
|
+
const firstSeen = duplicatePluginNames.get(manifest.name);
|
|
157
|
+
if (firstSeen) errors.push(`${manifestPath}: duplicate plugin name "${manifest.name}" (already defined in ${firstSeen})`);
|
|
158
|
+
const domains = [.../* @__PURE__ */ new Set([...manifest.credentials?.domains ?? [], ...manifest.domains ?? []])];
|
|
159
|
+
for (const domain of domains) {
|
|
160
|
+
const firstDomainSeen = duplicateProviderDomains.get(domain);
|
|
161
|
+
if (firstDomainSeen) errors.push(`${manifestPath}: duplicate provider domain "${domain}" (already defined in ${firstDomainSeen})`);
|
|
162
|
+
}
|
|
163
|
+
if (errors.length > 0) return {
|
|
164
|
+
manifestPath,
|
|
165
|
+
manifest,
|
|
166
|
+
errors
|
|
167
|
+
};
|
|
168
|
+
duplicatePluginNames.set(manifest.name, manifestPath);
|
|
169
|
+
for (const domain of domains) duplicateProviderDomains.set(domain, manifestPath);
|
|
170
|
+
return {
|
|
171
|
+
manifestPath,
|
|
172
|
+
manifest,
|
|
173
|
+
errors: []
|
|
174
|
+
};
|
|
241
175
|
}
|
|
242
176
|
function packageExportsEntryPath(exportsValue) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
for (const condition of ["default", "import", "node"]) {
|
|
257
|
-
const target = rootExport[condition];
|
|
258
|
-
if (typeof target === "string") {
|
|
259
|
-
return target;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
return void 0;
|
|
177
|
+
if (typeof exportsValue === "string") return exportsValue;
|
|
178
|
+
if (!isRecord(exportsValue)) return;
|
|
179
|
+
const rootExport = exportsValue["."] ?? exportsValue;
|
|
180
|
+
if (typeof rootExport === "string") return rootExport;
|
|
181
|
+
if (!isRecord(rootExport)) return;
|
|
182
|
+
for (const condition of [
|
|
183
|
+
"default",
|
|
184
|
+
"import",
|
|
185
|
+
"node"
|
|
186
|
+
]) {
|
|
187
|
+
const target = rootExport[condition];
|
|
188
|
+
if (typeof target === "string") return target;
|
|
189
|
+
}
|
|
263
190
|
}
|
|
264
191
|
async function resolvePackageEntryPath(packageDir) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const normalizedCandidate = candidate.startsWith("./") ? candidate : `./${candidate}`;
|
|
279
|
-
const entryPath = path.resolve(packageDir, normalizedCandidate);
|
|
280
|
-
if (await pathIsFile(entryPath)) {
|
|
281
|
-
return entryPath;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
return void 0;
|
|
192
|
+
const packageJson = await readJsonFile(path.join(packageDir, "package.json"));
|
|
193
|
+
const entry = packageExportsEntryPath(packageJson?.exports) ?? packageJson?.main;
|
|
194
|
+
const candidates = [
|
|
195
|
+
...entry ? [entry] : [],
|
|
196
|
+
"./index.js",
|
|
197
|
+
"./dist/index.js"
|
|
198
|
+
];
|
|
199
|
+
for (const candidate of candidates) {
|
|
200
|
+
if (path.isAbsolute(candidate) || candidate.startsWith("../")) continue;
|
|
201
|
+
const normalizedCandidate = candidate.startsWith("./") ? candidate : `./${candidate}`;
|
|
202
|
+
const entryPath = path.resolve(packageDir, normalizedCandidate);
|
|
203
|
+
if (await pathIsFile(entryPath)) return entryPath;
|
|
204
|
+
}
|
|
285
205
|
}
|
|
286
206
|
function isPluginFactoryExport(name, value) {
|
|
287
|
-
|
|
207
|
+
return typeof value === "function" && (name === "default" || name.endsWith("Plugin"));
|
|
288
208
|
}
|
|
289
209
|
function isPluginRegistrationLike(value) {
|
|
290
|
-
|
|
210
|
+
return isRecord(value) && isRecord(value.manifest);
|
|
291
211
|
}
|
|
292
212
|
async function collectPackageJsPluginSources(packageDir, packageName) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
pluginDir: packageDir,
|
|
330
|
-
packageName,
|
|
331
|
-
manifestPath,
|
|
332
|
-
errors: [
|
|
333
|
-
`${manifestPath}: ${error instanceof Error ? error.message : String(error)}`
|
|
334
|
-
]
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
return pluginSources;
|
|
213
|
+
const entryPath = await resolvePackageEntryPath(packageDir);
|
|
214
|
+
if (!entryPath) return [];
|
|
215
|
+
let moduleExports;
|
|
216
|
+
try {
|
|
217
|
+
moduleExports = await import(pathToFileURL(entryPath).href);
|
|
218
|
+
} catch {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
const pluginSources = [];
|
|
222
|
+
for (const [exportName, exportedValue] of Object.entries(moduleExports).sort(([left], [right]) => left.localeCompare(right))) {
|
|
223
|
+
if (!isPluginFactoryExport(exportName, exportedValue)) continue;
|
|
224
|
+
let registration;
|
|
225
|
+
try {
|
|
226
|
+
registration = await exportedValue();
|
|
227
|
+
} catch {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (!isPluginRegistrationLike(registration)) continue;
|
|
231
|
+
const manifestPath = `${entryPath}#${exportName}`;
|
|
232
|
+
try {
|
|
233
|
+
pluginSources.push({
|
|
234
|
+
pluginDir: packageDir,
|
|
235
|
+
packageName,
|
|
236
|
+
manifestPath,
|
|
237
|
+
manifest: parseInlinePluginManifest(registration.manifest, packageDir)
|
|
238
|
+
});
|
|
239
|
+
} catch (error) {
|
|
240
|
+
pluginSources.push({
|
|
241
|
+
pluginDir: packageDir,
|
|
242
|
+
packageName,
|
|
243
|
+
manifestPath,
|
|
244
|
+
errors: [`${manifestPath}: ${error instanceof Error ? error.message : String(error)}`]
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return pluginSources;
|
|
339
249
|
}
|
|
340
250
|
async function collectPluginDirectories(rootDir) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const pluginDir = path.join(contentRoot(rootDir, "plugins"), entry.name);
|
|
355
|
-
if (await pathIsFile(path.join(pluginDir, "plugin.yaml"))) {
|
|
356
|
-
pluginDirs.push(pluginDir);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
return pluginDirs.sort((left, right) => left.localeCompare(right));
|
|
251
|
+
const pluginDirs = [];
|
|
252
|
+
let entries;
|
|
253
|
+
try {
|
|
254
|
+
entries = await fs.readdir(contentRoot(rootDir, "plugins"), { withFileTypes: true });
|
|
255
|
+
} catch {
|
|
256
|
+
return pluginDirs;
|
|
257
|
+
}
|
|
258
|
+
for (const entry of entries) {
|
|
259
|
+
if (!entry.isDirectory()) continue;
|
|
260
|
+
const pluginDir = path.join(contentRoot(rootDir, "plugins"), entry.name);
|
|
261
|
+
if (await pathIsFile(path.join(pluginDir, "plugin.yaml"))) pluginDirs.push(pluginDir);
|
|
262
|
+
}
|
|
263
|
+
return pluginDirs.sort((left, right) => left.localeCompare(right));
|
|
360
264
|
}
|
|
361
265
|
async function collectPackagedContent(rootDir, packages) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
const pluginDir = path.join(nestedPluginsRoot, entry.name);
|
|
407
|
-
if (await pathIsFile(path.join(pluginDir, "plugin.yaml"))) {
|
|
408
|
-
pluginSources.push({
|
|
409
|
-
pluginDir,
|
|
410
|
-
manifestPath: path.join(pluginDir, "plugin.yaml"),
|
|
411
|
-
packageName: declaredPackage.name
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
return {
|
|
417
|
-
pluginSources: pluginSources.sort(
|
|
418
|
-
(left, right) => `${left.packageName}:${left.pluginDir}`.localeCompare(
|
|
419
|
-
`${right.packageName}:${right.pluginDir}`
|
|
420
|
-
)
|
|
421
|
-
),
|
|
422
|
-
skillRoots: skillRoots.sort(
|
|
423
|
-
(left, right) => `${left.packageName}:${left.root}`.localeCompare(
|
|
424
|
-
`${right.packageName}:${right.root}`
|
|
425
|
-
)
|
|
426
|
-
)
|
|
427
|
-
};
|
|
266
|
+
const pluginSources = [];
|
|
267
|
+
const skillRoots = [];
|
|
268
|
+
for (const declaredPackage of packages) {
|
|
269
|
+
const packageDir = packageInstallDir(rootDir, declaredPackage.name);
|
|
270
|
+
if (!await pathIsDirectory(packageDir)) continue;
|
|
271
|
+
const rootManifestPath = path.join(packageDir, "plugin.yaml");
|
|
272
|
+
const hasRootManifest = await pathIsFile(rootManifestPath);
|
|
273
|
+
const packageSkillsRoot = path.join(packageDir, "skills");
|
|
274
|
+
const hasPackageSkills = await pathIsDirectory(packageSkillsRoot);
|
|
275
|
+
if (hasRootManifest) pluginSources.push({
|
|
276
|
+
pluginDir: packageDir,
|
|
277
|
+
manifestPath: rootManifestPath,
|
|
278
|
+
packageName: declaredPackage.name
|
|
279
|
+
});
|
|
280
|
+
else if (hasPackageSkills) {
|
|
281
|
+
const jsPluginSources = await collectPackageJsPluginSources(packageDir, declaredPackage.name);
|
|
282
|
+
if (jsPluginSources.length > 0) pluginSources.push(...jsPluginSources);
|
|
283
|
+
else skillRoots.push({
|
|
284
|
+
root: packageSkillsRoot,
|
|
285
|
+
packageName: declaredPackage.name
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
const nestedPluginsRoot = path.join(packageDir, "plugins");
|
|
289
|
+
let nestedEntries;
|
|
290
|
+
try {
|
|
291
|
+
nestedEntries = await fs.readdir(nestedPluginsRoot, { withFileTypes: true });
|
|
292
|
+
} catch {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
for (const entry of nestedEntries) {
|
|
296
|
+
if (!entry.isDirectory()) continue;
|
|
297
|
+
const pluginDir = path.join(nestedPluginsRoot, entry.name);
|
|
298
|
+
if (await pathIsFile(path.join(pluginDir, "plugin.yaml"))) pluginSources.push({
|
|
299
|
+
pluginDir,
|
|
300
|
+
manifestPath: path.join(pluginDir, "plugin.yaml"),
|
|
301
|
+
packageName: declaredPackage.name
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return {
|
|
306
|
+
pluginSources: pluginSources.sort((left, right) => `${left.packageName}:${left.pluginDir}`.localeCompare(`${right.packageName}:${right.pluginDir}`)),
|
|
307
|
+
skillRoots: skillRoots.sort((left, right) => `${left.packageName}:${left.root}`.localeCompare(`${right.packageName}:${right.root}`))
|
|
308
|
+
};
|
|
428
309
|
}
|
|
429
310
|
async function collectSkillDirectories(root) {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
skillDirs.push(path.join(root, entry.name));
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
return skillDirs.sort((left, right) => left.localeCompare(right));
|
|
311
|
+
const skillDirs = [];
|
|
312
|
+
let entries;
|
|
313
|
+
try {
|
|
314
|
+
entries = await fs.readdir(root, { withFileTypes: true });
|
|
315
|
+
} catch {
|
|
316
|
+
return skillDirs;
|
|
317
|
+
}
|
|
318
|
+
for (const entry of entries) if (entry.isDirectory()) skillDirs.push(path.join(root, entry.name));
|
|
319
|
+
return skillDirs.sort((left, right) => left.localeCompare(right));
|
|
443
320
|
}
|
|
444
321
|
function formatDisplayPath(rootDir, targetPath) {
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
return displayPath.split(path.sep).join("/");
|
|
322
|
+
const relativePath = path.relative(rootDir, targetPath);
|
|
323
|
+
return (relativePath.length > 0 && !relativePath.startsWith("..") && !path.isAbsolute(relativePath) ? relativePath : targetPath).split(path.sep).join("/");
|
|
448
324
|
}
|
|
449
325
|
function formatStatus(errorCount, warningCount) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
if (warningCount > 0) {
|
|
454
|
-
return "warn";
|
|
455
|
-
}
|
|
456
|
-
return "ok";
|
|
326
|
+
if (errorCount > 0) return "error";
|
|
327
|
+
if (warningCount > 0) return "warn";
|
|
328
|
+
return "ok";
|
|
457
329
|
}
|
|
458
330
|
function statusIcon(status) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
case "error":
|
|
465
|
-
return color("\u2716", ANSI.red, ANSI.bold);
|
|
466
|
-
}
|
|
331
|
+
switch (status) {
|
|
332
|
+
case "ok": return color("✓", ANSI.green, ANSI.bold);
|
|
333
|
+
case "warn": return color("⚠", ANSI.yellow, ANSI.bold);
|
|
334
|
+
case "error": return color("✖", ANSI.red, ANSI.bold);
|
|
335
|
+
}
|
|
467
336
|
}
|
|
468
337
|
function formatHeading(status, label) {
|
|
469
|
-
|
|
470
|
-
|
|
338
|
+
const styledLabel = status === "ok" ? color(label, ANSI.bold) : status === "warn" ? color(label, ANSI.bold, ANSI.yellow) : color(label, ANSI.bold, ANSI.red);
|
|
339
|
+
return `${statusIcon(status)} ${styledLabel}`;
|
|
471
340
|
}
|
|
472
341
|
function reportSkillResult(result, io, indent, isLast) {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
342
|
+
const status = formatStatus(result.errors.length, result.warnings.length);
|
|
343
|
+
const skillName = result.skill?.name ?? path.basename(path.dirname(result.skillFile));
|
|
344
|
+
const branch = isLast ? "└─" : "├─";
|
|
345
|
+
io.info(`${indent}${branch} ${formatHeading(status, `skill ${skillName}`)}`);
|
|
477
346
|
}
|
|
478
347
|
function reportPluginResult(result, io) {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
);
|
|
487
|
-
const status = formatStatus(
|
|
488
|
-
result.errors.length + skillErrorCount,
|
|
489
|
-
skillWarningCount
|
|
490
|
-
);
|
|
491
|
-
const pluginName = result.manifest?.name ?? path.basename(result.pluginDir);
|
|
492
|
-
const label = result.packageName ? `packaged plugin ${pluginName} (${result.packageName})` : `plugin ${pluginName}`;
|
|
493
|
-
io.info(formatHeading(status, label));
|
|
494
|
-
for (const [index, skillResult] of result.skillResults.entries()) {
|
|
495
|
-
reportSkillResult(
|
|
496
|
-
skillResult,
|
|
497
|
-
io,
|
|
498
|
-
" ",
|
|
499
|
-
index === result.skillResults.length - 1
|
|
500
|
-
);
|
|
501
|
-
}
|
|
348
|
+
const skillErrorCount = result.skillResults.reduce((count, skillResult) => count + skillResult.errors.length, 0);
|
|
349
|
+
const skillWarningCount = result.skillResults.reduce((count, skillResult) => count + skillResult.warnings.length, 0);
|
|
350
|
+
const status = formatStatus(result.errors.length + skillErrorCount, skillWarningCount);
|
|
351
|
+
const pluginName = result.manifest?.name ?? path.basename(result.pluginDir);
|
|
352
|
+
const label = result.packageName ? `packaged plugin ${pluginName} (${result.packageName})` : `plugin ${pluginName}`;
|
|
353
|
+
io.info(formatHeading(status, label));
|
|
354
|
+
for (const [index, skillResult] of result.skillResults.entries()) reportSkillResult(skillResult, io, " ", index === result.skillResults.length - 1);
|
|
502
355
|
}
|
|
503
356
|
function reportSkillGroup(label, skillResults, io) {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
);
|
|
508
|
-
const warningCount = skillResults.reduce(
|
|
509
|
-
(count, skillResult) => count + skillResult.warnings.length,
|
|
510
|
-
0
|
|
511
|
-
);
|
|
512
|
-
const status = formatStatus(errorCount, warningCount);
|
|
513
|
-
io.info(formatHeading(status, label));
|
|
514
|
-
for (const [index, skillResult] of skillResults.entries()) {
|
|
515
|
-
reportSkillResult(skillResult, io, " ", index === skillResults.length - 1);
|
|
516
|
-
}
|
|
357
|
+
const status = formatStatus(skillResults.reduce((count, skillResult) => count + skillResult.errors.length, 0), skillResults.reduce((count, skillResult) => count + skillResult.warnings.length, 0));
|
|
358
|
+
io.info(formatHeading(status, label));
|
|
359
|
+
for (const [index, skillResult] of skillResults.entries()) reportSkillResult(skillResult, io, " ", index === skillResults.length - 1);
|
|
517
360
|
}
|
|
518
361
|
function reportAppSkills(skillResults, io) {
|
|
519
|
-
|
|
362
|
+
reportSkillGroup("app skills", skillResults, io);
|
|
520
363
|
}
|
|
521
364
|
function isRecord(value) {
|
|
522
|
-
|
|
365
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
523
366
|
}
|
|
524
367
|
async function hasJuniorDeploymentMarkers(rootDir, packages, nitroConfig) {
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
await readTextFile(path.join(rootDir, "server.js"))
|
|
534
|
-
]) {
|
|
535
|
-
if (source?.includes("@sentry/junior") || /\bjuniorNitro\s*\(/.test(source ?? "")) {
|
|
536
|
-
return true;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
const appDir = path.resolve(rootDir, "app");
|
|
540
|
-
return await pathIsDirectory(appDir) && await hasJuniorAppMarkers(appDir);
|
|
368
|
+
if (packages.some((declaredPackage) => declaredPackage.name === "@sentry/junior")) return true;
|
|
369
|
+
for (const source of [
|
|
370
|
+
nitroConfig,
|
|
371
|
+
await readTextFile(path.join(rootDir, "server.ts")),
|
|
372
|
+
await readTextFile(path.join(rootDir, "server.js"))
|
|
373
|
+
]) if (source?.includes("@sentry/junior") || /\bjuniorNitro\s*\(/.test(source ?? "")) return true;
|
|
374
|
+
const appDir = path.resolve(rootDir, "app");
|
|
375
|
+
return await pathIsDirectory(appDir) && await hasJuniorAppMarkers(appDir);
|
|
541
376
|
}
|
|
542
377
|
async function validateDeploymentConfig(rootDir, packages) {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
(cron) => isRecord(cron) && cron.path === JUNIOR_HEARTBEAT_ROUTE
|
|
599
|
-
);
|
|
600
|
-
if (hasHeartbeatCron) {
|
|
601
|
-
checked = true;
|
|
602
|
-
warnings.push(
|
|
603
|
-
`${vercelConfigPath}: ${JUNIOR_HEARTBEAT_ROUTE} cron is now emitted by juniorNitro() into Nitro's Vercel Build Output config. Remove the root cron entry to avoid deployment drift.`
|
|
604
|
-
);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
return { checked, errors, warnings };
|
|
378
|
+
const errors = [];
|
|
379
|
+
const warnings = [];
|
|
380
|
+
let checked = false;
|
|
381
|
+
const nitroConfigPath = path.join(rootDir, "nitro.config.ts");
|
|
382
|
+
const nitroConfig = await readTextFile(nitroConfigPath);
|
|
383
|
+
const hasJuniorMarkers = await hasJuniorDeploymentMarkers(rootDir, packages, nitroConfig);
|
|
384
|
+
if (nitroConfig !== void 0 && hasJuniorMarkers) {
|
|
385
|
+
checked = true;
|
|
386
|
+
if (!/\bjuniorNitro\s*\(/.test(nitroConfig)) errors.push(`${nitroConfigPath}: missing juniorNitro(). The Nitro module emits Junior's Vercel queue trigger and heartbeat cron into the deployed build output.`);
|
|
387
|
+
}
|
|
388
|
+
const vercelConfigPath = path.join(rootDir, "vercel.json");
|
|
389
|
+
const vercelConfigText = await readTextFile(vercelConfigPath);
|
|
390
|
+
if (vercelConfigText === void 0) return {
|
|
391
|
+
checked,
|
|
392
|
+
errors,
|
|
393
|
+
warnings
|
|
394
|
+
};
|
|
395
|
+
const containsLegacyJuniorQueuePath = vercelConfigText.includes(LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION);
|
|
396
|
+
if (!hasJuniorMarkers && !containsLegacyJuniorQueuePath) return {
|
|
397
|
+
checked,
|
|
398
|
+
errors,
|
|
399
|
+
warnings
|
|
400
|
+
};
|
|
401
|
+
let vercelConfig;
|
|
402
|
+
try {
|
|
403
|
+
vercelConfig = JSON.parse(vercelConfigText);
|
|
404
|
+
} catch (error) {
|
|
405
|
+
errors.push(`${vercelConfigPath}: invalid JSON (${error instanceof Error ? error.message : String(error)})`);
|
|
406
|
+
return {
|
|
407
|
+
checked,
|
|
408
|
+
errors,
|
|
409
|
+
warnings
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
if (hasJuniorMarkers && vercelConfig.framework !== void 0 && vercelConfig.framework !== "nitro") {
|
|
413
|
+
checked = true;
|
|
414
|
+
errors.push(`${vercelConfigPath}: framework must be "nitro" for Junior's Vercel deployment wiring.`);
|
|
415
|
+
}
|
|
416
|
+
if (isRecord(vercelConfig.functions)) {
|
|
417
|
+
if (vercelConfig.functions["api/internal/agent/continue.ts"] !== void 0) {
|
|
418
|
+
checked = true;
|
|
419
|
+
errors.push(`${vercelConfigPath}: functions.${LEGACY_JUNIOR_CONVERSATION_WORK_FUNCTION} targets a source file that Nitro does not deploy. Remove it; juniorNitro() emits the ${JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE} queue trigger through Nitro functionRules.`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (hasJuniorMarkers && Array.isArray(vercelConfig.crons)) {
|
|
423
|
+
if (vercelConfig.crons.some((cron) => isRecord(cron) && cron.path === "/api/internal/heartbeat")) {
|
|
424
|
+
checked = true;
|
|
425
|
+
warnings.push(`${vercelConfigPath}: ${JUNIOR_HEARTBEAT_ROUTE} cron is now emitted by juniorNitro() into Nitro's Vercel Build Output config. Remove the root cron entry to avoid deployment drift.`);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
checked,
|
|
430
|
+
errors,
|
|
431
|
+
warnings
|
|
432
|
+
};
|
|
608
433
|
}
|
|
609
434
|
async function validateAppSourceFiles(rootDir, registeredConfigKeys) {
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
`${sourcePath}: configDefaults key "${key}" is not a registered plugin config key`
|
|
639
|
-
);
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
return { errors, warnings };
|
|
435
|
+
const errors = [];
|
|
436
|
+
const warnings = [];
|
|
437
|
+
for (const fileName of [
|
|
438
|
+
"server.ts",
|
|
439
|
+
"server.js",
|
|
440
|
+
"nitro.config.ts"
|
|
441
|
+
]) {
|
|
442
|
+
const sourcePath = path.join(rootDir, fileName);
|
|
443
|
+
let source;
|
|
444
|
+
try {
|
|
445
|
+
source = await fs.readFile(sourcePath, "utf8");
|
|
446
|
+
} catch {
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
if (/\bpluginPackages\s*:/.test(source)) errors.push(`${sourcePath}: pluginPackages is no longer supported. Export a defineJuniorPlugins(...) set and point juniorNitro({ plugins: "./plugins" }) at it.`);
|
|
450
|
+
if (/\bplugins\s*:\s*\{\s*packages\s*:/.test(source)) errors.push(`${sourcePath}: plugins.packages is no longer supported. Export a defineJuniorPlugins(...) set and point juniorNitro({ plugins: "./plugins" }) at it.`);
|
|
451
|
+
for (const defaultsBlock of source.matchAll(/\bconfigDefaults\s*:\s*\{([\s\S]*?)\}/g)) {
|
|
452
|
+
const block = defaultsBlock[1] ?? "";
|
|
453
|
+
for (const keyMatch of block.matchAll(/["']([^"']+)["']\s*:/g)) {
|
|
454
|
+
const key = keyMatch[1];
|
|
455
|
+
if (key && !registeredConfigKeys.has(key)) warnings.push(`${sourcePath}: configDefaults key "${key}" is not a registered plugin config key`);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
errors,
|
|
461
|
+
warnings
|
|
462
|
+
};
|
|
645
463
|
}
|
|
646
464
|
async function validateAppFiles(appDir) {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
if (!await pathIsFile(path.join(appDir, "WORLD.md"))) {
|
|
658
|
-
warnings.push(`${path.join(appDir, "WORLD.md")}: missing WORLD.md`);
|
|
659
|
-
}
|
|
660
|
-
if (!await pathIsFile(path.join(appDir, "DESCRIPTION.md"))) {
|
|
661
|
-
warnings.push(
|
|
662
|
-
`${path.join(appDir, "DESCRIPTION.md")}: missing DESCRIPTION.md`
|
|
663
|
-
);
|
|
664
|
-
}
|
|
665
|
-
return { errors, warnings };
|
|
465
|
+
const errors = [];
|
|
466
|
+
const warnings = [];
|
|
467
|
+
if (await pathIsFile(path.join(appDir, "ABOUT.md"))) errors.push(`${path.join(appDir, "ABOUT.md")}: ABOUT.md is no longer supported. Rename to WORLD.md (operational context) and DESCRIPTION.md (user-facing description).`);
|
|
468
|
+
if (!await pathIsFile(path.join(appDir, "SOUL.md"))) warnings.push(`${path.join(appDir, "SOUL.md")}: missing SOUL.md`);
|
|
469
|
+
if (!await pathIsFile(path.join(appDir, "WORLD.md"))) warnings.push(`${path.join(appDir, "WORLD.md")}: missing WORLD.md`);
|
|
470
|
+
if (!await pathIsFile(path.join(appDir, "DESCRIPTION.md"))) warnings.push(`${path.join(appDir, "DESCRIPTION.md")}: missing DESCRIPTION.md`);
|
|
471
|
+
return {
|
|
472
|
+
errors,
|
|
473
|
+
warnings
|
|
474
|
+
};
|
|
666
475
|
}
|
|
667
476
|
async function hasJuniorAppMarkers(appDir) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
for (const dirName of ["skills", "plugins"]) {
|
|
679
|
-
if (await pathIsDirectory(path.join(appDir, dirName))) {
|
|
680
|
-
return true;
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
return false;
|
|
477
|
+
for (const fileName of [
|
|
478
|
+
"SOUL.md",
|
|
479
|
+
"WORLD.md",
|
|
480
|
+
"DESCRIPTION.md",
|
|
481
|
+
"ABOUT.md"
|
|
482
|
+
]) if (await pathIsFile(path.join(appDir, fileName))) return true;
|
|
483
|
+
for (const dirName of ["skills", "plugins"]) if (await pathIsDirectory(path.join(appDir, dirName))) return true;
|
|
484
|
+
return false;
|
|
684
485
|
}
|
|
685
486
|
async function runCheck(rootDir = process.cwd(), io = DEFAULT_IO) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
for (const skillDir of appAndLocalPluginSkillDirs) {
|
|
794
|
-
const result = await validateSkillDirectory(skillDir, duplicateSkillNames);
|
|
795
|
-
skillResultsByDir.set(skillDir, result);
|
|
796
|
-
warnings.push(...result.warnings);
|
|
797
|
-
errors.push(...result.errors);
|
|
798
|
-
}
|
|
799
|
-
for (const skillDir of packagedSkillDirs) {
|
|
800
|
-
const result = await validateSkillDirectory(
|
|
801
|
-
skillDir,
|
|
802
|
-
duplicatePackagedSkillNames
|
|
803
|
-
);
|
|
804
|
-
skillResultsByDir.set(skillDir, result);
|
|
805
|
-
warnings.push(...result.warnings);
|
|
806
|
-
errors.push(...result.errors);
|
|
807
|
-
}
|
|
808
|
-
for (const pluginResult of pluginResults) {
|
|
809
|
-
pluginResult.skillResults = (pluginSkillDirs.get(pluginResult.pluginDir) ?? []).map((skillDir) => skillResultsByDir.get(skillDir)).filter((result) => Boolean(result));
|
|
810
|
-
}
|
|
811
|
-
const appSkillResults = appSkillDirs.map((skillDir) => skillResultsByDir.get(skillDir)).filter((result) => Boolean(result));
|
|
812
|
-
const packagedStandaloneSkillResultsByPackage = /* @__PURE__ */ new Map();
|
|
813
|
-
for (const [packageName, packageSkillDirs] of packagedSkillDirsByPackage) {
|
|
814
|
-
const packageSkillResults = packageSkillDirs.map((skillDir) => skillResultsByDir.get(skillDir)).filter((result) => Boolean(result));
|
|
815
|
-
if (packageSkillResults.length > 0) {
|
|
816
|
-
packagedStandaloneSkillResultsByPackage.set(
|
|
817
|
-
packageName,
|
|
818
|
-
packageSkillResults
|
|
819
|
-
);
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
const appDir = path.resolve(resolvedRoot, "app");
|
|
823
|
-
let appFileResult = {
|
|
824
|
-
errors: [],
|
|
825
|
-
warnings: []
|
|
826
|
-
};
|
|
827
|
-
const shouldValidateAppFiles = await pathIsDirectory(appDir) && await hasJuniorAppMarkers(appDir);
|
|
828
|
-
if (shouldValidateAppFiles) {
|
|
829
|
-
appFileResult = await validateAppFiles(appDir);
|
|
830
|
-
warnings.push(...appFileResult.warnings);
|
|
831
|
-
errors.push(...appFileResult.errors);
|
|
832
|
-
}
|
|
833
|
-
io.info(
|
|
834
|
-
`${color("Checking", ANSI.bold, ANSI.cyan)} ${color(
|
|
835
|
-
formatDisplayPath(resolvedRoot, resolvedRoot),
|
|
836
|
-
ANSI.dim
|
|
837
|
-
)}`
|
|
838
|
-
);
|
|
839
|
-
if (shouldValidateAppFiles) {
|
|
840
|
-
const appFileStatus = formatStatus(
|
|
841
|
-
appFileResult.errors.length,
|
|
842
|
-
appFileResult.warnings.length
|
|
843
|
-
);
|
|
844
|
-
io.info(formatHeading(appFileStatus, "app files"));
|
|
845
|
-
}
|
|
846
|
-
if (deploymentConfigResult.checked) {
|
|
847
|
-
const deploymentConfigStatus = formatStatus(
|
|
848
|
-
deploymentConfigResult.errors.length,
|
|
849
|
-
deploymentConfigResult.warnings.length
|
|
850
|
-
);
|
|
851
|
-
io.info(formatHeading(deploymentConfigStatus, "deployment config"));
|
|
852
|
-
}
|
|
853
|
-
for (const pluginResult of pluginResults) {
|
|
854
|
-
reportPluginResult(pluginResult, io);
|
|
855
|
-
}
|
|
856
|
-
for (const [
|
|
857
|
-
packageName,
|
|
858
|
-
packageSkillResults
|
|
859
|
-
] of packagedStandaloneSkillResultsByPackage) {
|
|
860
|
-
reportSkillGroup(
|
|
861
|
-
`packaged skills (${packageName})`,
|
|
862
|
-
packageSkillResults,
|
|
863
|
-
io
|
|
864
|
-
);
|
|
865
|
-
}
|
|
866
|
-
if (appSkillResults.length > 0) {
|
|
867
|
-
reportAppSkills(appSkillResults, io);
|
|
868
|
-
}
|
|
869
|
-
for (const warning of warnings) {
|
|
870
|
-
io.warn(`${statusIcon("warn")} warning: ${warning}`);
|
|
871
|
-
}
|
|
872
|
-
if (errors.length > 0) {
|
|
873
|
-
for (const error of errors) {
|
|
874
|
-
io.error(`${statusIcon("error")} error: ${error}`);
|
|
875
|
-
}
|
|
876
|
-
throw new Error(
|
|
877
|
-
`Validation failed (${errors.length} error${errors.length === 1 ? "" : "s"}, ${pluginSources.length} plugin manifest${pluginSources.length === 1 ? "" : "s"}, ${skillDirs.length} skill director${skillDirs.length === 1 ? "y" : "ies"} checked).`
|
|
878
|
-
);
|
|
879
|
-
}
|
|
880
|
-
io.info(
|
|
881
|
-
`${formatHeading("ok", `Validation passed (${pluginSources.length} plugin manifest${pluginSources.length === 1 ? "" : "s"}, ${skillDirs.length} skill director${skillDirs.length === 1 ? "y" : "ies"} checked).`)}`
|
|
882
|
-
);
|
|
883
|
-
}
|
|
884
|
-
export {
|
|
885
|
-
runCheck
|
|
886
|
-
};
|
|
487
|
+
const resolvedRoot = path.resolve(rootDir);
|
|
488
|
+
if (!await pathIsDirectory(resolvedRoot)) throw new Error(`validation root does not exist or is not a directory: ${resolvedRoot}`);
|
|
489
|
+
const packages = declaredPackages(await readRootPackageJson(resolvedRoot));
|
|
490
|
+
const packageWarnings = await collectPackageWarnings(resolvedRoot, packages);
|
|
491
|
+
const packagedContent = await collectPackagedContent(resolvedRoot, packages);
|
|
492
|
+
const appPluginDirs = await collectPluginDirectories(resolvedRoot);
|
|
493
|
+
const packagedPluginSources = packagedContent.pluginSources;
|
|
494
|
+
const pluginSources = [...appPluginDirs.map((pluginDir) => ({
|
|
495
|
+
pluginDir,
|
|
496
|
+
manifestPath: path.join(pluginDir, "plugin.yaml")
|
|
497
|
+
})), ...packagedPluginSources];
|
|
498
|
+
const appSkillDirs = await collectSkillDirectories(contentRoot(resolvedRoot, "skills"));
|
|
499
|
+
const pluginSkillDirs = /* @__PURE__ */ new Map();
|
|
500
|
+
for (const { pluginDir } of pluginSources) pluginSkillDirs.set(pluginDir, await collectSkillDirectories(path.join(pluginDir, "skills")));
|
|
501
|
+
const packagedSkillDirsByPackage = /* @__PURE__ */ new Map();
|
|
502
|
+
for (const skillRoot of packagedContent.skillRoots) packagedSkillDirsByPackage.set(skillRoot.packageName, await collectSkillDirectories(skillRoot.root));
|
|
503
|
+
const packagedStandaloneSkillDirs = [...packagedSkillDirsByPackage.values()].flat();
|
|
504
|
+
const appAndLocalPluginSkillDirs = [...appSkillDirs, ...appPluginDirs.flatMap((pluginDir) => pluginSkillDirs.get(pluginDir) ?? [])].sort((left, right) => left.localeCompare(right));
|
|
505
|
+
const packagedSkillDirs = [...packagedPluginSources.flatMap(({ pluginDir }) => pluginSkillDirs.get(pluginDir) ?? []), ...packagedStandaloneSkillDirs].sort((left, right) => left.localeCompare(right));
|
|
506
|
+
const skillDirs = [...appAndLocalPluginSkillDirs, ...packagedSkillDirs].sort((left, right) => left.localeCompare(right));
|
|
507
|
+
const duplicateSkillNames = /* @__PURE__ */ new Map();
|
|
508
|
+
const duplicatePluginNames = /* @__PURE__ */ new Map();
|
|
509
|
+
const duplicateProviderDomains = /* @__PURE__ */ new Map();
|
|
510
|
+
const duplicatePackagedSkillNames = /* @__PURE__ */ new Map();
|
|
511
|
+
const duplicatePackagedPluginNames = /* @__PURE__ */ new Map();
|
|
512
|
+
const duplicatePackagedProviderDomains = /* @__PURE__ */ new Map();
|
|
513
|
+
const warnings = [];
|
|
514
|
+
const errors = [];
|
|
515
|
+
const pluginResults = [];
|
|
516
|
+
const skillResultsByDir = /* @__PURE__ */ new Map();
|
|
517
|
+
warnings.push(...packageWarnings);
|
|
518
|
+
const deploymentConfigResult = await validateDeploymentConfig(resolvedRoot, packages);
|
|
519
|
+
warnings.push(...deploymentConfigResult.warnings);
|
|
520
|
+
errors.push(...deploymentConfigResult.errors);
|
|
521
|
+
for (const source of pluginSources) {
|
|
522
|
+
const { pluginDir, packageName } = source;
|
|
523
|
+
const pluginNameMap = packageName ? duplicatePackagedPluginNames : duplicatePluginNames;
|
|
524
|
+
const providerDomainMap = packageName ? duplicatePackagedProviderDomains : duplicateProviderDomains;
|
|
525
|
+
const result = source.errors ? {
|
|
526
|
+
manifestPath: source.manifestPath,
|
|
527
|
+
...source.manifest ? { manifest: source.manifest } : void 0,
|
|
528
|
+
errors: source.errors
|
|
529
|
+
} : source.manifest ? validatePluginManifest(source.manifestPath, source.manifest, pluginNameMap, providerDomainMap) : await validatePluginDirectory(pluginDir, pluginNameMap, providerDomainMap);
|
|
530
|
+
pluginResults.push({
|
|
531
|
+
pluginDir,
|
|
532
|
+
manifestPath: result.manifestPath,
|
|
533
|
+
...packageName ? { packageName } : void 0,
|
|
534
|
+
...result.manifest ? { manifest: result.manifest } : void 0,
|
|
535
|
+
errors: result.errors,
|
|
536
|
+
skillResults: []
|
|
537
|
+
});
|
|
538
|
+
errors.push(...result.errors);
|
|
539
|
+
}
|
|
540
|
+
const appSourceResult = await validateAppSourceFiles(resolvedRoot, new Set(pluginResults.flatMap((result) => result.manifest?.configKeys ?? [])));
|
|
541
|
+
warnings.push(...appSourceResult.warnings);
|
|
542
|
+
errors.push(...appSourceResult.errors);
|
|
543
|
+
for (const skillDir of appAndLocalPluginSkillDirs) {
|
|
544
|
+
const result = await validateSkillDirectory(skillDir, duplicateSkillNames);
|
|
545
|
+
skillResultsByDir.set(skillDir, result);
|
|
546
|
+
warnings.push(...result.warnings);
|
|
547
|
+
errors.push(...result.errors);
|
|
548
|
+
}
|
|
549
|
+
for (const skillDir of packagedSkillDirs) {
|
|
550
|
+
const result = await validateSkillDirectory(skillDir, duplicatePackagedSkillNames);
|
|
551
|
+
skillResultsByDir.set(skillDir, result);
|
|
552
|
+
warnings.push(...result.warnings);
|
|
553
|
+
errors.push(...result.errors);
|
|
554
|
+
}
|
|
555
|
+
for (const pluginResult of pluginResults) pluginResult.skillResults = (pluginSkillDirs.get(pluginResult.pluginDir) ?? []).map((skillDir) => skillResultsByDir.get(skillDir)).filter((result) => Boolean(result));
|
|
556
|
+
const appSkillResults = appSkillDirs.map((skillDir) => skillResultsByDir.get(skillDir)).filter((result) => Boolean(result));
|
|
557
|
+
const packagedStandaloneSkillResultsByPackage = /* @__PURE__ */ new Map();
|
|
558
|
+
for (const [packageName, packageSkillDirs] of packagedSkillDirsByPackage) {
|
|
559
|
+
const packageSkillResults = packageSkillDirs.map((skillDir) => skillResultsByDir.get(skillDir)).filter((result) => Boolean(result));
|
|
560
|
+
if (packageSkillResults.length > 0) packagedStandaloneSkillResultsByPackage.set(packageName, packageSkillResults);
|
|
561
|
+
}
|
|
562
|
+
const appDir = path.resolve(resolvedRoot, "app");
|
|
563
|
+
let appFileResult = {
|
|
564
|
+
errors: [],
|
|
565
|
+
warnings: []
|
|
566
|
+
};
|
|
567
|
+
const shouldValidateAppFiles = await pathIsDirectory(appDir) && await hasJuniorAppMarkers(appDir);
|
|
568
|
+
if (shouldValidateAppFiles) {
|
|
569
|
+
appFileResult = await validateAppFiles(appDir);
|
|
570
|
+
warnings.push(...appFileResult.warnings);
|
|
571
|
+
errors.push(...appFileResult.errors);
|
|
572
|
+
}
|
|
573
|
+
io.info(`${color("Checking", ANSI.bold, ANSI.cyan)} ${color(formatDisplayPath(resolvedRoot, resolvedRoot), ANSI.dim)}`);
|
|
574
|
+
if (shouldValidateAppFiles) {
|
|
575
|
+
const appFileStatus = formatStatus(appFileResult.errors.length, appFileResult.warnings.length);
|
|
576
|
+
io.info(formatHeading(appFileStatus, "app files"));
|
|
577
|
+
}
|
|
578
|
+
if (deploymentConfigResult.checked) {
|
|
579
|
+
const deploymentConfigStatus = formatStatus(deploymentConfigResult.errors.length, deploymentConfigResult.warnings.length);
|
|
580
|
+
io.info(formatHeading(deploymentConfigStatus, "deployment config"));
|
|
581
|
+
}
|
|
582
|
+
for (const pluginResult of pluginResults) reportPluginResult(pluginResult, io);
|
|
583
|
+
for (const [packageName, packageSkillResults] of packagedStandaloneSkillResultsByPackage) reportSkillGroup(`packaged skills (${packageName})`, packageSkillResults, io);
|
|
584
|
+
if (appSkillResults.length > 0) reportAppSkills(appSkillResults, io);
|
|
585
|
+
for (const warning of warnings) io.warn(`${statusIcon("warn")} warning: ${warning}`);
|
|
586
|
+
if (errors.length > 0) {
|
|
587
|
+
for (const error of errors) io.error(`${statusIcon("error")} error: ${error}`);
|
|
588
|
+
throw new Error(`Validation failed (${errors.length} error${errors.length === 1 ? "" : "s"}, ${pluginSources.length} plugin manifest${pluginSources.length === 1 ? "" : "s"}, ${skillDirs.length} skill director${skillDirs.length === 1 ? "y" : "ies"} checked).`);
|
|
589
|
+
}
|
|
590
|
+
io.info(`${formatHeading("ok", `Validation passed (${pluginSources.length} plugin manifest${pluginSources.length === 1 ? "" : "s"}, ${skillDirs.length} skill director${skillDirs.length === 1 ? "y" : "ies"} checked).`)}`);
|
|
591
|
+
}
|
|
592
|
+
//#endregion
|
|
593
|
+
export { runCheck };
|