paperclip-github-plugin 0.9.0 → 0.9.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 +11 -2
- package/dist/manifest.js +1 -1
- package/dist/ui/index.js +133 -8
- package/dist/ui/index.js.map +2 -2
- package/dist/worker.js +264 -6
- package/package.json +1 -1
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
|
|
|
@@ -205,7 +206,13 @@ If Paperclip-managed secrets are not available, the worker can read a local fall
|
|
|
205
206
|
|
|
206
207
|
```json
|
|
207
208
|
{
|
|
208
|
-
"githubToken": "ghp_your_token_here"
|
|
209
|
+
"githubToken": "ghp_your_token_here",
|
|
210
|
+
"githubTokensByCompanyId": {
|
|
211
|
+
"company-uuid": "ghp_company_specific_token_here"
|
|
212
|
+
},
|
|
213
|
+
"paperclipBoardApiTokensByCompanyId": {
|
|
214
|
+
"company-uuid": "paperclip_board_api_token_here"
|
|
215
|
+
}
|
|
209
216
|
}
|
|
210
217
|
```
|
|
211
218
|
|
|
@@ -213,7 +220,9 @@ Notes:
|
|
|
213
220
|
|
|
214
221
|
- This file is read by the worker only.
|
|
215
222
|
- The raw token is never persisted back into plugin state or plugin config.
|
|
216
|
-
- A GitHub token secret saved through the settings UI
|
|
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.
|
|
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.
|
|
217
226
|
|
|
218
227
|
### Worker-facing Paperclip API URL
|
|
219
228
|
|
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.
|
|
645
|
+
var MANIFEST_VERSION = "0.9.2"?.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
|
@@ -23165,6 +23165,9 @@ function resolveToolbarButtonState(params) {
|
|
|
23165
23165
|
syncStartPending
|
|
23166
23166
|
};
|
|
23167
23167
|
}
|
|
23168
|
+
function clearGitHubTokenConfigSyncAttemptOnFailure(currentAttemptKey, failedAttemptKey) {
|
|
23169
|
+
return currentAttemptKey === failedAttemptKey ? null : currentAttemptKey;
|
|
23170
|
+
}
|
|
23168
23171
|
function getSyncToastTitle(syncState) {
|
|
23169
23172
|
if (getActiveRateLimitPause(syncState)) {
|
|
23170
23173
|
return "GitHub sync is paused";
|
|
@@ -28394,6 +28397,25 @@ async function resolveOrCreateCompanySecret(companyId, name2, value) {
|
|
|
28394
28397
|
})
|
|
28395
28398
|
});
|
|
28396
28399
|
}
|
|
28400
|
+
function isPluginSecretReferencesDisabledError(error) {
|
|
28401
|
+
return getActionErrorMessage(error, "").toLowerCase().includes("plugin secret references are disabled");
|
|
28402
|
+
}
|
|
28403
|
+
function stripPluginSecretRefConfig(config) {
|
|
28404
|
+
const {
|
|
28405
|
+
githubTokenRefs: _githubTokenRefs,
|
|
28406
|
+
paperclipBoardApiTokenRefs: _paperclipBoardApiTokenRefs,
|
|
28407
|
+
...safeConfig
|
|
28408
|
+
} = config;
|
|
28409
|
+
return safeConfig;
|
|
28410
|
+
}
|
|
28411
|
+
async function writePluginConfig(pluginId, config) {
|
|
28412
|
+
await fetchJson(`/api/plugins/${pluginId}/config`, {
|
|
28413
|
+
method: "POST",
|
|
28414
|
+
body: JSON.stringify({
|
|
28415
|
+
configJson: config
|
|
28416
|
+
})
|
|
28417
|
+
});
|
|
28418
|
+
}
|
|
28397
28419
|
async function patchPluginConfig(pluginId, patch5) {
|
|
28398
28420
|
const currentConfigResponse = await fetchJson(`/api/plugins/${pluginId}/config`);
|
|
28399
28421
|
const currentConfig = normalizePluginConfig(currentConfigResponse?.configJson);
|
|
@@ -28401,12 +28423,18 @@ async function patchPluginConfig(pluginId, patch5) {
|
|
|
28401
28423
|
if (JSON.stringify(nextConfig) === JSON.stringify(currentConfig)) {
|
|
28402
28424
|
return;
|
|
28403
28425
|
}
|
|
28404
|
-
|
|
28405
|
-
|
|
28406
|
-
|
|
28407
|
-
|
|
28408
|
-
|
|
28409
|
-
|
|
28426
|
+
try {
|
|
28427
|
+
await writePluginConfig(pluginId, nextConfig);
|
|
28428
|
+
} catch (error) {
|
|
28429
|
+
if (!isPluginSecretReferencesDisabledError(error)) {
|
|
28430
|
+
throw error;
|
|
28431
|
+
}
|
|
28432
|
+
const safeConfig = stripPluginSecretRefConfig(nextConfig);
|
|
28433
|
+
if (JSON.stringify(safeConfig) === JSON.stringify(nextConfig)) {
|
|
28434
|
+
throw error;
|
|
28435
|
+
}
|
|
28436
|
+
await writePluginConfig(pluginId, safeConfig);
|
|
28437
|
+
}
|
|
28410
28438
|
}
|
|
28411
28439
|
var GITHUB_TOKEN_PROPAGATION_CONCURRENCY_LIMIT = 4;
|
|
28412
28440
|
function normalizeAgentAdapterConfig(value) {
|
|
@@ -28430,7 +28458,8 @@ function getAgentPropagationPatch(params) {
|
|
|
28430
28458
|
...currentEnv,
|
|
28431
28459
|
GITHUB_TOKEN: {
|
|
28432
28460
|
type: "secret_ref",
|
|
28433
|
-
secretId: params.githubTokenSecretRef
|
|
28461
|
+
secretId: params.githubTokenSecretRef,
|
|
28462
|
+
version: "latest"
|
|
28434
28463
|
}
|
|
28435
28464
|
};
|
|
28436
28465
|
if (JSON.stringify(nextEnv2) === JSON.stringify(currentEnv)) {
|
|
@@ -28485,7 +28514,8 @@ async function applyGitHubTokenPropagationUpdate(params) {
|
|
|
28485
28514
|
await fetchJson(`/api/agents/${params.agentId}`, {
|
|
28486
28515
|
method: "PATCH",
|
|
28487
28516
|
body: JSON.stringify({
|
|
28488
|
-
adapterConfig: nextAdapterConfig
|
|
28517
|
+
adapterConfig: nextAdapterConfig,
|
|
28518
|
+
replaceAdapterConfig: true
|
|
28489
28519
|
})
|
|
28490
28520
|
});
|
|
28491
28521
|
}
|
|
@@ -31968,6 +31998,7 @@ function GitHubSyncSettingsPage() {
|
|
|
31968
31998
|
const saveRegistration = usePluginAction("settings.saveRegistration");
|
|
31969
31999
|
const updateBoardAccess = usePluginAction("settings.updateBoardAccess");
|
|
31970
32000
|
const validateToken = usePluginAction("settings.validateToken");
|
|
32001
|
+
const ensureGitHubTokenAvailable = usePluginAction("settings.ensureGitHubTokenAvailable");
|
|
31971
32002
|
const runSyncNow = usePluginAction("sync.runNow");
|
|
31972
32003
|
const cancelSync = usePluginAction("sync.cancel");
|
|
31973
32004
|
const [form, setForm] = useState2(EMPTY_SETTINGS);
|
|
@@ -31995,6 +32026,7 @@ function GitHubSyncSettingsPage() {
|
|
|
31995
32026
|
const themeMode = useResolvedThemeMode();
|
|
31996
32027
|
const boardAccessRequirement = usePaperclipBoardAccessRequirement();
|
|
31997
32028
|
const armSyncCompletionToast = useSyncCompletionToast(form.syncState, toast);
|
|
32029
|
+
const githubTokenConfigSyncAttemptRef = useRef(null);
|
|
31998
32030
|
const boardAccessConfigSyncAttemptRef = useRef(null);
|
|
31999
32031
|
const assigneeFallbackAttemptRef = useRef(null);
|
|
32000
32032
|
const currentSettings = settings.data ?? cachedSettings;
|
|
@@ -32111,6 +32143,76 @@ function GitHubSyncSettingsPage() {
|
|
|
32111
32143
|
cancelled = true;
|
|
32112
32144
|
};
|
|
32113
32145
|
}, [currentSettings?.availableAssignees?.length, currentSettings?.updatedAt, hostContext.companyId]);
|
|
32146
|
+
useEffect2(() => {
|
|
32147
|
+
const companyId = hostContext.companyId;
|
|
32148
|
+
const secretRef = settings.data?.githubTokenNeedsConfigSync ? settings.data.githubTokenConfigSyncRef : void 0;
|
|
32149
|
+
if (!companyId || !secretRef) {
|
|
32150
|
+
return;
|
|
32151
|
+
}
|
|
32152
|
+
const attemptKey = `${companyId}:${secretRef}`;
|
|
32153
|
+
if (githubTokenConfigSyncAttemptRef.current === attemptKey) {
|
|
32154
|
+
return;
|
|
32155
|
+
}
|
|
32156
|
+
githubTokenConfigSyncAttemptRef.current = attemptKey;
|
|
32157
|
+
let cancelled = false;
|
|
32158
|
+
void (async () => {
|
|
32159
|
+
try {
|
|
32160
|
+
const pluginId = await resolveCurrentPluginId(pluginIdFromLocation);
|
|
32161
|
+
if (!pluginId) {
|
|
32162
|
+
throw new Error("Plugin id is required to finish syncing the GitHub token into plugin config.");
|
|
32163
|
+
}
|
|
32164
|
+
await patchPluginConfig(pluginId, {
|
|
32165
|
+
githubTokenRefs: {
|
|
32166
|
+
[companyId]: secretRef
|
|
32167
|
+
}
|
|
32168
|
+
});
|
|
32169
|
+
const selectedAgentIds = normalizeAgentIds(settings.data?.advancedSettings?.githubTokenPropagationAgentIds);
|
|
32170
|
+
if (selectedAgentIds.length > 0) {
|
|
32171
|
+
await propagateGitHubTokenToSelectedAgents({
|
|
32172
|
+
selectedAgentIds,
|
|
32173
|
+
previousAgentIds: selectedAgentIds,
|
|
32174
|
+
githubTokenSecretRef: secretRef
|
|
32175
|
+
});
|
|
32176
|
+
}
|
|
32177
|
+
if (cancelled) {
|
|
32178
|
+
return;
|
|
32179
|
+
}
|
|
32180
|
+
notifyGitHubSyncSettingsChanged();
|
|
32181
|
+
try {
|
|
32182
|
+
await settings.refresh();
|
|
32183
|
+
} catch {
|
|
32184
|
+
return;
|
|
32185
|
+
}
|
|
32186
|
+
} catch (error) {
|
|
32187
|
+
githubTokenConfigSyncAttemptRef.current = clearGitHubTokenConfigSyncAttemptOnFailure(
|
|
32188
|
+
githubTokenConfigSyncAttemptRef.current,
|
|
32189
|
+
attemptKey
|
|
32190
|
+
);
|
|
32191
|
+
if (cancelled) {
|
|
32192
|
+
return;
|
|
32193
|
+
}
|
|
32194
|
+
toast({
|
|
32195
|
+
title: "GitHub token needs reconnection",
|
|
32196
|
+
body: getActionErrorMessage(
|
|
32197
|
+
error,
|
|
32198
|
+
"GitHub Sync could not finish migrating the saved GitHub token into plugin config."
|
|
32199
|
+
),
|
|
32200
|
+
tone: "error"
|
|
32201
|
+
});
|
|
32202
|
+
}
|
|
32203
|
+
})();
|
|
32204
|
+
return () => {
|
|
32205
|
+
cancelled = true;
|
|
32206
|
+
};
|
|
32207
|
+
}, [
|
|
32208
|
+
hostContext.companyId,
|
|
32209
|
+
pluginIdFromLocation,
|
|
32210
|
+
settings.data?.advancedSettings?.githubTokenPropagationAgentIds,
|
|
32211
|
+
settings.data?.githubTokenNeedsConfigSync,
|
|
32212
|
+
settings.data?.githubTokenConfigSyncRef,
|
|
32213
|
+
settings.refresh,
|
|
32214
|
+
toast
|
|
32215
|
+
]);
|
|
32114
32216
|
useEffect2(() => {
|
|
32115
32217
|
const companyId = hostContext.companyId;
|
|
32116
32218
|
const secretRef = settings.data?.paperclipBoardAccessNeedsConfigSync ? settings.data.paperclipBoardAccessConfigSyncRef : void 0;
|
|
@@ -32581,6 +32683,16 @@ function GitHubSyncSettingsPage() {
|
|
|
32581
32683
|
},
|
|
32582
32684
|
githubTokenLogin: validation.login
|
|
32583
32685
|
});
|
|
32686
|
+
let availabilityWarning = null;
|
|
32687
|
+
try {
|
|
32688
|
+
await ensureGitHubTokenAvailable({
|
|
32689
|
+
companyId,
|
|
32690
|
+
githubTokenRef: secret.id,
|
|
32691
|
+
token: trimmedToken
|
|
32692
|
+
});
|
|
32693
|
+
} catch (error) {
|
|
32694
|
+
availabilityWarning = error;
|
|
32695
|
+
}
|
|
32584
32696
|
const selectedAgentIds = normalizeAgentIds(currentSettings?.advancedSettings?.githubTokenPropagationAgentIds);
|
|
32585
32697
|
let propagationError = null;
|
|
32586
32698
|
try {
|
|
@@ -32616,6 +32728,16 @@ function GitHubSyncSettingsPage() {
|
|
|
32616
32728
|
tone: "error"
|
|
32617
32729
|
});
|
|
32618
32730
|
}
|
|
32731
|
+
if (availabilityWarning) {
|
|
32732
|
+
toast({
|
|
32733
|
+
title: "GitHub token saved, but worker token access needs attention",
|
|
32734
|
+
body: getActionErrorMessage(
|
|
32735
|
+
availabilityWarning,
|
|
32736
|
+
"GitHub Sync could not verify worker access to the saved token."
|
|
32737
|
+
),
|
|
32738
|
+
tone: "error"
|
|
32739
|
+
});
|
|
32740
|
+
}
|
|
32619
32741
|
notifyGitHubSyncSettingsChanged();
|
|
32620
32742
|
try {
|
|
32621
32743
|
await settings.refresh();
|
|
@@ -32672,6 +32794,7 @@ function GitHubSyncSettingsPage() {
|
|
|
32672
32794
|
await updateBoardAccess({
|
|
32673
32795
|
companyId,
|
|
32674
32796
|
paperclipBoardApiTokenRef: secret.id,
|
|
32797
|
+
paperclipBoardApiToken: boardApiToken,
|
|
32675
32798
|
paperclipBoardAccessIdentity: boardIdentity.label ?? "",
|
|
32676
32799
|
paperclipBoardAccessUserId: boardIdentity.userId ?? ""
|
|
32677
32800
|
});
|
|
@@ -35133,7 +35256,9 @@ export {
|
|
|
35133
35256
|
GitHubSyncProjectPullRequestsPage,
|
|
35134
35257
|
GitHubSyncProjectPullRequestsSidebarItem,
|
|
35135
35258
|
GitHubSyncSettingsPage,
|
|
35259
|
+
clearGitHubTokenConfigSyncAttemptOnFailure,
|
|
35136
35260
|
index_default as default,
|
|
35261
|
+
patchPluginConfig,
|
|
35137
35262
|
resolveGitHubIssueDetailTabState,
|
|
35138
35263
|
resolveOrCreateProject,
|
|
35139
35264
|
resolvePreviewPersonLabels,
|