paperclip-github-plugin 0.8.9 → 0.8.10
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 +29 -1
- package/dist/manifest.js +63 -1
- package/dist/ui/index.js +2 -2
- package/dist/ui/index.js.map +1 -1
- package/dist/worker.js +341 -2
- package/package.json +2 -2
package/dist/worker.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/worker.ts
|
|
2
|
+
import { Buffer } from "node:buffer";
|
|
2
3
|
import { realpathSync } from "node:fs";
|
|
3
4
|
import { readFile } from "node:fs/promises";
|
|
4
5
|
import { homedir } from "node:os";
|
|
@@ -532,6 +533,58 @@ var GITHUB_AGENT_TOOLS = [
|
|
|
532
533
|
}
|
|
533
534
|
}
|
|
534
535
|
},
|
|
536
|
+
{
|
|
537
|
+
name: "upload_pull_request_asset",
|
|
538
|
+
displayName: "Upload Pull Request Asset",
|
|
539
|
+
description: "Upload a PR-visible asset such as an image, PDF, log, archive, or report to a non-merge artifact branch and return durable markdown that can be embedded in the PR body.",
|
|
540
|
+
parametersSchema: {
|
|
541
|
+
type: "object",
|
|
542
|
+
additionalProperties: false,
|
|
543
|
+
required: ["fileName"],
|
|
544
|
+
allOf: [pullRequestTargetSchema],
|
|
545
|
+
anyOf: [
|
|
546
|
+
{ required: ["contentBase64"] },
|
|
547
|
+
{ required: ["dataUrl"] }
|
|
548
|
+
],
|
|
549
|
+
properties: {
|
|
550
|
+
repository: repositoryProperty,
|
|
551
|
+
pullRequestNumber: pullRequestNumberProperty,
|
|
552
|
+
paperclipIssueId: paperclipIssueIdProperty,
|
|
553
|
+
fileName: {
|
|
554
|
+
type: "string",
|
|
555
|
+
description: "Asset filename. The plugin sanitizes it and preserves a safe extension."
|
|
556
|
+
},
|
|
557
|
+
label: {
|
|
558
|
+
type: "string",
|
|
559
|
+
description: "Human-readable link text for the returned Markdown. Defaults to the sanitized filename."
|
|
560
|
+
},
|
|
561
|
+
alt: {
|
|
562
|
+
type: "string",
|
|
563
|
+
description: "Backward-compatible alias for label, useful as image alt text."
|
|
564
|
+
},
|
|
565
|
+
caption: {
|
|
566
|
+
type: "string",
|
|
567
|
+
description: "Optional human-facing caption returned with the uploaded asset metadata."
|
|
568
|
+
},
|
|
569
|
+
contentBase64: {
|
|
570
|
+
type: "string",
|
|
571
|
+
description: "Base64-encoded asset bytes. Assets are limited to 10 MiB."
|
|
572
|
+
},
|
|
573
|
+
dataUrl: {
|
|
574
|
+
type: "string",
|
|
575
|
+
description: "Alternative base64 data URL input such as data:application/pdf;base64,... or data:image/png;base64,... ."
|
|
576
|
+
},
|
|
577
|
+
mimeType: {
|
|
578
|
+
type: "string",
|
|
579
|
+
description: "Optional MIME type such as application/pdf or image/png. If omitted, the plugin infers common types from fileName and otherwise uses application/octet-stream."
|
|
580
|
+
},
|
|
581
|
+
artifactBranch: {
|
|
582
|
+
type: "string",
|
|
583
|
+
description: "Optional artifact branch name. Defaults to paperclip-artifacts-pr-<pullRequestNumber>."
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
},
|
|
535
588
|
{
|
|
536
589
|
name: "link_github_item",
|
|
537
590
|
displayName: "Link GitHub Item",
|
|
@@ -637,6 +690,9 @@ var COMPANY_METRIC_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_ID}/a
|
|
|
637
690
|
var ISSUE_LINK_API_ROUTE_KEY = "link-github-item";
|
|
638
691
|
var ISSUE_LINK_API_ROUTE_PATH = "/issue-link";
|
|
639
692
|
var ISSUE_LINK_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_ID}/api${ISSUE_LINK_API_ROUTE_PATH}`;
|
|
693
|
+
var PULL_REQUEST_ASSET_API_ROUTE_KEY = "upload-pull-request-asset";
|
|
694
|
+
var PULL_REQUEST_ASSET_API_ROUTE_PATH = "/pull-request-assets";
|
|
695
|
+
var PULL_REQUEST_ASSET_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_ID}/api${PULL_REQUEST_ASSET_API_ROUTE_PATH}`;
|
|
640
696
|
|
|
641
697
|
// src/paperclip-health.ts
|
|
642
698
|
function normalizeOptionalString(value) {
|
|
@@ -739,6 +795,40 @@ var AI_AUTHORED_MARKDOWN_FOOTER_PATTERN = /\n\n---\n###### ✨ This (?:comment|i
|
|
|
739
795
|
var HIDDEN_GITHUB_IMPORT_MARKER_PREFIX = "<!-- paperclip-github-plugin-imported-from: ";
|
|
740
796
|
var HIDDEN_GITHUB_IMPORT_MARKER_SUFFIX = " -->";
|
|
741
797
|
var EMPTY_GITHUB_ISSUE_DESCRIPTION_PLACEHOLDER = "_No description provided on GitHub._";
|
|
798
|
+
var MAX_PULL_REQUEST_ASSET_BYTES = 10 * 1024 * 1024;
|
|
799
|
+
var DEFAULT_PULL_REQUEST_ASSET_MIME_TYPE = "application/octet-stream";
|
|
800
|
+
var PULL_REQUEST_ASSET_MIME_TYPE_BY_EXTENSION = {
|
|
801
|
+
png: "image/png",
|
|
802
|
+
jpg: "image/jpeg",
|
|
803
|
+
jpeg: "image/jpeg",
|
|
804
|
+
webp: "image/webp",
|
|
805
|
+
gif: "image/gif",
|
|
806
|
+
pdf: "application/pdf",
|
|
807
|
+
txt: "text/plain",
|
|
808
|
+
md: "text/markdown",
|
|
809
|
+
markdown: "text/markdown",
|
|
810
|
+
json: "application/json",
|
|
811
|
+
csv: "text/csv",
|
|
812
|
+
xml: "application/xml",
|
|
813
|
+
zip: "application/zip",
|
|
814
|
+
gz: "application/gzip",
|
|
815
|
+
tgz: "application/gzip"
|
|
816
|
+
};
|
|
817
|
+
var PULL_REQUEST_ASSET_EXTENSION_BY_MIME_TYPE = {
|
|
818
|
+
"image/png": "png",
|
|
819
|
+
"image/jpeg": "jpg",
|
|
820
|
+
"image/webp": "webp",
|
|
821
|
+
"image/gif": "gif",
|
|
822
|
+
"application/pdf": "pdf",
|
|
823
|
+
"text/plain": "txt",
|
|
824
|
+
"text/markdown": "md",
|
|
825
|
+
"application/json": "json",
|
|
826
|
+
"text/csv": "csv",
|
|
827
|
+
"application/xml": "xml",
|
|
828
|
+
"application/zip": "zip",
|
|
829
|
+
"application/gzip": "gz",
|
|
830
|
+
"application/octet-stream": "bin"
|
|
831
|
+
};
|
|
742
832
|
var pluginRuntimeContext = null;
|
|
743
833
|
function normalizeCompanyId(value) {
|
|
744
834
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
@@ -10003,6 +10093,187 @@ function buildToolSuccessResult(content, data) {
|
|
|
10003
10093
|
data
|
|
10004
10094
|
};
|
|
10005
10095
|
}
|
|
10096
|
+
function normalizePullRequestAssetMimeType(value) {
|
|
10097
|
+
if (typeof value !== "string") {
|
|
10098
|
+
return void 0;
|
|
10099
|
+
}
|
|
10100
|
+
const normalized = value.trim().toLowerCase();
|
|
10101
|
+
if (!/^[a-z0-9][a-z0-9!#$&^_.+-]*\/[a-z0-9][a-z0-9!#$&^_.+-]*(?:;\s*[a-z0-9!#$&^_.+-]+=[a-z0-9!#$&^_.+-]+)*$/.test(normalized)) {
|
|
10102
|
+
return void 0;
|
|
10103
|
+
}
|
|
10104
|
+
return normalized.split(";", 1)[0];
|
|
10105
|
+
}
|
|
10106
|
+
function getPullRequestAssetMimeTypeFromFileName(fileName) {
|
|
10107
|
+
const extensionMatch = fileName.trim().toLowerCase().match(/\.([a-z0-9]+)$/);
|
|
10108
|
+
if (!extensionMatch) {
|
|
10109
|
+
return void 0;
|
|
10110
|
+
}
|
|
10111
|
+
return PULL_REQUEST_ASSET_MIME_TYPE_BY_EXTENSION[extensionMatch[1]];
|
|
10112
|
+
}
|
|
10113
|
+
function getPullRequestAssetExtension(fileName) {
|
|
10114
|
+
const extensionMatch = fileName.trim().toLowerCase().match(/\.([a-z0-9]+)$/);
|
|
10115
|
+
return extensionMatch?.[1];
|
|
10116
|
+
}
|
|
10117
|
+
function sanitizePullRequestAssetFileName(fileNameInput, mimeType) {
|
|
10118
|
+
const inferredExtension = PULL_REQUEST_ASSET_EXTENSION_BY_MIME_TYPE[mimeType] ?? "bin";
|
|
10119
|
+
const fallbackBaseName = `asset.${inferredExtension}`;
|
|
10120
|
+
const rawFileName = normalizeOptionalString2(fileNameInput) ?? fallbackBaseName;
|
|
10121
|
+
const lastSegment = rawFileName.split(/[\\/]+/).filter(Boolean).at(-1) ?? fallbackBaseName;
|
|
10122
|
+
const extension = getPullRequestAssetExtension(lastSegment) ?? inferredExtension;
|
|
10123
|
+
const withoutExtension = lastSegment.replace(/\.[A-Za-z0-9]+$/, "");
|
|
10124
|
+
const sanitizedBaseName = withoutExtension.normalize("NFKD").replace(/[^A-Za-z0-9._-]+/g, "-").replace(/[-_.]+$/g, "").replace(/^[-_.]+/g, "").slice(0, 80) || "asset";
|
|
10125
|
+
return `${sanitizedBaseName}.${extension}`;
|
|
10126
|
+
}
|
|
10127
|
+
function sanitizePullRequestAssetLabel(value, fallbackFileName) {
|
|
10128
|
+
const normalized = normalizeOptionalString2(value);
|
|
10129
|
+
return (normalized ?? fallbackFileName.replace(/[-_.]+/g, " ")).slice(0, 200);
|
|
10130
|
+
}
|
|
10131
|
+
function sanitizePullRequestAssetArtifactBranch(value, pullRequestNumber) {
|
|
10132
|
+
const normalized = normalizeOptionalString2(value);
|
|
10133
|
+
const candidate = normalized ?? `paperclip-artifacts-pr-${pullRequestNumber}`;
|
|
10134
|
+
const sanitized = candidate.replace(/[^A-Za-z0-9._/-]+/g, "-").replace(/\.\.+/g, ".").replace(/\/{2,}/g, "/").replace(/^[-/.]+|[-/.]+$/g, "");
|
|
10135
|
+
if (!sanitized || sanitized.includes("..") || sanitized.startsWith("/") || sanitized.endsWith(".lock")) {
|
|
10136
|
+
throw new Error("artifactBranch must be a safe Git branch name.");
|
|
10137
|
+
}
|
|
10138
|
+
return sanitized;
|
|
10139
|
+
}
|
|
10140
|
+
function decodePullRequestAssetContent(payload) {
|
|
10141
|
+
const dataUrl = normalizeOptionalString2(payload.dataUrl);
|
|
10142
|
+
let contentBase64 = normalizeOptionalString2(payload.contentBase64);
|
|
10143
|
+
let mimeType = normalizePullRequestAssetMimeType(payload.mimeType);
|
|
10144
|
+
if (dataUrl) {
|
|
10145
|
+
const match = dataUrl.match(/^data:([^;,]+(?:;[^,]+)?);base64,(.+)$/is);
|
|
10146
|
+
if (!match) {
|
|
10147
|
+
throw new Error("dataUrl must be a base64 data URL.");
|
|
10148
|
+
}
|
|
10149
|
+
const dataUrlMimeType = normalizePullRequestAssetMimeType(match[1]);
|
|
10150
|
+
if (!dataUrlMimeType) {
|
|
10151
|
+
throw new Error("dataUrl MIME type must be a valid MIME type such as image/png or application/pdf.");
|
|
10152
|
+
}
|
|
10153
|
+
mimeType = dataUrlMimeType;
|
|
10154
|
+
contentBase64 = match[2].replace(/\s+/g, "");
|
|
10155
|
+
}
|
|
10156
|
+
if (!contentBase64) {
|
|
10157
|
+
throw new Error("contentBase64 or dataUrl is required.");
|
|
10158
|
+
}
|
|
10159
|
+
mimeType = mimeType ?? getPullRequestAssetMimeTypeFromFileName(normalizeOptionalString2(payload.fileName) ?? "") ?? DEFAULT_PULL_REQUEST_ASSET_MIME_TYPE;
|
|
10160
|
+
const normalizedBase64 = contentBase64.replace(/\s+/g, "");
|
|
10161
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(normalizedBase64) || normalizedBase64.length % 4 === 1) {
|
|
10162
|
+
throw new Error("Asset content must be valid base64.");
|
|
10163
|
+
}
|
|
10164
|
+
const bytes = Buffer.from(normalizedBase64, "base64");
|
|
10165
|
+
if (bytes.length === 0) {
|
|
10166
|
+
throw new Error("Asset content must not be empty.");
|
|
10167
|
+
}
|
|
10168
|
+
if (bytes.length > MAX_PULL_REQUEST_ASSET_BYTES) {
|
|
10169
|
+
throw new Error(`Asset content exceeds the ${MAX_PULL_REQUEST_ASSET_BYTES} byte limit.`);
|
|
10170
|
+
}
|
|
10171
|
+
return {
|
|
10172
|
+
bytes,
|
|
10173
|
+
mimeType
|
|
10174
|
+
};
|
|
10175
|
+
}
|
|
10176
|
+
function buildPullRequestAssetMarkdown(label, rawUrl, mimeType) {
|
|
10177
|
+
const normalizedLabel = label.replace(/[\]\n\r]/g, " ").trim();
|
|
10178
|
+
if (mimeType.startsWith("image/")) {
|
|
10179
|
+
return ``;
|
|
10180
|
+
}
|
|
10181
|
+
return `[${normalizedLabel}](${rawUrl})`;
|
|
10182
|
+
}
|
|
10183
|
+
async function uploadPullRequestAssetArtifact(params) {
|
|
10184
|
+
const { bytes, mimeType } = decodePullRequestAssetContent(params.payload);
|
|
10185
|
+
const fileName = sanitizePullRequestAssetFileName(params.payload.fileName, mimeType);
|
|
10186
|
+
const label = sanitizePullRequestAssetLabel(params.payload.label ?? params.payload.alt, fileName);
|
|
10187
|
+
const caption = normalizeOptionalString2(params.payload.caption);
|
|
10188
|
+
const artifactBranch = sanitizePullRequestAssetArtifactBranch(params.payload.artifactBranch, params.pullRequestNumber);
|
|
10189
|
+
const pullRequestResponse = await params.octokit.rest.pulls.get({
|
|
10190
|
+
owner: params.repository.owner,
|
|
10191
|
+
repo: params.repository.repo,
|
|
10192
|
+
pull_number: params.pullRequestNumber,
|
|
10193
|
+
headers: {
|
|
10194
|
+
"X-GitHub-Api-Version": GITHUB_API_VERSION
|
|
10195
|
+
}
|
|
10196
|
+
});
|
|
10197
|
+
const headSha = pullRequestResponse.data.head.sha;
|
|
10198
|
+
const shortHeadSha = headSha.slice(0, 12);
|
|
10199
|
+
const contentPath = `assets/pr-${params.pullRequestNumber}/${shortHeadSha}/${fileName}`;
|
|
10200
|
+
let branchSha;
|
|
10201
|
+
try {
|
|
10202
|
+
const branchRefResponse = await params.octokit.rest.git.getRef({
|
|
10203
|
+
owner: params.repository.owner,
|
|
10204
|
+
repo: params.repository.repo,
|
|
10205
|
+
ref: `heads/${artifactBranch}`,
|
|
10206
|
+
headers: {
|
|
10207
|
+
"X-GitHub-Api-Version": GITHUB_API_VERSION
|
|
10208
|
+
}
|
|
10209
|
+
});
|
|
10210
|
+
branchSha = branchRefResponse.data.object.sha;
|
|
10211
|
+
} catch (error) {
|
|
10212
|
+
if (getErrorStatus(error) !== 404) {
|
|
10213
|
+
throw error;
|
|
10214
|
+
}
|
|
10215
|
+
}
|
|
10216
|
+
if (!branchSha) {
|
|
10217
|
+
await params.octokit.rest.git.createRef({
|
|
10218
|
+
owner: params.repository.owner,
|
|
10219
|
+
repo: params.repository.repo,
|
|
10220
|
+
ref: `refs/heads/${artifactBranch}`,
|
|
10221
|
+
sha: pullRequestResponse.data.base.sha,
|
|
10222
|
+
headers: {
|
|
10223
|
+
"X-GitHub-Api-Version": GITHUB_API_VERSION
|
|
10224
|
+
}
|
|
10225
|
+
});
|
|
10226
|
+
}
|
|
10227
|
+
let existingFileSha;
|
|
10228
|
+
try {
|
|
10229
|
+
const existingContentResponse = await params.octokit.rest.repos.getContent({
|
|
10230
|
+
owner: params.repository.owner,
|
|
10231
|
+
repo: params.repository.repo,
|
|
10232
|
+
path: contentPath,
|
|
10233
|
+
ref: artifactBranch,
|
|
10234
|
+
headers: {
|
|
10235
|
+
"X-GitHub-Api-Version": GITHUB_API_VERSION
|
|
10236
|
+
}
|
|
10237
|
+
});
|
|
10238
|
+
if (!Array.isArray(existingContentResponse.data) && existingContentResponse.data.type === "file") {
|
|
10239
|
+
existingFileSha = existingContentResponse.data.sha;
|
|
10240
|
+
}
|
|
10241
|
+
} catch (error) {
|
|
10242
|
+
if (getErrorStatus(error) !== 404) {
|
|
10243
|
+
throw error;
|
|
10244
|
+
}
|
|
10245
|
+
}
|
|
10246
|
+
const updateResponse = await params.octokit.rest.repos.createOrUpdateFileContents({
|
|
10247
|
+
owner: params.repository.owner,
|
|
10248
|
+
repo: params.repository.repo,
|
|
10249
|
+
path: contentPath,
|
|
10250
|
+
message: `Add asset for PR #${params.pullRequestNumber}`,
|
|
10251
|
+
content: bytes.toString("base64"),
|
|
10252
|
+
branch: artifactBranch,
|
|
10253
|
+
...existingFileSha ? { sha: existingFileSha } : {},
|
|
10254
|
+
headers: {
|
|
10255
|
+
"X-GitHub-Api-Version": GITHUB_API_VERSION
|
|
10256
|
+
}
|
|
10257
|
+
});
|
|
10258
|
+
const commitSha = updateResponse.data.commit.sha;
|
|
10259
|
+
const rawUrl = `https://raw.githubusercontent.com/${params.repository.owner}/${params.repository.repo}/${commitSha}/${contentPath}`;
|
|
10260
|
+
const markdown = buildPullRequestAssetMarkdown(label, rawUrl, mimeType);
|
|
10261
|
+
return {
|
|
10262
|
+
repository: params.repository.url,
|
|
10263
|
+
pullRequestNumber: params.pullRequestNumber,
|
|
10264
|
+
artifactBranch,
|
|
10265
|
+
path: contentPath,
|
|
10266
|
+
fileName,
|
|
10267
|
+
mimeType,
|
|
10268
|
+
sizeBytes: bytes.length,
|
|
10269
|
+
commitSha,
|
|
10270
|
+
rawUrl,
|
|
10271
|
+
markdown,
|
|
10272
|
+
label,
|
|
10273
|
+
...mimeType.startsWith("image/") ? { alt: label } : {},
|
|
10274
|
+
...caption ? { caption } : {}
|
|
10275
|
+
};
|
|
10276
|
+
}
|
|
10006
10277
|
function buildToolErrorResult(error) {
|
|
10007
10278
|
const rateLimitPause = getGitHubRateLimitPauseDetails(error);
|
|
10008
10279
|
if (rateLimitPause) {
|
|
@@ -10183,12 +10454,15 @@ async function handleCompanyMetricApiRoute(ctx, input) {
|
|
|
10183
10454
|
}
|
|
10184
10455
|
};
|
|
10185
10456
|
}
|
|
10186
|
-
function
|
|
10457
|
+
function parsePluginApiRouteJsonObjectBody(input, routeLabel) {
|
|
10187
10458
|
if (!input.body || typeof input.body !== "object" || Array.isArray(input.body)) {
|
|
10188
|
-
throw new Error(
|
|
10459
|
+
throw new Error(`${routeLabel} body must be a JSON object.`);
|
|
10189
10460
|
}
|
|
10190
10461
|
return input.body;
|
|
10191
10462
|
}
|
|
10463
|
+
function parseIssueLinkApiRouteBody(input) {
|
|
10464
|
+
return parsePluginApiRouteJsonObjectBody(input, "Issue link route");
|
|
10465
|
+
}
|
|
10192
10466
|
function normalizeIssueLinkApiRouteKind(payload) {
|
|
10193
10467
|
const explicitKind = normalizeIssueGitHubLinkKind(payload.kind);
|
|
10194
10468
|
if (explicitKind) {
|
|
@@ -10203,6 +10477,47 @@ function normalizeIssueLinkApiRouteKind(payload) {
|
|
|
10203
10477
|
}
|
|
10204
10478
|
return null;
|
|
10205
10479
|
}
|
|
10480
|
+
async function handlePullRequestAssetApiRoute(ctx, input) {
|
|
10481
|
+
if (input.actor.actorType !== "agent") {
|
|
10482
|
+
throw new Error("Pull request assets must be uploaded by an authenticated Paperclip agent.");
|
|
10483
|
+
}
|
|
10484
|
+
const payload = parsePluginApiRouteJsonObjectBody(input, "Pull request asset route");
|
|
10485
|
+
const rawPullRequestUrl = normalizeOptionalString2(payload.pullRequestUrl);
|
|
10486
|
+
const pullRequestUrl = normalizeGitHubPullRequestHtmlUrl(rawPullRequestUrl);
|
|
10487
|
+
if (rawPullRequestUrl && !pullRequestUrl) {
|
|
10488
|
+
throw new Error("pullRequestUrl must be a valid GitHub pull request URL.");
|
|
10489
|
+
}
|
|
10490
|
+
const parsedPullRequestUrl = pullRequestUrl ? parseGitHubPullRequestHtmlUrl(pullRequestUrl) : void 0;
|
|
10491
|
+
const repositoryInput = normalizeOptionalString2(payload.repository) ?? parsedPullRequestUrl?.repositoryUrl;
|
|
10492
|
+
if (!repositoryInput) {
|
|
10493
|
+
throw new Error("repository is required unless pullRequestUrl is provided.");
|
|
10494
|
+
}
|
|
10495
|
+
const repository = requireRepositoryReference(repositoryInput);
|
|
10496
|
+
const pullRequestNumber = normalizeToolPositiveInteger(payload.pullRequestNumber) ?? parsedPullRequestUrl?.pullRequestNumber;
|
|
10497
|
+
if (!pullRequestNumber) {
|
|
10498
|
+
throw new Error("pullRequestNumber is required unless pullRequestUrl is provided.");
|
|
10499
|
+
}
|
|
10500
|
+
if (parsedPullRequestUrl && !areRepositoriesEqual(repository, requireRepositoryReference(parsedPullRequestUrl.repositoryUrl))) {
|
|
10501
|
+
throw new Error("repository must match pullRequestUrl.");
|
|
10502
|
+
}
|
|
10503
|
+
const octokit = await createGitHubToolOctokit(ctx, input.companyId, {
|
|
10504
|
+
toolName: PULL_REQUEST_ASSET_API_ROUTE_KEY,
|
|
10505
|
+
repositoryUrl: repository.url
|
|
10506
|
+
});
|
|
10507
|
+
const asset = await uploadPullRequestAssetArtifact({
|
|
10508
|
+
octokit,
|
|
10509
|
+
repository,
|
|
10510
|
+
pullRequestNumber,
|
|
10511
|
+
payload
|
|
10512
|
+
});
|
|
10513
|
+
return {
|
|
10514
|
+
status: 201,
|
|
10515
|
+
body: {
|
|
10516
|
+
status: "uploaded",
|
|
10517
|
+
asset
|
|
10518
|
+
}
|
|
10519
|
+
};
|
|
10520
|
+
}
|
|
10206
10521
|
async function handleIssueLinkApiRoute(ctx, input) {
|
|
10207
10522
|
if (input.actor.actorType !== "agent") {
|
|
10208
10523
|
throw new Error("GitHub issue links must be recorded by an authenticated Paperclip agent.");
|
|
@@ -15085,6 +15400,27 @@ function registerGitHubAgentTools(ctx) {
|
|
|
15085
15400
|
);
|
|
15086
15401
|
})
|
|
15087
15402
|
);
|
|
15403
|
+
ctx.tools.register(
|
|
15404
|
+
"upload_pull_request_asset",
|
|
15405
|
+
getGitHubAgentToolDeclaration("upload_pull_request_asset"),
|
|
15406
|
+
async (params, runCtx) => executeGitHubTool(async () => {
|
|
15407
|
+
const input = getToolInputRecord(params);
|
|
15408
|
+
const target = await resolveGitHubPullRequestToolTarget(ctx, runCtx, input);
|
|
15409
|
+
const octokit = await createAgentToolOctokit(runCtx, "upload_pull_request_asset", target.repository);
|
|
15410
|
+
const asset = await uploadPullRequestAssetArtifact({
|
|
15411
|
+
octokit,
|
|
15412
|
+
repository: target.repository,
|
|
15413
|
+
pullRequestNumber: target.pullRequestNumber,
|
|
15414
|
+
payload: input
|
|
15415
|
+
});
|
|
15416
|
+
return buildToolSuccessResult(
|
|
15417
|
+
`Uploaded asset ${asset.fileName} for pull request #${target.pullRequestNumber}.`,
|
|
15418
|
+
{
|
|
15419
|
+
asset
|
|
15420
|
+
}
|
|
15421
|
+
);
|
|
15422
|
+
})
|
|
15423
|
+
);
|
|
15088
15424
|
ctx.tools.register(
|
|
15089
15425
|
"link_github_item",
|
|
15090
15426
|
getGitHubAgentToolDeclaration("link_github_item"),
|
|
@@ -15559,6 +15895,9 @@ var plugin = definePlugin({
|
|
|
15559
15895
|
if (input.routeKey === ISSUE_LINK_API_ROUTE_KEY) {
|
|
15560
15896
|
return handleIssueLinkApiRoute(pluginRuntimeContext, input);
|
|
15561
15897
|
}
|
|
15898
|
+
if (input.routeKey === PULL_REQUEST_ASSET_API_ROUTE_KEY) {
|
|
15899
|
+
return handlePullRequestAssetApiRoute(pluginRuntimeContext, input);
|
|
15900
|
+
}
|
|
15562
15901
|
return {
|
|
15563
15902
|
status: 404,
|
|
15564
15903
|
body: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paperclip-github-plugin",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.10",
|
|
4
4
|
"description": "Paperclip plugin for synchronizing GitHub issues into Paperclip projects.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@octokit/rest": "^22.0.1",
|
|
44
44
|
"@paperclipai/plugin-sdk": "^2026.428.0",
|
|
45
|
-
"react": "^19.2.
|
|
45
|
+
"react": "^19.2.6",
|
|
46
46
|
"react-markdown": "^10.1.0",
|
|
47
47
|
"rehype-raw": "^7.0.0",
|
|
48
48
|
"rehype-sanitize": "^6.0.0",
|