paperclip-github-plugin 0.4.9 → 0.5.0
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 +9 -5
- package/dist/manifest.js +1 -1
- package/dist/ui/index.js +334 -48
- package/dist/ui/index.js.map +3 -3
- package/dist/worker.js +618 -41
- package/package.json +1 -1
package/dist/worker.js
CHANGED
|
@@ -636,6 +636,7 @@ var MISSING_MAPPING_SYNC_ACTION = "Open settings, add a repository mapping, let
|
|
|
636
636
|
var MISSING_BOARD_ACCESS_SYNC_MESSAGE = "Connect Paperclip board access before running sync on this authenticated deployment.";
|
|
637
637
|
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.";
|
|
638
638
|
var IMPORTED_ISSUE_WAKE_REASON = "GitHub Sync imported an assigned issue that is ready for work.";
|
|
639
|
+
var STATUS_TRANSITION_WAKE_REASON = "GitHub Sync moved an assigned issue into its next active state.";
|
|
639
640
|
var ISSUE_LINK_ENTITY_TYPE = "paperclip-github-plugin.issue-link";
|
|
640
641
|
var PULL_REQUEST_LINK_ENTITY_TYPE = "paperclip-github-plugin.pull-request-link";
|
|
641
642
|
var COMMENT_ANNOTATION_ENTITY_TYPE = "paperclip-github-plugin.comment-annotation";
|
|
@@ -2668,6 +2669,7 @@ function getPublicSettings(settings) {
|
|
|
2668
2669
|
githubTokenRef: _githubTokenRef,
|
|
2669
2670
|
paperclipBoardApiTokenRefs: _paperclipBoardApiTokenRefs,
|
|
2670
2671
|
paperclipBoardAccessIdentityByCompanyId: _paperclipBoardAccessIdentityByCompanyId,
|
|
2672
|
+
paperclipBoardAccessUserIdByCompanyId: _paperclipBoardAccessUserIdByCompanyId,
|
|
2671
2673
|
companyAdvancedSettingsByCompanyId: _companyAdvancedSettingsByCompanyId,
|
|
2672
2674
|
syncStateByCompanyId: _syncStateByCompanyId,
|
|
2673
2675
|
scheduleFrequencyMinutesByCompanyId: _scheduleFrequencyMinutesByCompanyId,
|
|
@@ -2690,6 +2692,13 @@ function getPaperclipBoardAccessIdentity(settings, companyId) {
|
|
|
2690
2692
|
}
|
|
2691
2693
|
return normalizeOptionalString2(settings.paperclipBoardAccessIdentityByCompanyId?.[normalizedCompanyId]);
|
|
2692
2694
|
}
|
|
2695
|
+
function getPaperclipBoardAccessUserId(settings, companyId) {
|
|
2696
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
2697
|
+
if (!normalizedCompanyId) {
|
|
2698
|
+
return void 0;
|
|
2699
|
+
}
|
|
2700
|
+
return normalizeOptionalString2(settings.paperclipBoardAccessUserIdByCompanyId?.[normalizedCompanyId]);
|
|
2701
|
+
}
|
|
2693
2702
|
function getPublicSettingsForScope(settings, companyId) {
|
|
2694
2703
|
const publicSettings = getPublicSettings(settings);
|
|
2695
2704
|
const githubTokenLogin = getGitHubTokenLogin(settings, companyId);
|
|
@@ -2702,24 +2711,54 @@ function getPublicSettingsForScope(settings, companyId) {
|
|
|
2702
2711
|
...paperclipBoardAccessIdentity ? { paperclipBoardAccessIdentity } : {}
|
|
2703
2712
|
};
|
|
2704
2713
|
}
|
|
2705
|
-
|
|
2714
|
+
function compareGitHubSyncAssigneeOptions(left, right) {
|
|
2715
|
+
if (left.kind !== right.kind) {
|
|
2716
|
+
return left.kind === "user" ? -1 : 1;
|
|
2717
|
+
}
|
|
2718
|
+
return left.name.localeCompare(right.name);
|
|
2719
|
+
}
|
|
2720
|
+
function sortGitHubSyncAssigneeOptions(options) {
|
|
2721
|
+
return [...options].sort(compareGitHubSyncAssigneeOptions);
|
|
2722
|
+
}
|
|
2723
|
+
function getConnectedBoardUserAssigneeOption(settings, companyId) {
|
|
2724
|
+
const userId = getPaperclipBoardAccessUserId(settings, companyId);
|
|
2725
|
+
if (!userId) {
|
|
2726
|
+
return null;
|
|
2727
|
+
}
|
|
2728
|
+
const identityLabel = getPaperclipBoardAccessIdentity(settings, companyId);
|
|
2729
|
+
return {
|
|
2730
|
+
kind: "user",
|
|
2731
|
+
id: userId,
|
|
2732
|
+
name: "Me",
|
|
2733
|
+
...identityLabel ? { title: identityLabel } : {}
|
|
2734
|
+
};
|
|
2735
|
+
}
|
|
2736
|
+
async function listAvailableAssignees(ctx, companyId, settings) {
|
|
2737
|
+
const connectedBoardUserOption = settings ? getConnectedBoardUserAssigneeOption(settings, companyId) : null;
|
|
2706
2738
|
try {
|
|
2707
2739
|
const agents = await ctx.agents.list({
|
|
2708
2740
|
companyId,
|
|
2709
2741
|
limit: 500
|
|
2710
2742
|
});
|
|
2711
|
-
|
|
2743
|
+
const assignees = agents.filter((agent) => agent.status !== "terminated").map((agent) => ({
|
|
2744
|
+
kind: "agent",
|
|
2712
2745
|
id: agent.id,
|
|
2713
2746
|
name: agent.name,
|
|
2714
2747
|
...agent.title?.trim() ? { title: agent.title.trim() } : {},
|
|
2715
2748
|
...agent.status ? { status: agent.status } : {}
|
|
2716
|
-
}))
|
|
2749
|
+
}));
|
|
2750
|
+
if (connectedBoardUserOption) {
|
|
2751
|
+
assignees.push(connectedBoardUserOption);
|
|
2752
|
+
}
|
|
2753
|
+
return sortGitHubSyncAssigneeOptions(
|
|
2754
|
+
[...new Map(assignees.map((assignee) => [`${assignee.kind}:${assignee.id}`, assignee])).values()]
|
|
2755
|
+
);
|
|
2717
2756
|
} catch (error) {
|
|
2718
2757
|
ctx.logger.warn("Unable to list company agents for GitHub Sync advanced settings.", {
|
|
2719
2758
|
companyId,
|
|
2720
2759
|
error: getErrorMessage(error)
|
|
2721
2760
|
});
|
|
2722
|
-
return [];
|
|
2761
|
+
return connectedBoardUserOption ? [connectedBoardUserOption] : [];
|
|
2723
2762
|
}
|
|
2724
2763
|
}
|
|
2725
2764
|
async function resolvePaperclipIssueDrawerAgents(ctx, companyId, agentIds) {
|
|
@@ -3218,6 +3257,20 @@ function normalizePaperclipBoardAccessIdentityByCompanyId(value) {
|
|
|
3218
3257
|
}
|
|
3219
3258
|
return Object.fromEntries(entries);
|
|
3220
3259
|
}
|
|
3260
|
+
function normalizePaperclipBoardAccessUserIdByCompanyId(value) {
|
|
3261
|
+
if (!value || typeof value !== "object") {
|
|
3262
|
+
return void 0;
|
|
3263
|
+
}
|
|
3264
|
+
const entries = Object.entries(value).map(([companyId, userId]) => {
|
|
3265
|
+
const normalizedCompanyId = normalizeCompanyId(companyId);
|
|
3266
|
+
const normalizedUserId = normalizeOptionalString2(userId);
|
|
3267
|
+
return normalizedCompanyId && normalizedUserId ? [normalizedCompanyId, normalizedUserId] : null;
|
|
3268
|
+
}).filter((entry) => entry !== null);
|
|
3269
|
+
if (entries.length === 0) {
|
|
3270
|
+
return void 0;
|
|
3271
|
+
}
|
|
3272
|
+
return Object.fromEntries(entries);
|
|
3273
|
+
}
|
|
3221
3274
|
function normalizeSyncCancellationRequest(value) {
|
|
3222
3275
|
if (!value || typeof value !== "object") {
|
|
3223
3276
|
return null;
|
|
@@ -3308,17 +3361,46 @@ function normalizeAgentIds(value) {
|
|
|
3308
3361
|
const entries = Array.isArray(value) ? value.map((entry) => normalizeOptionalString2(entry)).filter((entry) => Boolean(entry)) : [];
|
|
3309
3362
|
return [...new Set(entries)].sort((left, right) => left.localeCompare(right));
|
|
3310
3363
|
}
|
|
3364
|
+
function normalizeAdvancedSettingsAssigneeOverride(record, keys) {
|
|
3365
|
+
const userId = normalizeOptionalString2(record[keys.userId]);
|
|
3366
|
+
if (userId) {
|
|
3367
|
+
return {
|
|
3368
|
+
[keys.userId]: userId
|
|
3369
|
+
};
|
|
3370
|
+
}
|
|
3371
|
+
const agentId = normalizeOptionalString2(record[keys.agentId]);
|
|
3372
|
+
if (agentId) {
|
|
3373
|
+
return {
|
|
3374
|
+
[keys.agentId]: agentId
|
|
3375
|
+
};
|
|
3376
|
+
}
|
|
3377
|
+
return {};
|
|
3378
|
+
}
|
|
3311
3379
|
function normalizeAdvancedSettings(value) {
|
|
3312
3380
|
if (!value || typeof value !== "object") {
|
|
3313
3381
|
return DEFAULT_ADVANCED_SETTINGS;
|
|
3314
3382
|
}
|
|
3315
3383
|
const record = value;
|
|
3316
|
-
const defaultAssigneeAgentId = normalizeOptionalString2(record.defaultAssigneeAgentId);
|
|
3317
3384
|
const defaultStatus = "defaultStatus" in record ? coercePaperclipIssueStatus(record.defaultStatus) : DEFAULT_ADVANCED_SETTINGS.defaultStatus;
|
|
3318
3385
|
const ignoredIssueAuthorUsernames = "ignoredIssueAuthorUsernames" in record ? normalizeIgnoredIssueAuthorUsernames(record.ignoredIssueAuthorUsernames) : DEFAULT_ADVANCED_SETTINGS.ignoredIssueAuthorUsernames;
|
|
3319
3386
|
const githubTokenPropagationAgentIds = "githubTokenPropagationAgentIds" in record ? normalizeAgentIds(record.githubTokenPropagationAgentIds) : [];
|
|
3320
3387
|
return {
|
|
3321
|
-
...
|
|
3388
|
+
...normalizeAdvancedSettingsAssigneeOverride(record, {
|
|
3389
|
+
agentId: "defaultAssigneeAgentId",
|
|
3390
|
+
userId: "defaultAssigneeUserId"
|
|
3391
|
+
}),
|
|
3392
|
+
...normalizeAdvancedSettingsAssigneeOverride(record, {
|
|
3393
|
+
agentId: "executorAssigneeAgentId",
|
|
3394
|
+
userId: "executorAssigneeUserId"
|
|
3395
|
+
}),
|
|
3396
|
+
...normalizeAdvancedSettingsAssigneeOverride(record, {
|
|
3397
|
+
agentId: "reviewerAssigneeAgentId",
|
|
3398
|
+
userId: "reviewerAssigneeUserId"
|
|
3399
|
+
}),
|
|
3400
|
+
...normalizeAdvancedSettingsAssigneeOverride(record, {
|
|
3401
|
+
agentId: "approverAssigneeAgentId",
|
|
3402
|
+
userId: "approverAssigneeUserId"
|
|
3403
|
+
}),
|
|
3322
3404
|
defaultStatus,
|
|
3323
3405
|
ignoredIssueAuthorUsernames,
|
|
3324
3406
|
...githubTokenPropagationAgentIds.length > 0 ? { githubTokenPropagationAgentIds } : {}
|
|
@@ -3476,6 +3558,9 @@ function normalizeSettings(value) {
|
|
|
3476
3558
|
const paperclipBoardAccessIdentityByCompanyId = normalizePaperclipBoardAccessIdentityByCompanyId(
|
|
3477
3559
|
record.paperclipBoardAccessIdentityByCompanyId
|
|
3478
3560
|
);
|
|
3561
|
+
const paperclipBoardAccessUserIdByCompanyId = normalizePaperclipBoardAccessUserIdByCompanyId(
|
|
3562
|
+
record.paperclipBoardAccessUserIdByCompanyId
|
|
3563
|
+
);
|
|
3479
3564
|
const companyAdvancedSettingsByCompanyId = normalizeCompanyAdvancedSettingsByCompanyId(record.companyAdvancedSettingsByCompanyId);
|
|
3480
3565
|
return {
|
|
3481
3566
|
mappings: normalizeMappings(record.mappings),
|
|
@@ -3491,6 +3576,7 @@ function normalizeSettings(value) {
|
|
|
3491
3576
|
...githubTokenLogin ? { githubTokenLogin } : {},
|
|
3492
3577
|
...paperclipBoardApiTokenRefs ? { paperclipBoardApiTokenRefs } : {},
|
|
3493
3578
|
...paperclipBoardAccessIdentityByCompanyId ? { paperclipBoardAccessIdentityByCompanyId } : {},
|
|
3579
|
+
...paperclipBoardAccessUserIdByCompanyId ? { paperclipBoardAccessUserIdByCompanyId } : {},
|
|
3494
3580
|
...companyAdvancedSettingsByCompanyId ? { companyAdvancedSettingsByCompanyId } : {},
|
|
3495
3581
|
updatedAt: typeof record.updatedAt === "string" ? record.updatedAt : void 0
|
|
3496
3582
|
};
|
|
@@ -4107,9 +4193,9 @@ function classifyGitHubPullRequestCiState(contexts) {
|
|
|
4107
4193
|
}
|
|
4108
4194
|
return hasPendingContext ? "unfinished" : "green";
|
|
4109
4195
|
}
|
|
4110
|
-
function resolvePaperclipStatusFromLinkedPullRequests(linkedPullRequests) {
|
|
4196
|
+
function resolvePaperclipStatusFromLinkedPullRequests(linkedPullRequests, options) {
|
|
4111
4197
|
if (linkedPullRequests.some((pullRequest) => pullRequest.hasUnresolvedReviewThreads || pullRequest.ciState === "red")) {
|
|
4112
|
-
return "todo";
|
|
4198
|
+
return options?.preferInProgress ? "in_progress" : "todo";
|
|
4113
4199
|
}
|
|
4114
4200
|
if (linkedPullRequests.length > 0 && linkedPullRequests.every(
|
|
4115
4201
|
(pullRequest) => pullRequest.ciState === "green" && pullRequest.hasUnresolvedReviewThreads === false
|
|
@@ -4141,6 +4227,354 @@ function formatPaperclipIssueStatus(status) {
|
|
|
4141
4227
|
function normalizePaperclipIssueStatus(value) {
|
|
4142
4228
|
return PAPERCLIP_ISSUE_STATUSES.includes(value) ? value : void 0;
|
|
4143
4229
|
}
|
|
4230
|
+
function normalizePaperclipIssueExecutionStageType(value) {
|
|
4231
|
+
return value === "review" || value === "approval" ? value : void 0;
|
|
4232
|
+
}
|
|
4233
|
+
function normalizePaperclipIssueAssigneePrincipal(value) {
|
|
4234
|
+
if (!value || typeof value !== "object") {
|
|
4235
|
+
return null;
|
|
4236
|
+
}
|
|
4237
|
+
const record = value;
|
|
4238
|
+
const principalType = normalizeOptionalString2(record.type);
|
|
4239
|
+
const agentId = normalizeOptionalString2(record.agentId);
|
|
4240
|
+
const userId = normalizeOptionalString2(record.userId);
|
|
4241
|
+
if ((principalType === void 0 || principalType === "agent") && agentId) {
|
|
4242
|
+
return {
|
|
4243
|
+
kind: "agent",
|
|
4244
|
+
id: agentId
|
|
4245
|
+
};
|
|
4246
|
+
}
|
|
4247
|
+
if ((principalType === void 0 || principalType === "user") && userId) {
|
|
4248
|
+
return {
|
|
4249
|
+
kind: "user",
|
|
4250
|
+
id: userId
|
|
4251
|
+
};
|
|
4252
|
+
}
|
|
4253
|
+
return null;
|
|
4254
|
+
}
|
|
4255
|
+
function normalizePaperclipIssueExecutionStage(value) {
|
|
4256
|
+
if (!value || typeof value !== "object") {
|
|
4257
|
+
return null;
|
|
4258
|
+
}
|
|
4259
|
+
const record = value;
|
|
4260
|
+
const type = normalizePaperclipIssueExecutionStageType(record.type);
|
|
4261
|
+
const participants = Array.isArray(record.participants) ? record.participants.map((participant) => normalizePaperclipIssueAssigneePrincipal(participant)).filter((participant) => participant !== null) : [];
|
|
4262
|
+
if (!type || participants.length === 0) {
|
|
4263
|
+
return null;
|
|
4264
|
+
}
|
|
4265
|
+
const id = normalizeOptionalString2(record.id);
|
|
4266
|
+
return {
|
|
4267
|
+
...id ? { id } : {},
|
|
4268
|
+
type,
|
|
4269
|
+
participants
|
|
4270
|
+
};
|
|
4271
|
+
}
|
|
4272
|
+
function normalizePaperclipIssueExecutionPolicy(value) {
|
|
4273
|
+
if (!value || typeof value !== "object") {
|
|
4274
|
+
return null;
|
|
4275
|
+
}
|
|
4276
|
+
const rawStages = value.stages;
|
|
4277
|
+
const stages = Array.isArray(rawStages) ? rawStages.map((stage) => normalizePaperclipIssueExecutionStage(stage)).filter((stage) => stage !== null) : [];
|
|
4278
|
+
return stages.length > 0 ? {
|
|
4279
|
+
stages
|
|
4280
|
+
} : null;
|
|
4281
|
+
}
|
|
4282
|
+
function normalizePaperclipIssueExecutionState(value) {
|
|
4283
|
+
if (!value || typeof value !== "object") {
|
|
4284
|
+
return null;
|
|
4285
|
+
}
|
|
4286
|
+
const record = value;
|
|
4287
|
+
const status = normalizeOptionalString2(record.status);
|
|
4288
|
+
const currentStageId = normalizeOptionalString2(record.currentStageId) ?? null;
|
|
4289
|
+
const currentStageIndex = typeof record.currentStageIndex === "number" && Number.isInteger(record.currentStageIndex) ? record.currentStageIndex : null;
|
|
4290
|
+
const currentStageType = normalizePaperclipIssueExecutionStageType(record.currentStageType) ?? null;
|
|
4291
|
+
const currentParticipant = normalizePaperclipIssueAssigneePrincipal(record.currentParticipant);
|
|
4292
|
+
const returnAssignee = normalizePaperclipIssueAssigneePrincipal(record.returnAssignee);
|
|
4293
|
+
const lastDecisionId = normalizeOptionalString2(record.lastDecisionId) ?? null;
|
|
4294
|
+
const lastDecisionOutcome = normalizeOptionalString2(record.lastDecisionOutcome) ?? null;
|
|
4295
|
+
const completedStageIds = Array.isArray(record.completedStageIds) ? record.completedStageIds.map((stageId) => normalizeOptionalString2(stageId)).filter((stageId) => Boolean(stageId)) : [];
|
|
4296
|
+
if (!status && currentStageId === null && currentStageIndex === null && currentStageType === null && currentParticipant === null && returnAssignee === null && completedStageIds.length === 0 && lastDecisionId === null && lastDecisionOutcome === null) {
|
|
4297
|
+
return null;
|
|
4298
|
+
}
|
|
4299
|
+
return {
|
|
4300
|
+
...status ? { status } : {},
|
|
4301
|
+
currentStageId,
|
|
4302
|
+
currentStageIndex,
|
|
4303
|
+
currentStageType,
|
|
4304
|
+
currentParticipant,
|
|
4305
|
+
returnAssignee,
|
|
4306
|
+
completedStageIds,
|
|
4307
|
+
...lastDecisionId !== null ? { lastDecisionId } : {},
|
|
4308
|
+
...lastDecisionOutcome !== null ? { lastDecisionOutcome } : {}
|
|
4309
|
+
};
|
|
4310
|
+
}
|
|
4311
|
+
function getPaperclipIssueAssigneePrincipal(issue) {
|
|
4312
|
+
const record = issue;
|
|
4313
|
+
const assigneeAgentId = normalizeOptionalString2(record.assigneeAgentId);
|
|
4314
|
+
if (assigneeAgentId) {
|
|
4315
|
+
return {
|
|
4316
|
+
kind: "agent",
|
|
4317
|
+
id: assigneeAgentId
|
|
4318
|
+
};
|
|
4319
|
+
}
|
|
4320
|
+
const assigneeUserId = normalizeOptionalString2(record.assigneeUserId);
|
|
4321
|
+
return assigneeUserId ? {
|
|
4322
|
+
kind: "user",
|
|
4323
|
+
id: assigneeUserId
|
|
4324
|
+
} : null;
|
|
4325
|
+
}
|
|
4326
|
+
function getPaperclipIssueSyncContext(issue) {
|
|
4327
|
+
const record = issue;
|
|
4328
|
+
return {
|
|
4329
|
+
assignee: getPaperclipIssueAssigneePrincipal(issue),
|
|
4330
|
+
executionPolicy: normalizePaperclipIssueExecutionPolicy(record.executionPolicy),
|
|
4331
|
+
executionState: normalizePaperclipIssueExecutionState(record.executionState)
|
|
4332
|
+
};
|
|
4333
|
+
}
|
|
4334
|
+
function isSamePaperclipIssueAssigneePrincipal(left, right) {
|
|
4335
|
+
if (!left || !right) {
|
|
4336
|
+
return !left && !right;
|
|
4337
|
+
}
|
|
4338
|
+
return left.kind === right.kind && left.id === right.id;
|
|
4339
|
+
}
|
|
4340
|
+
function serializePaperclipIssueAssigneePrincipal(principal) {
|
|
4341
|
+
if (!principal) {
|
|
4342
|
+
return null;
|
|
4343
|
+
}
|
|
4344
|
+
return principal.kind === "agent" ? {
|
|
4345
|
+
type: "agent",
|
|
4346
|
+
agentId: principal.id,
|
|
4347
|
+
userId: null
|
|
4348
|
+
} : {
|
|
4349
|
+
type: "user",
|
|
4350
|
+
agentId: null,
|
|
4351
|
+
userId: principal.id
|
|
4352
|
+
};
|
|
4353
|
+
}
|
|
4354
|
+
function selectPaperclipIssueExecutionStageParticipant(stage, options = {}) {
|
|
4355
|
+
const participants = stage.participants.filter((participant) => !isSamePaperclipIssueAssigneePrincipal(
|
|
4356
|
+
participant,
|
|
4357
|
+
options.exclude
|
|
4358
|
+
));
|
|
4359
|
+
if (participants.length === 0) {
|
|
4360
|
+
return null;
|
|
4361
|
+
}
|
|
4362
|
+
if (options.preferred) {
|
|
4363
|
+
const preferredParticipant = participants.find((participant) => isSamePaperclipIssueAssigneePrincipal(
|
|
4364
|
+
participant,
|
|
4365
|
+
options.preferred
|
|
4366
|
+
));
|
|
4367
|
+
if (preferredParticipant) {
|
|
4368
|
+
return preferredParticipant;
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
return participants[0] ?? null;
|
|
4372
|
+
}
|
|
4373
|
+
function findPaperclipIssueExecutionStageMatch(executionPolicy, executionState) {
|
|
4374
|
+
const stages = executionPolicy?.stages ?? [];
|
|
4375
|
+
if (stages.length === 0) {
|
|
4376
|
+
return null;
|
|
4377
|
+
}
|
|
4378
|
+
const currentStageId = executionState?.currentStageId;
|
|
4379
|
+
if (currentStageId) {
|
|
4380
|
+
const matchedStageIndex = stages.findIndex((stage) => stage.id === currentStageId);
|
|
4381
|
+
if (matchedStageIndex >= 0) {
|
|
4382
|
+
const matchedStage = stages[matchedStageIndex];
|
|
4383
|
+
if (matchedStage) {
|
|
4384
|
+
return {
|
|
4385
|
+
stage: matchedStage,
|
|
4386
|
+
index: matchedStageIndex
|
|
4387
|
+
};
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
const currentStageIndex = executionState?.currentStageIndex;
|
|
4392
|
+
if (typeof currentStageIndex === "number" && currentStageIndex >= 0 && currentStageIndex < stages.length) {
|
|
4393
|
+
const matchedStage = stages[currentStageIndex];
|
|
4394
|
+
if (matchedStage) {
|
|
4395
|
+
return {
|
|
4396
|
+
stage: matchedStage,
|
|
4397
|
+
index: currentStageIndex
|
|
4398
|
+
};
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
const completedStageIds = new Set(executionState?.completedStageIds ?? []);
|
|
4402
|
+
for (const [stageIndex, stage] of stages.entries()) {
|
|
4403
|
+
if (!stage.id || !completedStageIds.has(stage.id)) {
|
|
4404
|
+
return {
|
|
4405
|
+
stage,
|
|
4406
|
+
index: stageIndex
|
|
4407
|
+
};
|
|
4408
|
+
}
|
|
4409
|
+
}
|
|
4410
|
+
const firstStage = stages[0];
|
|
4411
|
+
return firstStage ? {
|
|
4412
|
+
stage: firstStage,
|
|
4413
|
+
index: 0
|
|
4414
|
+
} : null;
|
|
4415
|
+
}
|
|
4416
|
+
function getConfiguredAdvancedAssigneePrincipal(advancedSettings, role) {
|
|
4417
|
+
switch (role) {
|
|
4418
|
+
case "default":
|
|
4419
|
+
return advancedSettings.defaultAssigneeUserId ? { kind: "user", id: advancedSettings.defaultAssigneeUserId } : advancedSettings.defaultAssigneeAgentId ? { kind: "agent", id: advancedSettings.defaultAssigneeAgentId } : null;
|
|
4420
|
+
case "executor":
|
|
4421
|
+
return advancedSettings.executorAssigneeUserId ? { kind: "user", id: advancedSettings.executorAssigneeUserId } : advancedSettings.executorAssigneeAgentId ? { kind: "agent", id: advancedSettings.executorAssigneeAgentId } : null;
|
|
4422
|
+
case "reviewer":
|
|
4423
|
+
return advancedSettings.reviewerAssigneeUserId ? { kind: "user", id: advancedSettings.reviewerAssigneeUserId } : advancedSettings.reviewerAssigneeAgentId ? { kind: "agent", id: advancedSettings.reviewerAssigneeAgentId } : null;
|
|
4424
|
+
case "approver":
|
|
4425
|
+
return advancedSettings.approverAssigneeUserId ? { kind: "user", id: advancedSettings.approverAssigneeUserId } : advancedSettings.approverAssigneeAgentId ? { kind: "agent", id: advancedSettings.approverAssigneeAgentId } : null;
|
|
4426
|
+
}
|
|
4427
|
+
}
|
|
4428
|
+
function resolvePaperclipIssueReviewAssignee(syncContext, advancedSettings) {
|
|
4429
|
+
const currentParticipant = syncContext.executionState?.currentParticipant;
|
|
4430
|
+
const currentStageType = syncContext.executionState?.currentStageType;
|
|
4431
|
+
if (currentParticipant && currentStageType) {
|
|
4432
|
+
return {
|
|
4433
|
+
principal: currentParticipant,
|
|
4434
|
+
role: currentStageType === "approval" ? "approver" : "reviewer"
|
|
4435
|
+
};
|
|
4436
|
+
}
|
|
4437
|
+
const nextStageMatch = findPaperclipIssueExecutionStageMatch(syncContext.executionPolicy, syncContext.executionState);
|
|
4438
|
+
const nextStage = nextStageMatch?.stage ?? null;
|
|
4439
|
+
const nextStageParticipant = nextStage ? selectPaperclipIssueExecutionStageParticipant(nextStage, {
|
|
4440
|
+
exclude: syncContext.executionState?.returnAssignee ?? syncContext.assignee
|
|
4441
|
+
}) : null;
|
|
4442
|
+
if (nextStage && nextStageParticipant) {
|
|
4443
|
+
return {
|
|
4444
|
+
principal: nextStageParticipant,
|
|
4445
|
+
role: nextStage.type === "approval" ? "approver" : "reviewer"
|
|
4446
|
+
};
|
|
4447
|
+
}
|
|
4448
|
+
const approverOverride = getConfiguredAdvancedAssigneePrincipal(advancedSettings, "approver");
|
|
4449
|
+
if (nextStage?.type === "approval" && approverOverride) {
|
|
4450
|
+
return {
|
|
4451
|
+
principal: approverOverride,
|
|
4452
|
+
role: "approver"
|
|
4453
|
+
};
|
|
4454
|
+
}
|
|
4455
|
+
const reviewerOverride = getConfiguredAdvancedAssigneePrincipal(advancedSettings, "reviewer");
|
|
4456
|
+
if (reviewerOverride) {
|
|
4457
|
+
return {
|
|
4458
|
+
principal: reviewerOverride,
|
|
4459
|
+
role: "reviewer"
|
|
4460
|
+
};
|
|
4461
|
+
}
|
|
4462
|
+
if (approverOverride) {
|
|
4463
|
+
return {
|
|
4464
|
+
principal: approverOverride,
|
|
4465
|
+
role: "approver"
|
|
4466
|
+
};
|
|
4467
|
+
}
|
|
4468
|
+
return null;
|
|
4469
|
+
}
|
|
4470
|
+
function resolvePaperclipIssueExecutorAssignee(syncContext, advancedSettings) {
|
|
4471
|
+
const returnAssignee = syncContext.executionState?.returnAssignee;
|
|
4472
|
+
if (returnAssignee) {
|
|
4473
|
+
return {
|
|
4474
|
+
principal: returnAssignee,
|
|
4475
|
+
role: "executor"
|
|
4476
|
+
};
|
|
4477
|
+
}
|
|
4478
|
+
const executorOverride = getConfiguredAdvancedAssigneePrincipal(advancedSettings, "executor");
|
|
4479
|
+
if (executorOverride) {
|
|
4480
|
+
return {
|
|
4481
|
+
principal: executorOverride,
|
|
4482
|
+
role: "executor"
|
|
4483
|
+
};
|
|
4484
|
+
}
|
|
4485
|
+
const defaultOverride = getConfiguredAdvancedAssigneePrincipal(advancedSettings, "default");
|
|
4486
|
+
if (defaultOverride) {
|
|
4487
|
+
return {
|
|
4488
|
+
principal: defaultOverride,
|
|
4489
|
+
role: "executor"
|
|
4490
|
+
};
|
|
4491
|
+
}
|
|
4492
|
+
return null;
|
|
4493
|
+
}
|
|
4494
|
+
function resolveSyncTransitionAssignee(params) {
|
|
4495
|
+
const { nextStatus, syncContext, advancedSettings } = params;
|
|
4496
|
+
if (nextStatus === "in_review") {
|
|
4497
|
+
return resolvePaperclipIssueReviewAssignee(syncContext, advancedSettings);
|
|
4498
|
+
}
|
|
4499
|
+
if (nextStatus === "todo" || nextStatus === "in_progress") {
|
|
4500
|
+
return resolvePaperclipIssueExecutorAssignee(syncContext, advancedSettings);
|
|
4501
|
+
}
|
|
4502
|
+
return null;
|
|
4503
|
+
}
|
|
4504
|
+
function doesPaperclipIssueAssigneeMatch(currentAssignee, nextAssignee) {
|
|
4505
|
+
return isSamePaperclipIssueAssigneePrincipal(currentAssignee, nextAssignee);
|
|
4506
|
+
}
|
|
4507
|
+
function isActionablePaperclipIssueStatus(status) {
|
|
4508
|
+
return status === "todo" || status === "in_progress" || status === "in_review";
|
|
4509
|
+
}
|
|
4510
|
+
function buildPaperclipPendingExecutionState(params) {
|
|
4511
|
+
const { previousState, stage, stageIndex, participant, returnAssignee } = params;
|
|
4512
|
+
return {
|
|
4513
|
+
status: "pending",
|
|
4514
|
+
currentStageId: stage.id ?? null,
|
|
4515
|
+
currentStageIndex: stageIndex,
|
|
4516
|
+
currentStageType: stage.type,
|
|
4517
|
+
currentParticipant: serializePaperclipIssueAssigneePrincipal(participant),
|
|
4518
|
+
returnAssignee: serializePaperclipIssueAssigneePrincipal(returnAssignee),
|
|
4519
|
+
completedStageIds: [...previousState?.completedStageIds ?? []],
|
|
4520
|
+
lastDecisionId: previousState?.lastDecisionId ?? null,
|
|
4521
|
+
lastDecisionOutcome: previousState?.lastDecisionOutcome ?? null
|
|
4522
|
+
};
|
|
4523
|
+
}
|
|
4524
|
+
function buildPaperclipChangesRequestedExecutionState(params) {
|
|
4525
|
+
const { previousState, stage, stageIndex } = params;
|
|
4526
|
+
return {
|
|
4527
|
+
status: "changes_requested",
|
|
4528
|
+
currentStageId: stage.id ?? previousState.currentStageId ?? null,
|
|
4529
|
+
currentStageIndex: stageIndex,
|
|
4530
|
+
currentStageType: stage.type,
|
|
4531
|
+
currentParticipant: serializePaperclipIssueAssigneePrincipal(previousState.currentParticipant),
|
|
4532
|
+
returnAssignee: serializePaperclipIssueAssigneePrincipal(previousState.returnAssignee),
|
|
4533
|
+
completedStageIds: [...previousState.completedStageIds],
|
|
4534
|
+
lastDecisionId: previousState.lastDecisionId ?? null,
|
|
4535
|
+
lastDecisionOutcome: "changes_requested"
|
|
4536
|
+
};
|
|
4537
|
+
}
|
|
4538
|
+
function buildSyncFallbackExecutionStatePatch(params) {
|
|
4539
|
+
const { currentStatus, nextStatus, syncContext, nextAssignee } = params;
|
|
4540
|
+
const previousState = syncContext.executionState;
|
|
4541
|
+
if ((currentStatus === "done" || currentStatus === "cancelled") && nextStatus !== "done" && nextStatus !== "cancelled") {
|
|
4542
|
+
return previousState ? null : void 0;
|
|
4543
|
+
}
|
|
4544
|
+
if (nextStatus === "in_review" && syncContext.executionPolicy) {
|
|
4545
|
+
const nextStageMatch = findPaperclipIssueExecutionStageMatch(syncContext.executionPolicy, previousState);
|
|
4546
|
+
if (!nextStageMatch) {
|
|
4547
|
+
return previousState ? null : void 0;
|
|
4548
|
+
}
|
|
4549
|
+
const returnAssignee = previousState?.returnAssignee ?? syncContext.assignee;
|
|
4550
|
+
const participant = selectPaperclipIssueExecutionStageParticipant(nextStageMatch.stage, {
|
|
4551
|
+
preferred: previousState?.status === "changes_requested" ? nextAssignee ?? previousState.currentParticipant : nextAssignee ?? previousState?.currentParticipant,
|
|
4552
|
+
exclude: returnAssignee
|
|
4553
|
+
});
|
|
4554
|
+
if (!participant) {
|
|
4555
|
+
return previousState ? null : void 0;
|
|
4556
|
+
}
|
|
4557
|
+
return buildPaperclipPendingExecutionState({
|
|
4558
|
+
previousState,
|
|
4559
|
+
stage: nextStageMatch.stage,
|
|
4560
|
+
stageIndex: nextStageMatch.index,
|
|
4561
|
+
participant,
|
|
4562
|
+
returnAssignee
|
|
4563
|
+
});
|
|
4564
|
+
}
|
|
4565
|
+
if ((nextStatus === "todo" || nextStatus === "in_progress") && currentStatus === "in_review") {
|
|
4566
|
+
const currentStageMatch = findPaperclipIssueExecutionStageMatch(syncContext.executionPolicy, previousState);
|
|
4567
|
+
if (previousState?.status === "pending" && previousState.returnAssignee && currentStageMatch) {
|
|
4568
|
+
return buildPaperclipChangesRequestedExecutionState({
|
|
4569
|
+
previousState,
|
|
4570
|
+
stage: currentStageMatch.stage,
|
|
4571
|
+
stageIndex: currentStageMatch.index
|
|
4572
|
+
});
|
|
4573
|
+
}
|
|
4574
|
+
return previousState ? null : void 0;
|
|
4575
|
+
}
|
|
4576
|
+
return void 0;
|
|
4577
|
+
}
|
|
4144
4578
|
function describeGitHubStatusTransitionReason(params) {
|
|
4145
4579
|
const { snapshot, previousCommentCount, hasTrustedNewComment, maintainerAuthoredImportedIssue } = params;
|
|
4146
4580
|
if (snapshot.state === "closed") {
|
|
@@ -4233,7 +4667,8 @@ function resolvePaperclipIssueStatus(params) {
|
|
|
4233
4667
|
hasTrustedNewComment,
|
|
4234
4668
|
wasImportedThisRun,
|
|
4235
4669
|
defaultImportedStatus,
|
|
4236
|
-
maintainerAuthoredImportedIssue
|
|
4670
|
+
maintainerAuthoredImportedIssue,
|
|
4671
|
+
hasExecutorHandoffTarget
|
|
4237
4672
|
} = params;
|
|
4238
4673
|
if (snapshot.state === "closed") {
|
|
4239
4674
|
return snapshot.stateReason === "duplicate" || snapshot.stateReason === "not_planned" ? "cancelled" : "done";
|
|
@@ -4243,10 +4678,12 @@ function resolvePaperclipIssueStatus(params) {
|
|
|
4243
4678
|
}
|
|
4244
4679
|
const baselineCommentCount = previousCommentCount ?? snapshot.commentCount;
|
|
4245
4680
|
if (snapshot.commentCount > baselineCommentCount && hasTrustedNewComment) {
|
|
4246
|
-
return "todo";
|
|
4681
|
+
return hasExecutorHandoffTarget ? "in_progress" : "todo";
|
|
4247
4682
|
}
|
|
4248
4683
|
if (snapshot.linkedPullRequests.length > 0) {
|
|
4249
|
-
return resolvePaperclipStatusFromLinkedPullRequests(snapshot.linkedPullRequests
|
|
4684
|
+
return resolvePaperclipStatusFromLinkedPullRequests(snapshot.linkedPullRequests, {
|
|
4685
|
+
preferInProgress: hasExecutorHandoffTarget
|
|
4686
|
+
});
|
|
4250
4687
|
}
|
|
4251
4688
|
if (wasImportedThisRun) {
|
|
4252
4689
|
return maintainerAuthoredImportedIssue ? "todo" : defaultImportedStatus;
|
|
@@ -5398,7 +5835,7 @@ async function detectPaperclipBoardAccessRequirement(paperclipApiBaseUrl) {
|
|
|
5398
5835
|
return false;
|
|
5399
5836
|
}
|
|
5400
5837
|
}
|
|
5401
|
-
async function
|
|
5838
|
+
async function wakePaperclipIssueAssignee(ctx, params) {
|
|
5402
5839
|
if (!params.assigneeAgentId || !params.paperclipApiBaseUrl) {
|
|
5403
5840
|
return;
|
|
5404
5841
|
}
|
|
@@ -5414,10 +5851,12 @@ async function wakePaperclipAssigneeForImportedIssue(ctx, params) {
|
|
|
5414
5851
|
body: JSON.stringify({
|
|
5415
5852
|
source: "assignment",
|
|
5416
5853
|
triggerDetail: "system",
|
|
5417
|
-
reason:
|
|
5854
|
+
reason: params.reason,
|
|
5418
5855
|
payload: {
|
|
5419
5856
|
issueId: params.paperclipIssueId,
|
|
5420
|
-
mutation:
|
|
5857
|
+
mutation: params.mutation,
|
|
5858
|
+
...params.previousStatus ? { previousStatus: params.previousStatus } : {},
|
|
5859
|
+
...params.nextStatus ? { nextStatus: params.nextStatus } : {}
|
|
5421
5860
|
}
|
|
5422
5861
|
})
|
|
5423
5862
|
},
|
|
@@ -5429,11 +5868,12 @@ async function wakePaperclipAssigneeForImportedIssue(ctx, params) {
|
|
|
5429
5868
|
throw new Error(`Wakeup request failed with status ${response.status}.`);
|
|
5430
5869
|
}
|
|
5431
5870
|
} catch (error) {
|
|
5432
|
-
ctx.logger.warn("GitHub sync could not wake the assignee for
|
|
5871
|
+
ctx.logger.warn("GitHub sync could not wake the assignee for a Paperclip issue.", {
|
|
5433
5872
|
issueId: params.paperclipIssueId,
|
|
5434
5873
|
agentId: params.assigneeAgentId,
|
|
5435
5874
|
companyId: params.companyId,
|
|
5436
5875
|
paperclipApiBaseUrl: params.paperclipApiBaseUrl,
|
|
5876
|
+
mutation: params.mutation,
|
|
5437
5877
|
error: error instanceof Error ? error.message : String(error)
|
|
5438
5878
|
});
|
|
5439
5879
|
}
|
|
@@ -6132,10 +6572,32 @@ function doIssueNumberListsMatch(left, right) {
|
|
|
6132
6572
|
}
|
|
6133
6573
|
return left.every((value, index) => value === right[index]);
|
|
6134
6574
|
}
|
|
6135
|
-
async function
|
|
6136
|
-
const {
|
|
6575
|
+
async function updatePaperclipIssueState(ctx, params) {
|
|
6576
|
+
const {
|
|
6577
|
+
companyId,
|
|
6578
|
+
issueId,
|
|
6579
|
+
currentStatus,
|
|
6580
|
+
syncContext,
|
|
6581
|
+
nextStatus,
|
|
6582
|
+
nextAssignee,
|
|
6583
|
+
transitionComment,
|
|
6584
|
+
transitionCommentAnnotation,
|
|
6585
|
+
paperclipApiBaseUrl
|
|
6586
|
+
} = params;
|
|
6137
6587
|
const trimmedTransitionComment = transitionComment.trim();
|
|
6138
|
-
let
|
|
6588
|
+
let issueUpdated = false;
|
|
6589
|
+
const issuePatch = {
|
|
6590
|
+
status: nextStatus
|
|
6591
|
+
};
|
|
6592
|
+
if (nextAssignee) {
|
|
6593
|
+
if (nextAssignee.kind === "agent") {
|
|
6594
|
+
issuePatch.assigneeAgentId = nextAssignee.id;
|
|
6595
|
+
issuePatch.assigneeUserId = null;
|
|
6596
|
+
} else {
|
|
6597
|
+
issuePatch.assigneeAgentId = null;
|
|
6598
|
+
issuePatch.assigneeUserId = nextAssignee.id;
|
|
6599
|
+
}
|
|
6600
|
+
}
|
|
6139
6601
|
if (paperclipApiBaseUrl) {
|
|
6140
6602
|
try {
|
|
6141
6603
|
const response = await fetchPaperclipApi(
|
|
@@ -6146,9 +6608,7 @@ async function updatePaperclipIssueStatus(ctx, params) {
|
|
|
6146
6608
|
accept: "application/json",
|
|
6147
6609
|
"content-type": "application/json"
|
|
6148
6610
|
},
|
|
6149
|
-
body: JSON.stringify(
|
|
6150
|
-
status: nextStatus
|
|
6151
|
-
})
|
|
6611
|
+
body: JSON.stringify(issuePatch)
|
|
6152
6612
|
},
|
|
6153
6613
|
{
|
|
6154
6614
|
companyId
|
|
@@ -6159,7 +6619,7 @@ async function updatePaperclipIssueStatus(ctx, params) {
|
|
|
6159
6619
|
bodyRequired: false
|
|
6160
6620
|
});
|
|
6161
6621
|
if (!payloadResult.failure) {
|
|
6162
|
-
|
|
6622
|
+
issueUpdated = true;
|
|
6163
6623
|
}
|
|
6164
6624
|
if (payloadResult.failure && (payloadResult.failure.status ?? response.status) !== 404 && (payloadResult.failure.status ?? response.status) !== 405) {
|
|
6165
6625
|
logPaperclipIssueStatusUpdateFailure(ctx, {
|
|
@@ -6181,8 +6641,29 @@ async function updatePaperclipIssueStatus(ctx, params) {
|
|
|
6181
6641
|
});
|
|
6182
6642
|
}
|
|
6183
6643
|
}
|
|
6184
|
-
if (!
|
|
6185
|
-
|
|
6644
|
+
if (!issueUpdated) {
|
|
6645
|
+
const fallbackExecutionStatePatch = buildSyncFallbackExecutionStatePatch({
|
|
6646
|
+
currentStatus,
|
|
6647
|
+
nextStatus,
|
|
6648
|
+
syncContext,
|
|
6649
|
+
nextAssignee
|
|
6650
|
+
});
|
|
6651
|
+
const preserveExistingUserAssigneeWithoutLocalApi = nextAssignee?.kind === "user" && !paperclipApiBaseUrl;
|
|
6652
|
+
const sdkIssuePatch = {
|
|
6653
|
+
...issuePatch,
|
|
6654
|
+
...fallbackExecutionStatePatch !== void 0 ? { executionState: fallbackExecutionStatePatch } : {}
|
|
6655
|
+
};
|
|
6656
|
+
if (preserveExistingUserAssigneeWithoutLocalApi) {
|
|
6657
|
+
delete sdkIssuePatch.assigneeAgentId;
|
|
6658
|
+
delete sdkIssuePatch.assigneeUserId;
|
|
6659
|
+
ctx.logger.warn("GitHub sync could not reassign a Paperclip issue to a user without the local Paperclip API. Preserving the existing assignee.", {
|
|
6660
|
+
companyId,
|
|
6661
|
+
issueId,
|
|
6662
|
+
nextStatus,
|
|
6663
|
+
assigneeUserId: nextAssignee.id
|
|
6664
|
+
});
|
|
6665
|
+
}
|
|
6666
|
+
await ctx.issues.update(issueId, sdkIssuePatch, companyId);
|
|
6186
6667
|
}
|
|
6187
6668
|
if (trimmedTransitionComment && typeof ctx.issues.createComment === "function") {
|
|
6188
6669
|
const createdComment = await ctx.issues.createComment(issueId, trimmedTransitionComment, companyId);
|
|
@@ -6242,22 +6723,76 @@ function shouldIgnoreGitHubIssue(issue, advancedSettings) {
|
|
|
6242
6723
|
(ignoredUsername) => buildGitHubUsernameAliases(ignoredUsername).some((alias) => issueAuthorAliases.has(alias))
|
|
6243
6724
|
);
|
|
6244
6725
|
}
|
|
6726
|
+
async function assignImportedPaperclipIssueToUser(ctx, params) {
|
|
6727
|
+
if (!params.paperclipApiBaseUrl) {
|
|
6728
|
+
ctx.logger.warn("GitHub sync could not assign a newly imported Paperclip issue to a user without the local Paperclip API. Preserving the imported issue assignment.", {
|
|
6729
|
+
companyId: params.companyId,
|
|
6730
|
+
issueId: params.issueId,
|
|
6731
|
+
assigneeUserId: params.assigneeUserId
|
|
6732
|
+
});
|
|
6733
|
+
return;
|
|
6734
|
+
}
|
|
6735
|
+
try {
|
|
6736
|
+
const response = await fetchPaperclipApi(
|
|
6737
|
+
getPaperclipIssueEndpoint(params.paperclipApiBaseUrl, params.issueId),
|
|
6738
|
+
{
|
|
6739
|
+
method: "PATCH",
|
|
6740
|
+
headers: {
|
|
6741
|
+
accept: "application/json",
|
|
6742
|
+
"content-type": "application/json"
|
|
6743
|
+
},
|
|
6744
|
+
body: JSON.stringify({
|
|
6745
|
+
assigneeAgentId: null,
|
|
6746
|
+
assigneeUserId: params.assigneeUserId
|
|
6747
|
+
})
|
|
6748
|
+
},
|
|
6749
|
+
{
|
|
6750
|
+
companyId: params.companyId
|
|
6751
|
+
}
|
|
6752
|
+
);
|
|
6753
|
+
const payloadResult = await readPaperclipApiJsonResponse(response, {
|
|
6754
|
+
operationLabel: "issue update",
|
|
6755
|
+
bodyRequired: false
|
|
6756
|
+
});
|
|
6757
|
+
if (!payloadResult.failure) {
|
|
6758
|
+
return;
|
|
6759
|
+
}
|
|
6760
|
+
throw new Error(payloadResult.failure.errorMessage ?? `Issue update failed with status ${payloadResult.failure.status ?? response.status}.`);
|
|
6761
|
+
} catch (error) {
|
|
6762
|
+
ctx.logger.warn("GitHub sync could not assign a newly imported Paperclip issue to a Paperclip user.", {
|
|
6763
|
+
companyId: params.companyId,
|
|
6764
|
+
issueId: params.issueId,
|
|
6765
|
+
assigneeUserId: params.assigneeUserId,
|
|
6766
|
+
paperclipApiBaseUrl: params.paperclipApiBaseUrl,
|
|
6767
|
+
error: getErrorMessage(error)
|
|
6768
|
+
});
|
|
6769
|
+
}
|
|
6770
|
+
}
|
|
6245
6771
|
async function createPaperclipIssue(ctx, mapping, advancedSettings, issue, availableLabels, paperclipApiBaseUrl, syncFailureContext) {
|
|
6246
6772
|
if (!mapping.companyId || !mapping.paperclipProjectId) {
|
|
6247
6773
|
throw new Error(`Mapping ${mapping.id} is missing resolved Paperclip project identifiers.`);
|
|
6248
6774
|
}
|
|
6249
6775
|
const title = issue.title;
|
|
6250
6776
|
const description = buildPaperclipIssueDescription(issue);
|
|
6777
|
+
const defaultAssignee = getConfiguredAdvancedAssigneePrincipal(advancedSettings, "default");
|
|
6251
6778
|
const createdIssue = await ctx.issues.create({
|
|
6252
6779
|
companyId: mapping.companyId,
|
|
6253
6780
|
projectId: mapping.paperclipProjectId,
|
|
6254
6781
|
title,
|
|
6255
6782
|
...description ? { description } : {},
|
|
6256
|
-
...
|
|
6783
|
+
...defaultAssignee?.kind === "agent" ? { assigneeAgentId: defaultAssignee.id } : defaultAssignee?.kind === "user" ? { assigneeUserId: defaultAssignee.id } : {}
|
|
6257
6784
|
});
|
|
6258
6785
|
const ensuredCreatedIssueId = createdIssue.id;
|
|
6259
6786
|
const normalizedCreatedIssueDescription = createdIssue.description ?? void 0;
|
|
6260
6787
|
const createPath = "sdk";
|
|
6788
|
+
if (defaultAssignee?.kind === "user") {
|
|
6789
|
+
await assignImportedPaperclipIssueToUser(ctx, {
|
|
6790
|
+
companyId: mapping.companyId,
|
|
6791
|
+
issueId: ensuredCreatedIssueId,
|
|
6792
|
+
assigneeUserId: defaultAssignee.id,
|
|
6793
|
+
paperclipApiBaseUrl
|
|
6794
|
+
});
|
|
6795
|
+
}
|
|
6261
6796
|
if (normalizeIssueDescriptionValue(normalizedCreatedIssueDescription) !== description) {
|
|
6262
6797
|
logIssueDescriptionDiagnostic(
|
|
6263
6798
|
ctx,
|
|
@@ -6395,7 +6930,7 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
6395
6930
|
let updatedDescriptionsCount = 0;
|
|
6396
6931
|
let completedIssueCount = 0;
|
|
6397
6932
|
const totalIssueCount = importedIssues.length;
|
|
6398
|
-
const
|
|
6933
|
+
const queuedIssueWakeups = [];
|
|
6399
6934
|
for (const importedIssue of importedIssues) {
|
|
6400
6935
|
if (assertNotCancelled) {
|
|
6401
6936
|
await assertNotCancelled();
|
|
@@ -6518,6 +7053,11 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
6518
7053
|
githubIssue,
|
|
6519
7054
|
maintainerCache: repositoryMaintainerCache
|
|
6520
7055
|
}) : false;
|
|
7056
|
+
const paperclipIssueSyncContext = getPaperclipIssueSyncContext(paperclipIssue);
|
|
7057
|
+
const executorTransitionAssignee = resolvePaperclipIssueExecutorAssignee(
|
|
7058
|
+
paperclipIssueSyncContext,
|
|
7059
|
+
advancedSettings
|
|
7060
|
+
);
|
|
6521
7061
|
const nextStatus = resolvePaperclipIssueStatus({
|
|
6522
7062
|
currentStatus: paperclipIssue.status,
|
|
6523
7063
|
snapshot,
|
|
@@ -6525,16 +7065,26 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
6525
7065
|
hasTrustedNewComment,
|
|
6526
7066
|
wasImportedThisRun,
|
|
6527
7067
|
defaultImportedStatus: advancedSettings.defaultStatus,
|
|
6528
|
-
maintainerAuthoredImportedIssue
|
|
7068
|
+
maintainerAuthoredImportedIssue,
|
|
7069
|
+
hasExecutorHandoffTarget: Boolean(executorTransitionAssignee)
|
|
6529
7070
|
});
|
|
6530
|
-
const
|
|
7071
|
+
const nextTransitionAssignee = resolveSyncTransitionAssignee({
|
|
7072
|
+
nextStatus,
|
|
7073
|
+
syncContext: paperclipIssueSyncContext,
|
|
7074
|
+
advancedSettings
|
|
7075
|
+
});
|
|
7076
|
+
const nextAssigneeChanged = nextTransitionAssignee ? !doesPaperclipIssueAssigneeMatch(paperclipIssueSyncContext.assignee, nextTransitionAssignee.principal) : false;
|
|
7077
|
+
const shouldWakeImportedAssignee = wasImportedThisRun && paperclipIssue.status === nextStatus && nextStatus === "todo" && paperclipIssueSyncContext.assignee?.kind === "agent";
|
|
7078
|
+
const shouldWakeTransitionAssignee = paperclipIssue.status !== nextStatus && nextTransitionAssignee?.principal.kind === "agent" && isActionablePaperclipIssueStatus(nextStatus) && (nextAssigneeChanged || paperclipIssue.status !== nextStatus);
|
|
6531
7079
|
importedIssue.githubIssueNumber = githubIssue.number;
|
|
6532
7080
|
importedIssue.lastSeenCommentCount = snapshot.commentCount;
|
|
6533
7081
|
if (paperclipIssue.status === nextStatus) {
|
|
6534
7082
|
if (shouldWakeImportedAssignee) {
|
|
6535
|
-
|
|
6536
|
-
assigneeAgentId:
|
|
6537
|
-
paperclipIssueId: importedIssue.paperclipIssueId
|
|
7083
|
+
queuedIssueWakeups.push({
|
|
7084
|
+
assigneeAgentId: paperclipIssueSyncContext.assignee?.kind === "agent" ? paperclipIssueSyncContext.assignee.id : null,
|
|
7085
|
+
paperclipIssueId: importedIssue.paperclipIssueId,
|
|
7086
|
+
reason: IMPORTED_ISSUE_WAKE_REASON,
|
|
7087
|
+
mutation: "import"
|
|
6538
7088
|
});
|
|
6539
7089
|
}
|
|
6540
7090
|
continue;
|
|
@@ -6553,19 +7103,26 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
6553
7103
|
repositoryUrl: repository.url,
|
|
6554
7104
|
githubIssueNumber: githubIssue.number
|
|
6555
7105
|
});
|
|
6556
|
-
await
|
|
7106
|
+
await updatePaperclipIssueState(ctx, {
|
|
6557
7107
|
companyId: mapping.companyId,
|
|
6558
7108
|
issueId: importedIssue.paperclipIssueId,
|
|
7109
|
+
currentStatus: paperclipIssue.status,
|
|
7110
|
+
syncContext: paperclipIssueSyncContext,
|
|
6559
7111
|
nextStatus,
|
|
7112
|
+
...nextTransitionAssignee ? { nextAssignee: nextTransitionAssignee.principal } : {},
|
|
6560
7113
|
transitionComment: transitionComment.body,
|
|
6561
7114
|
transitionCommentAnnotation: transitionComment.annotation,
|
|
6562
7115
|
paperclipApiBaseUrl
|
|
6563
7116
|
});
|
|
6564
7117
|
updatedStatusesCount += 1;
|
|
6565
|
-
if (
|
|
6566
|
-
|
|
6567
|
-
assigneeAgentId:
|
|
6568
|
-
paperclipIssueId: importedIssue.paperclipIssueId
|
|
7118
|
+
if (shouldWakeTransitionAssignee && nextTransitionAssignee?.principal.kind === "agent") {
|
|
7119
|
+
queuedIssueWakeups.push({
|
|
7120
|
+
assigneeAgentId: nextTransitionAssignee.principal.id,
|
|
7121
|
+
paperclipIssueId: importedIssue.paperclipIssueId,
|
|
7122
|
+
reason: STATUS_TRANSITION_WAKE_REASON,
|
|
7123
|
+
mutation: "status_transition",
|
|
7124
|
+
previousStatus: paperclipIssue.status,
|
|
7125
|
+
nextStatus
|
|
6569
7126
|
});
|
|
6570
7127
|
}
|
|
6571
7128
|
} catch (error) {
|
|
@@ -6587,13 +7144,17 @@ async function synchronizePaperclipIssueStatuses(ctx, octokit, repository, mappi
|
|
|
6587
7144
|
}
|
|
6588
7145
|
}
|
|
6589
7146
|
await mapWithConcurrency(
|
|
6590
|
-
|
|
7147
|
+
queuedIssueWakeups,
|
|
6591
7148
|
IMPORTED_ISSUE_WAKEUP_CONCURRENCY,
|
|
6592
|
-
async (queuedWakeup) =>
|
|
7149
|
+
async (queuedWakeup) => wakePaperclipIssueAssignee(ctx, {
|
|
6593
7150
|
assigneeAgentId: queuedWakeup.assigneeAgentId,
|
|
6594
7151
|
paperclipIssueId: queuedWakeup.paperclipIssueId,
|
|
6595
7152
|
companyId: mapping.companyId,
|
|
6596
|
-
paperclipApiBaseUrl
|
|
7153
|
+
paperclipApiBaseUrl,
|
|
7154
|
+
reason: queuedWakeup.reason,
|
|
7155
|
+
mutation: queuedWakeup.mutation,
|
|
7156
|
+
previousStatus: queuedWakeup.previousStatus,
|
|
7157
|
+
nextStatus: queuedWakeup.nextStatus
|
|
6597
7158
|
})
|
|
6598
7159
|
);
|
|
6599
7160
|
return {
|
|
@@ -11026,7 +11587,7 @@ var plugin = definePlugin({
|
|
|
11026
11587
|
await saveSettingsSyncState(ctx, normalizedSettings, settingsForResponse.syncState, requestedCompanyId);
|
|
11027
11588
|
}
|
|
11028
11589
|
const scopedMappings = filterMappingsByCompany(settingsForResponse.mappings, requestedCompanyId);
|
|
11029
|
-
const availableAssignees = includeAssignees && requestedCompanyId ? await listAvailableAssignees(ctx, requestedCompanyId) : [];
|
|
11590
|
+
const availableAssignees = includeAssignees && requestedCompanyId ? await listAvailableAssignees(ctx, requestedCompanyId, settingsForResponse) : [];
|
|
11030
11591
|
return {
|
|
11031
11592
|
...getPublicSettingsForScope(settingsForResponse, requestedCompanyId),
|
|
11032
11593
|
...includeAssignees ? { availableAssignees } : {},
|
|
@@ -11137,6 +11698,7 @@ var plugin = definePlugin({
|
|
|
11137
11698
|
...githubTokenLogin ? { githubTokenLogin } : {},
|
|
11138
11699
|
paperclipBoardApiTokenRefs: previous.paperclipBoardApiTokenRefs,
|
|
11139
11700
|
paperclipBoardAccessIdentityByCompanyId: previous.paperclipBoardAccessIdentityByCompanyId,
|
|
11701
|
+
paperclipBoardAccessUserIdByCompanyId: previous.paperclipBoardAccessUserIdByCompanyId,
|
|
11140
11702
|
...Object.keys(nextCompanyAdvancedSettingsByCompanyId).length > 0 ? { companyAdvancedSettingsByCompanyId: nextCompanyAdvancedSettingsByCompanyId } : {},
|
|
11141
11703
|
...githubTokenRef ? { githubTokenRef } : {}
|
|
11142
11704
|
});
|
|
@@ -11163,6 +11725,7 @@ var plugin = definePlugin({
|
|
|
11163
11725
|
...current.githubTokenLogin ? { githubTokenLogin: current.githubTokenLogin } : {},
|
|
11164
11726
|
...current.paperclipBoardApiTokenRefs ? { paperclipBoardApiTokenRefs: current.paperclipBoardApiTokenRefs } : {},
|
|
11165
11727
|
...current.paperclipBoardAccessIdentityByCompanyId ? { paperclipBoardAccessIdentityByCompanyId: current.paperclipBoardAccessIdentityByCompanyId } : {},
|
|
11728
|
+
...current.paperclipBoardAccessUserIdByCompanyId ? { paperclipBoardAccessUserIdByCompanyId: current.paperclipBoardAccessUserIdByCompanyId } : {},
|
|
11166
11729
|
...current.companyAdvancedSettingsByCompanyId ? { companyAdvancedSettingsByCompanyId: current.companyAdvancedSettingsByCompanyId } : {},
|
|
11167
11730
|
...githubTokenRef ? { githubTokenRef } : {},
|
|
11168
11731
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -11177,7 +11740,7 @@ var plugin = definePlugin({
|
|
|
11177
11740
|
return {
|
|
11178
11741
|
...getPublicSettingsForScope(next, requestedCompanyId),
|
|
11179
11742
|
...scopedGitHubTokenLogin ? { githubTokenLogin: scopedGitHubTokenLogin } : {},
|
|
11180
|
-
availableAssignees: requestedCompanyId ? await listAvailableAssignees(ctx, requestedCompanyId) : []
|
|
11743
|
+
availableAssignees: requestedCompanyId ? await listAvailableAssignees(ctx, requestedCompanyId, next) : []
|
|
11181
11744
|
};
|
|
11182
11745
|
});
|
|
11183
11746
|
ctx.actions.register("settings.updateBoardAccess", async (input) => {
|
|
@@ -11195,11 +11758,15 @@ var plugin = definePlugin({
|
|
|
11195
11758
|
const nextPaperclipBoardAccessIdentityByCompanyId = {
|
|
11196
11759
|
...previous.paperclipBoardAccessIdentityByCompanyId ?? {}
|
|
11197
11760
|
};
|
|
11761
|
+
const nextPaperclipBoardAccessUserIdByCompanyId = {
|
|
11762
|
+
...previous.paperclipBoardAccessUserIdByCompanyId ?? {}
|
|
11763
|
+
};
|
|
11198
11764
|
if (nextSecretRef) {
|
|
11199
11765
|
nextPaperclipBoardApiTokenRefs[companyId] = nextSecretRef;
|
|
11200
11766
|
} else {
|
|
11201
11767
|
delete nextPaperclipBoardApiTokenRefs[companyId];
|
|
11202
11768
|
delete nextPaperclipBoardAccessIdentityByCompanyId[companyId];
|
|
11769
|
+
delete nextPaperclipBoardAccessUserIdByCompanyId[companyId];
|
|
11203
11770
|
}
|
|
11204
11771
|
if ("paperclipBoardAccessIdentity" in record) {
|
|
11205
11772
|
const nextIdentityLabel = normalizeOptionalString2(record.paperclipBoardAccessIdentity);
|
|
@@ -11209,15 +11776,25 @@ var plugin = definePlugin({
|
|
|
11209
11776
|
delete nextPaperclipBoardAccessIdentityByCompanyId[companyId];
|
|
11210
11777
|
}
|
|
11211
11778
|
}
|
|
11779
|
+
if ("paperclipBoardAccessUserId" in record) {
|
|
11780
|
+
const nextUserId = normalizeOptionalString2(record.paperclipBoardAccessUserId);
|
|
11781
|
+
if (nextUserId) {
|
|
11782
|
+
nextPaperclipBoardAccessUserIdByCompanyId[companyId] = nextUserId;
|
|
11783
|
+
} else {
|
|
11784
|
+
delete nextPaperclipBoardAccessUserIdByCompanyId[companyId];
|
|
11785
|
+
}
|
|
11786
|
+
}
|
|
11212
11787
|
const {
|
|
11213
11788
|
paperclipBoardApiTokenRefs: _previousPaperclipBoardApiTokenRefs,
|
|
11214
11789
|
paperclipBoardAccessIdentityByCompanyId: _previousPaperclipBoardAccessIdentityByCompanyId,
|
|
11790
|
+
paperclipBoardAccessUserIdByCompanyId: _previousPaperclipBoardAccessUserIdByCompanyId,
|
|
11215
11791
|
...previousWithoutBoardAccess
|
|
11216
11792
|
} = previous;
|
|
11217
11793
|
const nextBase = sanitizeSettingsForCurrentSetup({
|
|
11218
11794
|
...previousWithoutBoardAccess,
|
|
11219
11795
|
...Object.keys(nextPaperclipBoardApiTokenRefs).length > 0 ? { paperclipBoardApiTokenRefs: nextPaperclipBoardApiTokenRefs } : {},
|
|
11220
11796
|
...Object.keys(nextPaperclipBoardAccessIdentityByCompanyId).length > 0 ? { paperclipBoardAccessIdentityByCompanyId: nextPaperclipBoardAccessIdentityByCompanyId } : {},
|
|
11797
|
+
...Object.keys(nextPaperclipBoardAccessUserIdByCompanyId).length > 0 ? { paperclipBoardAccessUserIdByCompanyId: nextPaperclipBoardAccessUserIdByCompanyId } : {},
|
|
11221
11798
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
11222
11799
|
}, {
|
|
11223
11800
|
hasToken: hasConfiguredGithubToken(previous, config, companyId),
|