paperclip-github-plugin 0.8.9 → 0.8.11

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 CHANGED
@@ -227,7 +227,7 @@ The plugin exposes GitHub workflow tools to Paperclip agents, including:
227
227
 
228
228
  - repository-scoped search for issues and pull requests
229
229
  - issue reads, comment reads, comment writes, metadata updates, and `assign_to_current_user` assignment to the saved token owner
230
- - pull request creation, reads, updates, changed-file inspection, and CI-check inspection
230
+ - pull request creation, reads, updates, changed-file inspection, CI-check inspection, and asset upload for PR visual evidence
231
231
  - review-thread reads, replies, resolve and unresolve actions, and `request_pull_request_reviewers` reviewer requests
232
232
  - organization-level GitHub Project search/listing and pull-request-to-project association
233
233
 
@@ -272,6 +272,34 @@ Current host caveat: on authenticated Paperclip deployments, the Paperclip host
272
272
 
273
273
  Because the KPI attribution endpoint is a native plugin JSON route rather than a plugin tool, authenticated agent runs can still call it directly with `PAPERCLIP_API_KEY` even while that host bug blocks the GitHub Sync tool surface.
274
274
 
275
+ ### Pull request asset upload
276
+
277
+ For PRs that need durable assets in the description, agents can call the `upload_pull_request_asset` tool. The tool accepts a PR target plus `fileName` and either `contentBase64` or a `dataUrl`; optional fields include `label`, `alt` (an alias for image alt text), `caption`, `mimeType`, and `artifactBranch`. Common MIME types are inferred from filenames, including images and PDFs. Unknown types are stored as `application/octet-stream`. Assets are limited to 10 MiB.
278
+
279
+ The plugin writes the asset to a non-merge artifact branch named `paperclip-artifacts-pr-<number>` by default, stores it under `assets/pr-<number>/<head-sha>/`, and returns immutable raw GitHub URLs plus Markdown suitable for a PR description. Images return image Markdown; PDFs and other files return normal Markdown links.
280
+
281
+ Authenticated agent runs that cannot call plugin tools can post the same JSON payload to `/api/plugins/paperclip-github-plugin/api/pull-request-assets` with `Authorization: Bearer ${PAPERCLIP_API_KEY}`. The native plugin route is agent-authenticated by the Paperclip host before worker dispatch.
282
+
283
+ Example:
284
+
285
+ ```bash
286
+ contentBase64="$(base64 -w0 /tmp/review-report.pdf)"
287
+ payload="$(jq -n \
288
+ --arg repository paperclipai/example-repo \
289
+ --argjson pullRequestNumber 21 \
290
+ --arg fileName review-report.pdf \
291
+ --arg label 'Review report PDF' \
292
+ --arg contentBase64 "$contentBase64" \
293
+ '{repository:$repository,pullRequestNumber:$pullRequestNumber,fileName:$fileName,label:$label,contentBase64:$contentBase64,mimeType:"application/pdf"}')"
294
+
295
+ curl -X POST "${PAPERCLIP_API_URL%/}/api/plugins/paperclip-github-plugin/api/pull-request-assets" \
296
+ -H "content-type: application/json" \
297
+ -H "authorization: Bearer ${PAPERCLIP_API_KEY}" \
298
+ -d "${payload}"
299
+ ```
300
+
301
+ The response body contains `asset.markdown`, `asset.rawUrl`, `asset.artifactBranch`, `asset.path`, and `asset.commitSha`.
302
+
275
303
  ### Issue link API route
276
304
 
277
305
  Authenticated agent runs can link the current Paperclip issue to a GitHub issue or pull request by posting to `/api/plugins/paperclip-github-plugin/api/issue-link`. This is useful after creating a PR with `gh` in a repository that is not mapped to a Paperclip project.
package/dist/manifest.js CHANGED
@@ -523,6 +523,58 @@ var GITHUB_AGENT_TOOLS = [
523
523
  }
524
524
  }
525
525
  },
526
+ {
527
+ name: "upload_pull_request_asset",
528
+ displayName: "Upload Pull Request Asset",
529
+ 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.",
530
+ parametersSchema: {
531
+ type: "object",
532
+ additionalProperties: false,
533
+ required: ["fileName"],
534
+ allOf: [pullRequestTargetSchema],
535
+ anyOf: [
536
+ { required: ["contentBase64"] },
537
+ { required: ["dataUrl"] }
538
+ ],
539
+ properties: {
540
+ repository: repositoryProperty,
541
+ pullRequestNumber: pullRequestNumberProperty,
542
+ paperclipIssueId: paperclipIssueIdProperty,
543
+ fileName: {
544
+ type: "string",
545
+ description: "Asset filename. The plugin sanitizes it and preserves a safe extension."
546
+ },
547
+ label: {
548
+ type: "string",
549
+ description: "Human-readable link text for the returned Markdown. Defaults to the sanitized filename."
550
+ },
551
+ alt: {
552
+ type: "string",
553
+ description: "Backward-compatible alias for label, useful as image alt text."
554
+ },
555
+ caption: {
556
+ type: "string",
557
+ description: "Optional human-facing caption returned with the uploaded asset metadata."
558
+ },
559
+ contentBase64: {
560
+ type: "string",
561
+ description: "Base64-encoded asset bytes. Assets are limited to 10 MiB."
562
+ },
563
+ dataUrl: {
564
+ type: "string",
565
+ description: "Alternative base64 data URL input such as data:application/pdf;base64,... or data:image/png;base64,... ."
566
+ },
567
+ mimeType: {
568
+ type: "string",
569
+ 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."
570
+ },
571
+ artifactBranch: {
572
+ type: "string",
573
+ description: "Optional artifact branch name. Defaults to paperclip-artifacts-pr-<pullRequestNumber>."
574
+ }
575
+ }
576
+ }
577
+ },
526
578
  {
527
579
  name: "link_github_item",
528
580
  displayName: "Link GitHub Item",
@@ -582,12 +634,15 @@ var COMPANY_METRIC_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_ID}/a
582
634
  var ISSUE_LINK_API_ROUTE_KEY = "link-github-item";
583
635
  var ISSUE_LINK_API_ROUTE_PATH = "/issue-link";
584
636
  var ISSUE_LINK_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_ID}/api${ISSUE_LINK_API_ROUTE_PATH}`;
637
+ var PULL_REQUEST_ASSET_API_ROUTE_KEY = "upload-pull-request-asset";
638
+ var PULL_REQUEST_ASSET_API_ROUTE_PATH = "/pull-request-assets";
639
+ var PULL_REQUEST_ASSET_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_ID}/api${PULL_REQUEST_ASSET_API_ROUTE_PATH}`;
585
640
 
586
641
  // src/manifest.ts
587
642
  var require2 = createRequire(import.meta.url);
588
643
  var packageJson = require2("../package.json");
589
644
  var SCHEDULE_TICK_CRON = "* * * * *";
590
- var MANIFEST_VERSION = "0.8.9"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
645
+ var MANIFEST_VERSION = "0.8.11"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
591
646
  var manifest = {
592
647
  id: GITHUB_SYNC_PLUGIN_ID,
593
648
  apiVersion: 1,
@@ -666,6 +721,13 @@ var manifest = {
666
721
  path: ISSUE_LINK_API_ROUTE_PATH,
667
722
  auth: "agent",
668
723
  capability: "api.routes.register"
724
+ },
725
+ {
726
+ routeKey: PULL_REQUEST_ASSET_API_ROUTE_KEY,
727
+ method: "POST",
728
+ path: PULL_REQUEST_ASSET_API_ROUTE_PATH,
729
+ auth: "agent",
730
+ capability: "api.routes.register"
669
731
  }
670
732
  ],
671
733
  tools: GITHUB_AGENT_TOOLS,
package/dist/ui/index.js CHANGED
@@ -366,7 +366,7 @@ import {
366
366
  usePluginToast
367
367
  } from "@paperclipai/plugin-sdk/ui";
368
368
 
369
- // node_modules/.pnpm/@ungap+structured-clone@1.3.0/node_modules/@ungap/structured-clone/esm/types.js
369
+ // node_modules/.pnpm/@ungap+structured-clone@1.3.1/node_modules/@ungap/structured-clone/esm/types.js
370
370
  var VOID = -1;
371
371
  var PRIMITIVE = 0;
372
372
  var ARRAY = 1;
@@ -378,8 +378,20 @@ var SET = 6;
378
378
  var ERROR = 7;
379
379
  var BIGINT = 8;
380
380
 
381
- // node_modules/.pnpm/@ungap+structured-clone@1.3.0/node_modules/@ungap/structured-clone/esm/deserialize.js
381
+ // node_modules/.pnpm/@ungap+structured-clone@1.3.1/node_modules/@ungap/structured-clone/esm/deserialize.js
382
382
  var env = typeof self === "object" ? self : globalThis;
383
+ var guard = (name2, init) => {
384
+ switch (name2) {
385
+ case "Function":
386
+ case "SharedWorker":
387
+ case "Worker":
388
+ case "eval":
389
+ case "setInterval":
390
+ case "setTimeout":
391
+ throw new TypeError("unable to deserialize " + name2);
392
+ }
393
+ return new env[name2](init);
394
+ };
383
395
  var deserializer = ($2, _) => {
384
396
  const as = (out, index2) => {
385
397
  $2.set(index2, out);
@@ -425,7 +437,7 @@ var deserializer = ($2, _) => {
425
437
  }
426
438
  case ERROR: {
427
439
  const { name: name2, message } = value;
428
- return as(new env[name2](message), index2);
440
+ return as(guard(name2, message), index2);
429
441
  }
430
442
  case BIGINT:
431
443
  return as(BigInt(value), index2);
@@ -438,13 +450,13 @@ var deserializer = ($2, _) => {
438
450
  return as(new DataView(buffer), value);
439
451
  }
440
452
  }
441
- return as(new env[type](value), index2);
453
+ return as(guard(type, value), index2);
442
454
  };
443
455
  return unpair;
444
456
  };
445
457
  var deserialize = (serialized) => deserializer(/* @__PURE__ */ new Map(), serialized)(0);
446
458
 
447
- // node_modules/.pnpm/@ungap+structured-clone@1.3.0/node_modules/@ungap/structured-clone/esm/serialize.js
459
+ // node_modules/.pnpm/@ungap+structured-clone@1.3.1/node_modules/@ungap/structured-clone/esm/serialize.js
448
460
  var EMPTY = "";
449
461
  var { toString } = {};
450
462
  var { keys } = Object;
@@ -577,7 +589,7 @@ var serialize = (value, { json, lossy } = {}) => {
577
589
  return serializer(!(json || lossy), !!json, /* @__PURE__ */ new Map(), _)(value), _;
578
590
  };
579
591
 
580
- // node_modules/.pnpm/@ungap+structured-clone@1.3.0/node_modules/@ungap/structured-clone/esm/index.js
592
+ // node_modules/.pnpm/@ungap+structured-clone@1.3.1/node_modules/@ungap/structured-clone/esm/index.js
581
593
  var esm_default = typeof structuredClone === "function" ? (
582
594
  /* c8 ignore start */
583
595
  (any, options) => options && ("json" in options || "lossy" in options) ? deserialize(serialize(any, options)) : structuredClone(any)
@@ -11984,7 +11996,7 @@ var urlAttributes = {
11984
11996
  ]
11985
11997
  };
11986
11998
 
11987
- // node_modules/.pnpm/react-markdown@10.1.0_@types+react@19.2.14_react@19.2.5/node_modules/react-markdown/lib/index.js
11999
+ // node_modules/.pnpm/react-markdown@10.1.0_@types+react@19.2.14_react@19.2.6/node_modules/react-markdown/lib/index.js
11988
12000
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11989
12001
  import { useEffect, useState } from "react";
11990
12002
 
@@ -19140,7 +19152,7 @@ function isUint8Array2(value) {
19140
19152
  );
19141
19153
  }
19142
19154
 
19143
- // node_modules/.pnpm/react-markdown@10.1.0_@types+react@19.2.14_react@19.2.5/node_modules/react-markdown/lib/index.js
19155
+ // node_modules/.pnpm/react-markdown@10.1.0_@types+react@19.2.14_react@19.2.6/node_modules/react-markdown/lib/index.js
19144
19156
  var changelog = "https://github.com/remarkjs/react-markdown/blob/main/changelog.md";
19145
19157
  var emptyPlugins = [];
19146
19158
  var emptyRemarkRehypeOptions = { allowDangerousHtml: true };