paperclip-github-plugin 0.8.7 → 0.8.9
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 +40 -3
- package/dist/manifest.js +60 -1
- package/dist/ui/index.js +71 -10
- package/dist/ui/index.js.map +2 -2
- package/dist/worker.js +722 -81
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -72,6 +72,12 @@ Because GitHub alone cannot tell which pull requests came from a Paperclip compa
|
|
|
72
72
|
|
|
73
73
|
That API route path matters on authenticated Paperclip deployments today because a current host bug blocks agents from calling plugin tools unless the instance runs in `local_trusted` mode. Those agents can still use `gh` with the propagated `GITHUB_TOKEN`, then call the agent-authenticated plugin API route from the shell after they create a PR. The Paperclip host authenticates `Authorization: Bearer <PAPERCLIP_API_KEY>`, scopes the request to the calling agent's company, and rejects anonymous or non-agent calls before dispatching to the worker.
|
|
74
74
|
|
|
75
|
+
### Third-party issue links
|
|
76
|
+
|
|
77
|
+
Sometimes a Paperclip issue is implemented through a GitHub issue or pull request in a repository that is not mapped to any Paperclip project. GitHub Sync can now track those targeted links without enrolling the whole repository in sync. The `link_github_item` agent tool records the durable link on local-trusted Paperclip instances, and authenticated agent runs can post the same link to `/api/plugins/paperclip-github-plugin/api/issue-link` with `PAPERCLIP_API_KEY` when plugin tools are blocked by host board-auth gates.
|
|
78
|
+
|
|
79
|
+
Third-party links are company-scoped and sync only the linked GitHub issue or pull request. Future manual or scheduled company sync runs refresh those external records and update the Paperclip issue status from the same CI, mergeability, review, thread, and issue-state rules used for mapped repositories.
|
|
80
|
+
|
|
75
81
|
### Project pull request command center
|
|
76
82
|
|
|
77
83
|
Each mapped project can expose a **Pull Requests** entry in the sidebar that opens a live GitHub queue page for that repository. The sidebar badge uses a lightweight total-count read, while the queue keeps the default view fast by loading only the current 10-row page, uses a repo-wide metrics read for the summary cards, reuses that cached metrics scan to keep filtered views fast by fetching only the visible filtered rows, keeps repo-scoped count, metrics, and per-PR review/check insight caches warm for repeat visits, lets operators explicitly bust those caches with Refresh when they want a live reread, shows total, mergeable, reviewable, and failing cards that filter the table, only treats a pull request as mergeable when it targets the current default branch with green checks, at least one approval, no outstanding change requests, and no unresolved review threads, includes an **Up to date** column that distinguishes current branches, clean update candidates, conflict cases, and unknown freshness when GitHub cannot confirm the comparison, shows the PR target branch with a highlighted default-branch badge, keeps the list sorted by most recently updated first, paginates larger repositories, keeps a compact bottom detail pane with markdown-and-HTML-rendered conversation plus an inline comment composer, supports deterministic **Update branch** actions for clean behind-base pull requests, adds Copilot quick actions that post `@copilot` requests for **Fix CI**, **Rebase**, and **Address review feedback**, requests Copilot through GitHub’s native reviewer flow for **Review**, keeps comment, review, quick approve/request-changes, re-run CI, merge, and close actions available, lets the review modal submit comment-only, approve, or request-changes reviews, hides any pull request action whose required GitHub permission is not verified for the saved token, and opens linked Paperclip issues in a plugin-provided right drawer so operators can stay on the queue page.
|
|
@@ -93,6 +99,7 @@ Linked Paperclip issues can also be unlinked from the GitHub detail surface. Unl
|
|
|
93
99
|
### Agent workflows built in
|
|
94
100
|
|
|
95
101
|
Paperclip agents can search GitHub for duplicates, read and update issues, assign issues to the saved token owner, post comments, create pull requests, inspect changed files and CI, reply to review threads, resolve or unresolve threads, request reviewers, search org-level GitHub Projects, and associate pull requests with those projects without leaving the Paperclip plugin surface.
|
|
102
|
+
They can also link a Paperclip issue to a GitHub issue or pull request in any accessible repository with `link_github_item`, including third-party repositories that are not mapped to a Paperclip project.
|
|
96
103
|
|
|
97
104
|
## Requirements
|
|
98
105
|
|
|
@@ -208,6 +215,12 @@ Notes:
|
|
|
208
215
|
- The raw token is never persisted back into plugin state or plugin config.
|
|
209
216
|
- A GitHub token secret saved through the settings UI takes precedence over the local file.
|
|
210
217
|
|
|
218
|
+
### Worker-facing Paperclip API URL
|
|
219
|
+
|
|
220
|
+
GitHub Sync uses direct worker-side Paperclip REST calls for host paths that are not fully covered by the plugin SDK, such as label reconciliation and some issue repair paths. By default, manual setup and sync actions use the current browser origin. If that origin is not reachable from the plugin worker, set **Worker Paperclip API URL** in GitHub Sync settings; the value is saved internally as plugin config `paperclipApiBaseUrl`.
|
|
221
|
+
|
|
222
|
+
For private LAN, Docker, Kubernetes, custom DNS, or self-signed-certificate deployments, set that field to a local route the plugin worker can reach, such as `http://localhost:3100`, and port-forward or route that address to the Paperclip API when needed. Do not rely on exporting a process environment variable named `PAPERCLIP_API_URL` to Paperclip; Paperclip `2026.428.0` does not pass that value through to plugin worker runtime.
|
|
223
|
+
|
|
211
224
|
## GitHub agent tools
|
|
212
225
|
|
|
213
226
|
The plugin exposes GitHub workflow tools to Paperclip agents, including:
|
|
@@ -259,16 +272,40 @@ Current host caveat: on authenticated Paperclip deployments, the Paperclip host
|
|
|
259
272
|
|
|
260
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.
|
|
261
274
|
|
|
275
|
+
### Issue link API route
|
|
276
|
+
|
|
277
|
+
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.
|
|
278
|
+
|
|
279
|
+
Supported payload fields:
|
|
280
|
+
|
|
281
|
+
- `paperclipIssueId` required: the Paperclip issue id to link
|
|
282
|
+
- `kind` optional: `issue` or `pull_request`; omitted values are inferred from full GitHub URLs when possible
|
|
283
|
+
- `reference` optional: a GitHub issue or pull request number, or a full GitHub URL
|
|
284
|
+
- `repository` optional: `owner/repo` or `https://github.com/owner/repo`, required for number-only references when the issue project is not mapped to that repository
|
|
285
|
+
- `issueNumber`, `pullRequestNumber`, or `pullRequestUrl` optional alternatives to `reference`
|
|
286
|
+
|
|
287
|
+
Example:
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
payload='{"paperclipIssueId":"iss_123","pullRequestUrl":"https://github.com/third-party/external/pull/77"}'
|
|
291
|
+
|
|
292
|
+
curl -X POST "${PAPERCLIP_API_URL%/}/api/plugins/paperclip-github-plugin/api/issue-link" \
|
|
293
|
+
-H "content-type: application/json" \
|
|
294
|
+
-H "authorization: Bearer ${PAPERCLIP_API_KEY}" \
|
|
295
|
+
-d "${payload}"
|
|
296
|
+
```
|
|
297
|
+
|
|
262
298
|
## Troubleshooting
|
|
263
299
|
|
|
264
300
|
- If an older GitHub Sync build fails upgrade with `requires host version 2026.427.0 or newer, but this server is running 0.0.0`, upgrade to a build that removes the strict manifest host-version gate. The host is reporting a development-version sentinel, so the plugin now relies on declared capabilities and runtime fallbacks instead.
|
|
265
|
-
- If setup is reported as incomplete, confirm that a GitHub token has been saved or that `${PAPERCLIP_HOME:-~/.paperclip}/plugins/github-sync/config.json` contains `githubToken`, and make sure at least one mapping has a created Paperclip project.
|
|
301
|
+
- If setup is reported as incomplete, confirm that a GitHub token has been saved or that `${PAPERCLIP_HOME:-~/.paperclip}/plugins/github-sync/config.json` contains `githubToken`, and make sure at least one mapping has a created Paperclip project or at least one Paperclip issue has been linked to GitHub.
|
|
266
302
|
- If Paperclip says board access is required, open plugin settings inside the affected company and complete the Paperclip board access flow before retrying sync.
|
|
267
303
|
- If GitHub Sync agent tools fail with `403 {"error":"Board access required"}` on `/api/plugins/tools` or `/api/plugins/tools/execute`, the current Paperclip host rejected the request before the plugin worker ran. Re-run from a board-authenticated session or agent run that has board access to the target company.
|
|
268
304
|
- If a KPI API route call is rejected, make sure the request includes `Authorization: Bearer ${PAPERCLIP_API_KEY}`, that the token is still valid for the current run, and that any `companyId` in the payload matches the calling agent's company.
|
|
269
|
-
- If the worker reaches an authenticated HTML page instead of the Paperclip API JSON responses it expects, connect Paperclip board access for that company or set
|
|
305
|
+
- If the worker reaches an authenticated HTML page instead of the Paperclip API JSON responses it expects, connect Paperclip board access for that company or set **Worker Paperclip API URL** in GitHub Sync settings to a worker-accessible Paperclip API origin.
|
|
306
|
+
- If a Paperclip API fetch fails before any HTTP response is returned, the saved diagnostics include the method, URL, primary error, nested cause, and cause code when Node exposes them.
|
|
270
307
|
- If a sync run finishes with partial failures, open the saved troubleshooting panel in GitHub Sync to inspect the repository, issue number, raw error, and suggested fix for each recorded failure.
|
|
271
|
-
- If sync says the Paperclip API URL is not trusted,
|
|
308
|
+
- If sync says the Paperclip API URL is not trusted, set **Worker Paperclip API URL** in GitHub Sync settings to the worker-accessible Paperclip API origin and retry.
|
|
272
309
|
- If a pull request comment or review action is rejected, read the full toast message. Fine-grained GitHub tokens need write access to that repository, and GitHub requires a review summary when requesting changes.
|
|
273
310
|
- If a GitHub-linked project does not show the **Pull requests** sidebar entry, reopen the plugin settings and re-save the mapping. The project pull request surfaces also recover older mappings when saved ids are missing, and they can fall back to the active project's bound GitHub repository when the project already has a GitHub workspace configured.
|
|
274
311
|
- If GitHub rate limiting is hit, the plugin pauses sync until the reported reset time instead of retrying pointlessly.
|
package/dist/manifest.js
CHANGED
|
@@ -522,6 +522,55 @@ var GITHUB_AGENT_TOOLS = [
|
|
|
522
522
|
projectNumber: projectNumberProperty
|
|
523
523
|
}
|
|
524
524
|
}
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
name: "link_github_item",
|
|
528
|
+
displayName: "Link GitHub Item",
|
|
529
|
+
description: "Link a Paperclip issue to a GitHub issue or pull request so GitHub Sync can monitor status even when the repository is not mapped to a Paperclip project.",
|
|
530
|
+
parametersSchema: {
|
|
531
|
+
type: "object",
|
|
532
|
+
additionalProperties: false,
|
|
533
|
+
required: ["kind", "paperclipIssueId"],
|
|
534
|
+
anyOf: [
|
|
535
|
+
{
|
|
536
|
+
required: ["reference"]
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
required: ["issueNumber"]
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
required: ["pullRequestNumber"]
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
required: ["pullRequestUrl"]
|
|
546
|
+
}
|
|
547
|
+
],
|
|
548
|
+
properties: {
|
|
549
|
+
kind: {
|
|
550
|
+
type: "string",
|
|
551
|
+
enum: ["issue", "pull_request"],
|
|
552
|
+
description: "Whether to link a GitHub issue or pull request."
|
|
553
|
+
},
|
|
554
|
+
paperclipIssueId: {
|
|
555
|
+
type: "string",
|
|
556
|
+
description: "Paperclip issue id that should receive the GitHub link."
|
|
557
|
+
},
|
|
558
|
+
repository: {
|
|
559
|
+
type: "string",
|
|
560
|
+
description: "GitHub repository as owner/repo or https://github.com/owner/repo. Required when using a number instead of a full GitHub URL and the Paperclip issue project is not mapped to that repository."
|
|
561
|
+
},
|
|
562
|
+
reference: {
|
|
563
|
+
type: "string",
|
|
564
|
+
description: "GitHub issue or pull request number, or a full GitHub issue or pull request URL."
|
|
565
|
+
},
|
|
566
|
+
issueNumber: issueNumberProperty,
|
|
567
|
+
pullRequestNumber: pullRequestNumberProperty,
|
|
568
|
+
pullRequestUrl: {
|
|
569
|
+
type: "string",
|
|
570
|
+
description: "Full GitHub pull request URL."
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
525
574
|
}
|
|
526
575
|
];
|
|
527
576
|
|
|
@@ -530,12 +579,15 @@ var GITHUB_SYNC_PLUGIN_ID = "paperclip-github-plugin";
|
|
|
530
579
|
var COMPANY_METRIC_API_ROUTE_KEY = "record-company-metric-event";
|
|
531
580
|
var COMPANY_METRIC_API_ROUTE_PATH = "/company-metrics/events";
|
|
532
581
|
var COMPANY_METRIC_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_ID}/api${COMPANY_METRIC_API_ROUTE_PATH}`;
|
|
582
|
+
var ISSUE_LINK_API_ROUTE_KEY = "link-github-item";
|
|
583
|
+
var ISSUE_LINK_API_ROUTE_PATH = "/issue-link";
|
|
584
|
+
var ISSUE_LINK_API_ROUTE_URL_PATH = `/api/plugins/${GITHUB_SYNC_PLUGIN_ID}/api${ISSUE_LINK_API_ROUTE_PATH}`;
|
|
533
585
|
|
|
534
586
|
// src/manifest.ts
|
|
535
587
|
var require2 = createRequire(import.meta.url);
|
|
536
588
|
var packageJson = require2("../package.json");
|
|
537
589
|
var SCHEDULE_TICK_CRON = "* * * * *";
|
|
538
|
-
var MANIFEST_VERSION = "0.8.
|
|
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";
|
|
539
591
|
var manifest = {
|
|
540
592
|
id: GITHUB_SYNC_PLUGIN_ID,
|
|
541
593
|
apiVersion: 1,
|
|
@@ -607,6 +659,13 @@ var manifest = {
|
|
|
607
659
|
path: COMPANY_METRIC_API_ROUTE_PATH,
|
|
608
660
|
auth: "agent",
|
|
609
661
|
capability: "api.routes.register"
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
routeKey: ISSUE_LINK_API_ROUTE_KEY,
|
|
665
|
+
method: "POST",
|
|
666
|
+
path: ISSUE_LINK_API_ROUTE_PATH,
|
|
667
|
+
auth: "agent",
|
|
668
|
+
capability: "api.routes.register"
|
|
610
669
|
}
|
|
611
670
|
],
|
|
612
671
|
tools: GITHUB_AGENT_TOOLS,
|
package/dist/ui/index.js
CHANGED
|
@@ -22694,6 +22694,9 @@ function normalizePluginConfig(value) {
|
|
|
22694
22694
|
}
|
|
22695
22695
|
return record;
|
|
22696
22696
|
}
|
|
22697
|
+
function resolvePaperclipApiBaseUrlForPluginAction(value, fallbackOrigin) {
|
|
22698
|
+
return normalizePluginConfig(value).paperclipApiBaseUrl ?? normalizePaperclipApiBaseUrl(fallbackOrigin);
|
|
22699
|
+
}
|
|
22697
22700
|
function mergePluginConfig(currentValue, patch5) {
|
|
22698
22701
|
const current = normalizePluginConfig(currentValue);
|
|
22699
22702
|
const currentGitHubTokenRefs = normalizePluginConfigGitHubTokenRefs(current.githubTokenRefs);
|
|
@@ -27577,7 +27580,7 @@ function shouldAutofillProjectName(mapping) {
|
|
|
27577
27580
|
const previousSuggestedProjectName = formatProjectNameFromRepository(mapping.repositoryUrl);
|
|
27578
27581
|
return previousSuggestedProjectName !== "" && currentProjectName === previousSuggestedProjectName;
|
|
27579
27582
|
}
|
|
27580
|
-
function
|
|
27583
|
+
function getPaperclipApiBrowserOrigin() {
|
|
27581
27584
|
if (typeof window === "undefined" || !window.location?.origin) {
|
|
27582
27585
|
return void 0;
|
|
27583
27586
|
}
|
|
@@ -27616,23 +27619,22 @@ async function fetchPluginDataResult(params) {
|
|
|
27616
27619
|
return response.data;
|
|
27617
27620
|
}
|
|
27618
27621
|
async function syncTrustedPaperclipApiBaseUrl(pluginId) {
|
|
27619
|
-
const paperclipApiBaseUrl = getPaperclipApiBaseUrl();
|
|
27620
|
-
if (!paperclipApiBaseUrl) {
|
|
27621
|
-
return void 0;
|
|
27622
|
-
}
|
|
27623
27622
|
const resolvedPluginId = await resolveCurrentPluginId(pluginId);
|
|
27624
27623
|
if (!resolvedPluginId) {
|
|
27625
27624
|
throw new Error(
|
|
27626
27625
|
"Unable to sync the trusted Paperclip API origin because the plugin ID is missing. Reload the plugin and try again before saving or syncing."
|
|
27627
27626
|
);
|
|
27628
27627
|
}
|
|
27628
|
+
const currentConfigResponse = await fetchJson(`/api/plugins/${resolvedPluginId}/config`);
|
|
27629
|
+
const currentConfig = normalizePluginConfig(currentConfigResponse?.configJson);
|
|
27630
|
+
const paperclipApiBaseUrl = resolvePaperclipApiBaseUrlForPluginAction(currentConfig, getPaperclipApiBrowserOrigin());
|
|
27631
|
+
if (!paperclipApiBaseUrl) {
|
|
27632
|
+
return void 0;
|
|
27633
|
+
}
|
|
27629
27634
|
const lastSyncedPaperclipApiBaseUrl = syncedPaperclipApiBaseUrlsByPluginId.get(resolvedPluginId);
|
|
27630
27635
|
if (lastSyncedPaperclipApiBaseUrl === paperclipApiBaseUrl) {
|
|
27631
27636
|
return paperclipApiBaseUrl;
|
|
27632
27637
|
}
|
|
27633
|
-
await patchPluginConfig(resolvedPluginId, {
|
|
27634
|
-
paperclipApiBaseUrl
|
|
27635
|
-
});
|
|
27636
27638
|
syncedPaperclipApiBaseUrlsByPluginId.set(resolvedPluginId, paperclipApiBaseUrl);
|
|
27637
27639
|
return paperclipApiBaseUrl;
|
|
27638
27640
|
}
|
|
@@ -31964,6 +31966,7 @@ function GitHubSyncSettingsPage() {
|
|
|
31964
31966
|
const [cancellingSync, setCancellingSync] = useState2(false);
|
|
31965
31967
|
const [manualSyncRequestError, setManualSyncRequestError] = useState2(null);
|
|
31966
31968
|
const [scheduleFrequencyDraft, setScheduleFrequencyDraft] = useState2(String(DEFAULT_SCHEDULE_FREQUENCY_MINUTES));
|
|
31969
|
+
const [paperclipApiBaseUrlDraft, setPaperclipApiBaseUrlDraft] = useState2("");
|
|
31967
31970
|
const [ignoredAuthorsDraft, setIgnoredAuthorsDraft] = useState2(DEFAULT_ADVANCED_SETTINGS.ignoredIssueAuthorUsernames.join(", "));
|
|
31968
31971
|
const [tokenStatusOverride, setTokenStatusOverride] = useState2(null);
|
|
31969
31972
|
const [validatedLogin, setValidatedLogin] = useState2(null);
|
|
@@ -32012,6 +32015,9 @@ function GitHubSyncSettingsPage() {
|
|
|
32012
32015
|
updatedAt: settings.data.updatedAt
|
|
32013
32016
|
});
|
|
32014
32017
|
setScheduleFrequencyDraft(String(nextScheduleFrequencyMinutes));
|
|
32018
|
+
setPaperclipApiBaseUrlDraft(
|
|
32019
|
+
settings.data.paperclipApiBaseUrlConfigured ? settings.data.paperclipApiBaseUrl ?? "" : getPaperclipApiBrowserOrigin() ?? ""
|
|
32020
|
+
);
|
|
32015
32021
|
setIgnoredAuthorsDraft(normalizeAdvancedSettings(settings.data.advancedSettings).ignoredIssueAuthorUsernames.join(", "));
|
|
32016
32022
|
setTokenDraft("");
|
|
32017
32023
|
setValidatedLogin(tokenUiState.validatedLogin);
|
|
@@ -32283,12 +32289,21 @@ function GitHubSyncSettingsPage() {
|
|
|
32283
32289
|
const scheduleFrequencyMinutes = parseScheduleFrequencyDraft(scheduleFrequencyDraft) ?? form.scheduleFrequencyMinutes;
|
|
32284
32290
|
const savedScheduleFrequencyMinutes = normalizeScheduleFrequencyMinutes(currentSettings?.scheduleFrequencyMinutes);
|
|
32285
32291
|
const scheduleDirty = scheduleFrequencyError === null && scheduleFrequencyMinutes !== savedScheduleFrequencyMinutes;
|
|
32292
|
+
const browserPaperclipApiBaseUrl = getPaperclipApiBrowserOrigin();
|
|
32293
|
+
const normalizedPaperclipApiBaseUrlDraft = normalizePaperclipApiBaseUrl(paperclipApiBaseUrlDraft);
|
|
32294
|
+
const paperclipApiBaseUrlError = paperclipApiBaseUrlDraft.trim() && !normalizedPaperclipApiBaseUrlDraft ? "Enter a valid URL." : null;
|
|
32295
|
+
const effectivePaperclipApiBaseUrl = normalizedPaperclipApiBaseUrlDraft ?? browserPaperclipApiBaseUrl;
|
|
32296
|
+
const paperclipApiBaseUrlIsOverride = Boolean(
|
|
32297
|
+
normalizedPaperclipApiBaseUrlDraft && browserPaperclipApiBaseUrl && normalizedPaperclipApiBaseUrlDraft !== browserPaperclipApiBaseUrl
|
|
32298
|
+
);
|
|
32299
|
+
const savedPaperclipApiBaseUrl = currentSettings?.paperclipApiBaseUrlConfigured ? currentSettings.paperclipApiBaseUrl ?? "" : browserPaperclipApiBaseUrl ?? "";
|
|
32300
|
+
const paperclipApiBaseUrlDirty = paperclipApiBaseUrlError === null && (normalizedPaperclipApiBaseUrlDraft ?? "") !== savedPaperclipApiBaseUrl;
|
|
32286
32301
|
const mappings = form.mappings.length > 0 ? form.mappings : [createEmptyMapping(0)];
|
|
32287
32302
|
const settingsMutationsLocked = syncInFlight;
|
|
32288
32303
|
const settingsMutationsLockReason = settingsMutationsLocked ? "Settings are temporarily locked while a sync is running to avoid overwriting local edits." : null;
|
|
32289
32304
|
const syncStatus = getSyncStatus(displaySyncState, runningSync, syncUnlocked);
|
|
32290
32305
|
const canSaveToken = hasCompanyContext && !settingsMutationsLocked && !submittingToken && !showInitialLoadingState && tokenDraft.trim().length > 0;
|
|
32291
|
-
const canSaveSetup = hasCompanyContext && repositoriesUnlocked && !settingsMutationsLocked && !submittingSetup && !showInitialLoadingState && scheduleFrequencyError === null && (mappingsDirty || advancedSettingsDirty || scheduleDirty);
|
|
32306
|
+
const canSaveSetup = hasCompanyContext && repositoriesUnlocked && !settingsMutationsLocked && !submittingSetup && !showInitialLoadingState && scheduleFrequencyError === null && paperclipApiBaseUrlError === null && (mappingsDirty || advancedSettingsDirty || scheduleDirty || paperclipApiBaseUrlDirty);
|
|
32292
32307
|
const canConnectBoardAccess = hasCompanyContext && !settingsMutationsLocked && !connectingBoardAccess && !showInitialLoadingState;
|
|
32293
32308
|
const boardAccessStatusLabel = !hasCompanyContext ? "Unavailable" : boardAccessBannerLabel;
|
|
32294
32309
|
const boardAccessStatusTone = !hasCompanyContext ? boardAccessRequired ? "warning" : "neutral" : boardAccessTone;
|
|
@@ -32693,6 +32708,9 @@ function GitHubSyncSettingsPage() {
|
|
|
32693
32708
|
if (scheduleFrequencyError) {
|
|
32694
32709
|
throw new Error(scheduleFrequencyError);
|
|
32695
32710
|
}
|
|
32711
|
+
if (paperclipApiBaseUrlError) {
|
|
32712
|
+
throw new Error(paperclipApiBaseUrlError);
|
|
32713
|
+
}
|
|
32696
32714
|
const resolvedMappings = [];
|
|
32697
32715
|
for (const mapping of form.mappings) {
|
|
32698
32716
|
const repositoryInput = mapping.repositoryUrl.trim();
|
|
@@ -32717,7 +32735,18 @@ function GitHubSyncSettingsPage() {
|
|
|
32717
32735
|
companyId
|
|
32718
32736
|
});
|
|
32719
32737
|
}
|
|
32720
|
-
const
|
|
32738
|
+
const pluginId = await resolveCurrentPluginId(pluginIdFromLocation);
|
|
32739
|
+
if (!pluginId) {
|
|
32740
|
+
throw new Error("Plugin id is required to save setup.");
|
|
32741
|
+
}
|
|
32742
|
+
const trustedPaperclipApiBaseUrl = effectivePaperclipApiBaseUrl;
|
|
32743
|
+
if (!trustedPaperclipApiBaseUrl) {
|
|
32744
|
+
throw new Error("Could not resolve the current browser origin for Paperclip API calls.");
|
|
32745
|
+
}
|
|
32746
|
+
const nextConfiguredPaperclipApiBaseUrl = paperclipApiBaseUrlIsOverride ? normalizedPaperclipApiBaseUrlDraft ?? "" : "";
|
|
32747
|
+
await patchPluginConfig(pluginId, {
|
|
32748
|
+
paperclipApiBaseUrl: nextConfiguredPaperclipApiBaseUrl
|
|
32749
|
+
});
|
|
32721
32750
|
const result = await saveRegistration({
|
|
32722
32751
|
companyId,
|
|
32723
32752
|
mappings: resolvedMappings,
|
|
@@ -32743,9 +32772,11 @@ function GitHubSyncSettingsPage() {
|
|
|
32743
32772
|
advancedSettings: normalizeAdvancedSettings(result.advancedSettings),
|
|
32744
32773
|
availableAssignees: result.availableAssignees ?? current.availableAssignees,
|
|
32745
32774
|
paperclipApiBaseUrl: result.paperclipApiBaseUrl,
|
|
32775
|
+
paperclipApiBaseUrlConfigured: paperclipApiBaseUrlIsOverride,
|
|
32746
32776
|
updatedAt: result.updatedAt
|
|
32747
32777
|
}));
|
|
32748
32778
|
setScheduleFrequencyDraft(String(normalizeScheduleFrequencyMinutes(result.scheduleFrequencyMinutes)));
|
|
32779
|
+
setPaperclipApiBaseUrlDraft(paperclipApiBaseUrlIsOverride ? nextConfiguredPaperclipApiBaseUrl : trustedPaperclipApiBaseUrl);
|
|
32749
32780
|
toast({
|
|
32750
32781
|
title: "GitHub sync setup saved",
|
|
32751
32782
|
body: `Advanced defaults, mappings, and automatic sync are saved for ${currentCompanyName}.`,
|
|
@@ -33423,6 +33454,32 @@ function GitHubSyncSettingsPage() {
|
|
|
33423
33454
|
] })
|
|
33424
33455
|
] })
|
|
33425
33456
|
] }),
|
|
33457
|
+
/* @__PURE__ */ jsxs2("div", { className: "ghsync__schedule-card", children: [
|
|
33458
|
+
/* @__PURE__ */ jsxs2("div", { className: "ghsync__field", children: [
|
|
33459
|
+
/* @__PURE__ */ jsx2("label", { htmlFor: "paperclip-api-base-url", children: "Worker Paperclip API URL" }),
|
|
33460
|
+
/* @__PURE__ */ jsx2(
|
|
33461
|
+
"input",
|
|
33462
|
+
{
|
|
33463
|
+
id: "paperclip-api-base-url",
|
|
33464
|
+
className: "ghsync__input",
|
|
33465
|
+
type: "url",
|
|
33466
|
+
value: paperclipApiBaseUrlDraft,
|
|
33467
|
+
disabled: settingsMutationsLocked || !hasCompanyContext,
|
|
33468
|
+
onChange: (event) => {
|
|
33469
|
+
setPaperclipApiBaseUrlDraft(event.currentTarget.value);
|
|
33470
|
+
},
|
|
33471
|
+
placeholder: browserPaperclipApiBaseUrl ?? "https://paperclip.example.com",
|
|
33472
|
+
autoComplete: "off"
|
|
33473
|
+
}
|
|
33474
|
+
),
|
|
33475
|
+
/* @__PURE__ */ jsx2("p", { className: `ghsync__hint${paperclipApiBaseUrlError ? " ghsync__hint--error" : ""}`, children: paperclipApiBaseUrlError ?? "Edit this only when the plugin worker needs a different Paperclip API origin." })
|
|
33476
|
+
] }),
|
|
33477
|
+
/* @__PURE__ */ jsxs2("div", { className: "ghsync__schedule-meta", children: [
|
|
33478
|
+
/* @__PURE__ */ jsx2("span", { className: "ghsync__scope-pill ghsync__scope-pill--global", children: "Worker" }),
|
|
33479
|
+
/* @__PURE__ */ jsx2("strong", { children: effectivePaperclipApiBaseUrl ?? "Browser origin unavailable" }),
|
|
33480
|
+
/* @__PURE__ */ jsx2("span", { children: paperclipApiBaseUrlIsOverride ? "Configured override." : "Using browser origin." })
|
|
33481
|
+
] })
|
|
33482
|
+
] }),
|
|
33426
33483
|
!syncUnlocked ? /* @__PURE__ */ jsxs2("div", { className: "ghsync__locked", children: [
|
|
33427
33484
|
/* @__PURE__ */ jsxs2("div", { children: [
|
|
33428
33485
|
/* @__PURE__ */ jsx2("strong", { children: syncSetupIssue === "missing_board_access" ? "Paperclip board access is required" : "Manual sync is locked" }),
|
|
@@ -33544,6 +33601,10 @@ function GitHubSyncSettingsPage() {
|
|
|
33544
33601
|
/* @__PURE__ */ jsx2("span", { className: "ghsync__detail-label", children: "Auto-sync" }),
|
|
33545
33602
|
/* @__PURE__ */ jsx2("strong", { className: "ghsync__detail-value", children: scheduleDescription })
|
|
33546
33603
|
] }),
|
|
33604
|
+
/* @__PURE__ */ jsxs2("div", { className: "ghsync__detail", children: [
|
|
33605
|
+
/* @__PURE__ */ jsx2("span", { className: "ghsync__detail-label", children: "Worker API" }),
|
|
33606
|
+
/* @__PURE__ */ jsx2("strong", { className: "ghsync__detail-value", children: effectivePaperclipApiBaseUrl })
|
|
33607
|
+
] }),
|
|
33547
33608
|
/* @__PURE__ */ jsxs2("div", { className: "ghsync__detail", children: [
|
|
33548
33609
|
/* @__PURE__ */ jsx2("span", { className: "ghsync__detail-label", children: "Last sync" }),
|
|
33549
33610
|
/* @__PURE__ */ jsx2("strong", { className: "ghsync__detail-value", children: lastSync })
|