paperclip-github-plugin 0.9.9 → 0.9.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 CHANGED
@@ -19,7 +19,7 @@ With this plugin, you can:
19
19
  - import open GitHub issues into Paperclip without adding title prefixes or duplicate issues
20
20
  - keep descriptions, labels, and status aligned with GitHub over time
21
21
  - configure mappings and import defaults per Paperclip company
22
- - on authenticated Paperclip deployments, choose exactly which company agents should receive the saved GitHub token as `GITHUB_TOKEN`
22
+ - choose exactly which company agents should receive the saved GitHub token as `GITHUB_TOKEN`
23
23
  - run sync manually or on a schedule
24
24
  - triage open pull requests from mapped Paperclip projects in a hosted queue
25
25
  - give Paperclip agents native GitHub tools for issues, pull requests, CI, review threads, and org-level projects
@@ -29,7 +29,7 @@ With this plugin, you can:
29
29
  The plugin adds a full in-host workflow instead of a one-off import script:
30
30
 
31
31
  - a hosted settings page for GitHub auth, repository mappings, company defaults, execution-policy handoff fallbacks, and sync controls
32
- - authenticated-only setup controls for Paperclip board access and company-scoped agent token propagation
32
+ - setup controls for Paperclip board access and company-scoped agent token propagation
33
33
  - a dashboard widget that shows sync readiness, current sync status, and run/cancel controls
34
34
  - a separate KPI dashboard widget that tracks GitHub backlog size, GitHub issues closed, and Paperclip pull requests created with recent history and historical comparisons
35
35
  - saved sync diagnostics that let operators inspect the latest per-issue failures, raw errors, and suggested next steps
@@ -137,8 +137,8 @@ npx paperclipai plugin install --local "$PWD"
137
137
 
138
138
  1. Open the plugin settings for **GitHub Sync** from inside the Paperclip company you want to configure.
139
139
  2. Paste a GitHub token, validate it, and save it.
140
- 3. If the deployment is authenticated, connect Paperclip board access from the same settings page and complete the approval flow.
141
- 4. If the deployment is authenticated, choose which agents in the current company should receive the saved GitHub token as `GITHUB_TOKEN`.
140
+ 3. If the deployment is authenticated or local trusted, connect Paperclip board access from the same settings page and complete the approval flow when host API calls need board credentials.
141
+ 4. Choose which agents in the current company should receive the saved GitHub token as `GITHUB_TOKEN`.
142
142
  5. Add one or more repository mappings for the current company.
143
143
  6. For each mapping, either choose an existing GitHub-linked Paperclip project or enter the project name that should receive synced issues.
144
144
  7. Optionally configure company-wide defaults for imported issues, including the default assignee, the default Paperclip status, executor/reviewer/approver handoff assignees for sync-driven transitions, and ignored GitHub usernames. When Paperclip board access is connected, each assignee dropdown also offers `Me` for the connected board user. `Automatic routing` means GitHub Sync follows the issue's Paperclip execution policy first and only uses the saved fallback when Paperclip does not expose the next reviewer, approver, or return assignee yet. Bot aliases such as `renovate[bot]` are matched when you save `renovate`.
@@ -197,10 +197,10 @@ The plugin is designed to avoid persisting raw credentials in plugin state.
197
197
  - GitHub tokens saved through the UI are stored as per-company Paperclip secret references.
198
198
  - Paperclip board access tokens are also stored as per-company secret references.
199
199
  - The settings UI also keeps lightweight non-secret identity labels for those saved connections, so later visits can still show who each company GitHub token and board access are connected as.
200
- - On authenticated deployments, any selected propagation agents receive `GITHUB_TOKEN` as an agent env secret-ref binding that points at the same saved GitHub token secret instead of a copied raw token.
200
+ - Any selected propagation agents receive `GITHUB_TOKEN` as an agent env secret-ref binding that points at the same saved GitHub token secret instead of a copied raw token.
201
201
  - The worker resolves those secret references at runtime instead of storing raw tokens in plugin state.
202
202
  - When the current Paperclip host rejects plugin secret refs, GitHub Sync keeps company-scoped worker-local compatibility copies for GitHub tokens and Paperclip board-access tokens in `${PAPERCLIP_HOME:-~/.paperclip}/plugins/github-sync/config.json`. Reconnect board access once after upgrading if sync still cannot authenticate Paperclip label or issue REST calls.
203
- - On authenticated Paperclip deployments, sync is blocked until the relevant company has connected Paperclip board access.
203
+ - On authenticated Paperclip deployments, sync is blocked until the relevant company has connected Paperclip board access. On local trusted deployments, board access setup remains visible so operators can configure it for host API paths that still require board credentials, but missing board access does not by itself block sync preflight.
204
204
  - KPI API route requests must include `Authorization: Bearer <PAPERCLIP_API_KEY>` from an agent run; the Paperclip host authenticates the token and supplies the agent company before the worker records any metric event.
205
205
 
206
206
  ### Optional worker-local token file
@@ -225,7 +225,7 @@ Notes:
225
225
  - The raw token is never persisted back into plugin state or plugin config.
226
226
  - A GitHub token secret saved through the settings UI is the primary source. If the current Paperclip host rejects plugin secret-ref resolution while company-scoped plugin config is unavailable, GitHub Sync stores the validated token in `githubTokensByCompanyId` as a worker-local compatibility fallback.
227
227
  - A Paperclip board access secret saved through the settings UI is also the primary source. If the host cannot resolve it for plugin workers, reconnecting board access stores the approved board token in `paperclipBoardApiTokensByCompanyId` as a worker-local compatibility fallback for direct Paperclip REST calls.
228
- - On authenticated deployments, selected agents receive `GITHUB_TOKEN` as a latest-version secret-ref env binding, and the settings UI patches agent adapter config with `replaceAdapterConfig: true` so newer Paperclip hosts persist the merged env map.
228
+ - Selected agents receive `GITHUB_TOKEN` as a latest-version secret-ref env binding, and the settings UI patches agent adapter config with `replaceAdapterConfig: true` so newer Paperclip hosts persist the merged env map.
229
229
 
230
230
  ### Worker-facing Paperclip API URL
231
231
 
package/dist/manifest.js CHANGED
@@ -642,7 +642,7 @@ var PULL_REQUEST_ASSET_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_I
642
642
  var require2 = createRequire(import.meta.url);
643
643
  var packageJson = require2("../package.json");
644
644
  var SCHEDULE_TICK_CRON = "* * * * *";
645
- var MANIFEST_VERSION = "0.9.9"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
645
+ var MANIFEST_VERSION = "0.9.10"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
646
646
  var manifest = {
647
647
  id: GITHUB_SYNC_PLUGIN_ID,
648
648
  apiVersion: 1,
package/dist/ui/index.js CHANGED
@@ -22408,10 +22408,21 @@ function normalizePaperclipHealthResponse(value) {
22408
22408
  ...authReady !== void 0 ? { authReady } : {}
22409
22409
  };
22410
22410
  }
22411
- function requiresPaperclipBoardAccess(value) {
22412
- const health = normalizePaperclipHealthResponse(value);
22411
+ function requiresPaperclipBoardAccessForHealth(health) {
22413
22412
  return health?.deploymentMode?.toLowerCase() === "authenticated";
22414
22413
  }
22414
+ function shouldShowPaperclipBoardAccessSettingsForHealth(health) {
22415
+ const deploymentMode = health?.deploymentMode?.toLowerCase();
22416
+ return deploymentMode === "authenticated" || deploymentMode === "local_trusted";
22417
+ }
22418
+ function resolvePaperclipAuthControlsPolicy(value) {
22419
+ const health = normalizePaperclipHealthResponse(value);
22420
+ return {
22421
+ boardAccessRequired: requiresPaperclipBoardAccessForHealth(health),
22422
+ boardAccessSettingsVisible: shouldShowPaperclipBoardAccessSettingsForHealth(health),
22423
+ githubTokenPropagationSettingsVisible: true
22424
+ };
22425
+ }
22415
22426
 
22416
22427
  // src/ui/assignees.ts
22417
22428
  function normalizeCompanyAssigneeOptionsResponse(response) {
@@ -23098,7 +23109,12 @@ function getSyncSetupMessage(issue, hasCompanyContext) {
23098
23109
  }
23099
23110
  }
23100
23111
  function usePaperclipBoardAccessRequirement() {
23101
- const [status, setStatus] = useState2("loading");
23112
+ const [state, setState] = useState2({
23113
+ status: "loading",
23114
+ required: false,
23115
+ settingsVisible: false,
23116
+ githubTokenPropagationSettingsVisible: true
23117
+ });
23102
23118
  useEffect2(() => {
23103
23119
  let cancelled = false;
23104
23120
  void (async () => {
@@ -23107,19 +23123,28 @@ function usePaperclipBoardAccessRequirement() {
23107
23123
  return;
23108
23124
  }
23109
23125
  if (!health) {
23110
- setStatus("unknown");
23126
+ const policy2 = resolvePaperclipAuthControlsPolicy(null);
23127
+ setState({
23128
+ status: "unknown",
23129
+ required: policy2.boardAccessRequired,
23130
+ settingsVisible: policy2.boardAccessSettingsVisible,
23131
+ githubTokenPropagationSettingsVisible: policy2.githubTokenPropagationSettingsVisible
23132
+ });
23111
23133
  return;
23112
23134
  }
23113
- setStatus(requiresPaperclipBoardAccess(health) ? "required" : "not_required");
23135
+ const policy = resolvePaperclipAuthControlsPolicy(health);
23136
+ setState({
23137
+ status: policy.boardAccessRequired ? "required" : "not_required",
23138
+ required: policy.boardAccessRequired,
23139
+ settingsVisible: policy.boardAccessSettingsVisible,
23140
+ githubTokenPropagationSettingsVisible: policy.githubTokenPropagationSettingsVisible
23141
+ });
23114
23142
  })();
23115
23143
  return () => {
23116
23144
  cancelled = true;
23117
23145
  };
23118
23146
  }, []);
23119
- return {
23120
- status,
23121
- required: status === "required"
23122
- };
23147
+ return state;
23123
23148
  }
23124
23149
  function getGitHubRateLimitResourceLabel(resource) {
23125
23150
  switch (resource?.trim().toLowerCase()) {
@@ -32384,6 +32409,8 @@ function GitHubSyncSettingsPage() {
32384
32409
  const hasSavedToken = Boolean(form.githubTokenConfigured || showSavedTokenHint);
32385
32410
  const boardAccessConfigured = Boolean(form.paperclipBoardAccessConfigured);
32386
32411
  const boardAccessRequired = boardAccessRequirement.required;
32412
+ const boardAccessSettingsVisible = boardAccessRequirement.settingsVisible;
32413
+ const githubTokenPropagationSettingsVisible = boardAccessRequirement.githubTokenPropagationSettingsVisible;
32387
32414
  const boardAccessReady = !boardAccessRequired || hasCompanyContext && boardAccessConfigured;
32388
32415
  const tokenStatus = tokenStatusOverride ?? (hasSavedToken ? "valid" : "required");
32389
32416
  const tokenTone = tokenStatus === "valid" ? "success" : tokenStatus === "invalid" ? "danger" : "warning";
@@ -32492,7 +32519,7 @@ function GitHubSyncSettingsPage() {
32492
32519
  const manualSyncScopePillLabel = hasCompanyContext ? "This company" : "All companies";
32493
32520
  const manualSyncButtonLabel = hasCompanyContext ? "Run sync for this company" : "Run sync across all companies";
32494
32521
  const advancedSettingsSummary = formatAdvancedSettingsSummary(form.advancedSettings, availableAssignees, {
32495
- includePropagation: boardAccessRequired
32522
+ includePropagation: githubTokenPropagationSettingsVisible
32496
32523
  });
32497
32524
  const assigneeSelectOptions = [
32498
32525
  { value: "", label: "Unassigned" },
@@ -32646,9 +32673,6 @@ function GitHubSyncSettingsPage() {
32646
32673
  });
32647
32674
  }
32648
32675
  async function propagateGitHubTokenToSelectedAgents(options) {
32649
- if (!boardAccessRequired) {
32650
- return;
32651
- }
32652
32676
  const selectedAgentIds = normalizeAgentIds(options.selectedAgentIds);
32653
32677
  const previousAgentIds = normalizeAgentIds(options.previousAgentIds);
32654
32678
  if (selectedAgentIds.length === 0 && previousAgentIds.length === 0) {
@@ -32855,7 +32879,7 @@ function GitHubSyncSettingsPage() {
32855
32879
  }));
32856
32880
  toast({
32857
32881
  title: boardIdentity.label ? `Paperclip board access connected as ${boardIdentity.label}` : "Paperclip board access connected",
32858
- body: "Direct Paperclip REST calls can now authenticate in authenticated deployments.",
32882
+ body: "Direct Paperclip REST calls can now authenticate when the host requires board access.",
32859
32883
  tone: "success"
32860
32884
  });
32861
32885
  notifyGitHubSyncSettingsChanged();
@@ -33224,7 +33248,7 @@ function GitHubSyncSettingsPage() {
33224
33248
  /* @__PURE__ */ jsx2("div", { className: "ghsync__permission-audit-list", children: /* @__PURE__ */ jsx2("div", { className: "ghsync__permission-audit-item", children: /* @__PURE__ */ jsx2("span", { children: tokenPermissionAuditData?.warnings[0] ?? "Add a mapped repository in this company so GitHub Sync can verify the token permissions it needs." }) }) })
33225
33249
  ] }) : null
33226
33250
  ] }),
33227
- boardAccessRequired ? /* @__PURE__ */ jsxs2("section", { className: "ghsync__section", children: [
33251
+ boardAccessSettingsVisible ? /* @__PURE__ */ jsxs2("section", { className: "ghsync__section", children: [
33228
33252
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__section-head", children: [
33229
33253
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__section-copy", children: [
33230
33254
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__section-title-row", children: [
@@ -33565,7 +33589,7 @@ function GitHubSyncSettingsPage() {
33565
33589
  ),
33566
33590
  /* @__PURE__ */ jsx2("p", { className: "ghsync__hint", children: "Comma or newline separated." })
33567
33591
  ] }),
33568
- boardAccessRequired ? /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
33592
+ githubTokenPropagationSettingsVisible ? /* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
33569
33593
  /* @__PURE__ */ jsx2("label", { htmlFor: "advanced-token-propagation", children: "Propagate GitHub token to agents" }),
33570
33594
  /* @__PURE__ */ jsx2(
33571
33595
  SettingsAgentMultiPicker,
@@ -33769,7 +33793,7 @@ function GitHubSyncSettingsPage() {
33769
33793
  ] }),
33770
33794
  /* @__PURE__ */ jsx2("span", { children: !repositoriesUnlocked ? "Requires a token." : savedMappingCount > 0 ? hasCompanyContext ? `${savedMappingCount} saved.` : `${savedMappingCount} saved.` : hasCompanyContext ? "Add a repository." : "Select a company." })
33771
33795
  ] }),
33772
- boardAccessRequired ? /* @__PURE__ */ jsxs2("div", { className: "ghsync__check", children: [
33796
+ boardAccessSettingsVisible ? /* @__PURE__ */ jsxs2("div", { className: "ghsync__check", children: [
33773
33797
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__check-top", children: [
33774
33798
  /* @__PURE__ */ jsx2("strong", { children: "Paperclip board access" }),
33775
33799
  /* @__PURE__ */ jsx2("span", { className: `ghsync__badge ${getToneClass(boardAccessStatusTone)}`, children: boardAccessStatusLabel })