@sentry/junior 0.1.0 → 0.2.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/README.md +7 -56
- package/bin/junior.mjs +38 -128
- package/dist/app/layout.d.ts +3 -0
- package/dist/bot-T73QBC4J.js +16 -0
- package/dist/{chunk-OD6TOSY4.js → chunk-JDBWDYGR.js} +2 -2
- package/dist/{chunk-BBOVH5RF.js → chunk-PY4AI2GZ.js} +62 -15
- package/dist/chunk-QHDDCUTN.js +1940 -0
- package/dist/{chunk-7E56WM6K.js → chunk-RXNMJQPY.js} +8588 -6108
- package/dist/chunk-TQSDLJVE.js +272 -0
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.js +105 -0
- package/dist/cli/run.d.ts +11 -0
- package/dist/cli/run.js +30 -0
- package/dist/cli/snapshot-warmup.d.ts +3 -0
- package/dist/cli/snapshot-warmup.js +57 -0
- package/dist/handlers/health.d.ts +3 -0
- package/dist/handlers/queue-callback.d.ts +7 -0
- package/dist/handlers/queue-callback.js +5 -268
- package/dist/handlers/router.d.ts +18 -3
- package/dist/handlers/router.js +320 -13
- package/dist/handlers/webhooks.d.ts +13 -0
- package/dist/handlers/webhooks.js +2 -2
- package/dist/instrumentation.d.ts +6 -0
- package/dist/next-config.d.ts +8 -0
- package/dist/next-config.js +2 -0
- package/package.json +3 -8
- package/dist/bot-DLML4Z7F.js +0 -3108
- package/dist/channel-HJO33DGJ.js +0 -18
- package/dist/chunk-GDNDYMGX.js +0 -333
- package/dist/chunk-MM3YNA4F.js +0 -203
- package/dist/chunk-ZA2IDPVG.js +0 -39
- package/dist/chunk-ZBFSIN6G.js +0 -323
- package/dist/client-3GAEMIQ3.js +0 -10
- package/dist/route-XLYK6CKP.js +0 -309
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import {
|
|
2
|
+
appSlackRuntime,
|
|
3
|
+
createQueueCallbackHandler,
|
|
4
|
+
downloadPrivateSlackFile,
|
|
5
|
+
getThreadMessageTopic,
|
|
6
|
+
removeReactionFromMessage
|
|
7
|
+
} from "./chunk-RXNMJQPY.js";
|
|
8
|
+
import {
|
|
9
|
+
acquireQueueMessageProcessingOwnership,
|
|
10
|
+
completeQueueMessageProcessingOwnership,
|
|
11
|
+
failQueueMessageProcessingOwnership,
|
|
12
|
+
getQueueMessageProcessingState,
|
|
13
|
+
getStateAdapter,
|
|
14
|
+
refreshQueueMessageProcessingOwnership
|
|
15
|
+
} from "./chunk-QHDDCUTN.js";
|
|
16
|
+
import {
|
|
17
|
+
createRequestContext,
|
|
18
|
+
logError,
|
|
19
|
+
logException,
|
|
20
|
+
logWarn,
|
|
21
|
+
setSpanStatus,
|
|
22
|
+
withContext,
|
|
23
|
+
withSpan
|
|
24
|
+
} from "./chunk-PY4AI2GZ.js";
|
|
25
|
+
|
|
26
|
+
// src/chat/queue/process-thread-message.ts
|
|
27
|
+
import { Message, ThreadImpl } from "chat";
|
|
28
|
+
|
|
29
|
+
// src/chat/thread-runtime/process-thread-message-runtime.ts
|
|
30
|
+
function rehydrateAttachmentFetchers(payload) {
|
|
31
|
+
for (const attachment of payload.message.attachments) {
|
|
32
|
+
if (!attachment.fetchData && attachment.url) {
|
|
33
|
+
attachment.fetchData = () => downloadPrivateSlackFile(attachment.url);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function processThreadMessageRuntime(args) {
|
|
38
|
+
const runtimePayload = {
|
|
39
|
+
message: args.message,
|
|
40
|
+
thread: args.thread
|
|
41
|
+
};
|
|
42
|
+
rehydrateAttachmentFetchers(runtimePayload);
|
|
43
|
+
if (args.kind === "new_mention") {
|
|
44
|
+
await appSlackRuntime.handleNewMention(args.thread, args.message, {
|
|
45
|
+
beforeFirstResponsePost: args.beforeFirstResponsePost
|
|
46
|
+
});
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (args.kind === "subscribed_reply") {
|
|
50
|
+
await appSlackRuntime.handleSubscribedMessage(args.thread, args.message, {
|
|
51
|
+
beforeFirstResponsePost: args.beforeFirstResponsePost,
|
|
52
|
+
preApprovedReply: true
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
await appSlackRuntime.handleSubscribedMessage(args.thread, args.message, {
|
|
57
|
+
beforeFirstResponsePost: args.beforeFirstResponsePost
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/chat/queue/process-thread-message.ts
|
|
62
|
+
var stateAdapterConnected = false;
|
|
63
|
+
function isSerializedThread(thread) {
|
|
64
|
+
return typeof thread === "object" && thread !== null && thread._type === "chat:Thread";
|
|
65
|
+
}
|
|
66
|
+
function isSerializedMessage(message) {
|
|
67
|
+
return typeof message === "object" && message !== null && message._type === "chat:Message";
|
|
68
|
+
}
|
|
69
|
+
function getPayloadChannelId(payload) {
|
|
70
|
+
return payload.thread.channelId;
|
|
71
|
+
}
|
|
72
|
+
function getPayloadUserId(payload) {
|
|
73
|
+
return payload.message.author?.userId;
|
|
74
|
+
}
|
|
75
|
+
function createMessageOwnerToken() {
|
|
76
|
+
return `msg-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
77
|
+
}
|
|
78
|
+
var QueueMessageOwnershipError = class extends Error {
|
|
79
|
+
constructor(stage, dedupKey) {
|
|
80
|
+
super(`Queue message ownership lost during ${stage} for dedupKey=${dedupKey}`);
|
|
81
|
+
this.name = "QueueMessageOwnershipError";
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var defaultProcessQueuedThreadMessageDeps = {
|
|
85
|
+
clearProcessingReaction: async ({ channelId, timestamp }) => {
|
|
86
|
+
await removeReactionFromMessage({
|
|
87
|
+
channelId,
|
|
88
|
+
timestamp,
|
|
89
|
+
emoji: "eyes"
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
logWarn,
|
|
93
|
+
processRuntime: processThreadMessageRuntime
|
|
94
|
+
};
|
|
95
|
+
function deserializeThread(thread) {
|
|
96
|
+
if (isSerializedThread(thread)) {
|
|
97
|
+
return ThreadImpl.fromJSON(thread);
|
|
98
|
+
}
|
|
99
|
+
return thread;
|
|
100
|
+
}
|
|
101
|
+
function deserializeMessage(message) {
|
|
102
|
+
if (isSerializedMessage(message)) {
|
|
103
|
+
return Message.fromJSON(message);
|
|
104
|
+
}
|
|
105
|
+
return message;
|
|
106
|
+
}
|
|
107
|
+
async function logThreadMessageFailure(payload, errorMessage) {
|
|
108
|
+
logError(
|
|
109
|
+
"queue_message_failed",
|
|
110
|
+
{
|
|
111
|
+
slackThreadId: payload.normalizedThreadId,
|
|
112
|
+
slackChannelId: getPayloadChannelId(payload),
|
|
113
|
+
slackUserId: getPayloadUserId(payload)
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"messaging.message.id": payload.message.id,
|
|
117
|
+
"app.queue.message_kind": payload.kind,
|
|
118
|
+
"app.queue.message_id": payload.queueMessageId,
|
|
119
|
+
"error.message": errorMessage
|
|
120
|
+
},
|
|
121
|
+
"Queue message processing failed"
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
async function processQueuedThreadMessage(payload, deps = defaultProcessQueuedThreadMessageDeps) {
|
|
125
|
+
const existingMessageState = await getQueueMessageProcessingState(payload.dedupKey);
|
|
126
|
+
if (existingMessageState?.status === "completed") {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const ownerToken = createMessageOwnerToken();
|
|
130
|
+
const claimResult = await acquireQueueMessageProcessingOwnership({
|
|
131
|
+
rawKey: payload.dedupKey,
|
|
132
|
+
ownerToken,
|
|
133
|
+
queueMessageId: payload.queueMessageId
|
|
134
|
+
});
|
|
135
|
+
if (claimResult === "blocked") {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const threadWasSerialized = isSerializedThread(payload.thread);
|
|
139
|
+
if (threadWasSerialized && !stateAdapterConnected) {
|
|
140
|
+
await getStateAdapter().connect();
|
|
141
|
+
stateAdapterConnected = true;
|
|
142
|
+
}
|
|
143
|
+
const runtimePayload = {
|
|
144
|
+
...payload,
|
|
145
|
+
thread: deserializeThread(payload.thread),
|
|
146
|
+
message: deserializeMessage(payload.message)
|
|
147
|
+
};
|
|
148
|
+
try {
|
|
149
|
+
const refreshed = await refreshQueueMessageProcessingOwnership({
|
|
150
|
+
rawKey: payload.dedupKey,
|
|
151
|
+
ownerToken,
|
|
152
|
+
queueMessageId: payload.queueMessageId
|
|
153
|
+
});
|
|
154
|
+
if (!refreshed) {
|
|
155
|
+
throw new QueueMessageOwnershipError("refresh", payload.dedupKey);
|
|
156
|
+
}
|
|
157
|
+
let reactionCleared = false;
|
|
158
|
+
const clearReactionBeforeFirstResponsePost = async () => {
|
|
159
|
+
if (reactionCleared) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
reactionCleared = true;
|
|
163
|
+
try {
|
|
164
|
+
await deps.clearProcessingReaction({
|
|
165
|
+
channelId: runtimePayload.thread.channelId,
|
|
166
|
+
timestamp: runtimePayload.message.id
|
|
167
|
+
});
|
|
168
|
+
} catch (error) {
|
|
169
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
170
|
+
deps.logWarn(
|
|
171
|
+
"queue_processing_reaction_clear_failed",
|
|
172
|
+
{
|
|
173
|
+
slackThreadId: payload.normalizedThreadId,
|
|
174
|
+
slackChannelId: getPayloadChannelId(payload),
|
|
175
|
+
slackUserId: getPayloadUserId(payload)
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"messaging.message.id": payload.message.id,
|
|
179
|
+
"app.queue.message_kind": payload.kind,
|
|
180
|
+
"app.queue.message_id": payload.queueMessageId,
|
|
181
|
+
"error.message": errorMessage
|
|
182
|
+
},
|
|
183
|
+
"Failed to remove processing reaction before sending queue response"
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
await deps.processRuntime({
|
|
188
|
+
kind: runtimePayload.kind,
|
|
189
|
+
thread: runtimePayload.thread,
|
|
190
|
+
message: runtimePayload.message,
|
|
191
|
+
beforeFirstResponsePost: clearReactionBeforeFirstResponsePost
|
|
192
|
+
});
|
|
193
|
+
const completed = await completeQueueMessageProcessingOwnership({
|
|
194
|
+
rawKey: payload.dedupKey,
|
|
195
|
+
ownerToken,
|
|
196
|
+
queueMessageId: payload.queueMessageId
|
|
197
|
+
});
|
|
198
|
+
if (!completed) {
|
|
199
|
+
throw new QueueMessageOwnershipError("complete", payload.dedupKey);
|
|
200
|
+
}
|
|
201
|
+
} catch (error) {
|
|
202
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
203
|
+
await logThreadMessageFailure(payload, errorMessage);
|
|
204
|
+
const failed = await failQueueMessageProcessingOwnership({
|
|
205
|
+
rawKey: payload.dedupKey,
|
|
206
|
+
ownerToken,
|
|
207
|
+
errorMessage,
|
|
208
|
+
queueMessageId: payload.queueMessageId
|
|
209
|
+
});
|
|
210
|
+
if (!failed && !(error instanceof QueueMessageOwnershipError)) {
|
|
211
|
+
throw new Error(`Failed to persist queue message failure state for dedupKey=${payload.dedupKey}: ${errorMessage}`);
|
|
212
|
+
}
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/handlers/queue-callback.ts
|
|
218
|
+
var callbackHandler = createQueueCallbackHandler(async (message, metadata) => {
|
|
219
|
+
if (metadata.topicName === getThreadMessageTopic()) {
|
|
220
|
+
const payload = {
|
|
221
|
+
...message,
|
|
222
|
+
queueMessageId: metadata.messageId
|
|
223
|
+
};
|
|
224
|
+
await withSpan(
|
|
225
|
+
"queue.process_message",
|
|
226
|
+
"queue.process_message",
|
|
227
|
+
{
|
|
228
|
+
slackThreadId: payload.normalizedThreadId,
|
|
229
|
+
slackChannelId: payload.thread.channelId,
|
|
230
|
+
slackUserId: payload.message.author?.userId
|
|
231
|
+
},
|
|
232
|
+
async () => {
|
|
233
|
+
await processQueuedThreadMessage(payload);
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"messaging.message.id": payload.message.id,
|
|
237
|
+
"app.queue.message_kind": payload.kind,
|
|
238
|
+
"app.queue.message_id": payload.queueMessageId,
|
|
239
|
+
"app.queue.delivery_count": metadata.deliveryCount,
|
|
240
|
+
"app.queue.topic": metadata.topicName
|
|
241
|
+
}
|
|
242
|
+
);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
throw new Error(`Unexpected queue topic: ${metadata.topicName}`);
|
|
246
|
+
});
|
|
247
|
+
async function POST(request) {
|
|
248
|
+
const requestContext = createRequestContext(request, { platform: "queue" });
|
|
249
|
+
return withContext(requestContext, async () => {
|
|
250
|
+
try {
|
|
251
|
+
const response = await callbackHandler(request);
|
|
252
|
+
setSpanStatus(response.status >= 500 ? "error" : "ok");
|
|
253
|
+
return response;
|
|
254
|
+
} catch (error) {
|
|
255
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
256
|
+
logError(
|
|
257
|
+
"queue_callback_failed",
|
|
258
|
+
{},
|
|
259
|
+
{
|
|
260
|
+
"error.message": message
|
|
261
|
+
},
|
|
262
|
+
"Queue callback processing failed"
|
|
263
|
+
);
|
|
264
|
+
logException(error, "queue_callback_failed");
|
|
265
|
+
throw error;
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export {
|
|
271
|
+
POST
|
|
272
|
+
};
|
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// src/cli/init.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
function writeRouteModule(filePath, exportLine) {
|
|
5
|
+
fs.writeFileSync(filePath, `${exportLine}
|
|
6
|
+
export const runtime = "nodejs";
|
|
7
|
+
`);
|
|
8
|
+
}
|
|
9
|
+
function writeWrapperFiles(targetDir) {
|
|
10
|
+
const routeDir = path.join(targetDir, "app", "api", "[...path]");
|
|
11
|
+
fs.mkdirSync(routeDir, { recursive: true });
|
|
12
|
+
writeRouteModule(path.join(routeDir, "route.js"), 'export { GET, POST } from "@sentry/junior/handler";');
|
|
13
|
+
const queueRouteDir = path.join(targetDir, "app", "api", "queue", "callback");
|
|
14
|
+
fs.mkdirSync(queueRouteDir, { recursive: true });
|
|
15
|
+
writeRouteModule(
|
|
16
|
+
path.join(queueRouteDir, "route.js"),
|
|
17
|
+
'export { POST } from "@sentry/junior/handlers/queue-callback";'
|
|
18
|
+
);
|
|
19
|
+
fs.mkdirSync(path.join(targetDir, "app"), { recursive: true });
|
|
20
|
+
fs.writeFileSync(
|
|
21
|
+
path.join(targetDir, "app", "layout.js"),
|
|
22
|
+
'export { default } from "@sentry/junior/app/layout";\n'
|
|
23
|
+
);
|
|
24
|
+
fs.writeFileSync(
|
|
25
|
+
path.join(targetDir, "next.config.mjs"),
|
|
26
|
+
'import { withJunior } from "@sentry/junior/config";\nexport default withJunior();\n'
|
|
27
|
+
);
|
|
28
|
+
fs.writeFileSync(
|
|
29
|
+
path.join(targetDir, "instrumentation.js"),
|
|
30
|
+
'export { register, onRequestError } from "@sentry/junior/instrumentation";\n'
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
async function runInit(dir, log = console.log) {
|
|
34
|
+
const targetDir = dir.trim();
|
|
35
|
+
if (!targetDir) {
|
|
36
|
+
throw new Error("usage: junior init <dir>");
|
|
37
|
+
}
|
|
38
|
+
const target = path.resolve(targetDir);
|
|
39
|
+
if (fs.existsSync(target)) {
|
|
40
|
+
const stat = fs.statSync(target);
|
|
41
|
+
if (!stat.isDirectory()) {
|
|
42
|
+
throw new Error(`refusing to initialize non-directory path: ${target}`);
|
|
43
|
+
}
|
|
44
|
+
if (fs.readdirSync(target).length > 0) {
|
|
45
|
+
throw new Error(`refusing to initialize non-empty directory: ${target}`);
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
fs.mkdirSync(target, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
const name = path.basename(target);
|
|
51
|
+
const pkg = {
|
|
52
|
+
name,
|
|
53
|
+
version: "0.1.0",
|
|
54
|
+
private: true,
|
|
55
|
+
type: "module",
|
|
56
|
+
scripts: {
|
|
57
|
+
dev: "next dev",
|
|
58
|
+
build: "next build",
|
|
59
|
+
start: "next start"
|
|
60
|
+
},
|
|
61
|
+
dependencies: {
|
|
62
|
+
"@sentry/junior": "latest",
|
|
63
|
+
next: "^16.0.0",
|
|
64
|
+
react: "^19.0.0",
|
|
65
|
+
"react-dom": "^19.0.0",
|
|
66
|
+
"@sentry/nextjs": "^10.0.0"
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
fs.writeFileSync(path.join(target, "package.json"), `${JSON.stringify(pkg, null, 2)}
|
|
70
|
+
`);
|
|
71
|
+
const dataDir = path.join(target, "app", "data");
|
|
72
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
73
|
+
fs.writeFileSync(path.join(dataDir, "SOUL.md"), `# ${name}
|
|
74
|
+
|
|
75
|
+
You are ${name}, a helpful assistant.
|
|
76
|
+
`);
|
|
77
|
+
const skillsDir = path.join(target, "app", "skills");
|
|
78
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
79
|
+
fs.writeFileSync(path.join(skillsDir, ".gitkeep"), "");
|
|
80
|
+
const pluginsDir = path.join(target, "app", "plugins");
|
|
81
|
+
fs.mkdirSync(pluginsDir, { recursive: true });
|
|
82
|
+
fs.writeFileSync(path.join(pluginsDir, ".gitkeep"), "");
|
|
83
|
+
fs.writeFileSync(path.join(target, ".gitignore"), ["node_modules/", ".next/", ".env", ".env.local", ""].join("\n"));
|
|
84
|
+
fs.writeFileSync(
|
|
85
|
+
path.join(target, ".env.example"),
|
|
86
|
+
[
|
|
87
|
+
"SLACK_BOT_TOKEN=",
|
|
88
|
+
"SLACK_SIGNING_SECRET=",
|
|
89
|
+
"JUNIOR_BOT_NAME=",
|
|
90
|
+
"AI_MODEL=",
|
|
91
|
+
"AI_FAST_MODEL=",
|
|
92
|
+
"REDIS_URL=",
|
|
93
|
+
"NEXT_PUBLIC_SENTRY_DSN=",
|
|
94
|
+
""
|
|
95
|
+
].join("\n")
|
|
96
|
+
);
|
|
97
|
+
writeWrapperFiles(target);
|
|
98
|
+
log(`Created ${name} at ${target}`);
|
|
99
|
+
log("");
|
|
100
|
+
log(` cd ${targetDir} && pnpm install && pnpm dev`);
|
|
101
|
+
log("");
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
runInit
|
|
105
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare const CLI_USAGE = "usage: junior init <dir>\n junior snapshot create";
|
|
2
|
+
interface CliHandlers {
|
|
3
|
+
runInit: (dir: string) => Promise<void>;
|
|
4
|
+
runSnapshotCreate: () => Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
interface CliIo {
|
|
7
|
+
error: (line: string) => void;
|
|
8
|
+
}
|
|
9
|
+
declare function runCli(argv: string[], handlers: CliHandlers, io?: CliIo): Promise<number>;
|
|
10
|
+
|
|
11
|
+
export { CLI_USAGE, runCli };
|
package/dist/cli/run.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/cli/run.ts
|
|
2
|
+
var CLI_USAGE = "usage: junior init <dir>\n junior snapshot create";
|
|
3
|
+
var DEFAULT_IO = {
|
|
4
|
+
error: console.error
|
|
5
|
+
};
|
|
6
|
+
async function runCli(argv, handlers, io = DEFAULT_IO) {
|
|
7
|
+
const [command, subcommand, ...rest] = argv;
|
|
8
|
+
if (command === "init") {
|
|
9
|
+
if (!subcommand || rest.length > 0) {
|
|
10
|
+
io.error(CLI_USAGE);
|
|
11
|
+
return 1;
|
|
12
|
+
}
|
|
13
|
+
await handlers.runInit(subcommand);
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
if (command === "snapshot" && subcommand === "create") {
|
|
17
|
+
if (rest.length > 0) {
|
|
18
|
+
io.error(CLI_USAGE);
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
await handlers.runSnapshotCreate();
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
io.error(CLI_USAGE);
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
CLI_USAGE,
|
|
29
|
+
runCli
|
|
30
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
disconnectStateAdapter,
|
|
3
|
+
resolveRuntimeDependencySnapshot
|
|
4
|
+
} from "../chunk-QHDDCUTN.js";
|
|
5
|
+
import "../chunk-PY4AI2GZ.js";
|
|
6
|
+
|
|
7
|
+
// src/cli/snapshot-warmup.ts
|
|
8
|
+
var DEFAULT_RUNTIME = "node22";
|
|
9
|
+
var DEFAULT_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
10
|
+
function progressMessage(phase) {
|
|
11
|
+
if (phase === "resolve_start") {
|
|
12
|
+
return "Resolving sandbox snapshot profile...";
|
|
13
|
+
}
|
|
14
|
+
if (phase === "cache_hit") {
|
|
15
|
+
return "Using cached sandbox snapshot.";
|
|
16
|
+
}
|
|
17
|
+
if (phase === "waiting_for_lock") {
|
|
18
|
+
return "Waiting for sandbox snapshot build lock...";
|
|
19
|
+
}
|
|
20
|
+
if (phase === "building_snapshot") {
|
|
21
|
+
return "Building sandbox snapshot...";
|
|
22
|
+
}
|
|
23
|
+
return "Sandbox snapshot build complete.";
|
|
24
|
+
}
|
|
25
|
+
async function runSnapshotCreate(log = console.log) {
|
|
26
|
+
const runtime = DEFAULT_RUNTIME;
|
|
27
|
+
const timeoutMs = DEFAULT_TIMEOUT_MS;
|
|
28
|
+
try {
|
|
29
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
30
|
+
const snapshot = await resolveRuntimeDependencySnapshot({
|
|
31
|
+
runtime,
|
|
32
|
+
timeoutMs,
|
|
33
|
+
onProgress: async (phase) => {
|
|
34
|
+
if (emitted.has(phase)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
emitted.add(phase);
|
|
38
|
+
log(progressMessage(phase));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
const fields = [
|
|
42
|
+
`runtime=${runtime}`,
|
|
43
|
+
`resolve_outcome=${snapshot.resolveOutcome}`,
|
|
44
|
+
`cache_hit=${snapshot.cacheHit}`,
|
|
45
|
+
`dependency_count=${snapshot.dependencyCount}`,
|
|
46
|
+
...snapshot.profileHash ? [`profile_hash=${snapshot.profileHash}`] : [],
|
|
47
|
+
...snapshot.snapshotId ? [`snapshot_id=${snapshot.snapshotId}`] : [],
|
|
48
|
+
...snapshot.rebuildReason ? [`rebuild_reason=${snapshot.rebuildReason}`] : []
|
|
49
|
+
];
|
|
50
|
+
log(`Sandbox snapshot create complete: ${fields.join(" ")}`);
|
|
51
|
+
} finally {
|
|
52
|
+
await disconnectStateAdapter();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
runSnapshotCreate
|
|
57
|
+
};
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handles `POST /api/queue/callback` for asynchronous thread processing.
|
|
3
|
+
*
|
|
4
|
+
* Keep this route as a dedicated handler in app code. The catch-all router can
|
|
5
|
+
* mirror this path for local/dev parity, but production queue delivery should
|
|
6
|
+
* always target the dedicated endpoint.
|
|
7
|
+
*/
|
|
1
8
|
declare function POST(request: Request): Promise<Response>;
|
|
2
9
|
|
|
3
10
|
export { POST };
|