paperclip-github-plugin 0.4.0 → 0.4.2

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
@@ -53,11 +53,11 @@ Long-running syncs continue in the background, so quick actions do not have to w
53
53
 
54
54
  ### Company-aware configuration
55
55
 
56
- GitHub tokens and sync cadence are shared at the plugin instance level, while repository mappings, advanced import defaults, and Paperclip board access are managed per company. When you open settings inside a specific company, you only edit that company's mappings and defaults.
56
+ GitHub tokens, repository mappings, advanced import defaults, Paperclip board access, and sync cadence are managed per company. When you open settings inside a specific company, you only edit that company's setup.
57
57
 
58
58
  ### Project binding that respects existing work
59
59
 
60
- If a company already has a Paperclip project bound to a GitHub repository workspace, the settings UI can reuse that project instead of creating a duplicate. New mappings can also create and bind a Paperclip project automatically, and those newly created projects opt into isolated issue checkouts.
60
+ If a company already has a Paperclip project bound to a GitHub repository workspace, the settings UI can reuse that project instead of creating a duplicate. New mappings can also create and bind a Paperclip project automatically, and those newly created projects opt into isolated issue checkouts with new issues defaulting to isolated checkout.
61
61
 
62
62
  ### Status sync with delivery context
63
63
 
@@ -153,9 +153,9 @@ Additional behavior:
153
153
 
154
154
  The plugin is designed to avoid persisting raw credentials in plugin state.
155
155
 
156
- - GitHub tokens saved through the UI are stored as Paperclip secret references.
156
+ - GitHub tokens saved through the UI are stored as per-company Paperclip secret references.
157
157
  - Paperclip board access tokens are also stored as per-company secret references.
158
- - The settings UI also keeps lightweight non-secret identity labels for those saved connections, so later visits can still show who the shared GitHub token and company board access are connected as.
158
+ - 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.
159
159
  - 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.
160
160
  - The worker resolves those secret references at runtime instead of storing raw tokens in plugin state.
161
161
  - On authenticated Paperclip deployments, sync is blocked until the relevant company has connected Paperclip board access.
package/dist/manifest.js CHANGED
@@ -503,7 +503,7 @@ var require2 = createRequire(import.meta.url);
503
503
  var packageJson = require2("../package.json");
504
504
  var DASHBOARD_WIDGET_CAPABILITY = "ui.dashboardWidget.register";
505
505
  var SCHEDULE_TICK_CRON = "* * * * *";
506
- var MANIFEST_VERSION = "0.4.0"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
506
+ var MANIFEST_VERSION = "0.4.2"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
507
507
  var manifest = {
508
508
  id: "paperclip-github-plugin",
509
509
  apiVersion: 1,
@@ -537,9 +537,12 @@ var manifest = {
537
537
  instanceConfigSchema: {
538
538
  type: "object",
539
539
  properties: {
540
- githubTokenRef: {
541
- type: "string",
542
- title: "GitHub Token Secret"
540
+ githubTokenRefs: {
541
+ type: "object",
542
+ title: "GitHub Token Secrets",
543
+ additionalProperties: {
544
+ type: "string"
545
+ }
543
546
  },
544
547
  paperclipBoardApiTokenRefs: {
545
548
  type: "object",
@@ -591,10 +594,10 @@ var manifest = {
591
594
  exportName: "GitHubSyncDashboardWidget"
592
595
  },
593
596
  {
594
- type: "detailTab",
597
+ type: "taskDetailView",
595
598
  id: "paperclip-github-plugin-issue-detail-tab",
596
599
  displayName: "GitHub",
597
- exportName: "GitHubSyncIssueDetailTab",
600
+ exportName: "GitHubSyncIssueTaskDetailView",
598
601
  entityTypes: ["issue"]
599
602
  },
600
603
  {
@@ -615,7 +618,7 @@ var manifest = {
615
618
  id: "paperclip-github-plugin-toolbar-button",
616
619
  displayName: "GitHub Sync",
617
620
  exportName: "GitHubSyncEntityToolbarButton",
618
- entityTypes: ["project", "issue"]
621
+ entityTypes: ["project"]
619
622
  },
620
623
  {
621
624
  type: "settingsPage",
package/dist/ui/index.js CHANGED
@@ -22648,18 +22648,32 @@ function normalizePluginConfigBoardTokenRefs(value) {
22648
22648
  }
22649
22649
  return Object.fromEntries(entries);
22650
22650
  }
22651
+ function normalizePluginConfigGitHubTokenRefs(value) {
22652
+ if (!value || typeof value !== "object") {
22653
+ return void 0;
22654
+ }
22655
+ const entries = Object.entries(value).map(([companyId, secretRef]) => {
22656
+ const normalizedCompanyId = normalizeOptionalString2(companyId);
22657
+ const normalizedSecretRef = normalizeOptionalString2(secretRef);
22658
+ return normalizedCompanyId && normalizedSecretRef ? [normalizedCompanyId, normalizedSecretRef] : null;
22659
+ }).filter((entry) => Boolean(entry));
22660
+ if (entries.length === 0) {
22661
+ return void 0;
22662
+ }
22663
+ return Object.fromEntries(entries);
22664
+ }
22651
22665
  function normalizePluginConfig(value) {
22652
22666
  if (!value || typeof value !== "object") {
22653
22667
  return {};
22654
22668
  }
22655
22669
  const record = { ...value };
22656
- const githubTokenRef = normalizeOptionalString2(record.githubTokenRef);
22670
+ const githubTokenRefs = normalizePluginConfigGitHubTokenRefs(record.githubTokenRefs);
22657
22671
  const paperclipBoardApiTokenRefs = normalizePluginConfigBoardTokenRefs(record.paperclipBoardApiTokenRefs);
22658
22672
  const paperclipApiBaseUrl = normalizePaperclipApiBaseUrl(record.paperclipApiBaseUrl);
22659
- if (githubTokenRef) {
22660
- record.githubTokenRef = githubTokenRef;
22673
+ if (githubTokenRefs) {
22674
+ record.githubTokenRefs = githubTokenRefs;
22661
22675
  } else {
22662
- delete record.githubTokenRef;
22676
+ delete record.githubTokenRefs;
22663
22677
  }
22664
22678
  if (paperclipBoardApiTokenRefs) {
22665
22679
  record.paperclipBoardApiTokenRefs = paperclipBoardApiTokenRefs;
@@ -22675,12 +22689,27 @@ function normalizePluginConfig(value) {
22675
22689
  }
22676
22690
  function mergePluginConfig(currentValue, patch5) {
22677
22691
  const current = normalizePluginConfig(currentValue);
22692
+ const currentGitHubTokenRefs = normalizePluginConfigGitHubTokenRefs(current.githubTokenRefs);
22693
+ const patchGitHubTokenRefs = normalizePluginConfigGitHubTokenRefs(patch5.githubTokenRefs);
22678
22694
  const currentBoardTokenRefs = normalizePluginConfigBoardTokenRefs(current.paperclipBoardApiTokenRefs);
22679
22695
  const patchBoardTokenRefs = normalizePluginConfigBoardTokenRefs(patch5.paperclipBoardApiTokenRefs);
22680
22696
  const next2 = normalizePluginConfig({
22681
22697
  ...current,
22682
22698
  ...patch5
22683
22699
  });
22700
+ if ("githubTokenRefs" in patch5) {
22701
+ const mergedGitHubTokenRefs = {
22702
+ ...currentGitHubTokenRefs ?? {},
22703
+ ...patchGitHubTokenRefs ?? {}
22704
+ };
22705
+ if (Object.keys(mergedGitHubTokenRefs).length > 0) {
22706
+ next2.githubTokenRefs = mergedGitHubTokenRefs;
22707
+ } else {
22708
+ delete next2.githubTokenRefs;
22709
+ }
22710
+ } else if (currentGitHubTokenRefs) {
22711
+ next2.githubTokenRefs = currentGitHubTokenRefs;
22712
+ }
22684
22713
  if ("paperclipBoardApiTokenRefs" in patch5) {
22685
22714
  const mergedBoardTokenRefs = {
22686
22715
  ...currentBoardTokenRefs ?? {},
@@ -27733,7 +27762,8 @@ async function resolveOrCreateProject(companyId, projectName) {
27733
27762
  name: projectName.trim(),
27734
27763
  status: "planned",
27735
27764
  executionWorkspacePolicy: {
27736
- enabled: true
27765
+ enabled: true,
27766
+ defaultMode: "isolated_workspace"
27737
27767
  }
27738
27768
  })
27739
27769
  });
@@ -31294,7 +31324,7 @@ function GitHubSyncSettingsPage() {
31294
31324
  const tokenTone = tokenStatus === "valid" ? "success" : tokenStatus === "invalid" ? "danger" : "warning";
31295
31325
  const tokenBannerLabel = tokenStatus === "valid" ? "Token valid" : tokenStatus === "invalid" ? "Token invalid" : "Token required";
31296
31326
  const tokenBadgeLabel = tokenStatus === "valid" ? "Valid" : tokenStatus === "invalid" ? "Invalid" : "Required";
31297
- const tokenStatusDescription = tokenStatus === "invalid" ? "GitHub rejected the last token." : tokenStatus === "required" ? "Add a token." : "Shared token.";
31327
+ const tokenStatusDescription = tokenStatus === "invalid" ? "GitHub rejected the last token." : tokenStatus === "required" ? hasCompanyContext ? "Add a token for this company." : "Select a company." : hasCompanyContext ? "Token configured for the selected company context." : "Saved in one or more companies.";
31298
31328
  const tokenDescription = tokenStatusDescription;
31299
31329
  const tokenPermissionAuditData = tokenPermissionAudit.data;
31300
31330
  const tokenPermissionAuditMeta = getGitHubTokenPermissionAuditMeta(tokenPermissionAuditData);
@@ -31418,7 +31448,7 @@ function GitHubSyncSettingsPage() {
31418
31448
  ] }),
31419
31449
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__section-head-actions", children: [
31420
31450
  /* @__PURE__ */ jsx2("span", { className: `ghsync__scope-pill ${hasCompanyContext ? "ghsync__scope-pill--company" : "ghsync__scope-pill--mixed"}`, children: hasCompanyContext ? currentCompanyName : "No company" }),
31421
- /* @__PURE__ */ jsx2("span", { className: "ghsync__scope-pill ghsync__scope-pill--global", children: "Shared" }),
31451
+ hasCompanyContext ? /* @__PURE__ */ jsx2("span", { className: "ghsync__scope-pill ghsync__scope-pill--company", children: "Company" }) : null,
31422
31452
  /* @__PURE__ */ jsxs2("span", { className: "ghsync__badge ghsync__badge--neutral", children: [
31423
31453
  /* @__PURE__ */ jsx2(LoadingSpinner, { size: "sm", label: "Loading settings" }),
31424
31454
  "Loading"
@@ -31538,13 +31568,18 @@ function GitHubSyncSettingsPage() {
31538
31568
  return;
31539
31569
  }
31540
31570
  let githubTokenSecretRef = typeof options.githubTokenSecretRef === "string" && options.githubTokenSecretRef.trim() ? options.githubTokenSecretRef.trim() : void 0;
31571
+ const companyId = hostContext.companyId;
31541
31572
  if (!githubTokenSecretRef) {
31573
+ if (!companyId) {
31574
+ throw new Error("Company context is required to propagate the GitHub token.");
31575
+ }
31542
31576
  const pluginId = await resolveCurrentPluginId(pluginIdFromLocation);
31543
31577
  if (!pluginId) {
31544
31578
  throw new Error("Plugin id is required to propagate the GitHub token to selected agents.");
31545
31579
  }
31546
31580
  const currentConfigResponse = await fetchJson(`/api/plugins/${pluginId}/config`);
31547
- githubTokenSecretRef = normalizePluginConfig(currentConfigResponse?.configJson).githubTokenRef;
31581
+ const normalizedConfig = normalizePluginConfig(currentConfigResponse?.configJson);
31582
+ githubTokenSecretRef = normalizedConfig.githubTokenRefs?.[companyId];
31548
31583
  }
31549
31584
  if (!githubTokenSecretRef) {
31550
31585
  throw new Error("GitHub token propagation requires a GitHub token saved through this settings page.");
@@ -31594,11 +31629,15 @@ function GitHubSyncSettingsPage() {
31594
31629
  const secretName = `github_sync_${companyId.replace(/[^a-z0-9]+/gi, "_").toLowerCase()}`;
31595
31630
  const secret = await resolveOrCreateCompanySecret(companyId, secretName, trimmedToken);
31596
31631
  await patchPluginConfig(pluginId, {
31597
- githubTokenRef: secret.id
31632
+ githubTokenRefs: {
31633
+ [companyId]: secret.id
31634
+ }
31598
31635
  });
31599
31636
  await saveRegistration({
31600
31637
  companyId,
31601
- githubTokenRef: secret.id,
31638
+ githubTokenRefs: {
31639
+ [companyId]: secret.id
31640
+ },
31602
31641
  githubTokenLogin: validation.login
31603
31642
  });
31604
31643
  const selectedAgentIds = normalizeAgentIds(currentSettings?.advancedSettings?.githubTokenPropagationAgentIds);
@@ -31920,7 +31959,7 @@ function GitHubSyncSettingsPage() {
31920
31959
  ] }),
31921
31960
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__section-head-actions", children: [
31922
31961
  /* @__PURE__ */ jsx2("span", { className: `ghsync__scope-pill ${hasCompanyContext ? "ghsync__scope-pill--company" : "ghsync__scope-pill--mixed"}`, children: hasCompanyContext ? currentCompanyName : "No company" }),
31923
- /* @__PURE__ */ jsx2("span", { className: "ghsync__scope-pill ghsync__scope-pill--global", children: "Shared" }),
31962
+ /* @__PURE__ */ jsx2("span", { className: "ghsync__scope-pill ghsync__scope-pill--company", children: "Company" }),
31924
31963
  /* @__PURE__ */ jsxs2("span", { className: `ghsync__badge ${getToneClass(tokenTone)}`, children: [
31925
31964
  /* @__PURE__ */ jsx2("span", { className: "ghsync__badge-dot", "aria-hidden": "true" }),
31926
31965
  tokenBannerLabel
@@ -31942,7 +31981,7 @@ function GitHubSyncSettingsPage() {
31942
31981
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__section-copy", children: [
31943
31982
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__section-title-row", children: [
31944
31983
  /* @__PURE__ */ jsx2("h4", { children: "GitHub access" }),
31945
- /* @__PURE__ */ jsx2("div", { className: "ghsync__section-tags", children: /* @__PURE__ */ jsx2("span", { className: "ghsync__scope-pill ghsync__scope-pill--global", children: "Shared" }) })
31984
+ /* @__PURE__ */ jsx2("div", { className: "ghsync__section-tags", children: /* @__PURE__ */ jsx2("span", { className: "ghsync__scope-pill ghsync__scope-pill--company", children: "Company" }) })
31946
31985
  ] }),
31947
31986
  /* @__PURE__ */ jsx2("p", { children: tokenDescription })
31948
31987
  ] }),
@@ -31950,8 +31989,8 @@ function GitHubSyncSettingsPage() {
31950
31989
  ] }),
31951
31990
  !hasCompanyContext ? /* @__PURE__ */ jsxs2("div", { className: "ghsync__locked", children: [
31952
31991
  /* @__PURE__ */ jsxs2("div", { children: [
31953
- /* @__PURE__ */ jsx2("strong", { children: hasSavedToken ? "Shared token ready" : "Company required" }),
31954
- /* @__PURE__ */ jsx2("span", { children: hasSavedToken ? "Open a company to replace it." : "Open a company to save it." })
31992
+ /* @__PURE__ */ jsx2("strong", { children: "Company required" }),
31993
+ /* @__PURE__ */ jsx2("span", { children: "Open a company to view or save its token." })
31955
31994
  ] }),
31956
31995
  /* @__PURE__ */ jsx2("span", { className: "ghsync__badge ghsync__badge--neutral", children: "Read only" })
31957
31996
  ] }) : showTokenForm ? /* @__PURE__ */ jsxs2("form", { className: "ghsync__stack", onSubmit: handleSaveToken, children: [
@@ -32008,8 +32047,8 @@ function GitHubSyncSettingsPage() {
32008
32047
  ] }) })
32009
32048
  ] }) : /* @__PURE__ */ jsxs2("div", { className: "ghsync__connected", children: [
32010
32049
  /* @__PURE__ */ jsxs2("div", { children: [
32011
- /* @__PURE__ */ jsx2("strong", { children: validatedLogin ? `Authenticated as ${validatedLogin}` : "Shared token ready" }),
32012
- /* @__PURE__ */ jsx2("span", { children: "Shared across all companies." })
32050
+ /* @__PURE__ */ jsx2("strong", { children: validatedLogin ? `Authenticated as ${validatedLogin}` : "Company token ready" }),
32051
+ /* @__PURE__ */ jsx2("span", { children: `Used for sync in ${currentCompanyName}.` })
32013
32052
  ] }),
32014
32053
  /* @__PURE__ */ jsx2(
32015
32054
  "button",
@@ -32349,7 +32388,7 @@ function GitHubSyncSettingsPage() {
32349
32388
  /* @__PURE__ */ jsx2("h4", { children: "Sync" }),
32350
32389
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__section-tags", children: [
32351
32390
  /* @__PURE__ */ jsx2("span", { className: manualSyncScopePillClass, children: manualSyncScopePillLabel }),
32352
- /* @__PURE__ */ jsx2("span", { className: "ghsync__scope-pill ghsync__scope-pill--global", children: "Shared cadence" })
32391
+ /* @__PURE__ */ jsx2("span", { className: "ghsync__scope-pill ghsync__scope-pill--global", children: "Company cadence" })
32353
32392
  ] })
32354
32393
  ] }),
32355
32394
  syncSectionDescription ? /* @__PURE__ */ jsx2("p", { children: syncSectionDescription }) : null
@@ -32386,12 +32425,16 @@ function GitHubSyncSettingsPage() {
32386
32425
  /* @__PURE__ */ jsx2("p", { className: `ghsync__hint${scheduleFrequencyError ? " ghsync__hint--error" : ""}`, children: scheduleFrequencyError ?? "Minutes." })
32387
32426
  ] }),
32388
32427
  /* @__PURE__ */ jsxs2("div", { className: "ghsync__schedule-meta", children: [
32389
- /* @__PURE__ */ jsx2("span", { className: "ghsync__scope-pill ghsync__scope-pill--global", children: "Shared" }),
32428
+ /* @__PURE__ */ jsx2("span", { className: "ghsync__scope-pill ghsync__scope-pill--global", children: "Company" }),
32390
32429
  /* @__PURE__ */ jsxs2("strong", { children: [
32391
32430
  "Auto-sync ",
32392
32431
  scheduleDescription
32393
32432
  ] }),
32394
- /* @__PURE__ */ jsx2("span", { children: "All companies." })
32433
+ /* @__PURE__ */ jsxs2("span", { children: [
32434
+ "Used for sync in ",
32435
+ currentCompanyName,
32436
+ "."
32437
+ ] })
32395
32438
  ] })
32396
32439
  ] }),
32397
32440
  !syncUnlocked ? /* @__PURE__ */ jsxs2("div", { className: "ghsync__locked", children: [
@@ -33436,7 +33479,7 @@ function GitHubSyncIssueDetailTabContent(props) {
33436
33479
  ] }) : null
33437
33480
  ] });
33438
33481
  }
33439
- function GitHubSyncIssueDetailTab() {
33482
+ function GitHubSyncIssueTaskDetailView() {
33440
33483
  const context = useHostContext();
33441
33484
  const themeMode = useResolvedThemeMode();
33442
33485
  const theme = themeMode === "light" ? LIGHT_PALETTE : DARK_PALETTE;
@@ -33459,6 +33502,7 @@ function GitHubSyncIssueDetailTab() {
33459
33502
  detailKey
33460
33503
  );
33461
33504
  }
33505
+ var GitHubSyncIssueDetailTab = GitHubSyncIssueTaskDetailView;
33462
33506
  function GitHubSyncCommentAnnotation() {
33463
33507
  const context = useHostContext();
33464
33508
  const themeMode = useResolvedThemeMode();
@@ -33504,6 +33548,7 @@ export {
33504
33548
  GitHubSyncEntityToolbarButton,
33505
33549
  GitHubSyncGlobalToolbarButton,
33506
33550
  GitHubSyncIssueDetailTab,
33551
+ GitHubSyncIssueTaskDetailView,
33507
33552
  GitHubSyncProjectPullRequestsPage,
33508
33553
  GitHubSyncProjectPullRequestsSidebarItem,
33509
33554
  GitHubSyncSettingsPage,