paperclip-github-plugin 0.4.1 → 0.4.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 +15 -16
- package/dist/manifest.js +4 -4
- package/dist/ui/index.js +34 -104
- package/dist/ui/index.js.map +2 -2
- package/dist/worker.js +474 -179
- package/package.json +1 -1
package/dist/worker.js
CHANGED
|
@@ -618,23 +618,27 @@ var CANCELLING_SYNC_MESSAGE = "Cancellation requested. GitHub sync will stop aft
|
|
|
618
618
|
var SYNC_PROGRESS_PERSIST_INTERVAL_MS = 250;
|
|
619
619
|
var MAX_SYNC_FAILURE_LOG_ENTRIES = 25;
|
|
620
620
|
var GITHUB_SECONDARY_RATE_LIMIT_FALLBACK_MS = 6e4;
|
|
621
|
+
var IMPORTED_ISSUE_WAKEUP_CONCURRENCY = 4;
|
|
621
622
|
var MISSING_GITHUB_TOKEN_SYNC_MESSAGE = "Configure a GitHub token before running sync.";
|
|
622
623
|
var MISSING_GITHUB_TOKEN_SYNC_ACTION = 'Open settings and save a GitHub token secret, or create $PAPERCLIP_HOME/plugins/github-sync/config.json (or ~/.paperclip/plugins/github-sync/config.json when PAPERCLIP_HOME is unset) with a "githubToken" value, and then run sync again.';
|
|
623
624
|
var MISSING_MAPPING_SYNC_MESSAGE = "Save at least one mapping with a created Paperclip project before running sync.";
|
|
624
625
|
var MISSING_MAPPING_SYNC_ACTION = "Open settings, add a repository mapping, let Paperclip create the target project, and then retry sync.";
|
|
625
626
|
var MISSING_BOARD_ACCESS_SYNC_MESSAGE = "Connect Paperclip board access before running sync on this authenticated deployment.";
|
|
626
627
|
var MISSING_BOARD_ACCESS_SYNC_ACTION = "Open plugin settings for each mapped company that sync will touch, connect Paperclip board access, approve the flow, and then run sync again.";
|
|
628
|
+
var IMPORTED_ISSUE_WAKE_REASON = "GitHub Sync imported an assigned issue that is ready for work.";
|
|
627
629
|
var ISSUE_LINK_ENTITY_TYPE = "paperclip-github-plugin.issue-link";
|
|
628
630
|
var PULL_REQUEST_LINK_ENTITY_TYPE = "paperclip-github-plugin.pull-request-link";
|
|
629
631
|
var COMMENT_ANNOTATION_ENTITY_TYPE = "paperclip-github-plugin.comment-annotation";
|
|
630
632
|
var AI_AUTHORED_COMMENT_FOOTER_PREFIX = "Created by a Paperclip AI agent using ";
|
|
631
633
|
var HIDDEN_GITHUB_IMPORT_MARKER_PREFIX = "<!-- paperclip-github-plugin-imported-from: ";
|
|
632
634
|
var HIDDEN_GITHUB_IMPORT_MARKER_SUFFIX = " -->";
|
|
635
|
+
var EMPTY_GITHUB_ISSUE_DESCRIPTION_PLACEHOLDER = "_No description provided on GitHub._";
|
|
633
636
|
function normalizeCompanyId(value) {
|
|
634
637
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
635
638
|
}
|
|
636
639
|
var activeSyncPromise = null;
|
|
637
640
|
var activeRunningSyncState = null;
|
|
641
|
+
var activeRunningSyncCompanyId;
|
|
638
642
|
var activePaperclipApiAuthTokensByCompanyId = null;
|
|
639
643
|
var activeExternalConfigWarningKey = null;
|
|
640
644
|
var activeProjectPullRequestPageCache = /* @__PURE__ */ new Map();
|
|
@@ -710,6 +714,7 @@ var FAILED_STATUS_CONTEXT_STATES = /* @__PURE__ */ new Set(["ERROR", "FAILURE"])
|
|
|
710
714
|
var PENDING_STATUS_CONTEXT_STATES = /* @__PURE__ */ new Set(["EXPECTED", "PENDING"]);
|
|
711
715
|
var GITHUB_REPOSITORY_MAINTAINER_WARMUP_CONCURRENCY = 4;
|
|
712
716
|
var GITHUB_REPOSITORY_MAINTAINER_ROLE_NAMES = /* @__PURE__ */ new Set(["admin", "maintain"]);
|
|
717
|
+
var GITHUB_REPOSITORY_TRUSTED_AUTHOR_ASSOCIATIONS = /* @__PURE__ */ new Set(["collaborator", "member", "owner"]);
|
|
713
718
|
var GITHUB_ISSUE_STATUS_SNAPSHOT_QUERY = `
|
|
714
719
|
query GitHubIssueStatusSnapshot($owner: String!, $repo: String!, $issueNumber: Int!, $after: String) {
|
|
715
720
|
repository(owner: $owner, name: $repo) {
|
|
@@ -1613,6 +1618,20 @@ function normalizeGitHubUserLogin(value) {
|
|
|
1613
1618
|
function normalizeGitHubTokenRef(value) {
|
|
1614
1619
|
return normalizeSecretRef(value);
|
|
1615
1620
|
}
|
|
1621
|
+
function normalizeGitHubTokenRefs(value) {
|
|
1622
|
+
if (!value || typeof value !== "object") {
|
|
1623
|
+
return void 0;
|
|
1624
|
+
}
|
|
1625
|
+
const entries = Object.entries(value).map(([companyId, secretRef]) => {
|
|
1626
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
1627
|
+
const normalizedSecretRef = normalizeGitHubTokenRef(secretRef);
|
|
1628
|
+
return normalizedCompanyId && normalizedSecretRef ? [normalizedCompanyId, normalizedSecretRef] : null;
|
|
1629
|
+
}).filter((entry) => entry !== null);
|
|
1630
|
+
if (entries.length === 0) {
|
|
1631
|
+
return void 0;
|
|
1632
|
+
}
|
|
1633
|
+
return Object.fromEntries(entries);
|
|
1634
|
+
}
|
|
1616
1635
|
function formatUtcTimestamp(value) {
|
|
1617
1636
|
const parsed = new Date(value);
|
|
1618
1637
|
if (Number.isNaN(parsed.getTime())) {
|
|
@@ -2410,9 +2429,9 @@ function extractGitHubLinksFromCommentBody(body) {
|
|
|
2410
2429
|
return [...links.values()];
|
|
2411
2430
|
}
|
|
2412
2431
|
async function buildToolbarSyncState(ctx, input) {
|
|
2413
|
-
const settings = await getActiveOrCurrentSyncState(ctx);
|
|
2414
|
-
const config = await getResolvedConfig(ctx);
|
|
2415
2432
|
const companyId = typeof input.companyId === "string" && input.companyId.trim() ? input.companyId.trim() : void 0;
|
|
2433
|
+
const settings = await getActiveOrCurrentSyncState(ctx, companyId);
|
|
2434
|
+
const config = await getResolvedConfig(ctx);
|
|
2416
2435
|
const githubTokenConfigured = hasConfiguredGithubToken(settings, config, companyId);
|
|
2417
2436
|
const entityId = typeof input.entityId === "string" && input.entityId.trim() ? input.entityId.trim() : void 0;
|
|
2418
2437
|
const entityType = typeof input.entityType === "string" && input.entityType.trim() ? input.entityType.trim() : void 0;
|
|
@@ -2454,7 +2473,7 @@ async function buildToolbarSyncState(ctx, input) {
|
|
|
2454
2473
|
}) : [];
|
|
2455
2474
|
return {
|
|
2456
2475
|
kind: "issue",
|
|
2457
|
-
visible:
|
|
2476
|
+
visible: false,
|
|
2458
2477
|
canRun: githubTokenConfigured && mappings.length > 0,
|
|
2459
2478
|
label: link?.githubIssueNumber ? `Sync #${link.githubIssueNumber}` : "Sync issue",
|
|
2460
2479
|
message: link ? `Sync ${link.repositoryUrl.replace(/^https:\/\/github\.com\//, "")} issue #${link.githubIssueNumber}.` : "This Paperclip issue is not linked to GitHub yet.",
|
|
@@ -2626,15 +2645,25 @@ function sanitizeSettingsForCurrentSetup(settings, setup) {
|
|
|
2626
2645
|
function getPublicSettings(settings) {
|
|
2627
2646
|
const {
|
|
2628
2647
|
githubTokenRefs: _githubTokenRefs,
|
|
2629
|
-
|
|
2648
|
+
githubTokenLoginByCompanyId: _githubTokenLoginByCompanyId,
|
|
2630
2649
|
githubTokenRef: _githubTokenRef,
|
|
2631
2650
|
paperclipBoardApiTokenRefs: _paperclipBoardApiTokenRefs,
|
|
2632
2651
|
paperclipBoardAccessIdentityByCompanyId: _paperclipBoardAccessIdentityByCompanyId,
|
|
2633
2652
|
companyAdvancedSettingsByCompanyId: _companyAdvancedSettingsByCompanyId,
|
|
2653
|
+
syncStateByCompanyId: _syncStateByCompanyId,
|
|
2654
|
+
scheduleFrequencyMinutesByCompanyId: _scheduleFrequencyMinutesByCompanyId,
|
|
2655
|
+
paperclipApiBaseUrlByCompanyId: _paperclipApiBaseUrlByCompanyId,
|
|
2634
2656
|
...publicSettings
|
|
2635
2657
|
} = settings;
|
|
2636
2658
|
return publicSettings;
|
|
2637
2659
|
}
|
|
2660
|
+
function getGitHubTokenLogin(settings, companyId) {
|
|
2661
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
2662
|
+
if (!normalizedCompanyId) {
|
|
2663
|
+
return void 0;
|
|
2664
|
+
}
|
|
2665
|
+
return normalizeOptionalString2(settings.githubTokenLoginByCompanyId?.[normalizedCompanyId]) ?? normalizeOptionalString2(settings.githubTokenLogin);
|
|
2666
|
+
}
|
|
2638
2667
|
function getPaperclipBoardAccessIdentity(settings, companyId) {
|
|
2639
2668
|
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
2640
2669
|
if (!normalizedCompanyId) {
|
|
@@ -2644,34 +2673,16 @@ function getPaperclipBoardAccessIdentity(settings, companyId) {
|
|
|
2644
2673
|
}
|
|
2645
2674
|
function getPublicSettingsForScope(settings, companyId) {
|
|
2646
2675
|
const publicSettings = getPublicSettings(settings);
|
|
2676
|
+
const githubTokenLogin = getGitHubTokenLogin(settings, companyId);
|
|
2647
2677
|
const paperclipBoardAccessIdentity = getPaperclipBoardAccessIdentity(settings, companyId);
|
|
2648
2678
|
return {
|
|
2649
2679
|
...publicSettings,
|
|
2650
2680
|
mappings: filterMappingsByCompany(publicSettings.mappings, companyId),
|
|
2651
2681
|
advancedSettings: getCompanyAdvancedSettings(settings, companyId),
|
|
2682
|
+
...githubTokenLogin ? { githubTokenLogin } : {},
|
|
2652
2683
|
...paperclipBoardAccessIdentity ? { paperclipBoardAccessIdentity } : {}
|
|
2653
2684
|
};
|
|
2654
2685
|
}
|
|
2655
|
-
function getSavedGitHubTokenRef(settings, companyId) {
|
|
2656
|
-
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
2657
|
-
if (normalizedCompanyId) {
|
|
2658
|
-
const scopedSecretRef = normalizeSecretRef(settings?.githubTokenRefs?.[normalizedCompanyId]);
|
|
2659
|
-
if (scopedSecretRef) {
|
|
2660
|
-
return scopedSecretRef;
|
|
2661
|
-
}
|
|
2662
|
-
}
|
|
2663
|
-
return normalizeGitHubTokenRef(settings?.githubTokenRef);
|
|
2664
|
-
}
|
|
2665
|
-
function getSavedGitHubTokenLogin(settings, companyId) {
|
|
2666
|
-
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
2667
|
-
if (normalizedCompanyId) {
|
|
2668
|
-
const scopedLogin = normalizeOptionalString2(settings?.githubTokenLoginsByCompanyId?.[normalizedCompanyId]);
|
|
2669
|
-
if (scopedLogin) {
|
|
2670
|
-
return scopedLogin;
|
|
2671
|
-
}
|
|
2672
|
-
}
|
|
2673
|
-
return normalizeOptionalString2(settings?.githubTokenLogin);
|
|
2674
|
-
}
|
|
2675
2686
|
async function listAvailableAssignees(ctx, companyId) {
|
|
2676
2687
|
try {
|
|
2677
2688
|
const agents = await ctx.agents.list({
|
|
@@ -2852,14 +2863,11 @@ function createSetupConfigurationErrorSyncState(issue, trigger) {
|
|
|
2852
2863
|
});
|
|
2853
2864
|
}
|
|
2854
2865
|
}
|
|
2855
|
-
async function saveSettingsSyncState(ctx, settings, syncState) {
|
|
2856
|
-
const next =
|
|
2857
|
-
...settings,
|
|
2858
|
-
syncState
|
|
2859
|
-
};
|
|
2866
|
+
async function saveSettingsSyncState(ctx, settings, syncState, companyId) {
|
|
2867
|
+
const next = upsertScopedSyncState(normalizeSettings(settings), syncState, companyId);
|
|
2860
2868
|
await ctx.state.set(SETTINGS_SCOPE, next);
|
|
2861
|
-
await ctx.state.set(SYNC_STATE_SCOPE,
|
|
2862
|
-
return next;
|
|
2869
|
+
await ctx.state.set(SYNC_STATE_SCOPE, syncState);
|
|
2870
|
+
return materializeScopedSettings(next, null, companyId);
|
|
2863
2871
|
}
|
|
2864
2872
|
async function setSyncCancellationRequest(ctx, request) {
|
|
2865
2873
|
if (request) {
|
|
@@ -2884,8 +2892,8 @@ function buildCancelledSyncMessage(target, progress) {
|
|
|
2884
2892
|
const completionSummary = completedIssueCount !== void 0 && totalIssueCount !== void 0 ? ` Completed ${Math.min(completedIssueCount, totalIssueCount)} of ${totalIssueCount} issues before stopping.` : "";
|
|
2885
2893
|
return `${scopeLabel} was cancelled before it finished.${completionSummary}`;
|
|
2886
2894
|
}
|
|
2887
|
-
async function createUnexpectedSyncErrorResult(ctx, trigger, error) {
|
|
2888
|
-
const settings = normalizeSettings(await ctx.state.get(SETTINGS_SCOPE));
|
|
2895
|
+
async function createUnexpectedSyncErrorResult(ctx, trigger, error, companyId) {
|
|
2896
|
+
const settings = materializeScopedSettings(normalizeSettings(await ctx.state.get(SETTINGS_SCOPE)), null, companyId);
|
|
2889
2897
|
const errorDetails = buildSyncErrorDetails(error, {
|
|
2890
2898
|
phase: "configuration"
|
|
2891
2899
|
});
|
|
@@ -2910,7 +2918,8 @@ async function createUnexpectedSyncErrorResult(ctx, trigger, error) {
|
|
|
2910
2918
|
errorDetails
|
|
2911
2919
|
})
|
|
2912
2920
|
)
|
|
2913
|
-
})
|
|
2921
|
+
}),
|
|
2922
|
+
companyId
|
|
2914
2923
|
);
|
|
2915
2924
|
}
|
|
2916
2925
|
async function waitForSyncResultWithinGracePeriod(promise, timeoutMs) {
|
|
@@ -2928,15 +2937,13 @@ async function waitForSyncResultWithinGracePeriod(promise, timeoutMs) {
|
|
|
2928
2937
|
}
|
|
2929
2938
|
}
|
|
2930
2939
|
}
|
|
2931
|
-
async function getActiveOrCurrentSyncState(ctx) {
|
|
2932
|
-
|
|
2933
|
-
|
|
2940
|
+
async function getActiveOrCurrentSyncState(ctx, companyId) {
|
|
2941
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
2942
|
+
if (activeRunningSyncState?.syncState.status === "running" && activeRunningSyncCompanyId === normalizedCompanyId) {
|
|
2943
|
+
return materializeScopedSettings(activeRunningSyncState, null, normalizedCompanyId);
|
|
2934
2944
|
}
|
|
2935
2945
|
const current = normalizeSettings(await ctx.state.get(SETTINGS_SCOPE));
|
|
2936
|
-
|
|
2937
|
-
return current;
|
|
2938
|
-
}
|
|
2939
|
-
return current;
|
|
2946
|
+
return materializeScopedSettings(current, null, normalizedCompanyId);
|
|
2940
2947
|
}
|
|
2941
2948
|
function updateSyncFailureContext(current, next) {
|
|
2942
2949
|
if ("phase" in next) {
|
|
@@ -3060,12 +3067,6 @@ async function readExternalConfig(ctx) {
|
|
|
3060
3067
|
}
|
|
3061
3068
|
}
|
|
3062
3069
|
function normalizePaperclipBoardApiTokenRefs(value) {
|
|
3063
|
-
return normalizeCompanySecretRefs(value);
|
|
3064
|
-
}
|
|
3065
|
-
function normalizeGitHubTokenRefs(value) {
|
|
3066
|
-
return normalizeCompanySecretRefs(value);
|
|
3067
|
-
}
|
|
3068
|
-
function normalizeCompanySecretRefs(value) {
|
|
3069
3070
|
if (!value || typeof value !== "object") {
|
|
3070
3071
|
return void 0;
|
|
3071
3072
|
}
|
|
@@ -3079,7 +3080,7 @@ function normalizeCompanySecretRefs(value) {
|
|
|
3079
3080
|
}
|
|
3080
3081
|
return Object.fromEntries(entries);
|
|
3081
3082
|
}
|
|
3082
|
-
function
|
|
3083
|
+
function normalizeGitHubTokenLoginByCompanyId(value) {
|
|
3083
3084
|
if (!value || typeof value !== "object") {
|
|
3084
3085
|
return void 0;
|
|
3085
3086
|
}
|
|
@@ -3126,14 +3127,14 @@ function normalizeSyncState(value) {
|
|
|
3126
3127
|
const recentFailures = normalizeSyncFailureLogEntries(record.recentFailures);
|
|
3127
3128
|
return {
|
|
3128
3129
|
status: status === "running" || status === "success" || status === "error" || status === "cancelled" ? status : "idle",
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
lastRunTrigger
|
|
3136
|
-
|
|
3130
|
+
...typeof record.message === "string" ? { message: record.message } : {},
|
|
3131
|
+
...typeof record.checkedAt === "string" ? { checkedAt: record.checkedAt } : {},
|
|
3132
|
+
...typeof record.syncedIssuesCount === "number" ? { syncedIssuesCount: record.syncedIssuesCount } : {},
|
|
3133
|
+
...typeof record.createdIssuesCount === "number" ? { createdIssuesCount: record.createdIssuesCount } : {},
|
|
3134
|
+
...typeof record.skippedIssuesCount === "number" ? { skippedIssuesCount: record.skippedIssuesCount } : {},
|
|
3135
|
+
...typeof record.erroredIssuesCount === "number" ? { erroredIssuesCount: record.erroredIssuesCount } : {},
|
|
3136
|
+
...lastRunTrigger === "manual" || lastRunTrigger === "schedule" || lastRunTrigger === "retry" ? { lastRunTrigger } : {},
|
|
3137
|
+
...typeof record.cancelRequestedAt === "string" ? { cancelRequestedAt: record.cancelRequestedAt } : {},
|
|
3137
3138
|
...progress ? { progress } : {},
|
|
3138
3139
|
...errorDetails ? { errorDetails } : {},
|
|
3139
3140
|
...recentFailures ? { recentFailures } : {}
|
|
@@ -3243,6 +3244,26 @@ function normalizeScheduleFrequencyMinutes(value) {
|
|
|
3243
3244
|
}
|
|
3244
3245
|
return Math.floor(numericValue);
|
|
3245
3246
|
}
|
|
3247
|
+
function normalizeSyncStateByCompanyId(value) {
|
|
3248
|
+
if (!value || typeof value !== "object") {
|
|
3249
|
+
return void 0;
|
|
3250
|
+
}
|
|
3251
|
+
const entries = Object.entries(value).map(([companyId, syncState]) => {
|
|
3252
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
3253
|
+
return normalizedCompanyId ? [normalizedCompanyId, normalizeSyncState(syncState)] : null;
|
|
3254
|
+
}).filter((entry) => entry !== null);
|
|
3255
|
+
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
3256
|
+
}
|
|
3257
|
+
function normalizeScheduleFrequencyMinutesByCompanyId(value) {
|
|
3258
|
+
if (!value || typeof value !== "object") {
|
|
3259
|
+
return void 0;
|
|
3260
|
+
}
|
|
3261
|
+
const entries = Object.entries(value).map(([companyId, scheduleFrequencyMinutes]) => {
|
|
3262
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
3263
|
+
return normalizedCompanyId ? [normalizedCompanyId, normalizeScheduleFrequencyMinutes(scheduleFrequencyMinutes)] : null;
|
|
3264
|
+
}).filter((entry) => entry !== null);
|
|
3265
|
+
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
3266
|
+
}
|
|
3246
3267
|
function normalizePaperclipApiBaseUrl(value) {
|
|
3247
3268
|
if (typeof value !== "string") {
|
|
3248
3269
|
return void 0;
|
|
@@ -3257,6 +3278,17 @@ function normalizePaperclipApiBaseUrl(value) {
|
|
|
3257
3278
|
return void 0;
|
|
3258
3279
|
}
|
|
3259
3280
|
}
|
|
3281
|
+
function normalizePaperclipApiBaseUrlByCompanyId(value) {
|
|
3282
|
+
if (!value || typeof value !== "object") {
|
|
3283
|
+
return void 0;
|
|
3284
|
+
}
|
|
3285
|
+
const entries = Object.entries(value).map(([companyId, paperclipApiBaseUrl]) => {
|
|
3286
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
3287
|
+
const normalizedPaperclipApiBaseUrl = normalizePaperclipApiBaseUrl(paperclipApiBaseUrl);
|
|
3288
|
+
return normalizedCompanyId && normalizedPaperclipApiBaseUrl ? [normalizedCompanyId, normalizedPaperclipApiBaseUrl] : null;
|
|
3289
|
+
}).filter((entry) => entry !== null);
|
|
3290
|
+
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
|
|
3291
|
+
}
|
|
3260
3292
|
function getRuntimePaperclipApiBaseUrl() {
|
|
3261
3293
|
if (typeof process === "undefined" || !process?.env) {
|
|
3262
3294
|
return void 0;
|
|
@@ -3276,19 +3308,26 @@ function resolvePaperclipApiBaseUrl(...values) {
|
|
|
3276
3308
|
}
|
|
3277
3309
|
return void 0;
|
|
3278
3310
|
}
|
|
3279
|
-
function getConfiguredPaperclipApiBaseUrl(settings, config) {
|
|
3280
|
-
|
|
3311
|
+
function getConfiguredPaperclipApiBaseUrl(settings, config, companyId) {
|
|
3312
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
3313
|
+
return normalizedCompanyId ? resolvePaperclipApiBaseUrl(
|
|
3314
|
+
config?.paperclipApiBaseUrl,
|
|
3315
|
+
settings?.paperclipApiBaseUrlByCompanyId?.[normalizedCompanyId],
|
|
3316
|
+
settings?.paperclipApiBaseUrl
|
|
3317
|
+
) : resolvePaperclipApiBaseUrl(config?.paperclipApiBaseUrl, settings?.paperclipApiBaseUrl);
|
|
3281
3318
|
}
|
|
3282
|
-
function resolveTrustedPaperclipApiBaseUrlInput(value, settings, config) {
|
|
3319
|
+
function resolveTrustedPaperclipApiBaseUrlInput(value, settings, config, companyId) {
|
|
3283
3320
|
const runtimePaperclipApiBaseUrl = getRuntimePaperclipApiBaseUrl();
|
|
3284
3321
|
if (runtimePaperclipApiBaseUrl) {
|
|
3285
3322
|
return runtimePaperclipApiBaseUrl;
|
|
3286
3323
|
}
|
|
3287
3324
|
const requestedPaperclipApiBaseUrl = normalizePaperclipApiBaseUrl(value);
|
|
3288
3325
|
const configuredPaperclipApiBaseUrl = normalizePaperclipApiBaseUrl(config?.paperclipApiBaseUrl);
|
|
3326
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
3327
|
+
const savedCompanyPaperclipApiBaseUrl = normalizedCompanyId ? normalizePaperclipApiBaseUrl(settings?.paperclipApiBaseUrlByCompanyId?.[normalizedCompanyId]) : void 0;
|
|
3289
3328
|
const savedPaperclipApiBaseUrl = normalizePaperclipApiBaseUrl(settings?.paperclipApiBaseUrl);
|
|
3290
3329
|
if (!requestedPaperclipApiBaseUrl) {
|
|
3291
|
-
return configuredPaperclipApiBaseUrl ?? savedPaperclipApiBaseUrl;
|
|
3330
|
+
return configuredPaperclipApiBaseUrl ?? savedCompanyPaperclipApiBaseUrl ?? savedPaperclipApiBaseUrl;
|
|
3292
3331
|
}
|
|
3293
3332
|
if (configuredPaperclipApiBaseUrl) {
|
|
3294
3333
|
if (requestedPaperclipApiBaseUrl !== configuredPaperclipApiBaseUrl) {
|
|
@@ -3298,6 +3337,9 @@ function resolveTrustedPaperclipApiBaseUrlInput(value, settings, config) {
|
|
|
3298
3337
|
}
|
|
3299
3338
|
return configuredPaperclipApiBaseUrl;
|
|
3300
3339
|
}
|
|
3340
|
+
if (savedCompanyPaperclipApiBaseUrl && requestedPaperclipApiBaseUrl === savedCompanyPaperclipApiBaseUrl) {
|
|
3341
|
+
return savedCompanyPaperclipApiBaseUrl;
|
|
3342
|
+
}
|
|
3301
3343
|
if (savedPaperclipApiBaseUrl && requestedPaperclipApiBaseUrl === savedPaperclipApiBaseUrl) {
|
|
3302
3344
|
return savedPaperclipApiBaseUrl;
|
|
3303
3345
|
}
|
|
@@ -3310,9 +3352,14 @@ function normalizeSettings(value) {
|
|
|
3310
3352
|
return DEFAULT_SETTINGS;
|
|
3311
3353
|
}
|
|
3312
3354
|
const record = value;
|
|
3355
|
+
const syncStateByCompanyId = normalizeSyncStateByCompanyId(record.syncStateByCompanyId);
|
|
3356
|
+
const scheduleFrequencyMinutesByCompanyId = normalizeScheduleFrequencyMinutesByCompanyId(
|
|
3357
|
+
record.scheduleFrequencyMinutesByCompanyId
|
|
3358
|
+
);
|
|
3313
3359
|
const paperclipApiBaseUrl = resolvePaperclipApiBaseUrl(record.paperclipApiBaseUrl);
|
|
3360
|
+
const paperclipApiBaseUrlByCompanyId = normalizePaperclipApiBaseUrlByCompanyId(record.paperclipApiBaseUrlByCompanyId);
|
|
3314
3361
|
const githubTokenRefs = normalizeGitHubTokenRefs(record.githubTokenRefs);
|
|
3315
|
-
const
|
|
3362
|
+
const githubTokenLoginByCompanyId = normalizeGitHubTokenLoginByCompanyId(record.githubTokenLoginByCompanyId);
|
|
3316
3363
|
const githubTokenRef = normalizeGitHubTokenRef(record.githubTokenRef);
|
|
3317
3364
|
const githubTokenLogin = normalizeOptionalString2(record.githubTokenLogin);
|
|
3318
3365
|
const paperclipBoardApiTokenRefs = normalizePaperclipBoardApiTokenRefs(record.paperclipBoardApiTokenRefs);
|
|
@@ -3323,10 +3370,13 @@ function normalizeSettings(value) {
|
|
|
3323
3370
|
return {
|
|
3324
3371
|
mappings: normalizeMappings(record.mappings),
|
|
3325
3372
|
syncState: normalizeSyncState(record.syncState),
|
|
3373
|
+
...syncStateByCompanyId ? { syncStateByCompanyId } : {},
|
|
3326
3374
|
scheduleFrequencyMinutes: normalizeScheduleFrequencyMinutes(record.scheduleFrequencyMinutes),
|
|
3375
|
+
...scheduleFrequencyMinutesByCompanyId ? { scheduleFrequencyMinutesByCompanyId } : {},
|
|
3327
3376
|
...paperclipApiBaseUrl ? { paperclipApiBaseUrl } : {},
|
|
3377
|
+
...paperclipApiBaseUrlByCompanyId ? { paperclipApiBaseUrlByCompanyId } : {},
|
|
3328
3378
|
...githubTokenRefs ? { githubTokenRefs } : {},
|
|
3329
|
-
...
|
|
3379
|
+
...githubTokenLoginByCompanyId ? { githubTokenLoginByCompanyId } : {},
|
|
3330
3380
|
...githubTokenRef ? { githubTokenRef } : {},
|
|
3331
3381
|
...githubTokenLogin ? { githubTokenLogin } : {},
|
|
3332
3382
|
...paperclipBoardApiTokenRefs ? { paperclipBoardApiTokenRefs } : {},
|
|
@@ -3335,6 +3385,103 @@ function normalizeSettings(value) {
|
|
|
3335
3385
|
updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : void 0
|
|
3336
3386
|
};
|
|
3337
3387
|
}
|
|
3388
|
+
function getScopedSyncState(settings, companyId) {
|
|
3389
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
3390
|
+
if (!normalizedCompanyId) {
|
|
3391
|
+
return normalizeSyncState(settings.syncState);
|
|
3392
|
+
}
|
|
3393
|
+
const scopedSyncState = settings.syncStateByCompanyId?.[normalizedCompanyId];
|
|
3394
|
+
return scopedSyncState ? normalizeSyncState(scopedSyncState) : normalizeSyncState(settings.syncState);
|
|
3395
|
+
}
|
|
3396
|
+
function getScopedScheduleFrequencyMinutes(settings, companyId) {
|
|
3397
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
3398
|
+
if (!normalizedCompanyId) {
|
|
3399
|
+
return normalizeScheduleFrequencyMinutes(settings.scheduleFrequencyMinutes);
|
|
3400
|
+
}
|
|
3401
|
+
const scopedScheduleFrequencyMinutes = settings.scheduleFrequencyMinutesByCompanyId?.[normalizedCompanyId];
|
|
3402
|
+
return scopedScheduleFrequencyMinutes !== void 0 ? normalizeScheduleFrequencyMinutes(scopedScheduleFrequencyMinutes) : normalizeScheduleFrequencyMinutes(settings.scheduleFrequencyMinutes);
|
|
3403
|
+
}
|
|
3404
|
+
function materializeScopedSettings(settings, config, companyId) {
|
|
3405
|
+
const syncState = getScopedSyncState(settings, companyId);
|
|
3406
|
+
const scheduleFrequencyMinutes = getScopedScheduleFrequencyMinutes(settings, companyId);
|
|
3407
|
+
const paperclipApiBaseUrl = getConfiguredPaperclipApiBaseUrl(settings, config, companyId);
|
|
3408
|
+
return {
|
|
3409
|
+
...settings,
|
|
3410
|
+
syncState,
|
|
3411
|
+
scheduleFrequencyMinutes,
|
|
3412
|
+
...paperclipApiBaseUrl ? { paperclipApiBaseUrl } : {}
|
|
3413
|
+
};
|
|
3414
|
+
}
|
|
3415
|
+
function upsertScopedSyncState(settings, syncState, companyId) {
|
|
3416
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
3417
|
+
if (!normalizedCompanyId) {
|
|
3418
|
+
return {
|
|
3419
|
+
...settings,
|
|
3420
|
+
syncState
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3423
|
+
return {
|
|
3424
|
+
...settings,
|
|
3425
|
+
syncState,
|
|
3426
|
+
syncStateByCompanyId: {
|
|
3427
|
+
...settings.syncStateByCompanyId ?? {},
|
|
3428
|
+
[normalizedCompanyId]: syncState
|
|
3429
|
+
}
|
|
3430
|
+
};
|
|
3431
|
+
}
|
|
3432
|
+
function upsertScopedScheduleFrequencyMinutes(settings, scheduleFrequencyMinutes, companyId) {
|
|
3433
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
3434
|
+
if (!normalizedCompanyId) {
|
|
3435
|
+
return {
|
|
3436
|
+
...settings,
|
|
3437
|
+
scheduleFrequencyMinutes
|
|
3438
|
+
};
|
|
3439
|
+
}
|
|
3440
|
+
return {
|
|
3441
|
+
...settings,
|
|
3442
|
+
scheduleFrequencyMinutes,
|
|
3443
|
+
scheduleFrequencyMinutesByCompanyId: {
|
|
3444
|
+
...settings.scheduleFrequencyMinutesByCompanyId ?? {},
|
|
3445
|
+
[normalizedCompanyId]: scheduleFrequencyMinutes
|
|
3446
|
+
}
|
|
3447
|
+
};
|
|
3448
|
+
}
|
|
3449
|
+
function upsertScopedPaperclipApiBaseUrl(settings, paperclipApiBaseUrl, companyId) {
|
|
3450
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
3451
|
+
if (!normalizedCompanyId) {
|
|
3452
|
+
return {
|
|
3453
|
+
...settings,
|
|
3454
|
+
...paperclipApiBaseUrl ? { paperclipApiBaseUrl } : {}
|
|
3455
|
+
};
|
|
3456
|
+
}
|
|
3457
|
+
const nextPaperclipApiBaseUrlByCompanyId = {
|
|
3458
|
+
...settings.paperclipApiBaseUrlByCompanyId ?? {}
|
|
3459
|
+
};
|
|
3460
|
+
if (paperclipApiBaseUrl) {
|
|
3461
|
+
nextPaperclipApiBaseUrlByCompanyId[normalizedCompanyId] = paperclipApiBaseUrl;
|
|
3462
|
+
} else {
|
|
3463
|
+
delete nextPaperclipApiBaseUrlByCompanyId[normalizedCompanyId];
|
|
3464
|
+
}
|
|
3465
|
+
return {
|
|
3466
|
+
...settings,
|
|
3467
|
+
...paperclipApiBaseUrl ? { paperclipApiBaseUrl } : {},
|
|
3468
|
+
...Object.keys(nextPaperclipApiBaseUrlByCompanyId).length > 0 ? { paperclipApiBaseUrlByCompanyId: nextPaperclipApiBaseUrlByCompanyId } : {}
|
|
3469
|
+
};
|
|
3470
|
+
}
|
|
3471
|
+
function getScopedSyncTarget(companyId) {
|
|
3472
|
+
return {
|
|
3473
|
+
kind: "company",
|
|
3474
|
+
companyId,
|
|
3475
|
+
displayLabel: "this company"
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3478
|
+
function getSyncableMappingsForScope(mappings, companyId) {
|
|
3479
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
3480
|
+
return normalizedCompanyId ? getSyncableMappingsForTarget(mappings, getScopedSyncTarget(normalizedCompanyId)) : getSyncableMappings(mappings);
|
|
3481
|
+
}
|
|
3482
|
+
function hasAnyScopedValue(valueByCompanyId) {
|
|
3483
|
+
return Boolean(valueByCompanyId && Object.keys(valueByCompanyId).length > 0);
|
|
3484
|
+
}
|
|
3338
3485
|
function normalizeImportRegistry(value) {
|
|
3339
3486
|
if (!Array.isArray(value)) {
|
|
3340
3487
|
return [];
|
|
@@ -3494,6 +3641,7 @@ function normalizeGitHubIssueRecord(issue) {
|
|
|
3494
3641
|
body: typeof issue.body === "string" ? stripNullBytes(issue.body) : null,
|
|
3495
3642
|
htmlUrl: issue.html_url,
|
|
3496
3643
|
...normalizeGitHubUsername(issue.user?.login) ? { authorLogin: normalizeGitHubUsername(issue.user?.login) } : {},
|
|
3644
|
+
...normalizeGitHubLowercaseString(issue.author_association) ? { authorAssociation: normalizeGitHubLowercaseString(issue.author_association) } : {},
|
|
3497
3645
|
labels: normalizeGitHubIssueLabels(issue.labels),
|
|
3498
3646
|
state: issue.state === "closed" ? "closed" : "open",
|
|
3499
3647
|
stateReason: normalizeGitHubIssueStateReason(issue.state_reason),
|
|
@@ -4274,6 +4422,22 @@ async function getGitHubIssueStatusSnapshot(octokit, repository, issueNumber, gi
|
|
|
4274
4422
|
function buildGitHubRepositoryActorCacheKey(repository, login) {
|
|
4275
4423
|
return `${repository.owner.toLowerCase()}/${repository.repo.toLowerCase()}:${login}`;
|
|
4276
4424
|
}
|
|
4425
|
+
function getGitHubRepositoryTrustedAuthorStatusFromAssociation(authorAssociation) {
|
|
4426
|
+
const normalizedAssociation = normalizeGitHubLowercaseString(authorAssociation);
|
|
4427
|
+
if (!normalizedAssociation) {
|
|
4428
|
+
return void 0;
|
|
4429
|
+
}
|
|
4430
|
+
return GITHUB_REPOSITORY_TRUSTED_AUTHOR_ASSOCIATIONS.has(normalizedAssociation);
|
|
4431
|
+
}
|
|
4432
|
+
function cacheGitHubRepositoryTrustedAuthorStatusFromAssociation(repository, login, authorAssociation, cache) {
|
|
4433
|
+
const normalizedLogin = normalizeGitHubUserLogin(login);
|
|
4434
|
+
const trustedAuthorStatus = getGitHubRepositoryTrustedAuthorStatusFromAssociation(authorAssociation);
|
|
4435
|
+
if (!normalizedLogin || trustedAuthorStatus === void 0) {
|
|
4436
|
+
return trustedAuthorStatus;
|
|
4437
|
+
}
|
|
4438
|
+
cache.set(buildGitHubRepositoryActorCacheKey(repository, normalizedLogin), trustedAuthorStatus);
|
|
4439
|
+
return trustedAuthorStatus;
|
|
4440
|
+
}
|
|
4277
4441
|
async function isGitHubUserRepositoryMaintainer(octokit, repository, login, cache) {
|
|
4278
4442
|
const normalizedLogin = normalizeGitHubUserLogin(login);
|
|
4279
4443
|
if (!normalizedLogin) {
|
|
@@ -4339,6 +4503,7 @@ async function listNewGitHubIssueCommentsSinceCount(octokit, repository, issueNu
|
|
|
4339
4503
|
body: typeof comment.body === "string" ? stripNullBytes(comment.body) : "",
|
|
4340
4504
|
url: comment.html_url ?? void 0,
|
|
4341
4505
|
authorLogin: normalizeGitHubUserLogin(comment.user?.login),
|
|
4506
|
+
...normalizeGitHubLowercaseString(comment.author_association) ? { authorAssociation: normalizeGitHubLowercaseString(comment.author_association) } : {},
|
|
4342
4507
|
createdAt: comment.created_at ?? void 0,
|
|
4343
4508
|
updatedAt: comment.updated_at ?? void 0
|
|
4344
4509
|
});
|
|
@@ -4380,6 +4545,18 @@ async function hasTrustedNewGitHubIssueComment(params) {
|
|
|
4380
4545
|
if (originalPosterLogin && authorLogin === originalPosterLogin) {
|
|
4381
4546
|
return true;
|
|
4382
4547
|
}
|
|
4548
|
+
const trustedAuthorStatus = cacheGitHubRepositoryTrustedAuthorStatusFromAssociation(
|
|
4549
|
+
params.repository,
|
|
4550
|
+
authorLogin,
|
|
4551
|
+
comment.authorAssociation,
|
|
4552
|
+
params.maintainerCache
|
|
4553
|
+
);
|
|
4554
|
+
if (trustedAuthorStatus === true) {
|
|
4555
|
+
return true;
|
|
4556
|
+
}
|
|
4557
|
+
if (trustedAuthorStatus === false) {
|
|
4558
|
+
continue;
|
|
4559
|
+
}
|
|
4383
4560
|
unseenAuthors.add(authorLogin);
|
|
4384
4561
|
}
|
|
4385
4562
|
for (const authorLogin of unseenAuthors) {
|
|
@@ -4399,6 +4576,15 @@ async function isMaintainerAuthoredGitHubIssue(params) {
|
|
|
4399
4576
|
if (!authorLogin) {
|
|
4400
4577
|
return false;
|
|
4401
4578
|
}
|
|
4579
|
+
const trustedAuthorStatus = cacheGitHubRepositoryTrustedAuthorStatusFromAssociation(
|
|
4580
|
+
params.repository,
|
|
4581
|
+
authorLogin,
|
|
4582
|
+
params.githubIssue.authorAssociation,
|
|
4583
|
+
params.maintainerCache
|
|
4584
|
+
);
|
|
4585
|
+
if (trustedAuthorStatus !== void 0) {
|
|
4586
|
+
return trustedAuthorStatus;
|
|
4587
|
+
}
|
|
4402
4588
|
return isGitHubUserRepositoryMaintainer(
|
|
4403
4589
|
params.octokit,
|
|
4404
4590
|
params.repository,
|
|
@@ -4408,7 +4594,19 @@ async function isMaintainerAuthoredGitHubIssue(params) {
|
|
|
4408
4594
|
}
|
|
4409
4595
|
async function warmGitHubRepositoryMaintainerCache(params) {
|
|
4410
4596
|
const uniqueAuthorLogins = [...new Set(
|
|
4411
|
-
params.githubIssues.
|
|
4597
|
+
params.githubIssues.flatMap((issue) => {
|
|
4598
|
+
const authorLogin = normalizeGitHubUserLogin(issue.authorLogin);
|
|
4599
|
+
if (!authorLogin) {
|
|
4600
|
+
return [];
|
|
4601
|
+
}
|
|
4602
|
+
const trustedAuthorStatus = cacheGitHubRepositoryTrustedAuthorStatusFromAssociation(
|
|
4603
|
+
params.repository,
|
|
4604
|
+
authorLogin,
|
|
4605
|
+
issue.authorAssociation,
|
|
4606
|
+
params.maintainerCache
|
|
4607
|
+
);
|
|
4608
|
+
return trustedAuthorStatus === void 0 ? [authorLogin] : [];
|
|
4609
|
+
})
|
|
4412
4610
|
)].filter((authorLogin) => !params.maintainerCache.has(buildGitHubRepositoryActorCacheKey(params.repository, authorLogin)));
|
|
4413
4611
|
if (uniqueAuthorLogins.length === 0) {
|
|
4414
4612
|
return;
|
|
@@ -4623,7 +4821,9 @@ function buildPaperclipIssueDescription(issue, linkedPullRequestNumbers = []) {
|
|
|
4623
4821
|
return normalizedBody ?? "";
|
|
4624
4822
|
}
|
|
4625
4823
|
if (!normalizedBody) {
|
|
4626
|
-
return
|
|
4824
|
+
return `${EMPTY_GITHUB_ISSUE_DESCRIPTION_PLACEHOLDER}
|
|
4825
|
+
|
|
4826
|
+
${hiddenImportMarker}`;
|
|
4627
4827
|
}
|
|
4628
4828
|
return `${normalizedBody}
|
|
4629
4829
|
|
|
@@ -5027,6 +5227,9 @@ function getPaperclipIssueEndpoint(baseUrl, issueId) {
|
|
|
5027
5227
|
function getPaperclipHealthEndpoint(baseUrl) {
|
|
5028
5228
|
return new URL("/api/health", baseUrl).toString();
|
|
5029
5229
|
}
|
|
5230
|
+
function getPaperclipAgentWakeupEndpoint(baseUrl, agentId) {
|
|
5231
|
+
return new URL(`/api/agents/${agentId}/wakeup`, baseUrl).toString();
|
|
5232
|
+
}
|
|
5030
5233
|
function getActivePaperclipApiAuthToken(companyId) {
|
|
5031
5234
|
if (!companyId) {
|
|
5032
5235
|
return void 0;
|
|
@@ -5071,6 +5274,46 @@ async function detectPaperclipBoardAccessRequirement(paperclipApiBaseUrl) {
|
|
|
5071
5274
|
return false;
|
|
5072
5275
|
}
|
|
5073
5276
|
}
|
|
5277
|
+
async function wakePaperclipAssigneeForImportedIssue(ctx, params) {
|
|
5278
|
+
if (!params.assigneeAgentId || !params.paperclipApiBaseUrl) {
|
|
5279
|
+
return;
|
|
5280
|
+
}
|
|
5281
|
+
try {
|
|
5282
|
+
const response = await fetchPaperclipApi(
|
|
5283
|
+
getPaperclipAgentWakeupEndpoint(params.paperclipApiBaseUrl, params.assigneeAgentId),
|
|
5284
|
+
{
|
|
5285
|
+
method: "POST",
|
|
5286
|
+
headers: {
|
|
5287
|
+
accept: "application/json",
|
|
5288
|
+
"content-type": "application/json"
|
|
5289
|
+
},
|
|
5290
|
+
body: JSON.stringify({
|
|
5291
|
+
source: "assignment",
|
|
5292
|
+
triggerDetail: "system",
|
|
5293
|
+
reason: IMPORTED_ISSUE_WAKE_REASON,
|
|
5294
|
+
payload: {
|
|
5295
|
+
issueId: params.paperclipIssueId,
|
|
5296
|
+
mutation: "import"
|
|
5297
|
+
}
|
|
5298
|
+
})
|
|
5299
|
+
},
|
|
5300
|
+
{
|
|
5301
|
+
companyId: params.companyId
|
|
5302
|
+
}
|
|
5303
|
+
);
|
|
5304
|
+
if (!response.ok) {
|
|
5305
|
+
throw new Error(`Wakeup request failed with status ${response.status}.`);
|
|
5306
|
+
}
|
|
5307
|
+
} catch (error) {
|
|
5308
|
+
ctx.logger.warn("GitHub sync could not wake the assignee for an imported Paperclip issue.", {
|
|
5309
|
+
issueId: params.paperclipIssueId,
|
|
5310
|
+
agentId: params.assigneeAgentId,
|
|
5311
|
+
companyId: params.companyId,
|
|
5312
|
+
paperclipApiBaseUrl: params.paperclipApiBaseUrl,
|
|
5313
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5314
|
+
});
|
|
5315
|
+
}
|
|
5316
|
+
}
|
|
5074
5317
|
function parsePaperclipIssueDescription(value) {
|
|
5075
5318
|
if (!value || typeof value !== "object") {
|
|
5076
5319
|
return void 0;
|
|
@@ -6028,6 +6271,7 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
6028
6271
|
let updatedDescriptionsCount = 0;
|
|
6029
6272
|
let completedIssueCount = 0;
|
|
6030
6273
|
const totalIssueCount = importedIssues.length;
|
|
6274
|
+
const queuedImportedIssueWakeups = [];
|
|
6031
6275
|
for (const importedIssue of importedIssues) {
|
|
6032
6276
|
if (assertNotCancelled) {
|
|
6033
6277
|
await assertNotCancelled();
|
|
@@ -6159,9 +6403,16 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
6159
6403
|
defaultImportedStatus: advancedSettings.defaultStatus,
|
|
6160
6404
|
maintainerAuthoredImportedIssue
|
|
6161
6405
|
});
|
|
6406
|
+
const shouldWakeImportedAssignee = wasImportedThisRun && nextStatus === "todo" && Boolean(paperclipIssue.assigneeAgentId);
|
|
6162
6407
|
importedIssue.githubIssueNumber = githubIssue.number;
|
|
6163
6408
|
importedIssue.lastSeenCommentCount = snapshot.commentCount;
|
|
6164
6409
|
if (paperclipIssue.status === nextStatus) {
|
|
6410
|
+
if (shouldWakeImportedAssignee) {
|
|
6411
|
+
queuedImportedIssueWakeups.push({
|
|
6412
|
+
assigneeAgentId: paperclipIssue.assigneeAgentId,
|
|
6413
|
+
paperclipIssueId: importedIssue.paperclipIssueId
|
|
6414
|
+
});
|
|
6415
|
+
}
|
|
6165
6416
|
continue;
|
|
6166
6417
|
}
|
|
6167
6418
|
const transitionComment = buildPaperclipIssueStatusTransitionComment({
|
|
@@ -6187,6 +6438,12 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
6187
6438
|
paperclipApiBaseUrl
|
|
6188
6439
|
});
|
|
6189
6440
|
updatedStatusesCount += 1;
|
|
6441
|
+
if (shouldWakeImportedAssignee) {
|
|
6442
|
+
queuedImportedIssueWakeups.push({
|
|
6443
|
+
assigneeAgentId: paperclipIssue.assigneeAgentId,
|
|
6444
|
+
paperclipIssueId: importedIssue.paperclipIssueId
|
|
6445
|
+
});
|
|
6446
|
+
}
|
|
6190
6447
|
} catch (error) {
|
|
6191
6448
|
if (isGitHubRateLimitError(error)) {
|
|
6192
6449
|
throw error;
|
|
@@ -6205,6 +6462,16 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
6205
6462
|
}
|
|
6206
6463
|
}
|
|
6207
6464
|
}
|
|
6465
|
+
await mapWithConcurrency(
|
|
6466
|
+
queuedImportedIssueWakeups,
|
|
6467
|
+
IMPORTED_ISSUE_WAKEUP_CONCURRENCY,
|
|
6468
|
+
async (queuedWakeup) => wakePaperclipAssigneeForImportedIssue(ctx, {
|
|
6469
|
+
assigneeAgentId: queuedWakeup.assigneeAgentId,
|
|
6470
|
+
paperclipIssueId: queuedWakeup.paperclipIssueId,
|
|
6471
|
+
companyId: mapping.companyId,
|
|
6472
|
+
paperclipApiBaseUrl
|
|
6473
|
+
})
|
|
6474
|
+
);
|
|
6208
6475
|
return {
|
|
6209
6476
|
updatedStatusesCount,
|
|
6210
6477
|
updatedLabelsCount,
|
|
@@ -6223,19 +6490,32 @@ async function getResolvedConfig(ctx) {
|
|
|
6223
6490
|
}
|
|
6224
6491
|
function getConfiguredGithubTokenSource(settings, config, companyId) {
|
|
6225
6492
|
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
6226
|
-
const
|
|
6493
|
+
const hasScopedGitHubTokenRefs = hasAnyScopedValue(settings?.githubTokenRefs) || hasAnyScopedValue(config.githubTokenRefs);
|
|
6494
|
+
const secretRef = normalizedCompanyId ? normalizeSecretRef(config.githubTokenRefs?.[normalizedCompanyId]) ?? normalizeSecretRef(settings?.githubTokenRefs?.[normalizedCompanyId]) ?? (!hasScopedGitHubTokenRefs ? normalizeGitHubTokenRef(config.githubTokenRef) ?? normalizeGitHubTokenRef(settings?.githubTokenRef) : void 0) : normalizeGitHubTokenRef(config.githubTokenRef) ?? normalizeGitHubTokenRef(settings?.githubTokenRef) ?? (() => {
|
|
6495
|
+
const configuredRefs = [
|
|
6496
|
+
...Object.values(config.githubTokenRefs ?? {}),
|
|
6497
|
+
...Object.values(settings?.githubTokenRefs ?? {})
|
|
6498
|
+
].map((value) => normalizeGitHubTokenRef(value)).filter((value) => Boolean(value));
|
|
6499
|
+
const uniqueRefs = [...new Set(configuredRefs)];
|
|
6500
|
+
return uniqueRefs.length === 1 ? uniqueRefs[0] : void 0;
|
|
6501
|
+
})();
|
|
6227
6502
|
if (secretRef) {
|
|
6228
6503
|
return { secretRef };
|
|
6229
6504
|
}
|
|
6230
|
-
const token = normalizeGitHubToken(config.githubToken);
|
|
6505
|
+
const token = !normalizedCompanyId || !hasScopedGitHubTokenRefs ? normalizeGitHubToken(config.githubToken) : void 0;
|
|
6231
6506
|
return token ? { token } : {};
|
|
6232
6507
|
}
|
|
6233
|
-
function getConfiguredGithubTokenRef(settings, config, companyId) {
|
|
6234
|
-
return getConfiguredGithubTokenSource(settings, config, companyId).secretRef;
|
|
6235
|
-
}
|
|
6236
6508
|
function hasConfiguredGithubToken(settings, config, companyId) {
|
|
6237
6509
|
const configuredTokenSource = getConfiguredGithubTokenSource(settings, config, companyId);
|
|
6238
|
-
|
|
6510
|
+
if (configuredTokenSource.secretRef ?? configuredTokenSource.token) {
|
|
6511
|
+
return true;
|
|
6512
|
+
}
|
|
6513
|
+
if (normalizeCompanyId(companyId)) {
|
|
6514
|
+
return false;
|
|
6515
|
+
}
|
|
6516
|
+
return Boolean(
|
|
6517
|
+
settings?.githubTokenRefs && Object.keys(settings.githubTokenRefs).length > 0 || config.githubTokenRefs && Object.keys(config.githubTokenRefs).length > 0
|
|
6518
|
+
);
|
|
6239
6519
|
}
|
|
6240
6520
|
function getSavedPaperclipBoardApiTokenRef(settings, companyId) {
|
|
6241
6521
|
if (!companyId) {
|
|
@@ -6937,6 +7217,7 @@ async function listAllGitHubIssueComments(octokit, repository, issueNumber) {
|
|
|
6937
7217
|
body: typeof comment.body === "string" ? stripNullBytes(comment.body) : "",
|
|
6938
7218
|
url: comment.html_url ?? void 0,
|
|
6939
7219
|
authorLogin: normalizeGitHubUserLogin(comment.user?.login),
|
|
7220
|
+
...normalizeGitHubLowercaseString(comment.author_association) ? { authorAssociation: normalizeGitHubLowercaseString(comment.author_association) } : {},
|
|
6940
7221
|
authorUrl: comment.user?.html_url ?? void 0,
|
|
6941
7222
|
authorAvatarUrl: comment.user?.avatar_url ?? void 0,
|
|
6942
7223
|
createdAt: comment.created_at ?? void 0,
|
|
@@ -8079,7 +8360,7 @@ async function buildProjectPullRequestsPageData(ctx, input) {
|
|
|
8079
8360
|
}
|
|
8080
8361
|
const scope = await requireProjectPullRequestScope(ctx, input, projectMappings);
|
|
8081
8362
|
const config = await getResolvedConfig(ctx);
|
|
8082
|
-
if (!hasConfiguredGithubToken(settings, config, companyId)) {
|
|
8363
|
+
if (!hasConfiguredGithubToken(settings, config, scope.companyId)) {
|
|
8083
8364
|
return {
|
|
8084
8365
|
status: "missing_token",
|
|
8085
8366
|
projectId,
|
|
@@ -8098,7 +8379,7 @@ async function buildProjectPullRequestsPageData(ctx, input) {
|
|
|
8098
8379
|
};
|
|
8099
8380
|
}
|
|
8100
8381
|
try {
|
|
8101
|
-
const octokit = await createGitHubToolOctokit(ctx, companyId);
|
|
8382
|
+
const octokit = await createGitHubToolOctokit(ctx, scope.companyId);
|
|
8102
8383
|
const pageCacheKey = buildProjectPullRequestPageCacheKey(scope, filter, pageIndex, cursor);
|
|
8103
8384
|
const cachedPage = getFreshCacheValue(activeProjectPullRequestPageCache, pageCacheKey);
|
|
8104
8385
|
if (cachedPage) {
|
|
@@ -9171,12 +9452,25 @@ function shouldRunScheduledSync(settings, scheduledAt) {
|
|
|
9171
9452
|
}
|
|
9172
9453
|
return now - lastCheckedAt >= settings.scheduleFrequencyMinutes * 6e4;
|
|
9173
9454
|
}
|
|
9455
|
+
function listScheduledSyncTargets(settings) {
|
|
9456
|
+
const companyIds = [
|
|
9457
|
+
...new Set(
|
|
9458
|
+
settings.mappings.map((mapping) => normalizeCompanyId(mapping.companyId)).filter((companyId) => Boolean(companyId))
|
|
9459
|
+
)
|
|
9460
|
+
];
|
|
9461
|
+
if (companyIds.length === 0) {
|
|
9462
|
+
return settings.mappings.length > 0 ? [void 0] : [];
|
|
9463
|
+
}
|
|
9464
|
+
return companyIds.map((companyId) => getScopedSyncTarget(companyId));
|
|
9465
|
+
}
|
|
9174
9466
|
async function performSync(ctx, trigger, options = {}) {
|
|
9175
|
-
const
|
|
9467
|
+
const targetCompanyId = normalizeCompanyId(options.target?.companyId);
|
|
9468
|
+
const baseSettings = normalizeSettings(await ctx.state.get(SETTINGS_SCOPE));
|
|
9176
9469
|
const config = await getResolvedConfig(ctx);
|
|
9470
|
+
const settings = materializeScopedSettings(baseSettings, config, targetCompanyId);
|
|
9177
9471
|
const importRegistry = normalizeImportRegistry(await ctx.state.get(IMPORT_REGISTRY_SCOPE));
|
|
9178
|
-
const token = typeof options.resolvedToken === "string" ? options.resolvedToken : await resolveGithubToken(ctx);
|
|
9179
|
-
const paperclipApiBaseUrl = getConfiguredPaperclipApiBaseUrl(
|
|
9472
|
+
const token = typeof options.resolvedToken === "string" ? options.resolvedToken : await resolveGithubToken(ctx, { companyId: targetCompanyId });
|
|
9473
|
+
const paperclipApiBaseUrl = getConfiguredPaperclipApiBaseUrl(baseSettings, config, targetCompanyId);
|
|
9180
9474
|
const mappings = getSyncableMappingsForTarget(settings.mappings, options.target);
|
|
9181
9475
|
activePaperclipApiAuthTokensByCompanyId = null;
|
|
9182
9476
|
const failureContext = {
|
|
@@ -9187,18 +9481,14 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
9187
9481
|
...settings,
|
|
9188
9482
|
syncState: createSetupConfigurationErrorSyncState("missing_token", trigger)
|
|
9189
9483
|
};
|
|
9190
|
-
|
|
9191
|
-
await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
|
|
9192
|
-
return next;
|
|
9484
|
+
return saveSettingsSyncState(ctx, settings, next.syncState, targetCompanyId);
|
|
9193
9485
|
}
|
|
9194
9486
|
if (mappings.length === 0) {
|
|
9195
9487
|
const next = {
|
|
9196
9488
|
...settings,
|
|
9197
9489
|
syncState: createSetupConfigurationErrorSyncState("missing_mapping", trigger)
|
|
9198
9490
|
};
|
|
9199
|
-
|
|
9200
|
-
await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
|
|
9201
|
-
return next;
|
|
9491
|
+
return saveSettingsSyncState(ctx, settings, next.syncState, targetCompanyId);
|
|
9202
9492
|
}
|
|
9203
9493
|
const mappingsMissingBoardAccess = getMappingsMissingPaperclipBoardAccess(settings, config, mappings);
|
|
9204
9494
|
if (mappingsMissingBoardAccess.length > 0 && await detectPaperclipBoardAccessRequirement(paperclipApiBaseUrl)) {
|
|
@@ -9206,9 +9496,7 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
9206
9496
|
...settings,
|
|
9207
9497
|
syncState: createSetupConfigurationErrorSyncState("missing_board_access", trigger)
|
|
9208
9498
|
};
|
|
9209
|
-
|
|
9210
|
-
await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
|
|
9211
|
-
return next;
|
|
9499
|
+
return saveSettingsSyncState(ctx, settings, next.syncState, targetCompanyId);
|
|
9212
9500
|
}
|
|
9213
9501
|
if (!ctx.issues || typeof ctx.issues.create !== "function") {
|
|
9214
9502
|
const errorDetails = {
|
|
@@ -9234,9 +9522,7 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
9234
9522
|
)
|
|
9235
9523
|
})
|
|
9236
9524
|
};
|
|
9237
|
-
|
|
9238
|
-
await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
|
|
9239
|
-
return next;
|
|
9525
|
+
return saveSettingsSyncState(ctx, settings, next.syncState, targetCompanyId);
|
|
9240
9526
|
}
|
|
9241
9527
|
activePaperclipApiAuthTokensByCompanyId = await resolvePaperclipApiAuthTokens(ctx, settings, config, mappings);
|
|
9242
9528
|
const octokit = new Octokit({ auth: token });
|
|
@@ -9304,7 +9590,8 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
9304
9590
|
erroredIssuesCount: recoverableFailures.length,
|
|
9305
9591
|
progress,
|
|
9306
9592
|
recentFailures
|
|
9307
|
-
})
|
|
9593
|
+
}),
|
|
9594
|
+
targetCompanyId
|
|
9308
9595
|
);
|
|
9309
9596
|
activeRunningSyncState = currentSettings;
|
|
9310
9597
|
lastProgressPersistedAt = now;
|
|
@@ -9641,10 +9928,9 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
9641
9928
|
recentFailures: buildRecentSyncFailureLogEntries(recoverableFailures)
|
|
9642
9929
|
})
|
|
9643
9930
|
};
|
|
9644
|
-
await ctx.
|
|
9645
|
-
await ctx.state.set(SYNC_STATE_SCOPE, next2.syncState);
|
|
9931
|
+
await saveSettingsSyncState(ctx, currentSettings, next2.syncState, targetCompanyId);
|
|
9646
9932
|
await ctx.state.set(IMPORT_REGISTRY_SCOPE, nextRegistry);
|
|
9647
|
-
return next2;
|
|
9933
|
+
return materializeScopedSettings(next2, config, targetCompanyId);
|
|
9648
9934
|
}
|
|
9649
9935
|
const next = {
|
|
9650
9936
|
...currentSettings,
|
|
@@ -9700,10 +9986,9 @@ async function performSync(ctx, trigger, options = {}) {
|
|
|
9700
9986
|
)
|
|
9701
9987
|
})
|
|
9702
9988
|
};
|
|
9703
|
-
await ctx.
|
|
9704
|
-
await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
|
|
9989
|
+
await saveSettingsSyncState(ctx, currentSettings, next.syncState, targetCompanyId);
|
|
9705
9990
|
await ctx.state.set(IMPORT_REGISTRY_SCOPE, nextRegistry);
|
|
9706
|
-
return next;
|
|
9991
|
+
return materializeScopedSettings(next, config, targetCompanyId);
|
|
9707
9992
|
}
|
|
9708
9993
|
}
|
|
9709
9994
|
async function startSync(ctx, trigger, options = {}) {
|
|
@@ -9721,28 +10006,32 @@ async function startSync(ctx, trigger, options = {}) {
|
|
|
9721
10006
|
getResolvedConfig(ctx),
|
|
9722
10007
|
ctx.state.get(SETTINGS_SCOPE).then((value) => normalizeSettings(value))
|
|
9723
10008
|
]);
|
|
9724
|
-
const targetCompanyId = options.target?.companyId;
|
|
10009
|
+
const targetCompanyId = normalizeCompanyId(options.target?.companyId);
|
|
9725
10010
|
const token = await resolveGithubToken(ctx, {
|
|
10011
|
+
companyId: targetCompanyId,
|
|
9726
10012
|
config,
|
|
9727
|
-
settings: persistedSettings
|
|
9728
|
-
companyId: targetCompanyId
|
|
10013
|
+
settings: persistedSettings
|
|
9729
10014
|
}).catch(() => "");
|
|
9730
|
-
let currentSettings = sanitizeSettingsForCurrentSetup(persistedSettings, {
|
|
10015
|
+
let currentSettings = sanitizeSettingsForCurrentSetup(materializeScopedSettings(persistedSettings, config, targetCompanyId), {
|
|
9731
10016
|
hasToken: Boolean(token.trim()),
|
|
9732
|
-
hasMappings:
|
|
10017
|
+
hasMappings: getSyncableMappingsForScope(persistedSettings.mappings, targetCompanyId).length > 0
|
|
9733
10018
|
});
|
|
9734
|
-
const nextPaperclipApiBaseUrl = trigger === "manual" ? resolveTrustedPaperclipApiBaseUrlInput(options.paperclipApiBaseUrl,
|
|
10019
|
+
const nextPaperclipApiBaseUrl = trigger === "manual" ? resolveTrustedPaperclipApiBaseUrlInput(options.paperclipApiBaseUrl, persistedSettings, config, targetCompanyId) : getConfiguredPaperclipApiBaseUrl(persistedSettings, config, targetCompanyId);
|
|
9735
10020
|
if (nextPaperclipApiBaseUrl !== currentSettings.paperclipApiBaseUrl) {
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
10021
|
+
const nextPersistedSettings = upsertScopedPaperclipApiBaseUrl(
|
|
10022
|
+
{
|
|
10023
|
+
...persistedSettings,
|
|
10024
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10025
|
+
},
|
|
10026
|
+
nextPaperclipApiBaseUrl,
|
|
10027
|
+
targetCompanyId
|
|
10028
|
+
);
|
|
10029
|
+
await ctx.state.set(SETTINGS_SCOPE, nextPersistedSettings);
|
|
9742
10030
|
await ctx.state.set(SYNC_STATE_SCOPE, currentSettings.syncState);
|
|
10031
|
+
currentSettings = materializeScopedSettings(nextPersistedSettings, config, targetCompanyId);
|
|
9743
10032
|
}
|
|
9744
|
-
if (currentSettings !== persistedSettings) {
|
|
9745
|
-
await saveSettingsSyncState(ctx,
|
|
10033
|
+
if (JSON.stringify(currentSettings.syncState) !== JSON.stringify(getScopedSyncState(persistedSettings, targetCompanyId))) {
|
|
10034
|
+
await saveSettingsSyncState(ctx, persistedSettings, currentSettings.syncState, targetCompanyId);
|
|
9746
10035
|
}
|
|
9747
10036
|
if (getActiveGitHubRateLimitPause(currentSettings.syncState)) {
|
|
9748
10037
|
return currentSettings;
|
|
@@ -9771,7 +10060,8 @@ async function startSync(ctx, trigger, options = {}) {
|
|
|
9771
10060
|
...currentSettings,
|
|
9772
10061
|
syncState
|
|
9773
10062
|
};
|
|
9774
|
-
|
|
10063
|
+
activeRunningSyncCompanyId = targetCompanyId;
|
|
10064
|
+
return saveSettingsSyncState(ctx, currentSettings, syncState, targetCompanyId);
|
|
9775
10065
|
})();
|
|
9776
10066
|
activeSyncPromise = (async () => {
|
|
9777
10067
|
try {
|
|
@@ -9781,11 +10071,12 @@ async function startSync(ctx, trigger, options = {}) {
|
|
|
9781
10071
|
target: options.target
|
|
9782
10072
|
});
|
|
9783
10073
|
} catch (error) {
|
|
9784
|
-
return await createUnexpectedSyncErrorResult(ctx, trigger, error);
|
|
10074
|
+
return await createUnexpectedSyncErrorResult(ctx, trigger, error, targetCompanyId);
|
|
9785
10075
|
} finally {
|
|
9786
10076
|
await setSyncCancellationRequest(ctx, null);
|
|
9787
10077
|
activePaperclipApiAuthTokensByCompanyId = null;
|
|
9788
10078
|
activeRunningSyncState = null;
|
|
10079
|
+
activeRunningSyncCompanyId = void 0;
|
|
9789
10080
|
activeSyncPromise = null;
|
|
9790
10081
|
}
|
|
9791
10082
|
})();
|
|
@@ -10575,38 +10866,18 @@ var plugin = definePlugin({
|
|
|
10575
10866
|
const importRegistry = normalizeImportRegistry(await ctx.state.get(IMPORT_REGISTRY_SCOPE));
|
|
10576
10867
|
const normalizedSettings = normalizeSettings(saved);
|
|
10577
10868
|
const config = await getResolvedConfig(ctx);
|
|
10578
|
-
const configuredGitHubTokenRef = requestedCompanyId ? normalizeSecretRef(config.githubTokenRefs?.[requestedCompanyId]) : normalizeGitHubTokenRef(config.githubTokenRef);
|
|
10579
|
-
const savedGitHubTokenRef = getSavedGitHubTokenRef(normalizedSettings, requestedCompanyId);
|
|
10580
|
-
const githubTokenRef = getConfiguredGithubTokenRef(normalizedSettings, config, requestedCompanyId);
|
|
10581
|
-
const githubTokenLogin = getSavedGitHubTokenLogin(normalizedSettings, requestedCompanyId);
|
|
10582
|
-
const paperclipApiBaseUrl = getConfiguredPaperclipApiBaseUrl(normalizedSettings, config);
|
|
10583
10869
|
const githubTokenConfigured = hasConfiguredGithubToken(normalizedSettings, config, requestedCompanyId);
|
|
10584
10870
|
const configuredBoardTokenRef = getConfiguredPaperclipBoardApiTokenRef(config, requestedCompanyId);
|
|
10585
10871
|
const savedBoardTokenRef = getSavedPaperclipBoardApiTokenRef(normalizedSettings, requestedCompanyId);
|
|
10586
|
-
const
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
githubTokenLoginsByCompanyId: {
|
|
10596
|
-
...normalizedSettings.githubTokenLoginsByCompanyId ?? {},
|
|
10597
|
-
[requestedCompanyId]: githubTokenLogin
|
|
10598
|
-
}
|
|
10599
|
-
} : {},
|
|
10600
|
-
...!requestedCompanyId && githubTokenRef ? { githubTokenRef } : {},
|
|
10601
|
-
...!requestedCompanyId && githubTokenLogin ? { githubTokenLogin } : {},
|
|
10602
|
-
...paperclipApiBaseUrl ? { paperclipApiBaseUrl } : {}
|
|
10603
|
-
};
|
|
10604
|
-
const settingsForResponse = sanitizeSettingsForCurrentSetup(settingsWithResolvedToken, {
|
|
10605
|
-
hasToken: githubTokenConfigured,
|
|
10606
|
-
hasMappings: getSyncableMappings(settingsWithResolvedToken.mappings).length > 0
|
|
10607
|
-
});
|
|
10608
|
-
if (settingsForResponse !== normalizedSettings) {
|
|
10609
|
-
await saveSettingsSyncState(ctx, settingsForResponse, settingsForResponse.syncState);
|
|
10872
|
+
const settingsForResponse = sanitizeSettingsForCurrentSetup(
|
|
10873
|
+
materializeScopedSettings(normalizedSettings, config, requestedCompanyId),
|
|
10874
|
+
{
|
|
10875
|
+
hasToken: githubTokenConfigured,
|
|
10876
|
+
hasMappings: getSyncableMappingsForScope(normalizedSettings.mappings, requestedCompanyId).length > 0
|
|
10877
|
+
}
|
|
10878
|
+
);
|
|
10879
|
+
if (JSON.stringify(settingsForResponse.syncState) !== JSON.stringify(getScopedSyncState(normalizedSettings, requestedCompanyId))) {
|
|
10880
|
+
await saveSettingsSyncState(ctx, normalizedSettings, settingsForResponse.syncState, requestedCompanyId);
|
|
10610
10881
|
}
|
|
10611
10882
|
const scopedMappings = filterMappingsByCompany(settingsForResponse.mappings, requestedCompanyId);
|
|
10612
10883
|
const availableAssignees = includeAssignees && requestedCompanyId ? await listAvailableAssignees(ctx, requestedCompanyId) : [];
|
|
@@ -10615,9 +10886,6 @@ var plugin = definePlugin({
|
|
|
10615
10886
|
...includeAssignees ? { availableAssignees } : {},
|
|
10616
10887
|
totalSyncedIssuesCount: countImportedIssuesForMappings(importRegistry, scopedMappings),
|
|
10617
10888
|
githubTokenConfigured,
|
|
10618
|
-
...githubTokenLogin ? { githubTokenLogin } : {},
|
|
10619
|
-
...savedGitHubTokenRef ? { githubTokenConfigSyncRef: savedGitHubTokenRef } : {},
|
|
10620
|
-
githubTokenNeedsConfigSync: Boolean(requestedCompanyId && savedGitHubTokenRef && !configuredGitHubTokenRef),
|
|
10621
10889
|
paperclipBoardAccessConfigured: requestedCompanyId ? hasConfiguredPaperclipBoardAccess(settingsForResponse, config, requestedCompanyId) : hasConfiguredPaperclipBoardAccessForMappings(settingsForResponse, config, scopedMappings),
|
|
10622
10890
|
...savedBoardTokenRef ? { paperclipBoardAccessConfigSyncRef: savedBoardTokenRef } : {},
|
|
10623
10891
|
paperclipBoardAccessNeedsConfigSync: Boolean(savedBoardTokenRef && !configuredBoardTokenRef)
|
|
@@ -10668,34 +10936,38 @@ var plugin = definePlugin({
|
|
|
10668
10936
|
const config = await getResolvedConfig(ctx);
|
|
10669
10937
|
const record = input && typeof input === "object" ? input : {};
|
|
10670
10938
|
const requestedCompanyId = normalizeCompanyId(record.companyId);
|
|
10939
|
+
const requestedGitHubTokenLogin = "githubTokenLogin" in record ? normalizeOptionalString2(record.githubTokenLogin) : void 0;
|
|
10671
10940
|
const hasMappingsPatch = "mappings" in record;
|
|
10672
10941
|
const hasAdvancedSettingsPatch = "advancedSettings" in record;
|
|
10673
|
-
const
|
|
10674
|
-
const githubTokenLogin = "githubTokenLogin" in record ? normalizeOptionalString2(record.githubTokenLogin) : void 0;
|
|
10675
|
-
const inputMappings = hasMappingsPatch ? normalizeMappings(record.mappings) : previous.mappings;
|
|
10942
|
+
const previousScopedSettings = materializeScopedSettings(previous, config, requestedCompanyId);
|
|
10676
10943
|
const nextGitHubTokenRefs = {
|
|
10677
10944
|
...previous.githubTokenRefs ?? {}
|
|
10678
10945
|
};
|
|
10679
|
-
|
|
10680
|
-
|
|
10681
|
-
|
|
10682
|
-
|
|
10683
|
-
...previous.companyAdvancedSettingsByCompanyId ?? {}
|
|
10684
|
-
};
|
|
10685
|
-
if (requestedCompanyId && "githubTokenRef" in record) {
|
|
10686
|
-
if (githubTokenRef) {
|
|
10687
|
-
nextGitHubTokenRefs[requestedCompanyId] = githubTokenRef;
|
|
10688
|
-
} else {
|
|
10689
|
-
delete nextGitHubTokenRefs[requestedCompanyId];
|
|
10946
|
+
if (requestedCompanyId) {
|
|
10947
|
+
const companyScopedGitHubTokenRef = normalizeGitHubTokenRefs(record.githubTokenRefs)?.[requestedCompanyId] ?? ("githubTokenRef" in record ? normalizeGitHubTokenRef(record.githubTokenRef) : void 0);
|
|
10948
|
+
if (companyScopedGitHubTokenRef) {
|
|
10949
|
+
nextGitHubTokenRefs[requestedCompanyId] = companyScopedGitHubTokenRef;
|
|
10690
10950
|
}
|
|
10691
10951
|
}
|
|
10952
|
+
const githubTokenRefs = Object.keys(nextGitHubTokenRefs).length > 0 ? nextGitHubTokenRefs : void 0;
|
|
10953
|
+
const githubTokenRef = !requestedCompanyId && "githubTokenRef" in record ? normalizeGitHubTokenRef(record.githubTokenRef) : normalizeGitHubTokenRef(previous.githubTokenRef) ?? normalizeGitHubTokenRef(config.githubTokenRef);
|
|
10954
|
+
const nextGitHubTokenLoginByCompanyId = {
|
|
10955
|
+
...previous.githubTokenLoginByCompanyId ?? {}
|
|
10956
|
+
};
|
|
10692
10957
|
if (requestedCompanyId && "githubTokenLogin" in record) {
|
|
10693
|
-
|
|
10694
|
-
|
|
10958
|
+
const companyScopedGitHubTokenLogin = requestedGitHubTokenLogin;
|
|
10959
|
+
if (companyScopedGitHubTokenLogin) {
|
|
10960
|
+
nextGitHubTokenLoginByCompanyId[requestedCompanyId] = companyScopedGitHubTokenLogin;
|
|
10695
10961
|
} else {
|
|
10696
|
-
delete
|
|
10962
|
+
delete nextGitHubTokenLoginByCompanyId[requestedCompanyId];
|
|
10697
10963
|
}
|
|
10698
10964
|
}
|
|
10965
|
+
const githubTokenLoginByCompanyId = Object.keys(nextGitHubTokenLoginByCompanyId).length > 0 ? nextGitHubTokenLoginByCompanyId : void 0;
|
|
10966
|
+
const githubTokenLogin = !requestedCompanyId && "githubTokenLogin" in record ? requestedGitHubTokenLogin : previous.githubTokenLogin;
|
|
10967
|
+
const inputMappings = hasMappingsPatch ? normalizeMappings(record.mappings) : previous.mappings;
|
|
10968
|
+
const nextCompanyAdvancedSettingsByCompanyId = {
|
|
10969
|
+
...previous.companyAdvancedSettingsByCompanyId ?? {}
|
|
10970
|
+
};
|
|
10699
10971
|
if (requestedCompanyId && hasAdvancedSettingsPatch) {
|
|
10700
10972
|
nextCompanyAdvancedSettingsByCompanyId[requestedCompanyId] = normalizeAdvancedSettings(record.advancedSettings);
|
|
10701
10973
|
}
|
|
@@ -10706,19 +10978,26 @@ var plugin = definePlugin({
|
|
|
10706
10978
|
companyId: requestedCompanyId
|
|
10707
10979
|
}))
|
|
10708
10980
|
] : inputMappings;
|
|
10709
|
-
|
|
10981
|
+
let current = normalizeSettings({
|
|
10710
10982
|
mappings: mergedMappings,
|
|
10711
10983
|
syncState: previous.syncState,
|
|
10712
|
-
|
|
10713
|
-
|
|
10714
|
-
...
|
|
10715
|
-
...
|
|
10716
|
-
|
|
10984
|
+
...previous.syncStateByCompanyId ? { syncStateByCompanyId: previous.syncStateByCompanyId } : {},
|
|
10985
|
+
scheduleFrequencyMinutes: previous.scheduleFrequencyMinutes,
|
|
10986
|
+
...previous.scheduleFrequencyMinutesByCompanyId ? { scheduleFrequencyMinutesByCompanyId: previous.scheduleFrequencyMinutesByCompanyId } : {},
|
|
10987
|
+
...previous.paperclipApiBaseUrl ? { paperclipApiBaseUrl: previous.paperclipApiBaseUrl } : {},
|
|
10988
|
+
...previous.paperclipApiBaseUrlByCompanyId ? { paperclipApiBaseUrlByCompanyId: previous.paperclipApiBaseUrlByCompanyId } : {},
|
|
10989
|
+
...githubTokenRefs ? { githubTokenRefs } : {},
|
|
10990
|
+
...githubTokenLoginByCompanyId ? { githubTokenLoginByCompanyId } : {},
|
|
10991
|
+
...githubTokenLogin ? { githubTokenLogin } : {},
|
|
10717
10992
|
paperclipBoardApiTokenRefs: previous.paperclipBoardApiTokenRefs,
|
|
10718
10993
|
paperclipBoardAccessIdentityByCompanyId: previous.paperclipBoardAccessIdentityByCompanyId,
|
|
10719
10994
|
...Object.keys(nextCompanyAdvancedSettingsByCompanyId).length > 0 ? { companyAdvancedSettingsByCompanyId: nextCompanyAdvancedSettingsByCompanyId } : {},
|
|
10720
|
-
|
|
10995
|
+
...githubTokenRef ? { githubTokenRef } : {}
|
|
10721
10996
|
});
|
|
10997
|
+
const nextScheduleFrequencyMinutes = "scheduleFrequencyMinutes" in record ? normalizeScheduleFrequencyMinutes(record.scheduleFrequencyMinutes) : getScopedScheduleFrequencyMinutes(previous, requestedCompanyId);
|
|
10998
|
+
current = upsertScopedScheduleFrequencyMinutes(current, nextScheduleFrequencyMinutes, requestedCompanyId);
|
|
10999
|
+
const nextPaperclipApiBaseUrl = "paperclipApiBaseUrl" in record ? resolveTrustedPaperclipApiBaseUrlInput(record.paperclipApiBaseUrl, previous, config, requestedCompanyId) : getConfiguredPaperclipApiBaseUrl(previous, config, requestedCompanyId);
|
|
11000
|
+
current = upsertScopedPaperclipApiBaseUrl(current, nextPaperclipApiBaseUrl, requestedCompanyId);
|
|
10722
11001
|
const nextMappings = current.mappings.map((mapping, index) => ({
|
|
10723
11002
|
id: mapping.id.trim() || createMappingId(index),
|
|
10724
11003
|
repositoryUrl: parseRepositoryReference(mapping.repositoryUrl)?.url ?? mapping.repositoryUrl.trim(),
|
|
@@ -10726,27 +11005,29 @@ var plugin = definePlugin({
|
|
|
10726
11005
|
paperclipProjectId: mapping.paperclipProjectId,
|
|
10727
11006
|
companyId: mapping.companyId
|
|
10728
11007
|
}));
|
|
11008
|
+
const materializedCurrent = materializeScopedSettings(current, config, requestedCompanyId);
|
|
10729
11009
|
const next = sanitizeSettingsForCurrentSetup({
|
|
11010
|
+
...current,
|
|
10730
11011
|
mappings: nextMappings,
|
|
10731
|
-
syncState:
|
|
10732
|
-
scheduleFrequencyMinutes:
|
|
10733
|
-
...
|
|
11012
|
+
syncState: materializedCurrent.syncState,
|
|
11013
|
+
scheduleFrequencyMinutes: materializedCurrent.scheduleFrequencyMinutes,
|
|
11014
|
+
...materializedCurrent.paperclipApiBaseUrl ? { paperclipApiBaseUrl: materializedCurrent.paperclipApiBaseUrl } : {},
|
|
10734
11015
|
...current.githubTokenRefs ? { githubTokenRefs: current.githubTokenRefs } : {},
|
|
10735
|
-
...current.
|
|
11016
|
+
...current.githubTokenLoginByCompanyId ? { githubTokenLoginByCompanyId: current.githubTokenLoginByCompanyId } : {},
|
|
10736
11017
|
...current.githubTokenLogin ? { githubTokenLogin: current.githubTokenLogin } : {},
|
|
10737
11018
|
...current.paperclipBoardApiTokenRefs ? { paperclipBoardApiTokenRefs: current.paperclipBoardApiTokenRefs } : {},
|
|
10738
11019
|
...current.paperclipBoardAccessIdentityByCompanyId ? { paperclipBoardAccessIdentityByCompanyId: current.paperclipBoardAccessIdentityByCompanyId } : {},
|
|
10739
11020
|
...current.companyAdvancedSettingsByCompanyId ? { companyAdvancedSettingsByCompanyId: current.companyAdvancedSettingsByCompanyId } : {},
|
|
10740
|
-
...
|
|
11021
|
+
...githubTokenRef ? { githubTokenRef } : {},
|
|
10741
11022
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10742
11023
|
}, {
|
|
10743
11024
|
hasToken: hasConfiguredGithubToken(current, config, requestedCompanyId),
|
|
10744
|
-
hasMappings:
|
|
11025
|
+
hasMappings: getSyncableMappingsForScope(nextMappings, requestedCompanyId).length > 0
|
|
10745
11026
|
});
|
|
10746
11027
|
await ctx.state.set(SETTINGS_SCOPE, next);
|
|
10747
11028
|
await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
|
|
10748
11029
|
clearGitHubRepositoryTokenCapabilityAudits();
|
|
10749
|
-
const scopedGitHubTokenLogin =
|
|
11030
|
+
const scopedGitHubTokenLogin = (requestedCompanyId && "githubTokenLogin" in record ? requestedGitHubTokenLogin : void 0) ?? getGitHubTokenLogin(next, requestedCompanyId);
|
|
10750
11031
|
return {
|
|
10751
11032
|
...getPublicSettingsForScope(next, requestedCompanyId),
|
|
10752
11033
|
...scopedGitHubTokenLogin ? { githubTokenLogin: scopedGitHubTokenLogin } : {},
|
|
@@ -10787,15 +11068,16 @@ var plugin = definePlugin({
|
|
|
10787
11068
|
paperclipBoardAccessIdentityByCompanyId: _previousPaperclipBoardAccessIdentityByCompanyId,
|
|
10788
11069
|
...previousWithoutBoardAccess
|
|
10789
11070
|
} = previous;
|
|
10790
|
-
const
|
|
11071
|
+
const nextBase = sanitizeSettingsForCurrentSetup({
|
|
10791
11072
|
...previousWithoutBoardAccess,
|
|
10792
11073
|
...Object.keys(nextPaperclipBoardApiTokenRefs).length > 0 ? { paperclipBoardApiTokenRefs: nextPaperclipBoardApiTokenRefs } : {},
|
|
10793
11074
|
...Object.keys(nextPaperclipBoardAccessIdentityByCompanyId).length > 0 ? { paperclipBoardAccessIdentityByCompanyId: nextPaperclipBoardAccessIdentityByCompanyId } : {},
|
|
10794
11075
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10795
11076
|
}, {
|
|
10796
11077
|
hasToken: hasConfiguredGithubToken(previous, config, companyId),
|
|
10797
|
-
hasMappings:
|
|
11078
|
+
hasMappings: getSyncableMappingsForScope(previous.mappings, companyId).length > 0
|
|
10798
11079
|
});
|
|
11080
|
+
const next = materializeScopedSettings(nextBase, config, companyId);
|
|
10799
11081
|
await ctx.state.set(SETTINGS_SCOPE, next);
|
|
10800
11082
|
await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
|
|
10801
11083
|
return {
|
|
@@ -10866,7 +11148,7 @@ var plugin = definePlugin({
|
|
|
10866
11148
|
});
|
|
10867
11149
|
});
|
|
10868
11150
|
ctx.actions.register("sync.cancel", async () => {
|
|
10869
|
-
const currentSettings = await getActiveOrCurrentSyncState(ctx);
|
|
11151
|
+
const currentSettings = await getActiveOrCurrentSyncState(ctx, activeRunningSyncCompanyId);
|
|
10870
11152
|
if (currentSettings.syncState.status !== "running") {
|
|
10871
11153
|
return currentSettings;
|
|
10872
11154
|
}
|
|
@@ -10886,7 +11168,8 @@ var plugin = definePlugin({
|
|
|
10886
11168
|
progress: currentSettings.syncState.progress,
|
|
10887
11169
|
message: CANCELLING_SYNC_MESSAGE,
|
|
10888
11170
|
cancelRequestedAt: cancellationRequest.requestedAt
|
|
10889
|
-
})
|
|
11171
|
+
}),
|
|
11172
|
+
activeRunningSyncCompanyId
|
|
10890
11173
|
);
|
|
10891
11174
|
activeRunningSyncState = next;
|
|
10892
11175
|
return next;
|
|
@@ -10894,10 +11177,22 @@ var plugin = definePlugin({
|
|
|
10894
11177
|
registerGitHubAgentTools(ctx);
|
|
10895
11178
|
ctx.jobs.register("sync.github-issues", async (job) => {
|
|
10896
11179
|
const settings = normalizeSettings(await ctx.state.get(SETTINGS_SCOPE));
|
|
10897
|
-
|
|
11180
|
+
const trigger = job.trigger === "retry" ? "retry" : "schedule";
|
|
11181
|
+
const scheduledTargets = listScheduledSyncTargets(settings);
|
|
11182
|
+
if (scheduledTargets.length === 0) {
|
|
11183
|
+
if (job.trigger === "schedule" && !shouldRunScheduledSync(settings, job.scheduledAt)) {
|
|
11184
|
+
return;
|
|
11185
|
+
}
|
|
11186
|
+
await startSync(ctx, trigger);
|
|
10898
11187
|
return;
|
|
10899
11188
|
}
|
|
10900
|
-
|
|
11189
|
+
for (const target of scheduledTargets) {
|
|
11190
|
+
const scopedSettings = materializeScopedSettings(settings, null, target?.companyId);
|
|
11191
|
+
if (job.trigger === "schedule" && !shouldRunScheduledSync(scopedSettings, job.scheduledAt)) {
|
|
11192
|
+
continue;
|
|
11193
|
+
}
|
|
11194
|
+
await startSync(ctx, trigger, target ? { target } : {});
|
|
11195
|
+
}
|
|
10901
11196
|
});
|
|
10902
11197
|
}
|
|
10903
11198
|
});
|