paperclip-github-plugin 0.9.1 → 0.9.3

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
@@ -196,6 +196,7 @@ The plugin is designed to avoid persisting raw credentials in plugin state.
196
196
  - 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.
197
197
  - 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.
198
198
  - The worker resolves those secret references at runtime instead of storing raw tokens in plugin state.
199
+ - 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.
199
200
  - On authenticated Paperclip deployments, sync is blocked until the relevant company has connected Paperclip board access.
200
201
  - 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.
201
202
 
@@ -208,6 +209,9 @@ If Paperclip-managed secrets are not available, the worker can read a local fall
208
209
  "githubToken": "ghp_your_token_here",
209
210
  "githubTokensByCompanyId": {
210
211
  "company-uuid": "ghp_company_specific_token_here"
212
+ },
213
+ "paperclipBoardApiTokensByCompanyId": {
214
+ "company-uuid": "paperclip_board_api_token_here"
211
215
  }
212
216
  }
213
217
  ```
@@ -217,6 +221,7 @@ Notes:
217
221
  - This file is read by the worker only.
218
222
  - The raw token is never persisted back into plugin state or plugin config.
219
223
  - 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.
224
+ - 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.
220
225
  - 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.
221
226
 
222
227
  ### Worker-facing Paperclip API URL
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.1"?.trim() || typeof packageJson.version === "string" && packageJson.version.trim() || process.env.npm_package_version?.trim() || "0.0.0-dev";
645
+ var MANIFEST_VERSION = "0.9.3"?.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
@@ -28397,6 +28397,51 @@ async function resolveOrCreateCompanySecret(companyId, name2, value) {
28397
28397
  })
28398
28398
  });
28399
28399
  }
28400
+ function isPluginSecretReferencesDisabledError(error) {
28401
+ return getActionErrorMessage(error, "").toLowerCase().includes("plugin secret references are disabled");
28402
+ }
28403
+ var PLUGIN_CONFIG_SECRET_KEY_PATTERN = /(?:secret|token).*ref|ref.*(?:secret|token)|secretid|token$/iu;
28404
+ function isPlainConfigRecord(value) {
28405
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
28406
+ }
28407
+ function shouldStripPluginConfigValue(key, value) {
28408
+ const normalizedKey = key.replace(/[^a-z0-9]/giu, "").toLowerCase();
28409
+ if (PLUGIN_CONFIG_SECRET_KEY_PATTERN.test(normalizedKey)) {
28410
+ return true;
28411
+ }
28412
+ if (!isPlainConfigRecord(value)) {
28413
+ return false;
28414
+ }
28415
+ return value.type === "secret_ref" || typeof value.secretId === "string";
28416
+ }
28417
+ function stripPluginSecretRefValue(value) {
28418
+ if (Array.isArray(value)) {
28419
+ return value.map((entry) => stripPluginSecretRefValue(entry));
28420
+ }
28421
+ if (!isPlainConfigRecord(value)) {
28422
+ return value;
28423
+ }
28424
+ const nextEntries = [];
28425
+ for (const [key, entryValue] of Object.entries(value)) {
28426
+ if (shouldStripPluginConfigValue(key, entryValue)) {
28427
+ continue;
28428
+ }
28429
+ const nextValue = stripPluginSecretRefValue(entryValue);
28430
+ nextEntries.push([key, nextValue]);
28431
+ }
28432
+ return Object.fromEntries(nextEntries);
28433
+ }
28434
+ function stripPluginSecretRefConfig(config) {
28435
+ return stripPluginSecretRefValue(config);
28436
+ }
28437
+ async function writePluginConfig(pluginId, config) {
28438
+ await fetchJson(`/api/plugins/${pluginId}/config`, {
28439
+ method: "POST",
28440
+ body: JSON.stringify({
28441
+ configJson: config
28442
+ })
28443
+ });
28444
+ }
28400
28445
  async function patchPluginConfig(pluginId, patch5) {
28401
28446
  const currentConfigResponse = await fetchJson(`/api/plugins/${pluginId}/config`);
28402
28447
  const currentConfig = normalizePluginConfig(currentConfigResponse?.configJson);
@@ -28404,12 +28449,18 @@ async function patchPluginConfig(pluginId, patch5) {
28404
28449
  if (JSON.stringify(nextConfig) === JSON.stringify(currentConfig)) {
28405
28450
  return;
28406
28451
  }
28407
- await fetchJson(`/api/plugins/${pluginId}/config`, {
28408
- method: "POST",
28409
- body: JSON.stringify({
28410
- configJson: nextConfig
28411
- })
28412
- });
28452
+ try {
28453
+ await writePluginConfig(pluginId, nextConfig);
28454
+ } catch (error) {
28455
+ if (!isPluginSecretReferencesDisabledError(error)) {
28456
+ throw error;
28457
+ }
28458
+ const safeConfig = stripPluginSecretRefConfig(nextConfig);
28459
+ if (JSON.stringify(safeConfig) === JSON.stringify(nextConfig)) {
28460
+ throw error;
28461
+ }
28462
+ await writePluginConfig(pluginId, safeConfig);
28463
+ }
28413
28464
  }
28414
28465
  var GITHUB_TOKEN_PROPAGATION_CONCURRENCY_LIMIT = 4;
28415
28466
  function normalizeAgentAdapterConfig(value) {
@@ -32769,6 +32820,7 @@ function GitHubSyncSettingsPage() {
32769
32820
  await updateBoardAccess({
32770
32821
  companyId,
32771
32822
  paperclipBoardApiTokenRef: secret.id,
32823
+ paperclipBoardApiToken: boardApiToken,
32772
32824
  paperclipBoardAccessIdentity: boardIdentity.label ?? "",
32773
32825
  paperclipBoardAccessUserId: boardIdentity.userId ?? ""
32774
32826
  });
@@ -35232,6 +35284,7 @@ export {
35232
35284
  GitHubSyncSettingsPage,
35233
35285
  clearGitHubTokenConfigSyncAttemptOnFailure,
35234
35286
  index_default as default,
35287
+ patchPluginConfig,
35235
35288
  resolveGitHubIssueDetailTabState,
35236
35289
  resolveOrCreateProject,
35237
35290
  resolvePreviewPersonLabels,