@sentry/junior 0.7.0 → 0.9.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/chunk-4G2LA7RO.js +678 -0
- package/dist/{chunk-JRKU55W5.js → chunk-DIMXJUSL.js} +10952 -9015
- package/dist/{chunk-QHKQ2AWX.js → chunk-I3DYWLM6.js} +15 -6
- package/dist/chunk-IJVZEV3K.js +840 -0
- package/dist/{chunk-Z5E25LRN.js → chunk-KCLEEKYX.js} +124 -17
- package/dist/chunk-VM3CPAZF.js +448 -0
- package/dist/chunk-ZBWWHP6Q.js +1436 -0
- package/dist/{chunk-PY4AI2GZ.js → chunk-ZW4OVKF5.js} +376 -79
- package/dist/cli/check.js +4 -2
- package/dist/cli/snapshot-warmup.js +7 -6
- package/dist/handlers/queue-callback.js +7 -7
- package/dist/handlers/router.d.ts +1 -0
- package/dist/handlers/router.js +523 -85
- package/dist/handlers/webhooks.js +2 -2
- package/dist/next-config.js +1 -1
- package/dist/production-XMCJXOOI.js +15 -0
- package/package.json +13 -15
- package/dist/bot-ZKMCCT3D.js +0 -19
- package/dist/chunk-56WI5Q7P.js +0 -330
- package/dist/chunk-KT5HARSN.js +0 -164
- package/dist/chunk-RKOO42TW.js +0 -1797
- package/dist/chunk-VW26MOSO.js +0 -522
package/dist/next-config.js
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bot,
|
|
3
|
+
createNormalizingStream,
|
|
4
|
+
slackRuntime
|
|
5
|
+
} from "./chunk-DIMXJUSL.js";
|
|
6
|
+
import "./chunk-VM3CPAZF.js";
|
|
7
|
+
import "./chunk-IJVZEV3K.js";
|
|
8
|
+
import "./chunk-ZBWWHP6Q.js";
|
|
9
|
+
import "./chunk-KCLEEKYX.js";
|
|
10
|
+
import "./chunk-ZW4OVKF5.js";
|
|
11
|
+
export {
|
|
12
|
+
bot,
|
|
13
|
+
createNormalizingStream,
|
|
14
|
+
slackRuntime
|
|
15
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -27,16 +27,17 @@
|
|
|
27
27
|
"@chat-adapter/slack": "4.20.2",
|
|
28
28
|
"@chat-adapter/state-memory": "4.20.2",
|
|
29
29
|
"@chat-adapter/state-redis": "4.20.2",
|
|
30
|
-
"@mariozechner/pi-agent-core": "^0.
|
|
31
|
-
"@mariozechner/pi-ai": "^0.
|
|
30
|
+
"@mariozechner/pi-agent-core": "^0.59.0",
|
|
31
|
+
"@mariozechner/pi-ai": "^0.59.0",
|
|
32
|
+
"@modelcontextprotocol/sdk": "1.27.1",
|
|
32
33
|
"@sinclair/typebox": "^0.34.48",
|
|
33
|
-
"@slack/web-api": "^7.
|
|
34
|
-
"@vercel/queue": "^0.1.
|
|
35
|
-
"@vercel/sandbox": "^1.8.
|
|
34
|
+
"@slack/web-api": "^7.15.0",
|
|
35
|
+
"@vercel/queue": "^0.1.4",
|
|
36
|
+
"@vercel/sandbox": "^1.8.1",
|
|
36
37
|
"ai": "^6.0.116",
|
|
37
38
|
"bash-tool": "^1.3.15",
|
|
38
39
|
"chat": "4.20.2",
|
|
39
|
-
"just-bash": "^2.
|
|
40
|
+
"just-bash": "^2.13.1",
|
|
40
41
|
"node-html-markdown": "^2.0.0",
|
|
41
42
|
"yaml": "^2.8.2",
|
|
42
43
|
"zod": "^4.3.6"
|
|
@@ -48,26 +49,23 @@
|
|
|
48
49
|
"react-dom": ">=19.0.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
|
-
"@sentry/nextjs": "^10.
|
|
52
|
+
"@sentry/nextjs": "^10.44.0",
|
|
52
53
|
"@types/node": "^25.3.5",
|
|
53
54
|
"@types/react": "^19.2.14",
|
|
54
55
|
"@types/react-dom": "^19.2.3",
|
|
55
|
-
"msw": "^2.12.
|
|
56
|
-
"next": "^16.1.
|
|
56
|
+
"msw": "^2.12.13",
|
|
57
|
+
"next": "^16.1.7",
|
|
57
58
|
"react": "^19.2.4",
|
|
58
59
|
"react-dom": "^19.2.4",
|
|
59
60
|
"tsup": "^8.5.1",
|
|
60
61
|
"typescript": "^5.9.3",
|
|
61
|
-
"vercel": "^50.
|
|
62
|
-
"vitest": "^4.0
|
|
63
|
-
"vitest-evals": "^0.6.0"
|
|
62
|
+
"vercel": "^50.32.5",
|
|
63
|
+
"vitest": "^4.1.0"
|
|
64
64
|
},
|
|
65
65
|
"scripts": {
|
|
66
66
|
"build": "tsup",
|
|
67
67
|
"test": "pnpm run test:slack-boundary && vitest run",
|
|
68
68
|
"test:watch": "vitest",
|
|
69
|
-
"preevals": "pnpm run test:slack-boundary",
|
|
70
|
-
"evals": "JUNIOR_STATE_ADAPTER=memory pnpm exec vitest run -c vitest.evals.config.ts",
|
|
71
69
|
"test:slack-boundary": "node scripts/check-slack-test-boundary.mjs",
|
|
72
70
|
"typecheck": "tsc --noEmit",
|
|
73
71
|
"skills:check": "node scripts/check-skills.mjs"
|
package/dist/bot-ZKMCCT3D.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
appSlackRuntime,
|
|
3
|
-
bot,
|
|
4
|
-
createNormalizingStream,
|
|
5
|
-
resetBotDepsForTests,
|
|
6
|
-
setBotDepsForTests
|
|
7
|
-
} from "./chunk-JRKU55W5.js";
|
|
8
|
-
import "./chunk-KT5HARSN.js";
|
|
9
|
-
import "./chunk-RKOO42TW.js";
|
|
10
|
-
import "./chunk-Z5E25LRN.js";
|
|
11
|
-
import "./chunk-PY4AI2GZ.js";
|
|
12
|
-
import "./chunk-VW26MOSO.js";
|
|
13
|
-
export {
|
|
14
|
-
appSlackRuntime,
|
|
15
|
-
bot,
|
|
16
|
-
createNormalizingStream,
|
|
17
|
-
resetBotDepsForTests,
|
|
18
|
-
setBotDepsForTests
|
|
19
|
-
};
|
package/dist/chunk-56WI5Q7P.js
DELETED
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
appSlackRuntime,
|
|
3
|
-
createQueueCallbackHandler,
|
|
4
|
-
downloadPrivateSlackFile,
|
|
5
|
-
getThreadMessageTopic,
|
|
6
|
-
removeReactionFromMessage
|
|
7
|
-
} from "./chunk-JRKU55W5.js";
|
|
8
|
-
import {
|
|
9
|
-
acquireQueueMessageProcessingOwnership,
|
|
10
|
-
completeQueueMessageProcessingOwnership,
|
|
11
|
-
failQueueMessageProcessingOwnership,
|
|
12
|
-
getQueueMessageProcessingState,
|
|
13
|
-
getStateAdapter,
|
|
14
|
-
refreshQueueMessageProcessingOwnership
|
|
15
|
-
} from "./chunk-RKOO42TW.js";
|
|
16
|
-
import {
|
|
17
|
-
createRequestContext,
|
|
18
|
-
logError,
|
|
19
|
-
logException,
|
|
20
|
-
logInfo,
|
|
21
|
-
logWarn,
|
|
22
|
-
setSpanStatus,
|
|
23
|
-
withContext,
|
|
24
|
-
withSpan
|
|
25
|
-
} from "./chunk-PY4AI2GZ.js";
|
|
26
|
-
|
|
27
|
-
// src/chat/queue/process-thread-message.ts
|
|
28
|
-
import { Message, ThreadImpl } from "chat";
|
|
29
|
-
|
|
30
|
-
// src/chat/thread-runtime/process-thread-message-runtime.ts
|
|
31
|
-
function rehydrateAttachmentFetchers(payload) {
|
|
32
|
-
for (const attachment of payload.message.attachments) {
|
|
33
|
-
if (!attachment.fetchData && attachment.url) {
|
|
34
|
-
attachment.fetchData = () => downloadPrivateSlackFile(attachment.url);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
async function processThreadMessageRuntime(args) {
|
|
39
|
-
const runtimePayload = {
|
|
40
|
-
message: args.message,
|
|
41
|
-
thread: args.thread
|
|
42
|
-
};
|
|
43
|
-
rehydrateAttachmentFetchers(runtimePayload);
|
|
44
|
-
if (args.kind === "new_mention") {
|
|
45
|
-
await appSlackRuntime.handleNewMention(args.thread, args.message, {
|
|
46
|
-
beforeFirstResponsePost: args.beforeFirstResponsePost
|
|
47
|
-
});
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
if (args.kind === "subscribed_reply") {
|
|
51
|
-
await appSlackRuntime.handleSubscribedMessage(args.thread, args.message, {
|
|
52
|
-
beforeFirstResponsePost: args.beforeFirstResponsePost,
|
|
53
|
-
preApprovedReply: true
|
|
54
|
-
});
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
await appSlackRuntime.handleSubscribedMessage(args.thread, args.message, {
|
|
58
|
-
beforeFirstResponsePost: args.beforeFirstResponsePost
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// src/chat/queue/process-thread-message.ts
|
|
63
|
-
var stateAdapterConnected = false;
|
|
64
|
-
function isSerializedThread(thread) {
|
|
65
|
-
return typeof thread === "object" && thread !== null && thread._type === "chat:Thread";
|
|
66
|
-
}
|
|
67
|
-
function isSerializedMessage(message) {
|
|
68
|
-
return typeof message === "object" && message !== null && message._type === "chat:Message";
|
|
69
|
-
}
|
|
70
|
-
function getPayloadChannelId(payload) {
|
|
71
|
-
return payload.thread.channelId;
|
|
72
|
-
}
|
|
73
|
-
function getPayloadUserId(payload) {
|
|
74
|
-
return payload.message.author?.userId;
|
|
75
|
-
}
|
|
76
|
-
function createMessageOwnerToken() {
|
|
77
|
-
return `msg-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
78
|
-
}
|
|
79
|
-
var QueueMessageOwnershipError = class extends Error {
|
|
80
|
-
constructor(stage, dedupKey) {
|
|
81
|
-
super(
|
|
82
|
-
`Queue message ownership lost during ${stage} for dedupKey=${dedupKey}`
|
|
83
|
-
);
|
|
84
|
-
this.name = "QueueMessageOwnershipError";
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
var defaultProcessQueuedThreadMessageDeps = {
|
|
88
|
-
clearProcessingReaction: async ({ channelId, timestamp }) => {
|
|
89
|
-
await removeReactionFromMessage({
|
|
90
|
-
channelId,
|
|
91
|
-
timestamp,
|
|
92
|
-
emoji: "eyes"
|
|
93
|
-
});
|
|
94
|
-
},
|
|
95
|
-
logInfo,
|
|
96
|
-
logWarn,
|
|
97
|
-
processRuntime: processThreadMessageRuntime
|
|
98
|
-
};
|
|
99
|
-
function deserializeThread(thread) {
|
|
100
|
-
if (isSerializedThread(thread)) {
|
|
101
|
-
return ThreadImpl.fromJSON(thread);
|
|
102
|
-
}
|
|
103
|
-
return thread;
|
|
104
|
-
}
|
|
105
|
-
function deserializeMessage(message) {
|
|
106
|
-
if (isSerializedMessage(message)) {
|
|
107
|
-
return Message.fromJSON(message);
|
|
108
|
-
}
|
|
109
|
-
return message;
|
|
110
|
-
}
|
|
111
|
-
async function logThreadMessageFailure(payload, errorMessage) {
|
|
112
|
-
logError(
|
|
113
|
-
"queue_message_failed",
|
|
114
|
-
{
|
|
115
|
-
slackThreadId: payload.normalizedThreadId,
|
|
116
|
-
slackChannelId: getPayloadChannelId(payload),
|
|
117
|
-
slackUserId: getPayloadUserId(payload)
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
"messaging.message.id": payload.message.id,
|
|
121
|
-
"app.queue.message_kind": payload.kind,
|
|
122
|
-
"app.queue.message_id": payload.queueMessageId,
|
|
123
|
-
"error.message": errorMessage
|
|
124
|
-
},
|
|
125
|
-
"Queue message processing failed"
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
async function processQueuedThreadMessage(payload, deps = defaultProcessQueuedThreadMessageDeps) {
|
|
129
|
-
const existingMessageState = await getQueueMessageProcessingState(
|
|
130
|
-
payload.dedupKey
|
|
131
|
-
);
|
|
132
|
-
if (existingMessageState?.status === "completed") {
|
|
133
|
-
deps.logInfo(
|
|
134
|
-
"queue_message_skipped_completed",
|
|
135
|
-
{
|
|
136
|
-
slackThreadId: payload.normalizedThreadId,
|
|
137
|
-
slackChannelId: getPayloadChannelId(payload),
|
|
138
|
-
slackUserId: getPayloadUserId(payload)
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
"messaging.message.id": payload.message.id,
|
|
142
|
-
"app.queue.message_kind": payload.kind,
|
|
143
|
-
"app.queue.message_id": payload.queueMessageId,
|
|
144
|
-
"app.queue.processing_state": existingMessageState.status
|
|
145
|
-
},
|
|
146
|
-
"Skipping queue message because it is already completed"
|
|
147
|
-
);
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
const ownerToken = createMessageOwnerToken();
|
|
151
|
-
const claimResult = await acquireQueueMessageProcessingOwnership({
|
|
152
|
-
rawKey: payload.dedupKey,
|
|
153
|
-
ownerToken,
|
|
154
|
-
queueMessageId: payload.queueMessageId
|
|
155
|
-
});
|
|
156
|
-
if (claimResult === "blocked") {
|
|
157
|
-
deps.logInfo(
|
|
158
|
-
"queue_message_skipped_blocked",
|
|
159
|
-
{
|
|
160
|
-
slackThreadId: payload.normalizedThreadId,
|
|
161
|
-
slackChannelId: getPayloadChannelId(payload),
|
|
162
|
-
slackUserId: getPayloadUserId(payload)
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
"messaging.message.id": payload.message.id,
|
|
166
|
-
"app.queue.message_kind": payload.kind,
|
|
167
|
-
"app.queue.message_id": payload.queueMessageId,
|
|
168
|
-
"app.queue.claim_result": claimResult,
|
|
169
|
-
"app.queue.processing_state": "processing"
|
|
170
|
-
},
|
|
171
|
-
"Skipping queue message because another worker owns it"
|
|
172
|
-
);
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
const threadWasSerialized = isSerializedThread(payload.thread);
|
|
176
|
-
if (threadWasSerialized && !stateAdapterConnected) {
|
|
177
|
-
await getStateAdapter().connect();
|
|
178
|
-
stateAdapterConnected = true;
|
|
179
|
-
}
|
|
180
|
-
const runtimePayload = {
|
|
181
|
-
...payload,
|
|
182
|
-
thread: deserializeThread(payload.thread),
|
|
183
|
-
message: deserializeMessage(payload.message)
|
|
184
|
-
};
|
|
185
|
-
let reactionCleared = false;
|
|
186
|
-
const clearProcessingReaction = async () => {
|
|
187
|
-
if (reactionCleared) {
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
reactionCleared = true;
|
|
191
|
-
try {
|
|
192
|
-
await deps.clearProcessingReaction({
|
|
193
|
-
channelId: runtimePayload.thread.channelId,
|
|
194
|
-
timestamp: runtimePayload.message.id
|
|
195
|
-
});
|
|
196
|
-
} catch (error) {
|
|
197
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
198
|
-
deps.logWarn(
|
|
199
|
-
"queue_processing_reaction_clear_failed",
|
|
200
|
-
{
|
|
201
|
-
slackThreadId: payload.normalizedThreadId,
|
|
202
|
-
slackChannelId: getPayloadChannelId(payload),
|
|
203
|
-
slackUserId: getPayloadUserId(payload)
|
|
204
|
-
},
|
|
205
|
-
{
|
|
206
|
-
"messaging.message.id": payload.message.id,
|
|
207
|
-
"app.queue.message_kind": payload.kind,
|
|
208
|
-
"app.queue.message_id": payload.queueMessageId,
|
|
209
|
-
"error.message": errorMessage
|
|
210
|
-
},
|
|
211
|
-
"Failed to remove processing reaction after queue turn completion"
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
};
|
|
215
|
-
try {
|
|
216
|
-
const refreshed = await refreshQueueMessageProcessingOwnership({
|
|
217
|
-
rawKey: payload.dedupKey,
|
|
218
|
-
ownerToken,
|
|
219
|
-
queueMessageId: payload.queueMessageId
|
|
220
|
-
});
|
|
221
|
-
if (!refreshed) {
|
|
222
|
-
throw new QueueMessageOwnershipError("refresh", payload.dedupKey);
|
|
223
|
-
}
|
|
224
|
-
await deps.processRuntime({
|
|
225
|
-
kind: runtimePayload.kind,
|
|
226
|
-
thread: runtimePayload.thread,
|
|
227
|
-
message: runtimePayload.message
|
|
228
|
-
});
|
|
229
|
-
await clearProcessingReaction();
|
|
230
|
-
const completed = await completeQueueMessageProcessingOwnership({
|
|
231
|
-
rawKey: payload.dedupKey,
|
|
232
|
-
ownerToken,
|
|
233
|
-
queueMessageId: payload.queueMessageId
|
|
234
|
-
});
|
|
235
|
-
if (!completed) {
|
|
236
|
-
throw new QueueMessageOwnershipError("complete", payload.dedupKey);
|
|
237
|
-
}
|
|
238
|
-
} catch (error) {
|
|
239
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
240
|
-
await clearProcessingReaction();
|
|
241
|
-
await logThreadMessageFailure(payload, errorMessage);
|
|
242
|
-
const failed = await failQueueMessageProcessingOwnership({
|
|
243
|
-
rawKey: payload.dedupKey,
|
|
244
|
-
ownerToken,
|
|
245
|
-
errorMessage,
|
|
246
|
-
queueMessageId: payload.queueMessageId
|
|
247
|
-
});
|
|
248
|
-
if (!failed && !(error instanceof QueueMessageOwnershipError)) {
|
|
249
|
-
throw new Error(
|
|
250
|
-
`Failed to persist queue message failure state for dedupKey=${payload.dedupKey}: ${errorMessage}`
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
throw error;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// src/handlers/queue-callback.ts
|
|
258
|
-
var callbackHandler = createQueueCallbackHandler(
|
|
259
|
-
async (message, metadata) => {
|
|
260
|
-
if (metadata.topicName === getThreadMessageTopic()) {
|
|
261
|
-
const payload = {
|
|
262
|
-
...message,
|
|
263
|
-
queueMessageId: metadata.messageId
|
|
264
|
-
};
|
|
265
|
-
logInfo(
|
|
266
|
-
"queue_callback_received",
|
|
267
|
-
{
|
|
268
|
-
slackThreadId: payload.normalizedThreadId,
|
|
269
|
-
slackChannelId: payload.thread.channelId,
|
|
270
|
-
slackUserId: payload.message.author?.userId
|
|
271
|
-
},
|
|
272
|
-
{
|
|
273
|
-
"messaging.message.id": payload.message.id,
|
|
274
|
-
"app.queue.message_kind": payload.kind,
|
|
275
|
-
"app.queue.message_id": payload.queueMessageId,
|
|
276
|
-
"app.queue.delivery_count": metadata.deliveryCount,
|
|
277
|
-
"app.queue.topic": metadata.topicName
|
|
278
|
-
},
|
|
279
|
-
"Received queue callback payload"
|
|
280
|
-
);
|
|
281
|
-
await withSpan(
|
|
282
|
-
"queue.process_message",
|
|
283
|
-
"queue.process_message",
|
|
284
|
-
{
|
|
285
|
-
slackThreadId: payload.normalizedThreadId,
|
|
286
|
-
slackChannelId: payload.thread.channelId,
|
|
287
|
-
slackUserId: payload.message.author?.userId
|
|
288
|
-
},
|
|
289
|
-
async () => {
|
|
290
|
-
await processQueuedThreadMessage(payload);
|
|
291
|
-
},
|
|
292
|
-
{
|
|
293
|
-
"messaging.message.id": payload.message.id,
|
|
294
|
-
"app.queue.message_kind": payload.kind,
|
|
295
|
-
"app.queue.message_id": payload.queueMessageId,
|
|
296
|
-
"app.queue.delivery_count": metadata.deliveryCount,
|
|
297
|
-
"app.queue.topic": metadata.topicName
|
|
298
|
-
}
|
|
299
|
-
);
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
throw new Error(`Unexpected queue topic: ${metadata.topicName}`);
|
|
303
|
-
}
|
|
304
|
-
);
|
|
305
|
-
async function POST(request) {
|
|
306
|
-
const requestContext = createRequestContext(request, { platform: "queue" });
|
|
307
|
-
return withContext(requestContext, async () => {
|
|
308
|
-
try {
|
|
309
|
-
const response = await callbackHandler(request);
|
|
310
|
-
setSpanStatus(response.status >= 500 ? "error" : "ok");
|
|
311
|
-
return response;
|
|
312
|
-
} catch (error) {
|
|
313
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
314
|
-
logError(
|
|
315
|
-
"queue_callback_failed",
|
|
316
|
-
{},
|
|
317
|
-
{
|
|
318
|
-
"error.message": message
|
|
319
|
-
},
|
|
320
|
-
"Queue callback processing failed"
|
|
321
|
-
);
|
|
322
|
-
logException(error, "queue_callback_failed");
|
|
323
|
-
throw error;
|
|
324
|
-
}
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
export {
|
|
329
|
-
POST
|
|
330
|
-
};
|
package/dist/chunk-KT5HARSN.js
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
// src/chat/skill-frontmatter.ts
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { parse as parseYaml } from "yaml";
|
|
4
|
-
var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
5
|
-
var SKILL_NAME_RE = /^[a-z0-9-]+$/;
|
|
6
|
-
var CAPABILITY_TOKEN_RE = /^[a-z0-9]+(?:\.[a-z0-9-]+)+$/;
|
|
7
|
-
var MAX_NAME_LENGTH = 64;
|
|
8
|
-
var MAX_DESCRIPTION_LENGTH = 1024;
|
|
9
|
-
var MAX_COMPATIBILITY_LENGTH = 500;
|
|
10
|
-
function hasAngleBrackets(value) {
|
|
11
|
-
return value.includes("<") || value.includes(">");
|
|
12
|
-
}
|
|
13
|
-
function validateSkillName(name) {
|
|
14
|
-
if (!name) return "name must not be empty";
|
|
15
|
-
if (name.length > MAX_NAME_LENGTH)
|
|
16
|
-
return `name must be <= ${MAX_NAME_LENGTH} characters`;
|
|
17
|
-
if (!SKILL_NAME_RE.test(name))
|
|
18
|
-
return "name must contain only lowercase letters, digits, and hyphens";
|
|
19
|
-
if (name.startsWith("-") || name.endsWith("-"))
|
|
20
|
-
return "name must not start or end with a hyphen";
|
|
21
|
-
if (name.includes("--")) return "name must not contain consecutive hyphens";
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
function createTokenFieldSchema(fieldName, example) {
|
|
25
|
-
return z.string({
|
|
26
|
-
error: `Frontmatter field "${fieldName}" must be a string when present`
|
|
27
|
-
}).superRefine((value, ctx) => {
|
|
28
|
-
const tokens = value.split(/\s+/).map((token) => token.trim()).filter((token) => token.length > 0);
|
|
29
|
-
for (const token of tokens) {
|
|
30
|
-
if (!CAPABILITY_TOKEN_RE.test(token)) {
|
|
31
|
-
ctx.addIssue({
|
|
32
|
-
code: z.ZodIssueCode.custom,
|
|
33
|
-
message: `${fieldName} token "${token}" is invalid; expected dotted lowercase tokens (for example "${example}")`
|
|
34
|
-
});
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
function parseTokenList(value) {
|
|
41
|
-
if (typeof value !== "string") {
|
|
42
|
-
return void 0;
|
|
43
|
-
}
|
|
44
|
-
const tokens = value.split(/\s+/).map((token) => token.trim()).filter((token) => token.length > 0);
|
|
45
|
-
return tokens.length > 0 ? tokens : void 0;
|
|
46
|
-
}
|
|
47
|
-
var skillFrontmatterSchema = z.object({
|
|
48
|
-
name: z.string({ error: 'Frontmatter field "name" must be a string' }).superRefine((value, ctx) => {
|
|
49
|
-
const nameError = validateSkillName(value);
|
|
50
|
-
if (nameError) {
|
|
51
|
-
ctx.addIssue({
|
|
52
|
-
code: z.ZodIssueCode.custom,
|
|
53
|
-
message: nameError
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
}),
|
|
57
|
-
description: z.string({ error: 'Frontmatter field "description" must be a string' }).superRefine((value, ctx) => {
|
|
58
|
-
if (!value.trim()) {
|
|
59
|
-
ctx.addIssue({
|
|
60
|
-
code: z.ZodIssueCode.custom,
|
|
61
|
-
message: "description must not be empty"
|
|
62
|
-
});
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
if (value.length > MAX_DESCRIPTION_LENGTH) {
|
|
66
|
-
ctx.addIssue({
|
|
67
|
-
code: z.ZodIssueCode.custom,
|
|
68
|
-
message: `description must be <= ${MAX_DESCRIPTION_LENGTH} characters`
|
|
69
|
-
});
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
if (hasAngleBrackets(value)) {
|
|
73
|
-
ctx.addIssue({
|
|
74
|
-
code: z.ZodIssueCode.custom,
|
|
75
|
-
message: 'description must not contain "<" or ">"'
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
}),
|
|
79
|
-
metadata: z.record(z.string(), z.unknown(), {
|
|
80
|
-
error: 'Frontmatter field "metadata" must be an object when present'
|
|
81
|
-
}).optional(),
|
|
82
|
-
compatibility: z.string({
|
|
83
|
-
error: 'Frontmatter field "compatibility" must be a string when present'
|
|
84
|
-
}).superRefine((value, ctx) => {
|
|
85
|
-
if (value.length > MAX_COMPATIBILITY_LENGTH) {
|
|
86
|
-
ctx.addIssue({
|
|
87
|
-
code: z.ZodIssueCode.custom,
|
|
88
|
-
message: `compatibility must be <= ${MAX_COMPATIBILITY_LENGTH} characters`
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}).optional(),
|
|
92
|
-
license: z.string({
|
|
93
|
-
error: 'Frontmatter field "license" must be a string when present'
|
|
94
|
-
}).optional(),
|
|
95
|
-
"allowed-tools": z.string({
|
|
96
|
-
error: 'Frontmatter field "allowed-tools" must be a string when present'
|
|
97
|
-
}).optional(),
|
|
98
|
-
"requires-capabilities": createTokenFieldSchema(
|
|
99
|
-
"requires-capabilities",
|
|
100
|
-
"github.issues.write"
|
|
101
|
-
).optional(),
|
|
102
|
-
"uses-config": createTokenFieldSchema(
|
|
103
|
-
"uses-config",
|
|
104
|
-
"github.repo"
|
|
105
|
-
).optional()
|
|
106
|
-
}).passthrough();
|
|
107
|
-
function stripFrontmatter(raw) {
|
|
108
|
-
return raw.replace(FRONTMATTER_RE, "").trim();
|
|
109
|
-
}
|
|
110
|
-
function parseSkillFile(raw, expectedName) {
|
|
111
|
-
const match = FRONTMATTER_RE.exec(raw);
|
|
112
|
-
if (!match) {
|
|
113
|
-
return { ok: false, error: "Missing YAML frontmatter at start of file" };
|
|
114
|
-
}
|
|
115
|
-
let parsed;
|
|
116
|
-
try {
|
|
117
|
-
parsed = parseYaml(match[1]);
|
|
118
|
-
} catch (error) {
|
|
119
|
-
return {
|
|
120
|
-
ok: false,
|
|
121
|
-
error: `Invalid YAML frontmatter: ${error instanceof Error ? error.message : String(error)}`
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
125
|
-
return { ok: false, error: "Frontmatter must be a YAML object" };
|
|
126
|
-
}
|
|
127
|
-
const result = skillFrontmatterSchema.safeParse(parsed);
|
|
128
|
-
if (!result.success) {
|
|
129
|
-
return {
|
|
130
|
-
ok: false,
|
|
131
|
-
error: result.error.issues[0]?.message ?? "Invalid YAML frontmatter"
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
if (expectedName && result.data.name !== expectedName) {
|
|
135
|
-
return {
|
|
136
|
-
ok: false,
|
|
137
|
-
error: `name "${result.data.name}" must match directory "${expectedName}"`
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
const allowedTools = parseTokenList(result.data["allowed-tools"]);
|
|
141
|
-
const requiresCapabilities = parseTokenList(
|
|
142
|
-
result.data["requires-capabilities"]
|
|
143
|
-
);
|
|
144
|
-
const usesConfig = parseTokenList(result.data["uses-config"]);
|
|
145
|
-
return {
|
|
146
|
-
ok: true,
|
|
147
|
-
skill: {
|
|
148
|
-
name: result.data.name,
|
|
149
|
-
description: result.data.description,
|
|
150
|
-
body: stripFrontmatter(raw),
|
|
151
|
-
...result.data.metadata ? { metadata: result.data.metadata } : {},
|
|
152
|
-
...result.data.compatibility !== void 0 ? { compatibility: result.data.compatibility } : {},
|
|
153
|
-
...result.data.license !== void 0 ? { license: result.data.license } : {},
|
|
154
|
-
...allowedTools ? { allowedTools } : {},
|
|
155
|
-
...requiresCapabilities ? { requiresCapabilities } : {},
|
|
156
|
-
...usesConfig ? { usesConfig } : {}
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export {
|
|
162
|
-
stripFrontmatter,
|
|
163
|
-
parseSkillFile
|
|
164
|
-
};
|