@sentry/junior-dashboard 0.62.0 → 0.64.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/app.d.ts +2 -0
- package/dist/app.js +1285 -39
- package/dist/assets.d.ts +2 -0
- package/dist/client/api.d.ts +1 -1
- package/dist/client/components/Button.d.ts +15 -0
- package/dist/client/components/Metric.d.ts +22 -0
- package/dist/client/components/StatusBadge.d.ts +2 -1
- package/dist/client/components/TelemetryMetrics.d.ts +24 -0
- package/dist/client/components/ToolFrame.d.ts +3 -1
- package/dist/client/components/Transcript.d.ts +2 -0
- package/dist/client/components/TranscriptHeader.d.ts +2 -0
- package/dist/client/components/TranscriptHeadingRow.d.ts +16 -0
- package/dist/client/components/TranscriptThinkingView.d.ts +7 -0
- package/dist/client/components/TranscriptToolRun.d.ts +9 -0
- package/dist/client/components/transcriptRenderModel.d.ts +25 -9
- package/dist/client/format.d.ts +45 -8
- package/dist/client/markdownExport.d.ts +3 -0
- package/dist/client/toolInvocations.d.ts +7 -0
- package/dist/client/types.d.ts +17 -100
- package/dist/client.js +85 -55998
- package/dist/dashboardLoader.d.ts +1 -0
- package/dist/handler.js +1288 -40
- package/dist/index.d.ts +8 -0
- package/dist/index.js +1807 -0
- package/dist/mock-conversations.d.ts +3 -0
- package/dist/mock-release-conversation.d.ts +3 -0
- package/dist/nitro.d.ts +7 -1
- package/dist/tailwind.css +1 -1
- package/dist/url.d.ts +13 -0
- package/package.json +9 -5
- package/src/app.ts +42 -18
- package/src/assets.ts +2 -0
- package/src/auth.ts +2 -35
- package/src/client/App.tsx +33 -74
- package/src/client/code.tsx +1 -1
- package/src/client/components/Button.tsx +75 -0
- package/src/client/components/ConversationRowStats.tsx +3 -2
- package/src/client/components/FilterTabs.tsx +10 -11
- package/src/client/components/LoadingView.tsx +7 -2
- package/src/client/components/Metric.tsx +159 -0
- package/src/client/components/StatusBadge.tsx +10 -1
- package/src/client/components/TelemetryMetrics.tsx +124 -0
- package/src/client/components/ToolFrame.tsx +57 -14
- package/src/client/components/Transcript.tsx +6 -2
- package/src/client/components/TranscriptHeader.tsx +18 -19
- package/src/client/components/TranscriptHeadingRow.tsx +64 -0
- package/src/client/components/TranscriptThinkingView.tsx +157 -0
- package/src/client/components/TranscriptToolRun.tsx +66 -0
- package/src/client/components/TranscriptToolView.tsx +16 -8
- package/src/client/components/TranscriptTurn.tsx +368 -132
- package/src/client/components/TurnDurationChart.tsx +236 -78
- package/src/client/components/transcriptRenderModel.ts +60 -20
- package/src/client/format.ts +329 -87
- package/src/client/markdownExport.ts +360 -0
- package/src/client/pages/CommandCenter.tsx +1 -1
- package/src/client/pages/ConversationPage.tsx +142 -45
- package/src/client/pages/ConversationsPage.tsx +1 -1
- package/src/client/toolInvocations.ts +16 -0
- package/src/client/types.ts +34 -90
- package/src/config.ts +4 -0
- package/src/dashboardLoader.ts +12 -0
- package/src/index.ts +78 -0
- package/src/mock-conversations.ts +726 -0
- package/src/mock-release-conversation.ts +605 -0
- package/src/nitro.ts +7 -1
- package/src/tailwind.css +11 -0
- package/src/url.ts +68 -0
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
DashboardConversationReport,
|
|
3
|
+
DashboardTranscriptMessage,
|
|
4
|
+
} from "@sentry/junior/reporting";
|
|
5
|
+
|
|
6
|
+
const LONG_CONVERSATION_ID = "slack:CQA456:1770021600.000600";
|
|
7
|
+
|
|
8
|
+
function iso(nowMs: number, offsetMs = 0): string {
|
|
9
|
+
return new Date(nowMs + offsetMs).toISOString();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function sentryConversationUrl(conversationId: string): string {
|
|
13
|
+
return `https://sentry.example.com/organizations/acme/explore/conversations/${encodeURIComponent(conversationId)}/`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function sentryTraceUrl(traceId: string): string {
|
|
17
|
+
return `https://sentry.example.com/performance/trace/${traceId}/`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function mockSystemPrompt(): string {
|
|
21
|
+
return [
|
|
22
|
+
"You are Junior, a Slack-native helper for engineering workflows.",
|
|
23
|
+
"",
|
|
24
|
+
"# Operating Contract",
|
|
25
|
+
"",
|
|
26
|
+
"- Lead with the answer, then support it with evidence.",
|
|
27
|
+
"- Use available tools for repository, build, and deployment checks.",
|
|
28
|
+
"- Keep progress updates short when the work takes multiple steps.",
|
|
29
|
+
"- Do not expose secrets, private channel contents, or raw provider credentials.",
|
|
30
|
+
"- Verify package changes with install, check, typecheck, and build where possible.",
|
|
31
|
+
"- When a check fails, preserve the exact failure in the final summary.",
|
|
32
|
+
"",
|
|
33
|
+
"# Repository Defaults",
|
|
34
|
+
"",
|
|
35
|
+
"- Default application repo: acme/junior-demo.",
|
|
36
|
+
"- Default package namespace: @acme/junior.",
|
|
37
|
+
"- Open pull requests as drafts unless the requester asks for ready review.",
|
|
38
|
+
"",
|
|
39
|
+
"# Slack Output",
|
|
40
|
+
"",
|
|
41
|
+
"- Be concise.",
|
|
42
|
+
"- Prefer bullets for multi-step engineering results.",
|
|
43
|
+
"- Include the PR link when one was created.",
|
|
44
|
+
].join("\n");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function bashOutput(
|
|
48
|
+
command: string,
|
|
49
|
+
stdout: string,
|
|
50
|
+
options?: {
|
|
51
|
+
durationMs?: number;
|
|
52
|
+
exitCode?: number;
|
|
53
|
+
stderr?: string;
|
|
54
|
+
timedOut?: boolean;
|
|
55
|
+
},
|
|
56
|
+
): string {
|
|
57
|
+
const exitCode = options?.exitCode ?? 0;
|
|
58
|
+
return JSON.stringify({
|
|
59
|
+
ok: exitCode === 0 && !options?.timedOut,
|
|
60
|
+
command,
|
|
61
|
+
cwd: "/vercel/sandbox",
|
|
62
|
+
exit_code: exitCode,
|
|
63
|
+
signal: null,
|
|
64
|
+
timed_out: options?.timedOut ?? false,
|
|
65
|
+
stdout,
|
|
66
|
+
stderr: options?.stderr ?? "",
|
|
67
|
+
stdout_truncated: false,
|
|
68
|
+
stderr_truncated: false,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function toolCall(
|
|
73
|
+
startedAtMs: number,
|
|
74
|
+
offsetMs: number,
|
|
75
|
+
id: string,
|
|
76
|
+
name: string,
|
|
77
|
+
input: unknown,
|
|
78
|
+
): DashboardTranscriptMessage {
|
|
79
|
+
return {
|
|
80
|
+
role: "assistant",
|
|
81
|
+
timestamp: startedAtMs + offsetMs,
|
|
82
|
+
parts: [
|
|
83
|
+
{
|
|
84
|
+
id,
|
|
85
|
+
input,
|
|
86
|
+
name,
|
|
87
|
+
type: "tool_call",
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function toolResult(
|
|
94
|
+
startedAtMs: number,
|
|
95
|
+
offsetMs: number,
|
|
96
|
+
id: string,
|
|
97
|
+
name: string,
|
|
98
|
+
output: unknown,
|
|
99
|
+
): DashboardTranscriptMessage {
|
|
100
|
+
return {
|
|
101
|
+
role: "toolResult",
|
|
102
|
+
timestamp: startedAtMs + offsetMs,
|
|
103
|
+
parts: [
|
|
104
|
+
{
|
|
105
|
+
id,
|
|
106
|
+
name,
|
|
107
|
+
output,
|
|
108
|
+
type: "tool_result",
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function bashPair(
|
|
115
|
+
startedAtMs: number,
|
|
116
|
+
index: number,
|
|
117
|
+
offsetMs: number,
|
|
118
|
+
command: string,
|
|
119
|
+
stdout: string,
|
|
120
|
+
options?: {
|
|
121
|
+
durationMs?: number;
|
|
122
|
+
exitCode?: number;
|
|
123
|
+
stderr?: string;
|
|
124
|
+
timedOut?: boolean;
|
|
125
|
+
},
|
|
126
|
+
): DashboardTranscriptMessage[] {
|
|
127
|
+
const id = `toolu_mock_release_bash_${index}`;
|
|
128
|
+
const durationMs = options?.durationMs ?? 900 + ((index * 977) % 4200);
|
|
129
|
+
return [
|
|
130
|
+
toolCall(startedAtMs, offsetMs, id, "bash", {
|
|
131
|
+
command,
|
|
132
|
+
...(options?.timedOut ? { timeoutMs: 120_000 } : {}),
|
|
133
|
+
}),
|
|
134
|
+
toolResult(
|
|
135
|
+
startedAtMs,
|
|
136
|
+
offsetMs + durationMs,
|
|
137
|
+
id,
|
|
138
|
+
"bash",
|
|
139
|
+
bashOutput(command, stdout, options),
|
|
140
|
+
),
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function progressPair(
|
|
145
|
+
startedAtMs: number,
|
|
146
|
+
index: number,
|
|
147
|
+
offsetMs: number,
|
|
148
|
+
message: string,
|
|
149
|
+
): DashboardTranscriptMessage[] {
|
|
150
|
+
const id = `toolu_mock_release_progress_${index}`;
|
|
151
|
+
return [
|
|
152
|
+
toolCall(startedAtMs, offsetMs, id, "reportProgress", { message }),
|
|
153
|
+
toolResult(startedAtMs, offsetMs + 500, id, "reportProgress", "ok"),
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function releaseTranscriptTurnOne(
|
|
158
|
+
startedAtMs: number,
|
|
159
|
+
): DashboardTranscriptMessage[] {
|
|
160
|
+
return [
|
|
161
|
+
{
|
|
162
|
+
role: "system",
|
|
163
|
+
timestamp: startedAtMs,
|
|
164
|
+
parts: [{ type: "text", text: mockSystemPrompt() }],
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
role: "user",
|
|
168
|
+
timestamp: startedAtMs + 1_000,
|
|
169
|
+
parts: [
|
|
170
|
+
{
|
|
171
|
+
type: "text",
|
|
172
|
+
text: "minor bump junior",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
type: "text",
|
|
176
|
+
text: "please publish the package bump and open a self-update PR in the app repo once it is ready.",
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
role: "assistant",
|
|
182
|
+
timestamp: startedAtMs + 6_000,
|
|
183
|
+
parts: [
|
|
184
|
+
{
|
|
185
|
+
type: "thinking",
|
|
186
|
+
output:
|
|
187
|
+
"This is release work, so I need to inspect package versions first, publish, then update the app configuration repo.",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: "toolu_mock_release_skill_0",
|
|
191
|
+
input: { skill: "release-package" },
|
|
192
|
+
name: "loadSkill",
|
|
193
|
+
type: "tool_call",
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
toolResult(
|
|
198
|
+
startedAtMs,
|
|
199
|
+
7_100,
|
|
200
|
+
"toolu_mock_release_skill_0",
|
|
201
|
+
"loadSkill",
|
|
202
|
+
[
|
|
203
|
+
"Loaded skill: release-package",
|
|
204
|
+
"",
|
|
205
|
+
"Verify workspace package versions, run release checks, and publish with the package manager.",
|
|
206
|
+
].join("\n"),
|
|
207
|
+
),
|
|
208
|
+
...progressPair(startedAtMs, 0, 9_000, "Checking package versions"),
|
|
209
|
+
...bashPair(
|
|
210
|
+
startedAtMs,
|
|
211
|
+
0,
|
|
212
|
+
12_000,
|
|
213
|
+
"pnpm view @acme/junior version",
|
|
214
|
+
"0.62.0\n",
|
|
215
|
+
),
|
|
216
|
+
...bashPair(
|
|
217
|
+
startedAtMs,
|
|
218
|
+
1,
|
|
219
|
+
16_000,
|
|
220
|
+
"pnpm version --recursive minor --no-git-tag-version",
|
|
221
|
+
[
|
|
222
|
+
"@acme/junior 0.63.0",
|
|
223
|
+
"@acme/junior-dashboard 0.63.0",
|
|
224
|
+
"@acme/junior-github 0.63.0",
|
|
225
|
+
"@acme/junior-scheduler 0.63.0",
|
|
226
|
+
].join("\n") + "\n",
|
|
227
|
+
{ durationMs: 2_700 },
|
|
228
|
+
),
|
|
229
|
+
...progressPair(startedAtMs, 1, 22_000, "Running release checks"),
|
|
230
|
+
...bashPair(
|
|
231
|
+
startedAtMs,
|
|
232
|
+
2,
|
|
233
|
+
26_000,
|
|
234
|
+
"pnpm release:check",
|
|
235
|
+
[
|
|
236
|
+
"Checking release package manifests...",
|
|
237
|
+
"✓ package versions aligned",
|
|
238
|
+
"✓ changelog entries present",
|
|
239
|
+
"✓ dist tags ready",
|
|
240
|
+
].join("\n") + "\n",
|
|
241
|
+
{ durationMs: 4_800 },
|
|
242
|
+
),
|
|
243
|
+
...bashPair(
|
|
244
|
+
startedAtMs,
|
|
245
|
+
3,
|
|
246
|
+
34_000,
|
|
247
|
+
"pnpm publish -r --tag latest",
|
|
248
|
+
[
|
|
249
|
+
"Publishing packages:",
|
|
250
|
+
"+ @acme/junior@0.63.0",
|
|
251
|
+
"+ @acme/junior-dashboard@0.63.0",
|
|
252
|
+
"+ @acme/junior-github@0.63.0",
|
|
253
|
+
"+ @acme/junior-scheduler@0.63.0",
|
|
254
|
+
"Done in 12.4s.",
|
|
255
|
+
].join("\n") + "\n",
|
|
256
|
+
{ durationMs: 10_900 },
|
|
257
|
+
),
|
|
258
|
+
{
|
|
259
|
+
role: "assistant",
|
|
260
|
+
timestamp: startedAtMs + 46_000,
|
|
261
|
+
parts: [
|
|
262
|
+
{
|
|
263
|
+
type: "text",
|
|
264
|
+
text: "published `@acme/junior@0.63.0`. moving on to the self-update PR.",
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function releaseTranscriptTurnTwo(
|
|
272
|
+
startedAtMs: number,
|
|
273
|
+
): DashboardTranscriptMessage[] {
|
|
274
|
+
return [
|
|
275
|
+
{
|
|
276
|
+
role: "user",
|
|
277
|
+
timestamp: startedAtMs + 1_000,
|
|
278
|
+
parts: [
|
|
279
|
+
{
|
|
280
|
+
type: "text",
|
|
281
|
+
text: "continue with the self-update app PR for the 0.63.0 package set.",
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
role: "assistant",
|
|
287
|
+
timestamp: startedAtMs + 7_000,
|
|
288
|
+
parts: [
|
|
289
|
+
{
|
|
290
|
+
type: "thinking",
|
|
291
|
+
output:
|
|
292
|
+
"I need to clone the app repo, update every @acme/junior package to the same version, run checks, and create a draft PR.",
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
id: "toolu_mock_release_skill_1",
|
|
296
|
+
input: { skill: "self-update" },
|
|
297
|
+
name: "loadSkill",
|
|
298
|
+
type: "tool_call",
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
toolResult(
|
|
303
|
+
startedAtMs,
|
|
304
|
+
8_000,
|
|
305
|
+
"toolu_mock_release_skill_1",
|
|
306
|
+
"loadSkill",
|
|
307
|
+
[
|
|
308
|
+
"Loaded skill: self-update",
|
|
309
|
+
"",
|
|
310
|
+
"Use repository-native commands.",
|
|
311
|
+
"Check package manifests and lockfiles before editing.",
|
|
312
|
+
"Prefer a draft PR when publishing dependency bumps.",
|
|
313
|
+
].join("\n"),
|
|
314
|
+
),
|
|
315
|
+
...progressPair(startedAtMs, 2, 12_000, "Cloning app repo"),
|
|
316
|
+
...bashPair(
|
|
317
|
+
startedAtMs,
|
|
318
|
+
4,
|
|
319
|
+
15_000,
|
|
320
|
+
"git clone https://github.com/acme/junior-demo.git /tmp/junior-demo",
|
|
321
|
+
"Cloning into '/tmp/junior-demo'...\n",
|
|
322
|
+
{ durationMs: 3_300 },
|
|
323
|
+
),
|
|
324
|
+
...bashPair(
|
|
325
|
+
startedAtMs,
|
|
326
|
+
5,
|
|
327
|
+
19_000,
|
|
328
|
+
"cd /tmp/junior-demo && jq '.dependencies | keys[]' package.json",
|
|
329
|
+
[
|
|
330
|
+
'"@acme/junior"',
|
|
331
|
+
'"@acme/junior-agent-browser"',
|
|
332
|
+
'"@acme/junior-dashboard"',
|
|
333
|
+
'"@acme/junior-github"',
|
|
334
|
+
'"@acme/junior-scheduler"',
|
|
335
|
+
].join("\n") + "\n",
|
|
336
|
+
),
|
|
337
|
+
...bashPair(
|
|
338
|
+
startedAtMs,
|
|
339
|
+
6,
|
|
340
|
+
24_000,
|
|
341
|
+
"cd /tmp/junior-demo && git checkout -b build/update-junior-0.63.0",
|
|
342
|
+
"Switched to a new branch 'build/update-junior-0.63.0'\n",
|
|
343
|
+
),
|
|
344
|
+
...progressPair(startedAtMs, 3, 28_000, "Updating dependencies"),
|
|
345
|
+
...bashPair(
|
|
346
|
+
startedAtMs,
|
|
347
|
+
7,
|
|
348
|
+
32_000,
|
|
349
|
+
"cd /tmp/junior-demo && pnpm add -E @acme/junior@0.63.0 @acme/junior-dashboard@0.63.0 @acme/junior-github@0.63.0 @acme/junior-scheduler@0.63.0",
|
|
350
|
+
[
|
|
351
|
+
"Progress: resolved 1, reused 0, downloaded 0, added 0",
|
|
352
|
+
"Progress: resolved 63, reused 0, downloaded 48, added 0",
|
|
353
|
+
"Progress: resolved 184, reused 0, downloaded 139, added 0",
|
|
354
|
+
"Progress: resolved 312, reused 0, downloaded 240, added 0",
|
|
355
|
+
"Progress: resolved 628, reused 0, downloaded 552, added 0",
|
|
356
|
+
"Packages: +673",
|
|
357
|
+
"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
|
|
358
|
+
"dependencies:",
|
|
359
|
+
"+ @acme/junior 0.63.0",
|
|
360
|
+
"+ @acme/junior-dashboard 0.63.0",
|
|
361
|
+
"+ @acme/junior-github 0.63.0",
|
|
362
|
+
"+ @acme/junior-scheduler 0.63.0",
|
|
363
|
+
"",
|
|
364
|
+
"Ignored build scripts: native-compression@1.0.0, protobufjs@7.5.5.",
|
|
365
|
+
"Done in 16.8s using pnpm v10.34.1",
|
|
366
|
+
].join("\n") + "\n",
|
|
367
|
+
{ durationMs: 16_800, timedOut: false },
|
|
368
|
+
),
|
|
369
|
+
{
|
|
370
|
+
role: "assistant",
|
|
371
|
+
timestamp: startedAtMs + 58_000,
|
|
372
|
+
parts: [
|
|
373
|
+
{
|
|
374
|
+
type: "thinking",
|
|
375
|
+
output:
|
|
376
|
+
"The package install completed and updated the lockfile. Next I should run the app-specific registration check before broader checks.",
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
type: "text",
|
|
380
|
+
text: "dependencies updated; checking app package registration now.",
|
|
381
|
+
},
|
|
382
|
+
],
|
|
383
|
+
},
|
|
384
|
+
...bashPair(
|
|
385
|
+
startedAtMs,
|
|
386
|
+
8,
|
|
387
|
+
64_000,
|
|
388
|
+
"cd /tmp/junior-demo && node scripts/check-plugin-packages.mjs",
|
|
389
|
+
"✓ All @acme/junior-* plugin packages are registered in nitro.config.ts\n",
|
|
390
|
+
{ durationMs: 2_300 },
|
|
391
|
+
),
|
|
392
|
+
...bashPair(
|
|
393
|
+
startedAtMs,
|
|
394
|
+
9,
|
|
395
|
+
69_000,
|
|
396
|
+
"cd /tmp/junior-demo && git diff --name-only",
|
|
397
|
+
"package.json\npnpm-lock.yaml\n",
|
|
398
|
+
),
|
|
399
|
+
...progressPair(startedAtMs, 4, 73_000, "Running checks"),
|
|
400
|
+
...bashPair(
|
|
401
|
+
startedAtMs,
|
|
402
|
+
10,
|
|
403
|
+
78_000,
|
|
404
|
+
"cd /tmp/junior-demo && pnpm check",
|
|
405
|
+
[
|
|
406
|
+
"> @acme/junior-demo@0.1.0 check /tmp/junior-demo",
|
|
407
|
+
"> node scripts/check-plugin-packages.mjs && junior check",
|
|
408
|
+
"",
|
|
409
|
+
"✓ app files",
|
|
410
|
+
"✓ plugin github-repo",
|
|
411
|
+
"✓ packaged plugin agent-browser (@acme/junior-agent-browser)",
|
|
412
|
+
"✓ packaged plugin github (@acme/junior-github)",
|
|
413
|
+
"✓ packaged plugin scheduler (@acme/junior-scheduler)",
|
|
414
|
+
"✓ Validation passed (7 plugin manifests, 14 skill directories checked).",
|
|
415
|
+
].join("\n") + "\n",
|
|
416
|
+
{ durationMs: 9_400 },
|
|
417
|
+
),
|
|
418
|
+
...bashPair(
|
|
419
|
+
startedAtMs,
|
|
420
|
+
11,
|
|
421
|
+
93_000,
|
|
422
|
+
"cd /tmp/junior-demo && pnpm typecheck",
|
|
423
|
+
"> @acme/junior-demo@0.1.0 typecheck /tmp/junior-demo\n> tsc --noEmit\n\n",
|
|
424
|
+
{ durationMs: 4_100 },
|
|
425
|
+
),
|
|
426
|
+
{
|
|
427
|
+
role: "assistant",
|
|
428
|
+
timestamp: startedAtMs + 104_000,
|
|
429
|
+
parts: [
|
|
430
|
+
{
|
|
431
|
+
type: "thinking",
|
|
432
|
+
output:
|
|
433
|
+
"Check and typecheck are green. Build may need runtime secrets; if so, capture the exact failure and continue with a draft PR.",
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
type: "text",
|
|
437
|
+
text: "check + typecheck are green. trying build next.",
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
},
|
|
441
|
+
...bashPair(
|
|
442
|
+
startedAtMs,
|
|
443
|
+
12,
|
|
444
|
+
110_000,
|
|
445
|
+
"cd /tmp/junior-demo && pnpm build",
|
|
446
|
+
[
|
|
447
|
+
"> @acme/junior-demo@0.1.0 build /tmp/junior-demo",
|
|
448
|
+
"> nitro build",
|
|
449
|
+
"",
|
|
450
|
+
"Loaded plugins (2): github-repo, devinfra-docs",
|
|
451
|
+
"Resolving sandbox snapshot profile...",
|
|
452
|
+
"junior command failed: CACHE_URL is required for durable thread state",
|
|
453
|
+
].join("\n") + "\n",
|
|
454
|
+
{ durationMs: 7_300, exitCode: 1 },
|
|
455
|
+
),
|
|
456
|
+
{
|
|
457
|
+
role: "assistant",
|
|
458
|
+
timestamp: startedAtMs + 126_000,
|
|
459
|
+
parts: [
|
|
460
|
+
{
|
|
461
|
+
type: "thinking",
|
|
462
|
+
output:
|
|
463
|
+
"The build failure is an environment constraint, not a dependency failure. I should include it in the PR body and commit only the package files.",
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
type: "text",
|
|
467
|
+
text: "build hit the expected sandbox env wall: `CACHE_URL is required for durable thread state`. committing the package update with that called out.",
|
|
468
|
+
},
|
|
469
|
+
],
|
|
470
|
+
},
|
|
471
|
+
...bashPair(
|
|
472
|
+
startedAtMs,
|
|
473
|
+
13,
|
|
474
|
+
132_000,
|
|
475
|
+
'cd /tmp/junior-demo && git add package.json pnpm-lock.yaml && git commit -m "build(deps): update junior packages to 0.63.0"',
|
|
476
|
+
"[build/update-junior-0.63.0 885bf7f] build(deps): update junior packages to 0.63.0\n 2 files changed, 68 insertions(+), 67 deletions(-)\n",
|
|
477
|
+
{ durationMs: 2_600 },
|
|
478
|
+
),
|
|
479
|
+
...bashPair(
|
|
480
|
+
startedAtMs,
|
|
481
|
+
14,
|
|
482
|
+
142_000,
|
|
483
|
+
"cd /tmp/junior-demo && git push -u origin build/update-junior-0.63.0",
|
|
484
|
+
[
|
|
485
|
+
"remote:",
|
|
486
|
+
"remote: Create a pull request for 'build/update-junior-0.63.0' on GitHub by visiting:",
|
|
487
|
+
"remote: https://github.com/acme/junior-demo/pull/new/build/update-junior-0.63.0",
|
|
488
|
+
"To https://github.com/acme/junior-demo.git",
|
|
489
|
+
" * [new branch] build/update-junior-0.63.0 -> build/update-junior-0.63.0",
|
|
490
|
+
"branch 'build/update-junior-0.63.0' set up to track 'origin/build/update-junior-0.63.0'.",
|
|
491
|
+
].join("\n") + "\n",
|
|
492
|
+
{ durationMs: 4_400 },
|
|
493
|
+
),
|
|
494
|
+
...bashPair(
|
|
495
|
+
startedAtMs,
|
|
496
|
+
15,
|
|
497
|
+
154_000,
|
|
498
|
+
'cd /tmp/junior-demo && gh pr create --draft --title "build(deps): update junior packages to 0.63.0"',
|
|
499
|
+
"https://github.com/acme/junior-demo/pull/42\n",
|
|
500
|
+
{ durationMs: 3_100 },
|
|
501
|
+
),
|
|
502
|
+
...progressPair(startedAtMs, 5, 160_000, "Draft PR created"),
|
|
503
|
+
{
|
|
504
|
+
role: "assistant",
|
|
505
|
+
timestamp: startedAtMs + 166_000,
|
|
506
|
+
parts: [
|
|
507
|
+
{
|
|
508
|
+
type: "text",
|
|
509
|
+
text: [
|
|
510
|
+
"done. full recap:",
|
|
511
|
+
"",
|
|
512
|
+
"- **released** `@acme/junior@0.63.0`",
|
|
513
|
+
"- **self-update PR** opened at https://github.com/acme/junior-demo/pull/42",
|
|
514
|
+
"- `pnpm check` and `pnpm typecheck` passed",
|
|
515
|
+
"- `pnpm build` failed on missing `CACHE_URL`, which is a sandbox environment constraint",
|
|
516
|
+
].join("\n"),
|
|
517
|
+
},
|
|
518
|
+
],
|
|
519
|
+
},
|
|
520
|
+
];
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/** Build a long sanitized release/update transcript for dashboard visual QA. */
|
|
524
|
+
export function longReleaseConversation(
|
|
525
|
+
nowMs: number,
|
|
526
|
+
): DashboardConversationReport {
|
|
527
|
+
const traceId = "7a4f12c9e3d84901b6c7d8e9f0123456";
|
|
528
|
+
const firstStartedAt = iso(nowMs, -92 * 60_000);
|
|
529
|
+
const secondStartedAt = iso(nowMs, -90 * 60_000);
|
|
530
|
+
const firstTranscript = releaseTranscriptTurnOne(Date.parse(firstStartedAt));
|
|
531
|
+
const secondTranscript = releaseTranscriptTurnTwo(
|
|
532
|
+
Date.parse(secondStartedAt),
|
|
533
|
+
);
|
|
534
|
+
|
|
535
|
+
return {
|
|
536
|
+
conversationId: LONG_CONVERSATION_ID,
|
|
537
|
+
generatedAt: iso(nowMs),
|
|
538
|
+
turns: [
|
|
539
|
+
{
|
|
540
|
+
conversationId: LONG_CONVERSATION_ID,
|
|
541
|
+
conversationTitle: "Package release and self-update",
|
|
542
|
+
id: "mock-release-turn-1",
|
|
543
|
+
status: "completed",
|
|
544
|
+
startedAt: firstStartedAt,
|
|
545
|
+
lastProgressAt: iso(nowMs, -91 * 60_000),
|
|
546
|
+
lastSeenAt: iso(nowMs, -91 * 60_000),
|
|
547
|
+
completedAt: iso(nowMs, -91 * 60_000),
|
|
548
|
+
cumulativeDurationMs: 48_449,
|
|
549
|
+
cumulativeUsage: {
|
|
550
|
+
cachedInputTokens: 167_859,
|
|
551
|
+
cacheCreationTokens: 19_277,
|
|
552
|
+
inputTokens: 13,
|
|
553
|
+
outputTokens: 1037,
|
|
554
|
+
},
|
|
555
|
+
surface: "slack",
|
|
556
|
+
title: "Turn mock-release-turn-1",
|
|
557
|
+
requesterIdentity: {
|
|
558
|
+
fullName: "Jordan Blake",
|
|
559
|
+
slackUserId: "UQA777",
|
|
560
|
+
slackUserName: "jordan",
|
|
561
|
+
},
|
|
562
|
+
channel: "CQA456",
|
|
563
|
+
channelName: "proj-release",
|
|
564
|
+
sentryConversationUrl: sentryConversationUrl(LONG_CONVERSATION_ID),
|
|
565
|
+
sentryTraceUrl: sentryTraceUrl(traceId),
|
|
566
|
+
traceId,
|
|
567
|
+
transcriptAvailable: true,
|
|
568
|
+
transcriptMessageCount: firstTranscript.length,
|
|
569
|
+
transcript: firstTranscript,
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
conversationId: LONG_CONVERSATION_ID,
|
|
573
|
+
conversationTitle: "Package release and self-update",
|
|
574
|
+
id: "mock-release-turn-2",
|
|
575
|
+
status: "completed",
|
|
576
|
+
startedAt: secondStartedAt,
|
|
577
|
+
lastProgressAt: iso(nowMs, -81 * 60_000),
|
|
578
|
+
lastSeenAt: iso(nowMs, -81 * 60_000),
|
|
579
|
+
completedAt: iso(nowMs, -81 * 60_000),
|
|
580
|
+
cumulativeDurationMs: 552_761,
|
|
581
|
+
cumulativeUsage: {
|
|
582
|
+
cachedInputTokens: 1_266_200,
|
|
583
|
+
cacheCreationTokens: 21_129,
|
|
584
|
+
inputTokens: 43,
|
|
585
|
+
outputTokens: 5765,
|
|
586
|
+
},
|
|
587
|
+
surface: "slack",
|
|
588
|
+
title: "Turn mock-release-turn-2",
|
|
589
|
+
requesterIdentity: {
|
|
590
|
+
fullName: "Jordan Blake",
|
|
591
|
+
slackUserId: "UQA777",
|
|
592
|
+
slackUserName: "jordan",
|
|
593
|
+
},
|
|
594
|
+
channel: "CQA456",
|
|
595
|
+
channelName: "proj-release",
|
|
596
|
+
sentryConversationUrl: sentryConversationUrl(LONG_CONVERSATION_ID),
|
|
597
|
+
sentryTraceUrl: sentryTraceUrl(traceId),
|
|
598
|
+
traceId,
|
|
599
|
+
transcriptAvailable: true,
|
|
600
|
+
transcriptMessageCount: secondTranscript.length,
|
|
601
|
+
transcript: secondTranscript,
|
|
602
|
+
},
|
|
603
|
+
],
|
|
604
|
+
};
|
|
605
|
+
}
|
package/src/nitro.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface JuniorDashboardNitroOptions {
|
|
|
11
11
|
allowedEmails?: string[];
|
|
12
12
|
trustedOrigins?: string[];
|
|
13
13
|
sessionMaxAgeSeconds?: number;
|
|
14
|
+
mockConversations?: boolean;
|
|
14
15
|
disabled?: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -104,7 +105,12 @@ function dashboardPageRoutes(
|
|
|
104
105
|
};
|
|
105
106
|
}
|
|
106
107
|
|
|
107
|
-
/**
|
|
108
|
+
/**
|
|
109
|
+
* Mount the authenticated Junior dashboard into a Nitro deployment.
|
|
110
|
+
*
|
|
111
|
+
* @deprecated Register `juniorDashboardPlugin()` in `createApp({ plugins })`;
|
|
112
|
+
* this helper remains for existing Nitro apps.
|
|
113
|
+
*/
|
|
108
114
|
export function juniorDashboardNitro(options: JuniorDashboardNitroOptions): {
|
|
109
115
|
nitro: { setup(nitro: unknown): void };
|
|
110
116
|
} {
|
package/src/tailwind.css
CHANGED
|
@@ -9,5 +9,16 @@
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
@source "./app.ts";
|
|
12
|
+
@source "./dashboardLoader.ts";
|
|
12
13
|
@source "./client.tsx";
|
|
13
14
|
@source "./client/**/*.{ts,tsx}";
|
|
15
|
+
|
|
16
|
+
@keyframes junior-rainbow-flow {
|
|
17
|
+
0% {
|
|
18
|
+
background-position: 0% 50%;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
100% {
|
|
22
|
+
background-position: 200% 50%;
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/url.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export interface DashboardBaseURLConfig {
|
|
2
|
+
baseURL?: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface DashboardConversationURLConfig extends DashboardBaseURLConfig {
|
|
6
|
+
basePath?: string;
|
|
7
|
+
conversationId: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function withHttps(host: string): string {
|
|
11
|
+
return /^https?:\/\//.test(host) ? host : `https://${host}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function stripTrailingSlashes(value: string): string {
|
|
15
|
+
let end = value.length;
|
|
16
|
+
while (end > 1 && value.charCodeAt(end - 1) === 47) {
|
|
17
|
+
end -= 1;
|
|
18
|
+
}
|
|
19
|
+
return end === value.length ? value : value.slice(0, end);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Normalize dashboard route prefixes so plugin and Nitro paths agree. */
|
|
23
|
+
export function normalizeDashboardPath(
|
|
24
|
+
path: string | undefined,
|
|
25
|
+
fallback: string,
|
|
26
|
+
): string {
|
|
27
|
+
const value = path?.trim() || fallback;
|
|
28
|
+
const withSlash = value.startsWith("/") ? value : `/${value}`;
|
|
29
|
+
return stripTrailingSlashes(withSlash);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Resolve the dashboard origin used for browser auth and external links. */
|
|
33
|
+
export function resolveDashboardBaseURL(
|
|
34
|
+
config: DashboardBaseURLConfig = {},
|
|
35
|
+
): string {
|
|
36
|
+
const explicit =
|
|
37
|
+
config.baseURL ??
|
|
38
|
+
process.env.BETTER_AUTH_URL ??
|
|
39
|
+
process.env.JUNIOR_BASE_URL;
|
|
40
|
+
if (explicit?.trim()) {
|
|
41
|
+
return stripTrailingSlashes(withHttps(explicit.trim()));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const vercelProd = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
|
|
45
|
+
if (vercelProd) {
|
|
46
|
+
return stripTrailingSlashes(withHttps(vercelProd));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const vercelUrl = process.env.VERCEL_URL?.trim();
|
|
50
|
+
if (vercelUrl) {
|
|
51
|
+
return stripTrailingSlashes(withHttps(vercelUrl));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return "http://localhost:3000";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Build the dashboard conversation URL shown from outside the browser app. */
|
|
58
|
+
export function buildDashboardConversationURL(
|
|
59
|
+
config: DashboardConversationURLConfig,
|
|
60
|
+
): string {
|
|
61
|
+
const baseURL = resolveDashboardBaseURL({ baseURL: config.baseURL });
|
|
62
|
+
const basePath = normalizeDashboardPath(config.basePath, "/");
|
|
63
|
+
const path =
|
|
64
|
+
basePath === "/"
|
|
65
|
+
? `/conversations/${encodeURIComponent(config.conversationId)}`
|
|
66
|
+
: `${basePath}/conversations/${encodeURIComponent(config.conversationId)}`;
|
|
67
|
+
return `${baseURL}${path}`;
|
|
68
|
+
}
|