@sentry/junior-github 0.82.0 → 0.84.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/index.js +139 -151
- package/dist/tools/footer.d.ts +13 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -73,12 +73,81 @@ function permissionCapabilities(permissions) {
|
|
|
73
73
|
// src/tools/create-issue.ts
|
|
74
74
|
import {
|
|
75
75
|
EgressAuthRequired,
|
|
76
|
-
PluginToolInputError
|
|
76
|
+
PluginToolInputError as PluginToolInputError2
|
|
77
77
|
} from "@sentry/junior-plugin-api";
|
|
78
78
|
import { Type } from "@sinclair/typebox";
|
|
79
79
|
import { Value } from "@sinclair/typebox/value";
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
|
|
81
|
+
// src/tools/footer.ts
|
|
82
|
+
import { PluginToolInputError } from "@sentry/junior-plugin-api";
|
|
83
|
+
var GITHUB_SESSION_FOOTER_START = "<!-- junior-session-footer:start -->";
|
|
84
|
+
var GITHUB_SESSION_FOOTER_END = "<!-- junior-session-footer:end -->";
|
|
85
|
+
function nonEmptyString(value, name) {
|
|
86
|
+
if (!value?.trim()) {
|
|
87
|
+
throw new PluginToolInputError(`${name} is required`);
|
|
88
|
+
}
|
|
89
|
+
return value.trim();
|
|
90
|
+
}
|
|
91
|
+
function escapeRegExp(value) {
|
|
92
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
93
|
+
}
|
|
94
|
+
function sentryConversationUrl(conversationId) {
|
|
95
|
+
const dsn = process.env.SENTRY_DSN?.trim();
|
|
96
|
+
const orgSlug = process.env.SENTRY_ORG_SLUG?.trim();
|
|
97
|
+
if (!dsn || !orgSlug) {
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
let parsed;
|
|
101
|
+
try {
|
|
102
|
+
parsed = new URL(dsn);
|
|
103
|
+
} catch {
|
|
104
|
+
return void 0;
|
|
105
|
+
}
|
|
106
|
+
const projectId = parsed.pathname.split("/").filter(Boolean).at(-1);
|
|
107
|
+
if (!parsed.hostname || !projectId) {
|
|
108
|
+
return void 0;
|
|
109
|
+
}
|
|
110
|
+
const encodedConversationId = encodeURIComponent(conversationId);
|
|
111
|
+
const params = new URLSearchParams({ project: projectId });
|
|
112
|
+
const path = `explore/conversations/${encodedConversationId}/?${params.toString()}`;
|
|
113
|
+
if (parsed.hostname === "sentry.io" || parsed.hostname.endsWith(".sentry.io")) {
|
|
114
|
+
return `https://${orgSlug}.sentry.io/${path}`;
|
|
115
|
+
}
|
|
116
|
+
const port = parsed.port ? `:${parsed.port}` : "";
|
|
117
|
+
return `${parsed.protocol}//${parsed.hostname}${port}/organizations/${orgSlug}/${path}`;
|
|
118
|
+
}
|
|
119
|
+
function githubConversationFooter(conversationId) {
|
|
120
|
+
const id = nonEmptyString(conversationId, "conversationId");
|
|
121
|
+
const sessionUrl = sentryConversationUrl(id);
|
|
122
|
+
if (!sessionUrl) {
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
return `${GITHUB_SESSION_FOOTER_START}
|
|
126
|
+
|
|
127
|
+
--
|
|
128
|
+
|
|
129
|
+
[View Junior Session in Sentry](${sessionUrl})
|
|
130
|
+
|
|
131
|
+
${GITHUB_SESSION_FOOTER_END}`;
|
|
132
|
+
}
|
|
133
|
+
function appendGitHubFooter(body, conversationId) {
|
|
134
|
+
const footer = githubConversationFooter(conversationId);
|
|
135
|
+
const normalizedBody = body.trimEnd();
|
|
136
|
+
const existingFooter = new RegExp(
|
|
137
|
+
`${escapeRegExp(GITHUB_SESSION_FOOTER_START)}[\\s\\S]*?${escapeRegExp(GITHUB_SESSION_FOOTER_END)}`
|
|
138
|
+
);
|
|
139
|
+
if (existingFooter.test(normalizedBody)) {
|
|
140
|
+
return footer ? normalizedBody.replace(existingFooter, footer) : normalizedBody.replace(existingFooter, "").trimEnd();
|
|
141
|
+
}
|
|
142
|
+
if (!footer) {
|
|
143
|
+
return normalizedBody;
|
|
144
|
+
}
|
|
145
|
+
return normalizedBody ? `${normalizedBody}
|
|
146
|
+
|
|
147
|
+
${footer}` : footer;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/tools/create-issue.ts
|
|
82
151
|
var GITHUB_ISSUE_CREATE_IDEMPOTENCY_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
83
152
|
var GITHUB_ISSUE_CREATE_LOCK_TTL_MS = 6e4;
|
|
84
153
|
var GitHubIssueCreateRejectedError = class extends Error {
|
|
@@ -132,86 +201,28 @@ function parseCreateIssueInput(input) {
|
|
|
132
201
|
try {
|
|
133
202
|
return Value.Parse(createIssueInputSchema, input);
|
|
134
203
|
} catch (error) {
|
|
135
|
-
throw new
|
|
204
|
+
throw new PluginToolInputError2("Invalid GitHub createIssue input.", {
|
|
136
205
|
cause: error
|
|
137
206
|
});
|
|
138
207
|
}
|
|
139
208
|
}
|
|
140
|
-
function
|
|
209
|
+
function nonEmptyString2(value, name) {
|
|
141
210
|
if (!value?.trim()) {
|
|
142
|
-
throw new
|
|
211
|
+
throw new PluginToolInputError2(`${name} is required`);
|
|
143
212
|
}
|
|
144
213
|
return value.trim();
|
|
145
214
|
}
|
|
146
215
|
function parseRepo(value) {
|
|
147
|
-
const repo =
|
|
216
|
+
const repo = nonEmptyString2(value, "repo");
|
|
148
217
|
const parts = repo.split("/");
|
|
149
218
|
if (parts.length !== 2 || !parts[0]?.trim() || !parts[1]?.trim()) {
|
|
150
|
-
throw new
|
|
219
|
+
throw new PluginToolInputError2('repo must use "owner/name" format');
|
|
151
220
|
}
|
|
152
221
|
return {
|
|
153
222
|
owner: parts[0].trim(),
|
|
154
223
|
name: parts[1].trim()
|
|
155
224
|
};
|
|
156
225
|
}
|
|
157
|
-
function escapeRegExp(value) {
|
|
158
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
159
|
-
}
|
|
160
|
-
function sentryConversationUrl(conversationId) {
|
|
161
|
-
const dsn = process.env.SENTRY_DSN?.trim();
|
|
162
|
-
const orgSlug = process.env.SENTRY_ORG_SLUG?.trim();
|
|
163
|
-
if (!dsn || !orgSlug) {
|
|
164
|
-
return void 0;
|
|
165
|
-
}
|
|
166
|
-
let parsed;
|
|
167
|
-
try {
|
|
168
|
-
parsed = new URL(dsn);
|
|
169
|
-
} catch {
|
|
170
|
-
return void 0;
|
|
171
|
-
}
|
|
172
|
-
const projectId = parsed.pathname.split("/").filter(Boolean).at(-1);
|
|
173
|
-
if (!parsed.hostname || !projectId) {
|
|
174
|
-
return void 0;
|
|
175
|
-
}
|
|
176
|
-
const encodedConversationId = encodeURIComponent(conversationId);
|
|
177
|
-
const params = new URLSearchParams({ project: projectId });
|
|
178
|
-
const path = `explore/conversations/${encodedConversationId}/?${params.toString()}`;
|
|
179
|
-
if (parsed.hostname === "sentry.io" || parsed.hostname.endsWith(".sentry.io")) {
|
|
180
|
-
return `https://${orgSlug}.sentry.io/${path}`;
|
|
181
|
-
}
|
|
182
|
-
const port = parsed.port ? `:${parsed.port}` : "";
|
|
183
|
-
return `${parsed.protocol}//${parsed.hostname}${port}/organizations/${orgSlug}/${path}`;
|
|
184
|
-
}
|
|
185
|
-
function githubIssueConversationFooter(conversationId) {
|
|
186
|
-
const id = nonEmptyString(conversationId, "conversationId");
|
|
187
|
-
const sessionUrl = sentryConversationUrl(id);
|
|
188
|
-
if (!sessionUrl) {
|
|
189
|
-
return void 0;
|
|
190
|
-
}
|
|
191
|
-
return `${GITHUB_ISSUE_FOOTER_START}
|
|
192
|
-
|
|
193
|
-
[View Session in Sentry](${sessionUrl})
|
|
194
|
-
|
|
195
|
-
${GITHUB_ISSUE_FOOTER_END}`;
|
|
196
|
-
}
|
|
197
|
-
function appendGitHubIssueFooter(body, conversationId) {
|
|
198
|
-
const footer = githubIssueConversationFooter(conversationId);
|
|
199
|
-
const normalizedBody = body.trimEnd();
|
|
200
|
-
const existingFooter = new RegExp(
|
|
201
|
-
`${escapeRegExp(GITHUB_ISSUE_FOOTER_START)}[\\s\\S]*?${escapeRegExp(
|
|
202
|
-
GITHUB_ISSUE_FOOTER_END
|
|
203
|
-
)}`
|
|
204
|
-
);
|
|
205
|
-
if (existingFooter.test(normalizedBody)) {
|
|
206
|
-
return footer ? normalizedBody.replace(existingFooter, footer) : normalizedBody.replace(existingFooter, "").trimEnd();
|
|
207
|
-
}
|
|
208
|
-
if (!footer) {
|
|
209
|
-
return normalizedBody;
|
|
210
|
-
}
|
|
211
|
-
return normalizedBody ? `${normalizedBody}
|
|
212
|
-
|
|
213
|
-
${footer}` : footer;
|
|
214
|
-
}
|
|
215
226
|
async function readJsonResponse(response) {
|
|
216
227
|
const text = await response.text();
|
|
217
228
|
if (!text) {
|
|
@@ -259,11 +270,11 @@ function isDefinitiveGitHubIssueCreateRejection(error) {
|
|
|
259
270
|
function createGitHubIssueRequest(conversationId, input) {
|
|
260
271
|
const repo = parseRepo(input.repo);
|
|
261
272
|
const labels = input.labels?.map(
|
|
262
|
-
(label) =>
|
|
273
|
+
(label) => nonEmptyString2(label, "labels entry")
|
|
263
274
|
);
|
|
264
275
|
const payload = {
|
|
265
|
-
title:
|
|
266
|
-
body:
|
|
276
|
+
title: nonEmptyString2(input.title, "title"),
|
|
277
|
+
body: appendGitHubFooter(input.body ?? "", conversationId),
|
|
267
278
|
...labels?.length ? { labels } : {}
|
|
268
279
|
};
|
|
269
280
|
return new Request(
|
|
@@ -314,11 +325,11 @@ function createGitHubIssueTool(ctx) {
|
|
|
314
325
|
inputSchema: createIssueInputSchema,
|
|
315
326
|
async execute(input, options) {
|
|
316
327
|
const parsedInput = parseCreateIssueInput(input);
|
|
317
|
-
const conversationId =
|
|
328
|
+
const conversationId = nonEmptyString2(
|
|
318
329
|
ctx.conversationId,
|
|
319
330
|
"conversationId"
|
|
320
331
|
);
|
|
321
|
-
const toolCallId =
|
|
332
|
+
const toolCallId = nonEmptyString2(options?.toolCallId, "toolCallId");
|
|
322
333
|
const key = `createIssue:${conversationId}:${toolCallId}`;
|
|
323
334
|
return await ctx.state.withLock(
|
|
324
335
|
`${key}:lock`,
|
|
@@ -377,12 +388,10 @@ function createGitHubIssueTool(ctx) {
|
|
|
377
388
|
// src/tools/create-pull-request.ts
|
|
378
389
|
import {
|
|
379
390
|
EgressAuthRequired as EgressAuthRequired2,
|
|
380
|
-
PluginToolInputError as
|
|
391
|
+
PluginToolInputError as PluginToolInputError3
|
|
381
392
|
} from "@sentry/junior-plugin-api";
|
|
382
393
|
import { Type as Type2 } from "@sinclair/typebox";
|
|
383
394
|
import { Value as Value2 } from "@sinclair/typebox/value";
|
|
384
|
-
var GITHUB_PULL_REQUEST_FOOTER_START = "<!-- junior-session-footer:start -->";
|
|
385
|
-
var GITHUB_PULL_REQUEST_FOOTER_END = "<!-- junior-session-footer:end -->";
|
|
386
395
|
var GITHUB_PULL_REQUEST_CREATE_IDEMPOTENCY_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
387
396
|
var GITHUB_PULL_REQUEST_CREATE_LOCK_TTL_MS = 6e4;
|
|
388
397
|
var GitHubPullRequestCreateRejectedError = class extends Error {
|
|
@@ -424,6 +433,7 @@ var createPullRequestStateSchema = Type2.Union([
|
|
|
424
433
|
Type2.Object(
|
|
425
434
|
{
|
|
426
435
|
createdAtMs: Type2.Number(),
|
|
436
|
+
input: Type2.Optional(createPullRequestInputSchema),
|
|
427
437
|
number: Type2.Number(),
|
|
428
438
|
status: Type2.Literal("completed"),
|
|
429
439
|
url: Type2.String()
|
|
@@ -433,6 +443,7 @@ var createPullRequestStateSchema = Type2.Union([
|
|
|
433
443
|
Type2.Object(
|
|
434
444
|
{
|
|
435
445
|
createdAtMs: Type2.Number(),
|
|
446
|
+
input: Type2.Optional(createPullRequestInputSchema),
|
|
436
447
|
status: Type2.Literal("pending")
|
|
437
448
|
},
|
|
438
449
|
{ additionalProperties: false }
|
|
@@ -442,86 +453,28 @@ function parseCreatePullRequestInput(input) {
|
|
|
442
453
|
try {
|
|
443
454
|
return Value2.Parse(createPullRequestInputSchema, input);
|
|
444
455
|
} catch (error) {
|
|
445
|
-
throw new
|
|
456
|
+
throw new PluginToolInputError3("Invalid GitHub createPullRequest input.", {
|
|
446
457
|
cause: error
|
|
447
458
|
});
|
|
448
459
|
}
|
|
449
460
|
}
|
|
450
|
-
function
|
|
461
|
+
function nonEmptyString3(value, name) {
|
|
451
462
|
if (!value?.trim()) {
|
|
452
|
-
throw new
|
|
463
|
+
throw new PluginToolInputError3(`${name} is required`);
|
|
453
464
|
}
|
|
454
465
|
return value.trim();
|
|
455
466
|
}
|
|
456
467
|
function parseRepo2(value) {
|
|
457
|
-
const repo =
|
|
468
|
+
const repo = nonEmptyString3(value, "repo");
|
|
458
469
|
const parts = repo.split("/");
|
|
459
470
|
if (parts.length !== 2 || !parts[0]?.trim() || !parts[1]?.trim()) {
|
|
460
|
-
throw new
|
|
471
|
+
throw new PluginToolInputError3('repo must use "owner/name" format');
|
|
461
472
|
}
|
|
462
473
|
return {
|
|
463
474
|
owner: parts[0].trim(),
|
|
464
475
|
name: parts[1].trim()
|
|
465
476
|
};
|
|
466
477
|
}
|
|
467
|
-
function escapeRegExp2(value) {
|
|
468
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
469
|
-
}
|
|
470
|
-
function sentryConversationUrl2(conversationId) {
|
|
471
|
-
const dsn = process.env.SENTRY_DSN?.trim();
|
|
472
|
-
const orgSlug = process.env.SENTRY_ORG_SLUG?.trim();
|
|
473
|
-
if (!dsn || !orgSlug) {
|
|
474
|
-
return void 0;
|
|
475
|
-
}
|
|
476
|
-
let parsed;
|
|
477
|
-
try {
|
|
478
|
-
parsed = new URL(dsn);
|
|
479
|
-
} catch {
|
|
480
|
-
return void 0;
|
|
481
|
-
}
|
|
482
|
-
const projectId = parsed.pathname.split("/").filter(Boolean).at(-1);
|
|
483
|
-
if (!parsed.hostname || !projectId) {
|
|
484
|
-
return void 0;
|
|
485
|
-
}
|
|
486
|
-
const encodedConversationId = encodeURIComponent(conversationId);
|
|
487
|
-
const params = new URLSearchParams({ project: projectId });
|
|
488
|
-
const path = `explore/conversations/${encodedConversationId}/?${params.toString()}`;
|
|
489
|
-
if (parsed.hostname === "sentry.io" || parsed.hostname.endsWith(".sentry.io")) {
|
|
490
|
-
return `https://${orgSlug}.sentry.io/${path}`;
|
|
491
|
-
}
|
|
492
|
-
const port = parsed.port ? `:${parsed.port}` : "";
|
|
493
|
-
return `${parsed.protocol}//${parsed.hostname}${port}/organizations/${orgSlug}/${path}`;
|
|
494
|
-
}
|
|
495
|
-
function githubPullRequestConversationFooter(conversationId) {
|
|
496
|
-
const id = nonEmptyString2(conversationId, "conversationId");
|
|
497
|
-
const sessionUrl = sentryConversationUrl2(id);
|
|
498
|
-
if (!sessionUrl) {
|
|
499
|
-
return void 0;
|
|
500
|
-
}
|
|
501
|
-
return `${GITHUB_PULL_REQUEST_FOOTER_START}
|
|
502
|
-
|
|
503
|
-
[View Session in Sentry](${sessionUrl})
|
|
504
|
-
|
|
505
|
-
${GITHUB_PULL_REQUEST_FOOTER_END}`;
|
|
506
|
-
}
|
|
507
|
-
function appendGitHubPullRequestFooter(body, conversationId) {
|
|
508
|
-
const footer = githubPullRequestConversationFooter(conversationId);
|
|
509
|
-
const normalizedBody = body.trimEnd();
|
|
510
|
-
const existingFooter = new RegExp(
|
|
511
|
-
`${escapeRegExp2(GITHUB_PULL_REQUEST_FOOTER_START)}[\\s\\S]*?${escapeRegExp2(
|
|
512
|
-
GITHUB_PULL_REQUEST_FOOTER_END
|
|
513
|
-
)}`
|
|
514
|
-
);
|
|
515
|
-
if (existingFooter.test(normalizedBody)) {
|
|
516
|
-
return footer ? normalizedBody.replace(existingFooter, footer) : normalizedBody.replace(existingFooter, "").trimEnd();
|
|
517
|
-
}
|
|
518
|
-
if (!footer) {
|
|
519
|
-
return normalizedBody;
|
|
520
|
-
}
|
|
521
|
-
return normalizedBody ? `${normalizedBody}
|
|
522
|
-
|
|
523
|
-
${footer}` : footer;
|
|
524
|
-
}
|
|
525
478
|
async function readJsonResponse2(response) {
|
|
526
479
|
const text = await response.text();
|
|
527
480
|
if (!text) {
|
|
@@ -569,10 +522,10 @@ function isDefinitiveGitHubPullRequestCreateRejection(error) {
|
|
|
569
522
|
function createGitHubPullRequestRequest(conversationId, input) {
|
|
570
523
|
const repo = parseRepo2(input.repo);
|
|
571
524
|
const payload = {
|
|
572
|
-
title:
|
|
573
|
-
head:
|
|
574
|
-
base:
|
|
575
|
-
body:
|
|
525
|
+
title: nonEmptyString3(input.title, "title"),
|
|
526
|
+
head: nonEmptyString3(input.head, "head"),
|
|
527
|
+
base: nonEmptyString3(input.base, "base"),
|
|
528
|
+
body: appendGitHubFooter(input.body ?? "", conversationId),
|
|
576
529
|
...input.draft !== void 0 ? { draft: input.draft } : {}
|
|
577
530
|
};
|
|
578
531
|
return new Request(
|
|
@@ -621,17 +574,51 @@ async function createGitHubPullRequest(ctx, request) {
|
|
|
621
574
|
url: pullRequest.html_url
|
|
622
575
|
};
|
|
623
576
|
}
|
|
577
|
+
function gitHubPullRequestSubscribable(input, result) {
|
|
578
|
+
const repo = parseRepo2(input.repo);
|
|
579
|
+
const repoRef = `${repo.owner}/${repo.name}`;
|
|
580
|
+
const supportedEvents = [
|
|
581
|
+
"checks.failed",
|
|
582
|
+
"checks.recovered",
|
|
583
|
+
"review.approved",
|
|
584
|
+
"review.changes_requested",
|
|
585
|
+
"state.merged",
|
|
586
|
+
"state.closed_unmerged"
|
|
587
|
+
];
|
|
588
|
+
return {
|
|
589
|
+
label: `GitHub PR ${repoRef}#${result.number}`,
|
|
590
|
+
provider: "github",
|
|
591
|
+
resourceRef: `github:pull_request:${repoRef}#${result.number}`,
|
|
592
|
+
suggestedEvents: [
|
|
593
|
+
"checks.failed",
|
|
594
|
+
"review.changes_requested",
|
|
595
|
+
"state.merged",
|
|
596
|
+
"state.closed_unmerged"
|
|
597
|
+
],
|
|
598
|
+
supportedEvents,
|
|
599
|
+
type: "pull_request"
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function gitHubPullRequestToolResult(input, result) {
|
|
603
|
+
if (!process.env.GITHUB_WEBHOOK_SECRET?.trim()) {
|
|
604
|
+
return result;
|
|
605
|
+
}
|
|
606
|
+
return {
|
|
607
|
+
...result,
|
|
608
|
+
subscribable: gitHubPullRequestSubscribable(input, result)
|
|
609
|
+
};
|
|
610
|
+
}
|
|
624
611
|
function createGitHubPullRequestTool(ctx) {
|
|
625
612
|
return {
|
|
626
613
|
description: "Create a GitHub pull request with a runtime-owned Junior conversation footer. Use this instead of shelling out to gh pr create when creating pull requests.",
|
|
627
614
|
inputSchema: createPullRequestInputSchema,
|
|
628
615
|
async execute(input, options) {
|
|
629
616
|
const parsedInput = parseCreatePullRequestInput(input);
|
|
630
|
-
const conversationId =
|
|
617
|
+
const conversationId = nonEmptyString3(
|
|
631
618
|
ctx.conversationId,
|
|
632
619
|
"conversationId"
|
|
633
620
|
);
|
|
634
|
-
const toolCallId =
|
|
621
|
+
const toolCallId = nonEmptyString3(options?.toolCallId, "toolCallId");
|
|
635
622
|
const key = `createPullRequest:${conversationId}:${toolCallId}`;
|
|
636
623
|
return await ctx.state.withLock(
|
|
637
624
|
`${key}:lock`,
|
|
@@ -639,10 +626,10 @@ function createGitHubPullRequestTool(ctx) {
|
|
|
639
626
|
async () => {
|
|
640
627
|
const state = createPullRequestState(await ctx.state.get(key));
|
|
641
628
|
if (state?.status === "completed") {
|
|
642
|
-
return {
|
|
629
|
+
return gitHubPullRequestToolResult(state.input ?? parsedInput, {
|
|
643
630
|
number: state.number,
|
|
644
631
|
url: state.url
|
|
645
|
-
};
|
|
632
|
+
});
|
|
646
633
|
}
|
|
647
634
|
if (state?.status === "pending") {
|
|
648
635
|
throw new Error(
|
|
@@ -655,7 +642,8 @@ function createGitHubPullRequestTool(ctx) {
|
|
|
655
642
|
);
|
|
656
643
|
const pendingState = {
|
|
657
644
|
status: "pending",
|
|
658
|
-
createdAtMs: Date.now()
|
|
645
|
+
createdAtMs: Date.now(),
|
|
646
|
+
input: parsedInput
|
|
659
647
|
};
|
|
660
648
|
await ctx.state.set(
|
|
661
649
|
key,
|
|
@@ -677,7 +665,7 @@ function createGitHubPullRequestTool(ctx) {
|
|
|
677
665
|
{ cause: error }
|
|
678
666
|
);
|
|
679
667
|
}
|
|
680
|
-
return result;
|
|
668
|
+
return gitHubPullRequestToolResult(parsedInput, result);
|
|
681
669
|
} catch (error) {
|
|
682
670
|
if (isEgressAuthRequired2(error) || isDefinitiveGitHubPullRequestCreateRejection(error)) {
|
|
683
671
|
await ctx.state.delete(key);
|
|
@@ -1359,7 +1347,7 @@ function parseGitHubGraphqlOperation(bodyText) {
|
|
|
1359
1347
|
if (operationName) {
|
|
1360
1348
|
const namedOperation = normalized.match(
|
|
1361
1349
|
new RegExp(
|
|
1362
|
-
`\\b(query|mutation|subscription)\\s+${
|
|
1350
|
+
`\\b(query|mutation|subscription)\\s+${escapeRegExp2(operationName)}\\b`
|
|
1363
1351
|
)
|
|
1364
1352
|
)?.[1];
|
|
1365
1353
|
return namedOperation ? graphqlOperationAccess(namedOperation) : void 0;
|
|
@@ -1400,7 +1388,7 @@ function parseGitHubGraphqlRequest(bodyText) {
|
|
|
1400
1388
|
...operationName ? { operationName } : {}
|
|
1401
1389
|
};
|
|
1402
1390
|
}
|
|
1403
|
-
function
|
|
1391
|
+
function escapeRegExp2(value) {
|
|
1404
1392
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1405
1393
|
}
|
|
1406
1394
|
function graphqlOperationAccess(operation) {
|
|
@@ -1529,7 +1517,7 @@ function isGitHubIssueCreateGraphqlMutation(method, upstreamUrl, bodyText) {
|
|
|
1529
1517
|
return /\bmutation\b/.test(parsed.normalized);
|
|
1530
1518
|
}
|
|
1531
1519
|
return new RegExp(
|
|
1532
|
-
`\\bmutation\\s+${
|
|
1520
|
+
`\\bmutation\\s+${escapeRegExp2(parsed.operationName)}\\b`
|
|
1533
1521
|
).test(parsed.normalized);
|
|
1534
1522
|
}
|
|
1535
1523
|
function isGitHubPullCreateGraphqlMutation(method, upstreamUrl, bodyText) {
|
|
@@ -1547,7 +1535,7 @@ function isGitHubPullCreateGraphqlMutation(method, upstreamUrl, bodyText) {
|
|
|
1547
1535
|
return /\bmutation\b/.test(parsed.normalized);
|
|
1548
1536
|
}
|
|
1549
1537
|
return new RegExp(
|
|
1550
|
-
`\\bmutation\\s+${
|
|
1538
|
+
`\\bmutation\\s+${escapeRegExp2(parsed.operationName)}\\b`
|
|
1551
1539
|
).test(parsed.normalized);
|
|
1552
1540
|
}
|
|
1553
1541
|
function assertGitHubWriteAllowed(input) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const GITHUB_SESSION_FOOTER_START = "<!-- junior-session-footer:start -->";
|
|
2
|
+
export declare const GITHUB_SESSION_FOOTER_END = "<!-- junior-session-footer:end -->";
|
|
3
|
+
export declare function sentryConversationUrl(conversationId: string): string | undefined;
|
|
4
|
+
/**
|
|
5
|
+
* Build the Junior session footer block for GitHub issues and pull requests.
|
|
6
|
+
* Returns undefined when Sentry DSN/org are not configured.
|
|
7
|
+
*/
|
|
8
|
+
export declare function githubConversationFooter(conversationId: string): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Append (or replace an existing) Junior session footer to a GitHub body string.
|
|
11
|
+
* When Sentry is not configured, returns the body unchanged (existing footer stripped).
|
|
12
|
+
*/
|
|
13
|
+
export declare function appendGitHubFooter(body: string, conversationId: string): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.84.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
],
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@sinclair/typebox": "^0.34.49",
|
|
27
|
-
"@sentry/junior-plugin-api": "0.
|
|
27
|
+
"@sentry/junior-plugin-api": "0.84.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^25.9.1",
|