paperclip-github-plugin 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/worker.js CHANGED
@@ -630,11 +630,13 @@ var COMMENT_ANNOTATION_ENTITY_TYPE = "paperclip-github-plugin.comment-annotation
630
630
  var AI_AUTHORED_COMMENT_FOOTER_PREFIX = "Created by a Paperclip AI agent using ";
631
631
  var HIDDEN_GITHUB_IMPORT_MARKER_PREFIX = "<!-- paperclip-github-plugin-imported-from: ";
632
632
  var HIDDEN_GITHUB_IMPORT_MARKER_SUFFIX = " -->";
633
+ var EMPTY_GITHUB_ISSUE_DESCRIPTION_PLACEHOLDER = "_No description provided on GitHub._";
633
634
  function normalizeCompanyId(value) {
634
635
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
635
636
  }
636
637
  var activeSyncPromise = null;
637
638
  var activeRunningSyncState = null;
639
+ var activeRunningSyncCompanyId;
638
640
  var activePaperclipApiAuthTokensByCompanyId = null;
639
641
  var activeExternalConfigWarningKey = null;
640
642
  var activeProjectPullRequestPageCache = /* @__PURE__ */ new Map();
@@ -710,6 +712,7 @@ var FAILED_STATUS_CONTEXT_STATES = /* @__PURE__ */ new Set(["ERROR", "FAILURE"])
710
712
  var PENDING_STATUS_CONTEXT_STATES = /* @__PURE__ */ new Set(["EXPECTED", "PENDING"]);
711
713
  var GITHUB_REPOSITORY_MAINTAINER_WARMUP_CONCURRENCY = 4;
712
714
  var GITHUB_REPOSITORY_MAINTAINER_ROLE_NAMES = /* @__PURE__ */ new Set(["admin", "maintain"]);
715
+ var GITHUB_REPOSITORY_TRUSTED_AUTHOR_ASSOCIATIONS = /* @__PURE__ */ new Set(["collaborator", "member", "owner"]);
713
716
  var GITHUB_ISSUE_STATUS_SNAPSHOT_QUERY = `
714
717
  query GitHubIssueStatusSnapshot($owner: String!, $repo: String!, $issueNumber: Int!, $after: String) {
715
718
  repository(owner: $owner, name: $repo) {
@@ -1613,6 +1616,20 @@ function normalizeGitHubUserLogin(value) {
1613
1616
  function normalizeGitHubTokenRef(value) {
1614
1617
  return normalizeSecretRef(value);
1615
1618
  }
1619
+ function normalizeGitHubTokenRefs(value) {
1620
+ if (!value || typeof value !== "object") {
1621
+ return void 0;
1622
+ }
1623
+ const entries = Object.entries(value).map(([companyId, secretRef]) => {
1624
+ const normalizedCompanyId = normalizeCompanyId(companyId);
1625
+ const normalizedSecretRef = normalizeGitHubTokenRef(secretRef);
1626
+ return normalizedCompanyId && normalizedSecretRef ? [normalizedCompanyId, normalizedSecretRef] : null;
1627
+ }).filter((entry) => entry !== null);
1628
+ if (entries.length === 0) {
1629
+ return void 0;
1630
+ }
1631
+ return Object.fromEntries(entries);
1632
+ }
1616
1633
  function formatUtcTimestamp(value) {
1617
1634
  const parsed = new Date(value);
1618
1635
  if (Number.isNaN(parsed.getTime())) {
@@ -2410,9 +2427,9 @@ function extractGitHubLinksFromCommentBody(body) {
2410
2427
  return [...links.values()];
2411
2428
  }
2412
2429
  async function buildToolbarSyncState(ctx, input) {
2413
- const settings = await getActiveOrCurrentSyncState(ctx);
2414
- const config = await getResolvedConfig(ctx);
2415
2430
  const companyId = typeof input.companyId === "string" && input.companyId.trim() ? input.companyId.trim() : void 0;
2431
+ const settings = await getActiveOrCurrentSyncState(ctx, companyId);
2432
+ const config = await getResolvedConfig(ctx);
2416
2433
  const githubTokenConfigured = hasConfiguredGithubToken(settings, config, companyId);
2417
2434
  const entityId = typeof input.entityId === "string" && input.entityId.trim() ? input.entityId.trim() : void 0;
2418
2435
  const entityType = typeof input.entityType === "string" && input.entityType.trim() ? input.entityType.trim() : void 0;
@@ -2454,7 +2471,7 @@ async function buildToolbarSyncState(ctx, input) {
2454
2471
  }) : [];
2455
2472
  return {
2456
2473
  kind: "issue",
2457
- visible: Boolean(link),
2474
+ visible: false,
2458
2475
  canRun: githubTokenConfigured && mappings.length > 0,
2459
2476
  label: link?.githubIssueNumber ? `Sync #${link.githubIssueNumber}` : "Sync issue",
2460
2477
  message: link ? `Sync ${link.repositoryUrl.replace(/^https:\/\/github\.com\//, "")} issue #${link.githubIssueNumber}.` : "This Paperclip issue is not linked to GitHub yet.",
@@ -2626,15 +2643,25 @@ function sanitizeSettingsForCurrentSetup(settings, setup) {
2626
2643
  function getPublicSettings(settings) {
2627
2644
  const {
2628
2645
  githubTokenRefs: _githubTokenRefs,
2629
- githubTokenLoginsByCompanyId: _githubTokenLoginsByCompanyId,
2646
+ githubTokenLoginByCompanyId: _githubTokenLoginByCompanyId,
2630
2647
  githubTokenRef: _githubTokenRef,
2631
2648
  paperclipBoardApiTokenRefs: _paperclipBoardApiTokenRefs,
2632
2649
  paperclipBoardAccessIdentityByCompanyId: _paperclipBoardAccessIdentityByCompanyId,
2633
2650
  companyAdvancedSettingsByCompanyId: _companyAdvancedSettingsByCompanyId,
2651
+ syncStateByCompanyId: _syncStateByCompanyId,
2652
+ scheduleFrequencyMinutesByCompanyId: _scheduleFrequencyMinutesByCompanyId,
2653
+ paperclipApiBaseUrlByCompanyId: _paperclipApiBaseUrlByCompanyId,
2634
2654
  ...publicSettings
2635
2655
  } = settings;
2636
2656
  return publicSettings;
2637
2657
  }
2658
+ function getGitHubTokenLogin(settings, companyId) {
2659
+ const normalizedCompanyId = normalizeCompanyId(companyId);
2660
+ if (!normalizedCompanyId) {
2661
+ return void 0;
2662
+ }
2663
+ return normalizeOptionalString2(settings.githubTokenLoginByCompanyId?.[normalizedCompanyId]) ?? normalizeOptionalString2(settings.githubTokenLogin);
2664
+ }
2638
2665
  function getPaperclipBoardAccessIdentity(settings, companyId) {
2639
2666
  const normalizedCompanyId = normalizeCompanyId(companyId);
2640
2667
  if (!normalizedCompanyId) {
@@ -2644,34 +2671,16 @@ function getPaperclipBoardAccessIdentity(settings, companyId) {
2644
2671
  }
2645
2672
  function getPublicSettingsForScope(settings, companyId) {
2646
2673
  const publicSettings = getPublicSettings(settings);
2674
+ const githubTokenLogin = getGitHubTokenLogin(settings, companyId);
2647
2675
  const paperclipBoardAccessIdentity = getPaperclipBoardAccessIdentity(settings, companyId);
2648
2676
  return {
2649
2677
  ...publicSettings,
2650
2678
  mappings: filterMappingsByCompany(publicSettings.mappings, companyId),
2651
2679
  advancedSettings: getCompanyAdvancedSettings(settings, companyId),
2680
+ ...githubTokenLogin ? { githubTokenLogin } : {},
2652
2681
  ...paperclipBoardAccessIdentity ? { paperclipBoardAccessIdentity } : {}
2653
2682
  };
2654
2683
  }
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
2684
  async function listAvailableAssignees(ctx, companyId) {
2676
2685
  try {
2677
2686
  const agents = await ctx.agents.list({
@@ -2852,14 +2861,11 @@ function createSetupConfigurationErrorSyncState(issue, trigger) {
2852
2861
  });
2853
2862
  }
2854
2863
  }
2855
- async function saveSettingsSyncState(ctx, settings, syncState) {
2856
- const next = {
2857
- ...settings,
2858
- syncState
2859
- };
2864
+ async function saveSettingsSyncState(ctx, settings, syncState, companyId) {
2865
+ const next = upsertScopedSyncState(normalizeSettings(settings), syncState, companyId);
2860
2866
  await ctx.state.set(SETTINGS_SCOPE, next);
2861
- await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
2862
- return next;
2867
+ await ctx.state.set(SYNC_STATE_SCOPE, syncState);
2868
+ return materializeScopedSettings(next, null, companyId);
2863
2869
  }
2864
2870
  async function setSyncCancellationRequest(ctx, request) {
2865
2871
  if (request) {
@@ -2884,8 +2890,8 @@ function buildCancelledSyncMessage(target, progress) {
2884
2890
  const completionSummary = completedIssueCount !== void 0 && totalIssueCount !== void 0 ? ` Completed ${Math.min(completedIssueCount, totalIssueCount)} of ${totalIssueCount} issues before stopping.` : "";
2885
2891
  return `${scopeLabel} was cancelled before it finished.${completionSummary}`;
2886
2892
  }
2887
- async function createUnexpectedSyncErrorResult(ctx, trigger, error) {
2888
- const settings = normalizeSettings(await ctx.state.get(SETTINGS_SCOPE));
2893
+ async function createUnexpectedSyncErrorResult(ctx, trigger, error, companyId) {
2894
+ const settings = materializeScopedSettings(normalizeSettings(await ctx.state.get(SETTINGS_SCOPE)), null, companyId);
2889
2895
  const errorDetails = buildSyncErrorDetails(error, {
2890
2896
  phase: "configuration"
2891
2897
  });
@@ -2910,7 +2916,8 @@ async function createUnexpectedSyncErrorResult(ctx, trigger, error) {
2910
2916
  errorDetails
2911
2917
  })
2912
2918
  )
2913
- })
2919
+ }),
2920
+ companyId
2914
2921
  );
2915
2922
  }
2916
2923
  async function waitForSyncResultWithinGracePeriod(promise, timeoutMs) {
@@ -2928,15 +2935,13 @@ async function waitForSyncResultWithinGracePeriod(promise, timeoutMs) {
2928
2935
  }
2929
2936
  }
2930
2937
  }
2931
- async function getActiveOrCurrentSyncState(ctx) {
2932
- if (activeRunningSyncState?.syncState.status === "running") {
2933
- return activeRunningSyncState;
2938
+ async function getActiveOrCurrentSyncState(ctx, companyId) {
2939
+ const normalizedCompanyId = normalizeCompanyId(companyId);
2940
+ if (activeRunningSyncState?.syncState.status === "running" && activeRunningSyncCompanyId === normalizedCompanyId) {
2941
+ return materializeScopedSettings(activeRunningSyncState, null, normalizedCompanyId);
2934
2942
  }
2935
2943
  const current = normalizeSettings(await ctx.state.get(SETTINGS_SCOPE));
2936
- if (current.syncState.status === "running") {
2937
- return current;
2938
- }
2939
- return current;
2944
+ return materializeScopedSettings(current, null, normalizedCompanyId);
2940
2945
  }
2941
2946
  function updateSyncFailureContext(current, next) {
2942
2947
  if ("phase" in next) {
@@ -3060,12 +3065,6 @@ async function readExternalConfig(ctx) {
3060
3065
  }
3061
3066
  }
3062
3067
  function normalizePaperclipBoardApiTokenRefs(value) {
3063
- return normalizeCompanySecretRefs(value);
3064
- }
3065
- function normalizeGitHubTokenRefs(value) {
3066
- return normalizeCompanySecretRefs(value);
3067
- }
3068
- function normalizeCompanySecretRefs(value) {
3069
3068
  if (!value || typeof value !== "object") {
3070
3069
  return void 0;
3071
3070
  }
@@ -3079,7 +3078,7 @@ function normalizeCompanySecretRefs(value) {
3079
3078
  }
3080
3079
  return Object.fromEntries(entries);
3081
3080
  }
3082
- function normalizeGitHubTokenLoginsByCompanyId(value) {
3081
+ function normalizeGitHubTokenLoginByCompanyId(value) {
3083
3082
  if (!value || typeof value !== "object") {
3084
3083
  return void 0;
3085
3084
  }
@@ -3126,14 +3125,14 @@ function normalizeSyncState(value) {
3126
3125
  const recentFailures = normalizeSyncFailureLogEntries(record.recentFailures);
3127
3126
  return {
3128
3127
  status: status === "running" || status === "success" || status === "error" || status === "cancelled" ? status : "idle",
3129
- message: typeof record.message === "string" ? record.message : void 0,
3130
- checkedAt: typeof record.checkedAt === "string" ? record.checkedAt : void 0,
3131
- syncedIssuesCount: typeof record.syncedIssuesCount === "number" ? record.syncedIssuesCount : void 0,
3132
- createdIssuesCount: typeof record.createdIssuesCount === "number" ? record.createdIssuesCount : void 0,
3133
- skippedIssuesCount: typeof record.skippedIssuesCount === "number" ? record.skippedIssuesCount : void 0,
3134
- erroredIssuesCount: typeof record.erroredIssuesCount === "number" ? record.erroredIssuesCount : void 0,
3135
- lastRunTrigger: lastRunTrigger === "manual" || lastRunTrigger === "schedule" || lastRunTrigger === "retry" ? lastRunTrigger : void 0,
3136
- cancelRequestedAt: typeof record.cancelRequestedAt === "string" ? record.cancelRequestedAt : void 0,
3128
+ ...typeof record.message === "string" ? { message: record.message } : {},
3129
+ ...typeof record.checkedAt === "string" ? { checkedAt: record.checkedAt } : {},
3130
+ ...typeof record.syncedIssuesCount === "number" ? { syncedIssuesCount: record.syncedIssuesCount } : {},
3131
+ ...typeof record.createdIssuesCount === "number" ? { createdIssuesCount: record.createdIssuesCount } : {},
3132
+ ...typeof record.skippedIssuesCount === "number" ? { skippedIssuesCount: record.skippedIssuesCount } : {},
3133
+ ...typeof record.erroredIssuesCount === "number" ? { erroredIssuesCount: record.erroredIssuesCount } : {},
3134
+ ...lastRunTrigger === "manual" || lastRunTrigger === "schedule" || lastRunTrigger === "retry" ? { lastRunTrigger } : {},
3135
+ ...typeof record.cancelRequestedAt === "string" ? { cancelRequestedAt: record.cancelRequestedAt } : {},
3137
3136
  ...progress ? { progress } : {},
3138
3137
  ...errorDetails ? { errorDetails } : {},
3139
3138
  ...recentFailures ? { recentFailures } : {}
@@ -3243,6 +3242,26 @@ function normalizeScheduleFrequencyMinutes(value) {
3243
3242
  }
3244
3243
  return Math.floor(numericValue);
3245
3244
  }
3245
+ function normalizeSyncStateByCompanyId(value) {
3246
+ if (!value || typeof value !== "object") {
3247
+ return void 0;
3248
+ }
3249
+ const entries = Object.entries(value).map(([companyId, syncState]) => {
3250
+ const normalizedCompanyId = normalizeCompanyId(companyId);
3251
+ return normalizedCompanyId ? [normalizedCompanyId, normalizeSyncState(syncState)] : null;
3252
+ }).filter((entry) => entry !== null);
3253
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
3254
+ }
3255
+ function normalizeScheduleFrequencyMinutesByCompanyId(value) {
3256
+ if (!value || typeof value !== "object") {
3257
+ return void 0;
3258
+ }
3259
+ const entries = Object.entries(value).map(([companyId, scheduleFrequencyMinutes]) => {
3260
+ const normalizedCompanyId = normalizeCompanyId(companyId);
3261
+ return normalizedCompanyId ? [normalizedCompanyId, normalizeScheduleFrequencyMinutes(scheduleFrequencyMinutes)] : null;
3262
+ }).filter((entry) => entry !== null);
3263
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
3264
+ }
3246
3265
  function normalizePaperclipApiBaseUrl(value) {
3247
3266
  if (typeof value !== "string") {
3248
3267
  return void 0;
@@ -3257,6 +3276,17 @@ function normalizePaperclipApiBaseUrl(value) {
3257
3276
  return void 0;
3258
3277
  }
3259
3278
  }
3279
+ function normalizePaperclipApiBaseUrlByCompanyId(value) {
3280
+ if (!value || typeof value !== "object") {
3281
+ return void 0;
3282
+ }
3283
+ const entries = Object.entries(value).map(([companyId, paperclipApiBaseUrl]) => {
3284
+ const normalizedCompanyId = normalizeCompanyId(companyId);
3285
+ const normalizedPaperclipApiBaseUrl = normalizePaperclipApiBaseUrl(paperclipApiBaseUrl);
3286
+ return normalizedCompanyId && normalizedPaperclipApiBaseUrl ? [normalizedCompanyId, normalizedPaperclipApiBaseUrl] : null;
3287
+ }).filter((entry) => entry !== null);
3288
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
3289
+ }
3260
3290
  function getRuntimePaperclipApiBaseUrl() {
3261
3291
  if (typeof process === "undefined" || !process?.env) {
3262
3292
  return void 0;
@@ -3276,19 +3306,26 @@ function resolvePaperclipApiBaseUrl(...values) {
3276
3306
  }
3277
3307
  return void 0;
3278
3308
  }
3279
- function getConfiguredPaperclipApiBaseUrl(settings, config) {
3280
- return resolvePaperclipApiBaseUrl(config?.paperclipApiBaseUrl, settings?.paperclipApiBaseUrl);
3309
+ function getConfiguredPaperclipApiBaseUrl(settings, config, companyId) {
3310
+ const normalizedCompanyId = normalizeCompanyId(companyId);
3311
+ return normalizedCompanyId ? resolvePaperclipApiBaseUrl(
3312
+ config?.paperclipApiBaseUrl,
3313
+ settings?.paperclipApiBaseUrlByCompanyId?.[normalizedCompanyId],
3314
+ settings?.paperclipApiBaseUrl
3315
+ ) : resolvePaperclipApiBaseUrl(config?.paperclipApiBaseUrl, settings?.paperclipApiBaseUrl);
3281
3316
  }
3282
- function resolveTrustedPaperclipApiBaseUrlInput(value, settings, config) {
3317
+ function resolveTrustedPaperclipApiBaseUrlInput(value, settings, config, companyId) {
3283
3318
  const runtimePaperclipApiBaseUrl = getRuntimePaperclipApiBaseUrl();
3284
3319
  if (runtimePaperclipApiBaseUrl) {
3285
3320
  return runtimePaperclipApiBaseUrl;
3286
3321
  }
3287
3322
  const requestedPaperclipApiBaseUrl = normalizePaperclipApiBaseUrl(value);
3288
3323
  const configuredPaperclipApiBaseUrl = normalizePaperclipApiBaseUrl(config?.paperclipApiBaseUrl);
3324
+ const normalizedCompanyId = normalizeCompanyId(companyId);
3325
+ const savedCompanyPaperclipApiBaseUrl = normalizedCompanyId ? normalizePaperclipApiBaseUrl(settings?.paperclipApiBaseUrlByCompanyId?.[normalizedCompanyId]) : void 0;
3289
3326
  const savedPaperclipApiBaseUrl = normalizePaperclipApiBaseUrl(settings?.paperclipApiBaseUrl);
3290
3327
  if (!requestedPaperclipApiBaseUrl) {
3291
- return configuredPaperclipApiBaseUrl ?? savedPaperclipApiBaseUrl;
3328
+ return configuredPaperclipApiBaseUrl ?? savedCompanyPaperclipApiBaseUrl ?? savedPaperclipApiBaseUrl;
3292
3329
  }
3293
3330
  if (configuredPaperclipApiBaseUrl) {
3294
3331
  if (requestedPaperclipApiBaseUrl !== configuredPaperclipApiBaseUrl) {
@@ -3298,6 +3335,9 @@ function resolveTrustedPaperclipApiBaseUrlInput(value, settings, config) {
3298
3335
  }
3299
3336
  return configuredPaperclipApiBaseUrl;
3300
3337
  }
3338
+ if (savedCompanyPaperclipApiBaseUrl && requestedPaperclipApiBaseUrl === savedCompanyPaperclipApiBaseUrl) {
3339
+ return savedCompanyPaperclipApiBaseUrl;
3340
+ }
3301
3341
  if (savedPaperclipApiBaseUrl && requestedPaperclipApiBaseUrl === savedPaperclipApiBaseUrl) {
3302
3342
  return savedPaperclipApiBaseUrl;
3303
3343
  }
@@ -3310,9 +3350,14 @@ function normalizeSettings(value) {
3310
3350
  return DEFAULT_SETTINGS;
3311
3351
  }
3312
3352
  const record = value;
3353
+ const syncStateByCompanyId = normalizeSyncStateByCompanyId(record.syncStateByCompanyId);
3354
+ const scheduleFrequencyMinutesByCompanyId = normalizeScheduleFrequencyMinutesByCompanyId(
3355
+ record.scheduleFrequencyMinutesByCompanyId
3356
+ );
3313
3357
  const paperclipApiBaseUrl = resolvePaperclipApiBaseUrl(record.paperclipApiBaseUrl);
3358
+ const paperclipApiBaseUrlByCompanyId = normalizePaperclipApiBaseUrlByCompanyId(record.paperclipApiBaseUrlByCompanyId);
3314
3359
  const githubTokenRefs = normalizeGitHubTokenRefs(record.githubTokenRefs);
3315
- const githubTokenLoginsByCompanyId = normalizeGitHubTokenLoginsByCompanyId(record.githubTokenLoginsByCompanyId);
3360
+ const githubTokenLoginByCompanyId = normalizeGitHubTokenLoginByCompanyId(record.githubTokenLoginByCompanyId);
3316
3361
  const githubTokenRef = normalizeGitHubTokenRef(record.githubTokenRef);
3317
3362
  const githubTokenLogin = normalizeOptionalString2(record.githubTokenLogin);
3318
3363
  const paperclipBoardApiTokenRefs = normalizePaperclipBoardApiTokenRefs(record.paperclipBoardApiTokenRefs);
@@ -3323,10 +3368,13 @@ function normalizeSettings(value) {
3323
3368
  return {
3324
3369
  mappings: normalizeMappings(record.mappings),
3325
3370
  syncState: normalizeSyncState(record.syncState),
3371
+ ...syncStateByCompanyId ? { syncStateByCompanyId } : {},
3326
3372
  scheduleFrequencyMinutes: normalizeScheduleFrequencyMinutes(record.scheduleFrequencyMinutes),
3373
+ ...scheduleFrequencyMinutesByCompanyId ? { scheduleFrequencyMinutesByCompanyId } : {},
3327
3374
  ...paperclipApiBaseUrl ? { paperclipApiBaseUrl } : {},
3375
+ ...paperclipApiBaseUrlByCompanyId ? { paperclipApiBaseUrlByCompanyId } : {},
3328
3376
  ...githubTokenRefs ? { githubTokenRefs } : {},
3329
- ...githubTokenLoginsByCompanyId ? { githubTokenLoginsByCompanyId } : {},
3377
+ ...githubTokenLoginByCompanyId ? { githubTokenLoginByCompanyId } : {},
3330
3378
  ...githubTokenRef ? { githubTokenRef } : {},
3331
3379
  ...githubTokenLogin ? { githubTokenLogin } : {},
3332
3380
  ...paperclipBoardApiTokenRefs ? { paperclipBoardApiTokenRefs } : {},
@@ -3335,6 +3383,103 @@ function normalizeSettings(value) {
3335
3383
  updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : void 0
3336
3384
  };
3337
3385
  }
3386
+ function getScopedSyncState(settings, companyId) {
3387
+ const normalizedCompanyId = normalizeCompanyId(companyId);
3388
+ if (!normalizedCompanyId) {
3389
+ return normalizeSyncState(settings.syncState);
3390
+ }
3391
+ const scopedSyncState = settings.syncStateByCompanyId?.[normalizedCompanyId];
3392
+ return scopedSyncState ? normalizeSyncState(scopedSyncState) : normalizeSyncState(settings.syncState);
3393
+ }
3394
+ function getScopedScheduleFrequencyMinutes(settings, companyId) {
3395
+ const normalizedCompanyId = normalizeCompanyId(companyId);
3396
+ if (!normalizedCompanyId) {
3397
+ return normalizeScheduleFrequencyMinutes(settings.scheduleFrequencyMinutes);
3398
+ }
3399
+ const scopedScheduleFrequencyMinutes = settings.scheduleFrequencyMinutesByCompanyId?.[normalizedCompanyId];
3400
+ return scopedScheduleFrequencyMinutes !== void 0 ? normalizeScheduleFrequencyMinutes(scopedScheduleFrequencyMinutes) : normalizeScheduleFrequencyMinutes(settings.scheduleFrequencyMinutes);
3401
+ }
3402
+ function materializeScopedSettings(settings, config, companyId) {
3403
+ const syncState = getScopedSyncState(settings, companyId);
3404
+ const scheduleFrequencyMinutes = getScopedScheduleFrequencyMinutes(settings, companyId);
3405
+ const paperclipApiBaseUrl = getConfiguredPaperclipApiBaseUrl(settings, config, companyId);
3406
+ return {
3407
+ ...settings,
3408
+ syncState,
3409
+ scheduleFrequencyMinutes,
3410
+ ...paperclipApiBaseUrl ? { paperclipApiBaseUrl } : {}
3411
+ };
3412
+ }
3413
+ function upsertScopedSyncState(settings, syncState, companyId) {
3414
+ const normalizedCompanyId = normalizeCompanyId(companyId);
3415
+ if (!normalizedCompanyId) {
3416
+ return {
3417
+ ...settings,
3418
+ syncState
3419
+ };
3420
+ }
3421
+ return {
3422
+ ...settings,
3423
+ syncState,
3424
+ syncStateByCompanyId: {
3425
+ ...settings.syncStateByCompanyId ?? {},
3426
+ [normalizedCompanyId]: syncState
3427
+ }
3428
+ };
3429
+ }
3430
+ function upsertScopedScheduleFrequencyMinutes(settings, scheduleFrequencyMinutes, companyId) {
3431
+ const normalizedCompanyId = normalizeCompanyId(companyId);
3432
+ if (!normalizedCompanyId) {
3433
+ return {
3434
+ ...settings,
3435
+ scheduleFrequencyMinutes
3436
+ };
3437
+ }
3438
+ return {
3439
+ ...settings,
3440
+ scheduleFrequencyMinutes,
3441
+ scheduleFrequencyMinutesByCompanyId: {
3442
+ ...settings.scheduleFrequencyMinutesByCompanyId ?? {},
3443
+ [normalizedCompanyId]: scheduleFrequencyMinutes
3444
+ }
3445
+ };
3446
+ }
3447
+ function upsertScopedPaperclipApiBaseUrl(settings, paperclipApiBaseUrl, companyId) {
3448
+ const normalizedCompanyId = normalizeCompanyId(companyId);
3449
+ if (!normalizedCompanyId) {
3450
+ return {
3451
+ ...settings,
3452
+ ...paperclipApiBaseUrl ? { paperclipApiBaseUrl } : {}
3453
+ };
3454
+ }
3455
+ const nextPaperclipApiBaseUrlByCompanyId = {
3456
+ ...settings.paperclipApiBaseUrlByCompanyId ?? {}
3457
+ };
3458
+ if (paperclipApiBaseUrl) {
3459
+ nextPaperclipApiBaseUrlByCompanyId[normalizedCompanyId] = paperclipApiBaseUrl;
3460
+ } else {
3461
+ delete nextPaperclipApiBaseUrlByCompanyId[normalizedCompanyId];
3462
+ }
3463
+ return {
3464
+ ...settings,
3465
+ ...paperclipApiBaseUrl ? { paperclipApiBaseUrl } : {},
3466
+ ...Object.keys(nextPaperclipApiBaseUrlByCompanyId).length > 0 ? { paperclipApiBaseUrlByCompanyId: nextPaperclipApiBaseUrlByCompanyId } : {}
3467
+ };
3468
+ }
3469
+ function getScopedSyncTarget(companyId) {
3470
+ return {
3471
+ kind: "company",
3472
+ companyId,
3473
+ displayLabel: "this company"
3474
+ };
3475
+ }
3476
+ function getSyncableMappingsForScope(mappings, companyId) {
3477
+ const normalizedCompanyId = normalizeCompanyId(companyId);
3478
+ return normalizedCompanyId ? getSyncableMappingsForTarget(mappings, getScopedSyncTarget(normalizedCompanyId)) : getSyncableMappings(mappings);
3479
+ }
3480
+ function hasAnyScopedValue(valueByCompanyId) {
3481
+ return Boolean(valueByCompanyId && Object.keys(valueByCompanyId).length > 0);
3482
+ }
3338
3483
  function normalizeImportRegistry(value) {
3339
3484
  if (!Array.isArray(value)) {
3340
3485
  return [];
@@ -3494,6 +3639,7 @@ function normalizeGitHubIssueRecord(issue) {
3494
3639
  body: typeof issue.body === "string" ? stripNullBytes(issue.body) : null,
3495
3640
  htmlUrl: issue.html_url,
3496
3641
  ...normalizeGitHubUsername(issue.user?.login) ? { authorLogin: normalizeGitHubUsername(issue.user?.login) } : {},
3642
+ ...normalizeGitHubLowercaseString(issue.author_association) ? { authorAssociation: normalizeGitHubLowercaseString(issue.author_association) } : {},
3497
3643
  labels: normalizeGitHubIssueLabels(issue.labels),
3498
3644
  state: issue.state === "closed" ? "closed" : "open",
3499
3645
  stateReason: normalizeGitHubIssueStateReason(issue.state_reason),
@@ -4274,6 +4420,22 @@ async function getGitHubIssueStatusSnapshot(octokit, repository, issueNumber, gi
4274
4420
  function buildGitHubRepositoryActorCacheKey(repository, login) {
4275
4421
  return `${repository.owner.toLowerCase()}/${repository.repo.toLowerCase()}:${login}`;
4276
4422
  }
4423
+ function getGitHubRepositoryTrustedAuthorStatusFromAssociation(authorAssociation) {
4424
+ const normalizedAssociation = normalizeGitHubLowercaseString(authorAssociation);
4425
+ if (!normalizedAssociation) {
4426
+ return void 0;
4427
+ }
4428
+ return GITHUB_REPOSITORY_TRUSTED_AUTHOR_ASSOCIATIONS.has(normalizedAssociation);
4429
+ }
4430
+ function cacheGitHubRepositoryTrustedAuthorStatusFromAssociation(repository, login, authorAssociation, cache) {
4431
+ const normalizedLogin = normalizeGitHubUserLogin(login);
4432
+ const trustedAuthorStatus = getGitHubRepositoryTrustedAuthorStatusFromAssociation(authorAssociation);
4433
+ if (!normalizedLogin || trustedAuthorStatus === void 0) {
4434
+ return trustedAuthorStatus;
4435
+ }
4436
+ cache.set(buildGitHubRepositoryActorCacheKey(repository, normalizedLogin), trustedAuthorStatus);
4437
+ return trustedAuthorStatus;
4438
+ }
4277
4439
  async function isGitHubUserRepositoryMaintainer(octokit, repository, login, cache) {
4278
4440
  const normalizedLogin = normalizeGitHubUserLogin(login);
4279
4441
  if (!normalizedLogin) {
@@ -4339,6 +4501,7 @@ async function listNewGitHubIssueCommentsSinceCount(octokit, repository, issueNu
4339
4501
  body: typeof comment.body === "string" ? stripNullBytes(comment.body) : "",
4340
4502
  url: comment.html_url ?? void 0,
4341
4503
  authorLogin: normalizeGitHubUserLogin(comment.user?.login),
4504
+ ...normalizeGitHubLowercaseString(comment.author_association) ? { authorAssociation: normalizeGitHubLowercaseString(comment.author_association) } : {},
4342
4505
  createdAt: comment.created_at ?? void 0,
4343
4506
  updatedAt: comment.updated_at ?? void 0
4344
4507
  });
@@ -4380,6 +4543,18 @@ async function hasTrustedNewGitHubIssueComment(params) {
4380
4543
  if (originalPosterLogin && authorLogin === originalPosterLogin) {
4381
4544
  return true;
4382
4545
  }
4546
+ const trustedAuthorStatus = cacheGitHubRepositoryTrustedAuthorStatusFromAssociation(
4547
+ params.repository,
4548
+ authorLogin,
4549
+ comment.authorAssociation,
4550
+ params.maintainerCache
4551
+ );
4552
+ if (trustedAuthorStatus === true) {
4553
+ return true;
4554
+ }
4555
+ if (trustedAuthorStatus === false) {
4556
+ continue;
4557
+ }
4383
4558
  unseenAuthors.add(authorLogin);
4384
4559
  }
4385
4560
  for (const authorLogin of unseenAuthors) {
@@ -4399,6 +4574,15 @@ async function isMaintainerAuthoredGitHubIssue(params) {
4399
4574
  if (!authorLogin) {
4400
4575
  return false;
4401
4576
  }
4577
+ const trustedAuthorStatus = cacheGitHubRepositoryTrustedAuthorStatusFromAssociation(
4578
+ params.repository,
4579
+ authorLogin,
4580
+ params.githubIssue.authorAssociation,
4581
+ params.maintainerCache
4582
+ );
4583
+ if (trustedAuthorStatus !== void 0) {
4584
+ return trustedAuthorStatus;
4585
+ }
4402
4586
  return isGitHubUserRepositoryMaintainer(
4403
4587
  params.octokit,
4404
4588
  params.repository,
@@ -4408,7 +4592,19 @@ async function isMaintainerAuthoredGitHubIssue(params) {
4408
4592
  }
4409
4593
  async function warmGitHubRepositoryMaintainerCache(params) {
4410
4594
  const uniqueAuthorLogins = [...new Set(
4411
- params.githubIssues.map((issue) => normalizeGitHubUserLogin(issue.authorLogin)).filter((authorLogin) => Boolean(authorLogin))
4595
+ params.githubIssues.flatMap((issue) => {
4596
+ const authorLogin = normalizeGitHubUserLogin(issue.authorLogin);
4597
+ if (!authorLogin) {
4598
+ return [];
4599
+ }
4600
+ const trustedAuthorStatus = cacheGitHubRepositoryTrustedAuthorStatusFromAssociation(
4601
+ params.repository,
4602
+ authorLogin,
4603
+ issue.authorAssociation,
4604
+ params.maintainerCache
4605
+ );
4606
+ return trustedAuthorStatus === void 0 ? [authorLogin] : [];
4607
+ })
4412
4608
  )].filter((authorLogin) => !params.maintainerCache.has(buildGitHubRepositoryActorCacheKey(params.repository, authorLogin)));
4413
4609
  if (uniqueAuthorLogins.length === 0) {
4414
4610
  return;
@@ -4623,7 +4819,9 @@ function buildPaperclipIssueDescription(issue, linkedPullRequestNumbers = []) {
4623
4819
  return normalizedBody ?? "";
4624
4820
  }
4625
4821
  if (!normalizedBody) {
4626
- return hiddenImportMarker;
4822
+ return `${EMPTY_GITHUB_ISSUE_DESCRIPTION_PLACEHOLDER}
4823
+
4824
+ ${hiddenImportMarker}`;
4627
4825
  }
4628
4826
  return `${normalizedBody}
4629
4827
 
@@ -6223,19 +6421,32 @@ async function getResolvedConfig(ctx) {
6223
6421
  }
6224
6422
  function getConfiguredGithubTokenSource(settings, config, companyId) {
6225
6423
  const normalizedCompanyId = normalizeCompanyId(companyId);
6226
- const secretRef = (normalizedCompanyId ? normalizeSecretRef(config.githubTokenRefs?.[normalizedCompanyId]) ?? getSavedGitHubTokenRef(settings, normalizedCompanyId) : void 0) ?? normalizeGitHubTokenRef(config.githubTokenRef) ?? getSavedGitHubTokenRef(settings);
6424
+ const hasScopedGitHubTokenRefs = hasAnyScopedValue(settings?.githubTokenRefs) || hasAnyScopedValue(config.githubTokenRefs);
6425
+ 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) ?? (() => {
6426
+ const configuredRefs = [
6427
+ ...Object.values(config.githubTokenRefs ?? {}),
6428
+ ...Object.values(settings?.githubTokenRefs ?? {})
6429
+ ].map((value) => normalizeGitHubTokenRef(value)).filter((value) => Boolean(value));
6430
+ const uniqueRefs = [...new Set(configuredRefs)];
6431
+ return uniqueRefs.length === 1 ? uniqueRefs[0] : void 0;
6432
+ })();
6227
6433
  if (secretRef) {
6228
6434
  return { secretRef };
6229
6435
  }
6230
- const token = normalizeGitHubToken(config.githubToken);
6436
+ const token = !normalizedCompanyId || !hasScopedGitHubTokenRefs ? normalizeGitHubToken(config.githubToken) : void 0;
6231
6437
  return token ? { token } : {};
6232
6438
  }
6233
- function getConfiguredGithubTokenRef(settings, config, companyId) {
6234
- return getConfiguredGithubTokenSource(settings, config, companyId).secretRef;
6235
- }
6236
6439
  function hasConfiguredGithubToken(settings, config, companyId) {
6237
6440
  const configuredTokenSource = getConfiguredGithubTokenSource(settings, config, companyId);
6238
- return Boolean(configuredTokenSource.secretRef ?? configuredTokenSource.token);
6441
+ if (configuredTokenSource.secretRef ?? configuredTokenSource.token) {
6442
+ return true;
6443
+ }
6444
+ if (normalizeCompanyId(companyId)) {
6445
+ return false;
6446
+ }
6447
+ return Boolean(
6448
+ settings?.githubTokenRefs && Object.keys(settings.githubTokenRefs).length > 0 || config.githubTokenRefs && Object.keys(config.githubTokenRefs).length > 0
6449
+ );
6239
6450
  }
6240
6451
  function getSavedPaperclipBoardApiTokenRef(settings, companyId) {
6241
6452
  if (!companyId) {
@@ -6937,6 +7148,7 @@ async function listAllGitHubIssueComments(octokit, repository, issueNumber) {
6937
7148
  body: typeof comment.body === "string" ? stripNullBytes(comment.body) : "",
6938
7149
  url: comment.html_url ?? void 0,
6939
7150
  authorLogin: normalizeGitHubUserLogin(comment.user?.login),
7151
+ ...normalizeGitHubLowercaseString(comment.author_association) ? { authorAssociation: normalizeGitHubLowercaseString(comment.author_association) } : {},
6940
7152
  authorUrl: comment.user?.html_url ?? void 0,
6941
7153
  authorAvatarUrl: comment.user?.avatar_url ?? void 0,
6942
7154
  createdAt: comment.created_at ?? void 0,
@@ -8079,7 +8291,7 @@ async function buildProjectPullRequestsPageData(ctx, input) {
8079
8291
  }
8080
8292
  const scope = await requireProjectPullRequestScope(ctx, input, projectMappings);
8081
8293
  const config = await getResolvedConfig(ctx);
8082
- if (!hasConfiguredGithubToken(settings, config, companyId)) {
8294
+ if (!hasConfiguredGithubToken(settings, config, scope.companyId)) {
8083
8295
  return {
8084
8296
  status: "missing_token",
8085
8297
  projectId,
@@ -8098,7 +8310,7 @@ async function buildProjectPullRequestsPageData(ctx, input) {
8098
8310
  };
8099
8311
  }
8100
8312
  try {
8101
- const octokit = await createGitHubToolOctokit(ctx, companyId);
8313
+ const octokit = await createGitHubToolOctokit(ctx, scope.companyId);
8102
8314
  const pageCacheKey = buildProjectPullRequestPageCacheKey(scope, filter, pageIndex, cursor);
8103
8315
  const cachedPage = getFreshCacheValue(activeProjectPullRequestPageCache, pageCacheKey);
8104
8316
  if (cachedPage) {
@@ -9171,12 +9383,25 @@ function shouldRunScheduledSync(settings, scheduledAt) {
9171
9383
  }
9172
9384
  return now - lastCheckedAt >= settings.scheduleFrequencyMinutes * 6e4;
9173
9385
  }
9386
+ function listScheduledSyncTargets(settings) {
9387
+ const companyIds = [
9388
+ ...new Set(
9389
+ settings.mappings.map((mapping) => normalizeCompanyId(mapping.companyId)).filter((companyId) => Boolean(companyId))
9390
+ )
9391
+ ];
9392
+ if (companyIds.length === 0) {
9393
+ return settings.mappings.length > 0 ? [void 0] : [];
9394
+ }
9395
+ return companyIds.map((companyId) => getScopedSyncTarget(companyId));
9396
+ }
9174
9397
  async function performSync(ctx, trigger, options = {}) {
9175
- const settings = normalizeSettings(await ctx.state.get(SETTINGS_SCOPE));
9398
+ const targetCompanyId = normalizeCompanyId(options.target?.companyId);
9399
+ const baseSettings = normalizeSettings(await ctx.state.get(SETTINGS_SCOPE));
9176
9400
  const config = await getResolvedConfig(ctx);
9401
+ const settings = materializeScopedSettings(baseSettings, config, targetCompanyId);
9177
9402
  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(settings, config);
9403
+ const token = typeof options.resolvedToken === "string" ? options.resolvedToken : await resolveGithubToken(ctx, { companyId: targetCompanyId });
9404
+ const paperclipApiBaseUrl = getConfiguredPaperclipApiBaseUrl(baseSettings, config, targetCompanyId);
9180
9405
  const mappings = getSyncableMappingsForTarget(settings.mappings, options.target);
9181
9406
  activePaperclipApiAuthTokensByCompanyId = null;
9182
9407
  const failureContext = {
@@ -9187,18 +9412,14 @@ async function performSync(ctx, trigger, options = {}) {
9187
9412
  ...settings,
9188
9413
  syncState: createSetupConfigurationErrorSyncState("missing_token", trigger)
9189
9414
  };
9190
- await ctx.state.set(SETTINGS_SCOPE, next);
9191
- await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
9192
- return next;
9415
+ return saveSettingsSyncState(ctx, settings, next.syncState, targetCompanyId);
9193
9416
  }
9194
9417
  if (mappings.length === 0) {
9195
9418
  const next = {
9196
9419
  ...settings,
9197
9420
  syncState: createSetupConfigurationErrorSyncState("missing_mapping", trigger)
9198
9421
  };
9199
- await ctx.state.set(SETTINGS_SCOPE, next);
9200
- await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
9201
- return next;
9422
+ return saveSettingsSyncState(ctx, settings, next.syncState, targetCompanyId);
9202
9423
  }
9203
9424
  const mappingsMissingBoardAccess = getMappingsMissingPaperclipBoardAccess(settings, config, mappings);
9204
9425
  if (mappingsMissingBoardAccess.length > 0 && await detectPaperclipBoardAccessRequirement(paperclipApiBaseUrl)) {
@@ -9206,9 +9427,7 @@ async function performSync(ctx, trigger, options = {}) {
9206
9427
  ...settings,
9207
9428
  syncState: createSetupConfigurationErrorSyncState("missing_board_access", trigger)
9208
9429
  };
9209
- await ctx.state.set(SETTINGS_SCOPE, next);
9210
- await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
9211
- return next;
9430
+ return saveSettingsSyncState(ctx, settings, next.syncState, targetCompanyId);
9212
9431
  }
9213
9432
  if (!ctx.issues || typeof ctx.issues.create !== "function") {
9214
9433
  const errorDetails = {
@@ -9234,9 +9453,7 @@ async function performSync(ctx, trigger, options = {}) {
9234
9453
  )
9235
9454
  })
9236
9455
  };
9237
- await ctx.state.set(SETTINGS_SCOPE, next);
9238
- await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
9239
- return next;
9456
+ return saveSettingsSyncState(ctx, settings, next.syncState, targetCompanyId);
9240
9457
  }
9241
9458
  activePaperclipApiAuthTokensByCompanyId = await resolvePaperclipApiAuthTokens(ctx, settings, config, mappings);
9242
9459
  const octokit = new Octokit({ auth: token });
@@ -9304,7 +9521,8 @@ async function performSync(ctx, trigger, options = {}) {
9304
9521
  erroredIssuesCount: recoverableFailures.length,
9305
9522
  progress,
9306
9523
  recentFailures
9307
- })
9524
+ }),
9525
+ targetCompanyId
9308
9526
  );
9309
9527
  activeRunningSyncState = currentSettings;
9310
9528
  lastProgressPersistedAt = now;
@@ -9641,10 +9859,9 @@ async function performSync(ctx, trigger, options = {}) {
9641
9859
  recentFailures: buildRecentSyncFailureLogEntries(recoverableFailures)
9642
9860
  })
9643
9861
  };
9644
- await ctx.state.set(SETTINGS_SCOPE, next2);
9645
- await ctx.state.set(SYNC_STATE_SCOPE, next2.syncState);
9862
+ await saveSettingsSyncState(ctx, currentSettings, next2.syncState, targetCompanyId);
9646
9863
  await ctx.state.set(IMPORT_REGISTRY_SCOPE, nextRegistry);
9647
- return next2;
9864
+ return materializeScopedSettings(next2, config, targetCompanyId);
9648
9865
  }
9649
9866
  const next = {
9650
9867
  ...currentSettings,
@@ -9700,10 +9917,9 @@ async function performSync(ctx, trigger, options = {}) {
9700
9917
  )
9701
9918
  })
9702
9919
  };
9703
- await ctx.state.set(SETTINGS_SCOPE, next);
9704
- await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
9920
+ await saveSettingsSyncState(ctx, currentSettings, next.syncState, targetCompanyId);
9705
9921
  await ctx.state.set(IMPORT_REGISTRY_SCOPE, nextRegistry);
9706
- return next;
9922
+ return materializeScopedSettings(next, config, targetCompanyId);
9707
9923
  }
9708
9924
  }
9709
9925
  async function startSync(ctx, trigger, options = {}) {
@@ -9721,28 +9937,32 @@ async function startSync(ctx, trigger, options = {}) {
9721
9937
  getResolvedConfig(ctx),
9722
9938
  ctx.state.get(SETTINGS_SCOPE).then((value) => normalizeSettings(value))
9723
9939
  ]);
9724
- const targetCompanyId = options.target?.companyId;
9940
+ const targetCompanyId = normalizeCompanyId(options.target?.companyId);
9725
9941
  const token = await resolveGithubToken(ctx, {
9942
+ companyId: targetCompanyId,
9726
9943
  config,
9727
- settings: persistedSettings,
9728
- companyId: targetCompanyId
9944
+ settings: persistedSettings
9729
9945
  }).catch(() => "");
9730
- let currentSettings = sanitizeSettingsForCurrentSetup(persistedSettings, {
9946
+ let currentSettings = sanitizeSettingsForCurrentSetup(materializeScopedSettings(persistedSettings, config, targetCompanyId), {
9731
9947
  hasToken: Boolean(token.trim()),
9732
- hasMappings: getSyncableMappings(persistedSettings.mappings).length > 0
9948
+ hasMappings: getSyncableMappingsForScope(persistedSettings.mappings, targetCompanyId).length > 0
9733
9949
  });
9734
- const nextPaperclipApiBaseUrl = trigger === "manual" ? resolveTrustedPaperclipApiBaseUrlInput(options.paperclipApiBaseUrl, currentSettings, config) : getConfiguredPaperclipApiBaseUrl(currentSettings, config);
9950
+ const nextPaperclipApiBaseUrl = trigger === "manual" ? resolveTrustedPaperclipApiBaseUrlInput(options.paperclipApiBaseUrl, persistedSettings, config, targetCompanyId) : getConfiguredPaperclipApiBaseUrl(persistedSettings, config, targetCompanyId);
9735
9951
  if (nextPaperclipApiBaseUrl !== currentSettings.paperclipApiBaseUrl) {
9736
- currentSettings = {
9737
- ...currentSettings,
9738
- ...nextPaperclipApiBaseUrl ? { paperclipApiBaseUrl: nextPaperclipApiBaseUrl } : {},
9739
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9740
- };
9741
- await ctx.state.set(SETTINGS_SCOPE, currentSettings);
9952
+ const nextPersistedSettings = upsertScopedPaperclipApiBaseUrl(
9953
+ {
9954
+ ...persistedSettings,
9955
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
9956
+ },
9957
+ nextPaperclipApiBaseUrl,
9958
+ targetCompanyId
9959
+ );
9960
+ await ctx.state.set(SETTINGS_SCOPE, nextPersistedSettings);
9742
9961
  await ctx.state.set(SYNC_STATE_SCOPE, currentSettings.syncState);
9962
+ currentSettings = materializeScopedSettings(nextPersistedSettings, config, targetCompanyId);
9743
9963
  }
9744
- if (currentSettings !== persistedSettings) {
9745
- await saveSettingsSyncState(ctx, currentSettings, currentSettings.syncState);
9964
+ if (JSON.stringify(currentSettings.syncState) !== JSON.stringify(getScopedSyncState(persistedSettings, targetCompanyId))) {
9965
+ await saveSettingsSyncState(ctx, persistedSettings, currentSettings.syncState, targetCompanyId);
9746
9966
  }
9747
9967
  if (getActiveGitHubRateLimitPause(currentSettings.syncState)) {
9748
9968
  return currentSettings;
@@ -9771,7 +9991,8 @@ async function startSync(ctx, trigger, options = {}) {
9771
9991
  ...currentSettings,
9772
9992
  syncState
9773
9993
  };
9774
- return saveSettingsSyncState(ctx, currentSettings, syncState);
9994
+ activeRunningSyncCompanyId = targetCompanyId;
9995
+ return saveSettingsSyncState(ctx, currentSettings, syncState, targetCompanyId);
9775
9996
  })();
9776
9997
  activeSyncPromise = (async () => {
9777
9998
  try {
@@ -9781,11 +10002,12 @@ async function startSync(ctx, trigger, options = {}) {
9781
10002
  target: options.target
9782
10003
  });
9783
10004
  } catch (error) {
9784
- return await createUnexpectedSyncErrorResult(ctx, trigger, error);
10005
+ return await createUnexpectedSyncErrorResult(ctx, trigger, error, targetCompanyId);
9785
10006
  } finally {
9786
10007
  await setSyncCancellationRequest(ctx, null);
9787
10008
  activePaperclipApiAuthTokensByCompanyId = null;
9788
10009
  activeRunningSyncState = null;
10010
+ activeRunningSyncCompanyId = void 0;
9789
10011
  activeSyncPromise = null;
9790
10012
  }
9791
10013
  })();
@@ -10575,38 +10797,18 @@ var plugin = definePlugin({
10575
10797
  const importRegistry = normalizeImportRegistry(await ctx.state.get(IMPORT_REGISTRY_SCOPE));
10576
10798
  const normalizedSettings = normalizeSettings(saved);
10577
10799
  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
10800
  const githubTokenConfigured = hasConfiguredGithubToken(normalizedSettings, config, requestedCompanyId);
10584
10801
  const configuredBoardTokenRef = getConfiguredPaperclipBoardApiTokenRef(config, requestedCompanyId);
10585
10802
  const savedBoardTokenRef = getSavedPaperclipBoardApiTokenRef(normalizedSettings, requestedCompanyId);
10586
- const settingsWithResolvedToken = githubTokenRef === getSavedGitHubTokenRef(normalizedSettings, requestedCompanyId) && githubTokenLogin === getSavedGitHubTokenLogin(normalizedSettings, requestedCompanyId) && paperclipApiBaseUrl === normalizedSettings.paperclipApiBaseUrl ? normalizedSettings : {
10587
- ...normalizedSettings,
10588
- ...requestedCompanyId && githubTokenRef ? {
10589
- githubTokenRefs: {
10590
- ...normalizedSettings.githubTokenRefs ?? {},
10591
- [requestedCompanyId]: githubTokenRef
10592
- }
10593
- } : {},
10594
- ...requestedCompanyId && githubTokenLogin ? {
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);
10803
+ const settingsForResponse = sanitizeSettingsForCurrentSetup(
10804
+ materializeScopedSettings(normalizedSettings, config, requestedCompanyId),
10805
+ {
10806
+ hasToken: githubTokenConfigured,
10807
+ hasMappings: getSyncableMappingsForScope(normalizedSettings.mappings, requestedCompanyId).length > 0
10808
+ }
10809
+ );
10810
+ if (JSON.stringify(settingsForResponse.syncState) !== JSON.stringify(getScopedSyncState(normalizedSettings, requestedCompanyId))) {
10811
+ await saveSettingsSyncState(ctx, normalizedSettings, settingsForResponse.syncState, requestedCompanyId);
10610
10812
  }
10611
10813
  const scopedMappings = filterMappingsByCompany(settingsForResponse.mappings, requestedCompanyId);
10612
10814
  const availableAssignees = includeAssignees && requestedCompanyId ? await listAvailableAssignees(ctx, requestedCompanyId) : [];
@@ -10615,9 +10817,6 @@ var plugin = definePlugin({
10615
10817
  ...includeAssignees ? { availableAssignees } : {},
10616
10818
  totalSyncedIssuesCount: countImportedIssuesForMappings(importRegistry, scopedMappings),
10617
10819
  githubTokenConfigured,
10618
- ...githubTokenLogin ? { githubTokenLogin } : {},
10619
- ...savedGitHubTokenRef ? { githubTokenConfigSyncRef: savedGitHubTokenRef } : {},
10620
- githubTokenNeedsConfigSync: Boolean(requestedCompanyId && savedGitHubTokenRef && !configuredGitHubTokenRef),
10621
10820
  paperclipBoardAccessConfigured: requestedCompanyId ? hasConfiguredPaperclipBoardAccess(settingsForResponse, config, requestedCompanyId) : hasConfiguredPaperclipBoardAccessForMappings(settingsForResponse, config, scopedMappings),
10622
10821
  ...savedBoardTokenRef ? { paperclipBoardAccessConfigSyncRef: savedBoardTokenRef } : {},
10623
10822
  paperclipBoardAccessNeedsConfigSync: Boolean(savedBoardTokenRef && !configuredBoardTokenRef)
@@ -10668,34 +10867,38 @@ var plugin = definePlugin({
10668
10867
  const config = await getResolvedConfig(ctx);
10669
10868
  const record = input && typeof input === "object" ? input : {};
10670
10869
  const requestedCompanyId = normalizeCompanyId(record.companyId);
10870
+ const requestedGitHubTokenLogin = "githubTokenLogin" in record ? normalizeOptionalString2(record.githubTokenLogin) : void 0;
10671
10871
  const hasMappingsPatch = "mappings" in record;
10672
10872
  const hasAdvancedSettingsPatch = "advancedSettings" in record;
10673
- const githubTokenRef = "githubTokenRef" in record ? normalizeGitHubTokenRef(record.githubTokenRef) : void 0;
10674
- const githubTokenLogin = "githubTokenLogin" in record ? normalizeOptionalString2(record.githubTokenLogin) : void 0;
10675
- const inputMappings = hasMappingsPatch ? normalizeMappings(record.mappings) : previous.mappings;
10873
+ const previousScopedSettings = materializeScopedSettings(previous, config, requestedCompanyId);
10676
10874
  const nextGitHubTokenRefs = {
10677
10875
  ...previous.githubTokenRefs ?? {}
10678
10876
  };
10679
- const nextGitHubTokenLoginsByCompanyId = {
10680
- ...previous.githubTokenLoginsByCompanyId ?? {}
10681
- };
10682
- const nextCompanyAdvancedSettingsByCompanyId = {
10683
- ...previous.companyAdvancedSettingsByCompanyId ?? {}
10684
- };
10685
- if (requestedCompanyId && "githubTokenRef" in record) {
10686
- if (githubTokenRef) {
10687
- nextGitHubTokenRefs[requestedCompanyId] = githubTokenRef;
10688
- } else {
10689
- delete nextGitHubTokenRefs[requestedCompanyId];
10877
+ if (requestedCompanyId) {
10878
+ const companyScopedGitHubTokenRef = normalizeGitHubTokenRefs(record.githubTokenRefs)?.[requestedCompanyId] ?? ("githubTokenRef" in record ? normalizeGitHubTokenRef(record.githubTokenRef) : void 0);
10879
+ if (companyScopedGitHubTokenRef) {
10880
+ nextGitHubTokenRefs[requestedCompanyId] = companyScopedGitHubTokenRef;
10690
10881
  }
10691
10882
  }
10883
+ const githubTokenRefs = Object.keys(nextGitHubTokenRefs).length > 0 ? nextGitHubTokenRefs : void 0;
10884
+ const githubTokenRef = !requestedCompanyId && "githubTokenRef" in record ? normalizeGitHubTokenRef(record.githubTokenRef) : normalizeGitHubTokenRef(previous.githubTokenRef) ?? normalizeGitHubTokenRef(config.githubTokenRef);
10885
+ const nextGitHubTokenLoginByCompanyId = {
10886
+ ...previous.githubTokenLoginByCompanyId ?? {}
10887
+ };
10692
10888
  if (requestedCompanyId && "githubTokenLogin" in record) {
10693
- if (githubTokenLogin) {
10694
- nextGitHubTokenLoginsByCompanyId[requestedCompanyId] = githubTokenLogin;
10889
+ const companyScopedGitHubTokenLogin = requestedGitHubTokenLogin;
10890
+ if (companyScopedGitHubTokenLogin) {
10891
+ nextGitHubTokenLoginByCompanyId[requestedCompanyId] = companyScopedGitHubTokenLogin;
10695
10892
  } else {
10696
- delete nextGitHubTokenLoginsByCompanyId[requestedCompanyId];
10893
+ delete nextGitHubTokenLoginByCompanyId[requestedCompanyId];
10697
10894
  }
10698
10895
  }
10896
+ const githubTokenLoginByCompanyId = Object.keys(nextGitHubTokenLoginByCompanyId).length > 0 ? nextGitHubTokenLoginByCompanyId : void 0;
10897
+ const githubTokenLogin = !requestedCompanyId && "githubTokenLogin" in record ? requestedGitHubTokenLogin : previous.githubTokenLogin;
10898
+ const inputMappings = hasMappingsPatch ? normalizeMappings(record.mappings) : previous.mappings;
10899
+ const nextCompanyAdvancedSettingsByCompanyId = {
10900
+ ...previous.companyAdvancedSettingsByCompanyId ?? {}
10901
+ };
10699
10902
  if (requestedCompanyId && hasAdvancedSettingsPatch) {
10700
10903
  nextCompanyAdvancedSettingsByCompanyId[requestedCompanyId] = normalizeAdvancedSettings(record.advancedSettings);
10701
10904
  }
@@ -10706,19 +10909,26 @@ var plugin = definePlugin({
10706
10909
  companyId: requestedCompanyId
10707
10910
  }))
10708
10911
  ] : inputMappings;
10709
- const current = normalizeSettings({
10912
+ let current = normalizeSettings({
10710
10913
  mappings: mergedMappings,
10711
10914
  syncState: previous.syncState,
10712
- scheduleFrequencyMinutes: "scheduleFrequencyMinutes" in record ? record.scheduleFrequencyMinutes : previous.scheduleFrequencyMinutes,
10713
- paperclipApiBaseUrl: "paperclipApiBaseUrl" in record ? resolveTrustedPaperclipApiBaseUrlInput(record.paperclipApiBaseUrl, previous, config) : getConfiguredPaperclipApiBaseUrl(previous, config),
10714
- ...Object.keys(nextGitHubTokenRefs).length > 0 ? { githubTokenRefs: nextGitHubTokenRefs } : {},
10715
- ...Object.keys(nextGitHubTokenLoginsByCompanyId).length > 0 ? { githubTokenLoginsByCompanyId: nextGitHubTokenLoginsByCompanyId } : {},
10716
- ...!requestedCompanyId ? githubTokenLogin ? { githubTokenLogin } : previous.githubTokenLogin ? { githubTokenLogin: previous.githubTokenLogin } : {} : {},
10915
+ ...previous.syncStateByCompanyId ? { syncStateByCompanyId: previous.syncStateByCompanyId } : {},
10916
+ scheduleFrequencyMinutes: previous.scheduleFrequencyMinutes,
10917
+ ...previous.scheduleFrequencyMinutesByCompanyId ? { scheduleFrequencyMinutesByCompanyId: previous.scheduleFrequencyMinutesByCompanyId } : {},
10918
+ ...previous.paperclipApiBaseUrl ? { paperclipApiBaseUrl: previous.paperclipApiBaseUrl } : {},
10919
+ ...previous.paperclipApiBaseUrlByCompanyId ? { paperclipApiBaseUrlByCompanyId: previous.paperclipApiBaseUrlByCompanyId } : {},
10920
+ ...githubTokenRefs ? { githubTokenRefs } : {},
10921
+ ...githubTokenLoginByCompanyId ? { githubTokenLoginByCompanyId } : {},
10922
+ ...githubTokenLogin ? { githubTokenLogin } : {},
10717
10923
  paperclipBoardApiTokenRefs: previous.paperclipBoardApiTokenRefs,
10718
10924
  paperclipBoardAccessIdentityByCompanyId: previous.paperclipBoardAccessIdentityByCompanyId,
10719
10925
  ...Object.keys(nextCompanyAdvancedSettingsByCompanyId).length > 0 ? { companyAdvancedSettingsByCompanyId: nextCompanyAdvancedSettingsByCompanyId } : {},
10720
- ...!requestedCompanyId ? githubTokenRef ? { githubTokenRef } : previous.githubTokenRef ? { githubTokenRef: previous.githubTokenRef } : {} : {}
10926
+ ...githubTokenRef ? { githubTokenRef } : {}
10721
10927
  });
10928
+ const nextScheduleFrequencyMinutes = "scheduleFrequencyMinutes" in record ? normalizeScheduleFrequencyMinutes(record.scheduleFrequencyMinutes) : getScopedScheduleFrequencyMinutes(previous, requestedCompanyId);
10929
+ current = upsertScopedScheduleFrequencyMinutes(current, nextScheduleFrequencyMinutes, requestedCompanyId);
10930
+ const nextPaperclipApiBaseUrl = "paperclipApiBaseUrl" in record ? resolveTrustedPaperclipApiBaseUrlInput(record.paperclipApiBaseUrl, previous, config, requestedCompanyId) : getConfiguredPaperclipApiBaseUrl(previous, config, requestedCompanyId);
10931
+ current = upsertScopedPaperclipApiBaseUrl(current, nextPaperclipApiBaseUrl, requestedCompanyId);
10722
10932
  const nextMappings = current.mappings.map((mapping, index) => ({
10723
10933
  id: mapping.id.trim() || createMappingId(index),
10724
10934
  repositoryUrl: parseRepositoryReference(mapping.repositoryUrl)?.url ?? mapping.repositoryUrl.trim(),
@@ -10726,27 +10936,29 @@ var plugin = definePlugin({
10726
10936
  paperclipProjectId: mapping.paperclipProjectId,
10727
10937
  companyId: mapping.companyId
10728
10938
  }));
10939
+ const materializedCurrent = materializeScopedSettings(current, config, requestedCompanyId);
10729
10940
  const next = sanitizeSettingsForCurrentSetup({
10941
+ ...current,
10730
10942
  mappings: nextMappings,
10731
- syncState: previous.syncState,
10732
- scheduleFrequencyMinutes: current.scheduleFrequencyMinutes,
10733
- ...current.paperclipApiBaseUrl ? { paperclipApiBaseUrl: current.paperclipApiBaseUrl } : {},
10943
+ syncState: materializedCurrent.syncState,
10944
+ scheduleFrequencyMinutes: materializedCurrent.scheduleFrequencyMinutes,
10945
+ ...materializedCurrent.paperclipApiBaseUrl ? { paperclipApiBaseUrl: materializedCurrent.paperclipApiBaseUrl } : {},
10734
10946
  ...current.githubTokenRefs ? { githubTokenRefs: current.githubTokenRefs } : {},
10735
- ...current.githubTokenLoginsByCompanyId ? { githubTokenLoginsByCompanyId: current.githubTokenLoginsByCompanyId } : {},
10947
+ ...current.githubTokenLoginByCompanyId ? { githubTokenLoginByCompanyId: current.githubTokenLoginByCompanyId } : {},
10736
10948
  ...current.githubTokenLogin ? { githubTokenLogin: current.githubTokenLogin } : {},
10737
10949
  ...current.paperclipBoardApiTokenRefs ? { paperclipBoardApiTokenRefs: current.paperclipBoardApiTokenRefs } : {},
10738
10950
  ...current.paperclipBoardAccessIdentityByCompanyId ? { paperclipBoardAccessIdentityByCompanyId: current.paperclipBoardAccessIdentityByCompanyId } : {},
10739
10951
  ...current.companyAdvancedSettingsByCompanyId ? { companyAdvancedSettingsByCompanyId: current.companyAdvancedSettingsByCompanyId } : {},
10740
- ...current.githubTokenRef ? { githubTokenRef: current.githubTokenRef } : {},
10952
+ ...githubTokenRef ? { githubTokenRef } : {},
10741
10953
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
10742
10954
  }, {
10743
10955
  hasToken: hasConfiguredGithubToken(current, config, requestedCompanyId),
10744
- hasMappings: getSyncableMappings(nextMappings).length > 0
10956
+ hasMappings: getSyncableMappingsForScope(nextMappings, requestedCompanyId).length > 0
10745
10957
  });
10746
10958
  await ctx.state.set(SETTINGS_SCOPE, next);
10747
10959
  await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
10748
10960
  clearGitHubRepositoryTokenCapabilityAudits();
10749
- const scopedGitHubTokenLogin = getSavedGitHubTokenLogin(next, requestedCompanyId);
10961
+ const scopedGitHubTokenLogin = (requestedCompanyId && "githubTokenLogin" in record ? requestedGitHubTokenLogin : void 0) ?? getGitHubTokenLogin(next, requestedCompanyId);
10750
10962
  return {
10751
10963
  ...getPublicSettingsForScope(next, requestedCompanyId),
10752
10964
  ...scopedGitHubTokenLogin ? { githubTokenLogin: scopedGitHubTokenLogin } : {},
@@ -10787,15 +10999,16 @@ var plugin = definePlugin({
10787
10999
  paperclipBoardAccessIdentityByCompanyId: _previousPaperclipBoardAccessIdentityByCompanyId,
10788
11000
  ...previousWithoutBoardAccess
10789
11001
  } = previous;
10790
- const next = sanitizeSettingsForCurrentSetup({
11002
+ const nextBase = sanitizeSettingsForCurrentSetup({
10791
11003
  ...previousWithoutBoardAccess,
10792
11004
  ...Object.keys(nextPaperclipBoardApiTokenRefs).length > 0 ? { paperclipBoardApiTokenRefs: nextPaperclipBoardApiTokenRefs } : {},
10793
11005
  ...Object.keys(nextPaperclipBoardAccessIdentityByCompanyId).length > 0 ? { paperclipBoardAccessIdentityByCompanyId: nextPaperclipBoardAccessIdentityByCompanyId } : {},
10794
11006
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
10795
11007
  }, {
10796
11008
  hasToken: hasConfiguredGithubToken(previous, config, companyId),
10797
- hasMappings: getSyncableMappings(previous.mappings).length > 0
11009
+ hasMappings: getSyncableMappingsForScope(previous.mappings, companyId).length > 0
10798
11010
  });
11011
+ const next = materializeScopedSettings(nextBase, config, companyId);
10799
11012
  await ctx.state.set(SETTINGS_SCOPE, next);
10800
11013
  await ctx.state.set(SYNC_STATE_SCOPE, next.syncState);
10801
11014
  return {
@@ -10866,7 +11079,7 @@ var plugin = definePlugin({
10866
11079
  });
10867
11080
  });
10868
11081
  ctx.actions.register("sync.cancel", async () => {
10869
- const currentSettings = await getActiveOrCurrentSyncState(ctx);
11082
+ const currentSettings = await getActiveOrCurrentSyncState(ctx, activeRunningSyncCompanyId);
10870
11083
  if (currentSettings.syncState.status !== "running") {
10871
11084
  return currentSettings;
10872
11085
  }
@@ -10886,7 +11099,8 @@ var plugin = definePlugin({
10886
11099
  progress: currentSettings.syncState.progress,
10887
11100
  message: CANCELLING_SYNC_MESSAGE,
10888
11101
  cancelRequestedAt: cancellationRequest.requestedAt
10889
- })
11102
+ }),
11103
+ activeRunningSyncCompanyId
10890
11104
  );
10891
11105
  activeRunningSyncState = next;
10892
11106
  return next;
@@ -10894,10 +11108,22 @@ var plugin = definePlugin({
10894
11108
  registerGitHubAgentTools(ctx);
10895
11109
  ctx.jobs.register("sync.github-issues", async (job) => {
10896
11110
  const settings = normalizeSettings(await ctx.state.get(SETTINGS_SCOPE));
10897
- if (job.trigger === "schedule" && !shouldRunScheduledSync(settings, job.scheduledAt)) {
11111
+ const trigger = job.trigger === "retry" ? "retry" : "schedule";
11112
+ const scheduledTargets = listScheduledSyncTargets(settings);
11113
+ if (scheduledTargets.length === 0) {
11114
+ if (job.trigger === "schedule" && !shouldRunScheduledSync(settings, job.scheduledAt)) {
11115
+ return;
11116
+ }
11117
+ await startSync(ctx, trigger);
10898
11118
  return;
10899
11119
  }
10900
- await startSync(ctx, job.trigger === "retry" ? "retry" : "schedule");
11120
+ for (const target of scheduledTargets) {
11121
+ const scopedSettings = materializeScopedSettings(settings, null, target?.companyId);
11122
+ if (job.trigger === "schedule" && !shouldRunScheduledSync(scopedSettings, job.scheduledAt)) {
11123
+ continue;
11124
+ }
11125
+ await startSync(ctx, trigger, target ? { target } : {});
11126
+ }
10901
11127
  });
10902
11128
  }
10903
11129
  });