@sideboard-ai/core 0.1.324 → 0.1.326
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/{abletime-CU2UPMJF.js → abletime-RDHYSZL2.js} +5 -4
- package/dist/{abletime-5ZY3ZUUO.js → abletime-RYYFGKMU.js} +6 -5
- package/dist/{abletime-oauth-7EOSBEFM.js → abletime-oauth-OYGS2DAU.js} +3 -3
- package/dist/{abletime-oauth-4TTCR3CT.js → abletime-oauth-QR2QDVMK.js} +3 -3
- package/dist/{abletime-rest-KOKE7V4B.js → abletime-rest-BKYKNPTC.js} +3 -3
- package/dist/{abletime-rest-3TG4G7LW.js → abletime-rest-QDPTWNS4.js} +3 -3
- package/dist/agents/claude-runner.cjs +59 -22
- package/dist/agents/claude-runner.js +39 -16
- package/dist/agents/cursor-runner.cjs +97 -32
- package/dist/agents/cursor-runner.js +74 -24
- package/dist/{agents-YK65UDLT.js → agents-HANWZNI6.js} +9 -8
- package/dist/{agents-LUSTCMDN.js → agents-QO7GKBYT.js} +12 -11
- package/dist/{app-settings-XIF4EC4K.js → app-settings-RXBH7KY4.js} +1 -1
- package/dist/{app-settings-I5HM5RMP.js → app-settings-TUVLZNDE.js} +1 -1
- package/dist/{chunk-7MQSYPAY.js → chunk-2EOLWHXH.js} +206 -188
- package/dist/{chunk-QOS4465Y.js → chunk-3Q4XNYHY.js} +1 -1
- package/dist/{chunk-RR4WCGNB.js → chunk-46FECFZU.js} +18 -15
- package/dist/{chunk-5GDJSFCT.js → chunk-4H5MWZ6J.js} +2 -2
- package/dist/{chunk-EQ52JR3Q.js → chunk-5MXLDDYF.js} +1 -1
- package/dist/{chunk-NCHOTN3C.js → chunk-5YCKATG6.js} +4 -3
- package/dist/{chunk-ROOZDDFA.js → chunk-75RPUXJS.js} +7 -8
- package/dist/{chunk-INBUV7HY.js → chunk-B244TJWC.js} +2 -2
- package/dist/{chunk-JH5SMVM5.js → chunk-BBNWKL4I.js} +268 -235
- package/dist/{chunk-ODH3XHMI.js → chunk-C7HUCBX7.js} +5 -5
- package/dist/{chunk-OIHRC2ZJ.js → chunk-D744HOI5.js} +25 -3
- package/dist/chunk-DBKHL7NC.js +214 -0
- package/dist/chunk-F6GE7V3Q.js +216 -0
- package/dist/{chunk-JE3JLHMO.js → chunk-FWVUGLBG.js} +3 -3
- package/dist/{chunk-DF5BM5OY.js → chunk-GP3RLZ6A.js} +207 -186
- package/dist/{chunk-FR3SAYUL.js → chunk-JM2TGOHP.js} +5 -5
- package/dist/{chunk-Y3WREIPQ.js → chunk-K7M7MIWL.js} +12 -210
- package/dist/{chunk-FUFNMUNR.js → chunk-KECHK3ZT.js} +7 -8
- package/dist/{chunk-EKU7FQVG.js → chunk-KXRF6RNL.js} +2 -2
- package/dist/chunk-LRHUBGEM.js +1259 -0
- package/dist/{chunk-MZIZTSR2.js → chunk-MAW7DIG7.js} +2 -2
- package/dist/{chunk-ZHGMRBSC.js → chunk-NURZOXGN.js} +4 -4
- package/dist/{chunk-QIC2JXHW.js → chunk-OOXRMTU6.js} +1 -1
- package/dist/{chunk-5XNT6BCG.js → chunk-OPXEJJBM.js} +1 -1
- package/dist/chunk-OYRN356W.js +80 -0
- package/dist/{chunk-7PVT73H6.js → chunk-PB6HW7SA.js} +12 -210
- package/dist/{chunk-B4CDSSRN.js → chunk-Q3FHTGIW.js} +2 -0
- package/dist/{chunk-7D5QIU3N.js → chunk-QG4CYRA3.js} +301 -268
- package/dist/{chunk-EJ66LEKH.js → chunk-QPSKQP6E.js} +24 -3
- package/dist/{chunk-Q3YJZPQI.js → chunk-RZVOX2MT.js} +3 -3
- package/dist/chunk-SO7G6O24.js +1445 -0
- package/dist/{chunk-YDUHLWI5.js → chunk-SUZOPJZC.js} +21 -7
- package/dist/chunk-TMCK5TLA.js +25 -0
- package/dist/chunk-W3ZLE4PO.js +77 -0
- package/dist/chunk-WUCAVIIP.js +28 -0
- package/dist/{chunk-BYUCNIXK.js → chunk-XLVLMQKN.js} +13 -9
- package/dist/{chunk-ZIRQZUZY.js → chunk-YGVGXQA7.js} +3 -3
- package/dist/{chunk-NXUN4U5P.js → chunk-YWQF7WAG.js} +2 -2
- package/dist/{chunk-IFWGG2M2.js → chunk-ZSHS46V5.js} +2 -0
- package/dist/{chunk-74OPNF7U.js → chunk-ZUAYA57W.js} +2 -2
- package/dist/{conductor-SPZOTC7W.js → conductor-4GVTHUUB.js} +7 -7
- package/dist/{conductor-JFABMXDA.js → conductor-TGWI6ETZ.js} +6 -6
- package/dist/{coordinator-prompt-K66RGNWK.js → coordinator-prompt-3E6SIDTM.js} +4 -4
- package/dist/{coordinator-prompt-Z6UFHFDH.js → coordinator-prompt-WEYJREV6.js} +4 -4
- package/dist/{cursor-recover-TEBFKAZ4.js → cursor-recover-7F46PAJ5.js} +1 -1
- package/dist/{cursor-recover-OWPQUHFH.js → cursor-recover-BDIAZIE3.js} +1 -1
- package/dist/{global-workspace-A6UO6PJV.js → global-workspace-FPDZKUPF.js} +4 -4
- package/dist/{global-workspace-RHEO55B4.js → global-workspace-UIEXAG2B.js} +4 -4
- package/dist/index.cjs +21094 -20840
- package/dist/index.d.cts +36 -17
- package/dist/index.d.ts +36 -17
- package/dist/index.js +343 -1678
- package/dist/linear-6S7MK5YK.js +69 -0
- package/dist/linear-Z2T4KUAQ.js +67 -0
- package/dist/linear-list-A37WLUBO.js +6 -0
- package/dist/linear-list-Q4M7AZO2.js +8 -0
- package/dist/mcp/run-stdio.cjs +15903 -15640
- package/dist/mcp/run-stdio.js +67 -1247
- package/dist/{optional-cli-RMMS5L6F.js → optional-cli-AANBL4U5.js} +3 -3
- package/dist/{optional-cli-Y3Z3GIPZ.js → optional-cli-CA4LL322.js} +4 -4
- package/dist/{orchestrator-7VTAUY24.js → orchestrator-7FVDBE7Y.js} +15 -13
- package/dist/{orchestrator-2O6OJQLX.js → orchestrator-RQXRZW7Z.js} +14 -12
- package/dist/{thread-store-3F7GR64A.js → thread-store-4ZN3DS2W.js} +1 -1
- package/dist/{thread-store-JRSL4HQY.js → thread-store-IC4BQXDW.js} +1 -1
- package/dist/{workspaces-NRKEXOYR.js → workspaces-LEW4UP56.js} +4 -4
- package/dist/{workspaces-42E6AGXD.js → workspaces-QONNLHNR.js} +4 -4
- package/dist/{worktree-ZDA2UJ7C.js → worktree-6GIONDQO.js} +3 -3
- package/dist/{worktree-4NGXCCUO.js → worktree-KFEJAQ2H.js} +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ableTimeDependencyTarget,
|
|
3
|
+
ableTimeStateIsClosed,
|
|
4
|
+
commentAbleTimeTask,
|
|
5
|
+
createAbleTimeTask,
|
|
6
|
+
downloadAbleTimeAttachment,
|
|
7
|
+
ensureAbleTimeTask,
|
|
8
|
+
getAbleTimeOrientation,
|
|
9
|
+
getAbleTimeTask,
|
|
10
|
+
issueAttachmentForAbleTimeTask,
|
|
11
|
+
listAbleTimeAssignedIssues,
|
|
12
|
+
listAbleTimeProjects,
|
|
13
|
+
listAbleTimeTasks,
|
|
14
|
+
mapAbleTimeTask,
|
|
15
|
+
searchAbleTimeTasks,
|
|
16
|
+
taskUrl,
|
|
17
|
+
toAbleTimeIssueInfo,
|
|
18
|
+
updateAbleTimeTask,
|
|
19
|
+
verifyAbleTimeConnection
|
|
20
|
+
} from "./chunk-PB6HW7SA.js";
|
|
21
|
+
import {
|
|
22
|
+
clearOptionalCliPresenceCache,
|
|
23
|
+
detectOptionalServiceClis,
|
|
24
|
+
installOptionalServiceCli,
|
|
25
|
+
optionalCliPresence
|
|
26
|
+
} from "./chunk-ZUAYA57W.js";
|
|
1
27
|
import {
|
|
2
28
|
AGENT_GIT_ACTIONS,
|
|
3
29
|
BOARD_COLUMN_DEFS,
|
|
@@ -79,7 +105,6 @@ import {
|
|
|
79
105
|
formatDevAccessHint,
|
|
80
106
|
formatEntireGitDirective,
|
|
81
107
|
formatFleetArchive,
|
|
82
|
-
formatGitHubSearchUpdatedSince,
|
|
83
108
|
formatInjectedNoticesForTurn,
|
|
84
109
|
formatIssueToolsDirective,
|
|
85
110
|
formatIssueToolsReminder,
|
|
@@ -125,7 +150,6 @@ import {
|
|
|
125
150
|
isThisProcessDesktopHost,
|
|
126
151
|
isUnclaimedRunScriptRequest,
|
|
127
152
|
issueBlockedByIds,
|
|
128
|
-
issueMatchesUpdatedSince,
|
|
129
153
|
issueNeedsWorkspacePick,
|
|
130
154
|
issueTicketFromThread,
|
|
131
155
|
joinSteerPrompt,
|
|
@@ -153,11 +177,9 @@ import {
|
|
|
153
177
|
openStackLayer,
|
|
154
178
|
packagedDetachedJobPath,
|
|
155
179
|
parseDurationMs,
|
|
156
|
-
parseIssueSince,
|
|
157
180
|
parseWeeklyDays,
|
|
158
181
|
pendingInjectedNotices,
|
|
159
182
|
prependQueuedItem,
|
|
160
|
-
previewIssueCommentBody,
|
|
161
183
|
previewLand,
|
|
162
184
|
readRunLog,
|
|
163
185
|
readSetupLog,
|
|
@@ -216,7 +238,23 @@ import {
|
|
|
216
238
|
worktreeCleanupSettings,
|
|
217
239
|
writeAgentBlock,
|
|
218
240
|
writeWorktreeFile
|
|
219
|
-
} from "./chunk-
|
|
241
|
+
} from "./chunk-GP3RLZ6A.js";
|
|
242
|
+
import {
|
|
243
|
+
OPTIONAL_SERVICES,
|
|
244
|
+
OPTIONAL_SERVICE_IDS,
|
|
245
|
+
connectOptionalService,
|
|
246
|
+
connectedOptionalServices,
|
|
247
|
+
disconnectOptionalService,
|
|
248
|
+
formatOptionalServicesDirective,
|
|
249
|
+
formatOptionalServicesDirectiveForTurn,
|
|
250
|
+
formatOptionalServicesReminder,
|
|
251
|
+
formatOptionalServicesReminderForTurn,
|
|
252
|
+
isOptionalServiceId,
|
|
253
|
+
normalizeServiceOrigin,
|
|
254
|
+
optionalServiceConnected,
|
|
255
|
+
optionalServiceSpec,
|
|
256
|
+
verifyOptionalService
|
|
257
|
+
} from "./chunk-B244TJWC.js";
|
|
220
258
|
import {
|
|
221
259
|
LEGACY_PLAN_FILE_REL,
|
|
222
260
|
PLAN_FILE_NAME,
|
|
@@ -232,6 +270,61 @@ import {
|
|
|
232
270
|
threadMayPresentPlan,
|
|
233
271
|
writePlanFile
|
|
234
272
|
} from "./chunk-PGPSIEMW.js";
|
|
273
|
+
import {
|
|
274
|
+
LINEAR_OAUTH_CANCELLED,
|
|
275
|
+
LINEAR_OAUTH_PORT,
|
|
276
|
+
LINEAR_OAUTH_REDIRECT,
|
|
277
|
+
LINEAR_OAUTH_SCOPES,
|
|
278
|
+
LinearOAuthCancelledError,
|
|
279
|
+
buildLinearIssueFilter,
|
|
280
|
+
commentLinearIssue,
|
|
281
|
+
createLinearIssue,
|
|
282
|
+
createLinearPkce,
|
|
283
|
+
disconnectLinear,
|
|
284
|
+
downloadLinearAttachment,
|
|
285
|
+
flipLinearRelationType,
|
|
286
|
+
getLinearAuthToken,
|
|
287
|
+
getLinearIssue,
|
|
288
|
+
hasBakedLinearOAuth,
|
|
289
|
+
isLinearNoneToken,
|
|
290
|
+
isLinearOAuthCancelled,
|
|
291
|
+
linearAuthorizationHeader,
|
|
292
|
+
linearCycleIsActive,
|
|
293
|
+
linearGraphql,
|
|
294
|
+
linearOAuthAuthorizeUrl,
|
|
295
|
+
linearOAuthCredentials,
|
|
296
|
+
linearRelationCreateInput,
|
|
297
|
+
listLinearAssignedIssues,
|
|
298
|
+
listLinearCommentsSince,
|
|
299
|
+
listLinearIssuesDirect,
|
|
300
|
+
listLinearIssuesFiltered,
|
|
301
|
+
listLinearProjects,
|
|
302
|
+
listLinearTeamCycles,
|
|
303
|
+
listLinearTeams,
|
|
304
|
+
normalizeLinearRelationType,
|
|
305
|
+
resolveLinearCycle,
|
|
306
|
+
resolveLinearLabel,
|
|
307
|
+
resolveLinearProject,
|
|
308
|
+
resolveLinearState,
|
|
309
|
+
resolveLinearTeam,
|
|
310
|
+
rewriteLinearError,
|
|
311
|
+
startLinearOAuth,
|
|
312
|
+
updateLinearIssue,
|
|
313
|
+
validateLinearApiKey
|
|
314
|
+
} from "./chunk-SO7G6O24.js";
|
|
315
|
+
import {
|
|
316
|
+
downloadAuthenticatedFile,
|
|
317
|
+
extractHttpUrls,
|
|
318
|
+
hostMatches,
|
|
319
|
+
issueAttachmentWorktreePath,
|
|
320
|
+
mergeIssueAttachments
|
|
321
|
+
} from "./chunk-DBKHL7NC.js";
|
|
322
|
+
import {
|
|
323
|
+
formatGitHubSearchUpdatedSince,
|
|
324
|
+
issueMatchesUpdatedSince,
|
|
325
|
+
parseIssueSince,
|
|
326
|
+
previewIssueCommentBody
|
|
327
|
+
} from "./chunk-W3ZLE4PO.js";
|
|
235
328
|
import {
|
|
236
329
|
BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
237
330
|
CLAUDE_MODEL_CATALOG,
|
|
@@ -306,7 +399,19 @@ import {
|
|
|
306
399
|
userMcpNamesToDisable,
|
|
307
400
|
worktreeMcpToolNames,
|
|
308
401
|
writeInjectedMcpConfig
|
|
309
|
-
} from "./chunk-
|
|
402
|
+
} from "./chunk-BBNWKL4I.js";
|
|
403
|
+
import {
|
|
404
|
+
getAgentSetupInfo,
|
|
405
|
+
installAgent,
|
|
406
|
+
installNpmGlobalPackage,
|
|
407
|
+
listAgentSetupInfo,
|
|
408
|
+
loginAgent,
|
|
409
|
+
loginManagedAccount,
|
|
410
|
+
openInSystemTerminal,
|
|
411
|
+
prepareTerminalCommand,
|
|
412
|
+
resolveLoginCommand,
|
|
413
|
+
whichOnPath
|
|
414
|
+
} from "./chunk-4H5MWZ6J.js";
|
|
310
415
|
import {
|
|
311
416
|
brightsyMcpServerName,
|
|
312
417
|
connectBrightsyTeam,
|
|
@@ -317,6 +422,7 @@ import {
|
|
|
317
422
|
listConnectedBrightsyTeams,
|
|
318
423
|
switchBrightsyAccount
|
|
319
424
|
} from "./chunk-S5CJUIPY.js";
|
|
425
|
+
import "./chunk-TMCK5TLA.js";
|
|
320
426
|
import {
|
|
321
427
|
BRIGHTSY_SUMMARIZE_CONTEXT_TOOL,
|
|
322
428
|
CHARS_PER_CONTEXT_TOKEN,
|
|
@@ -437,7 +543,7 @@ import {
|
|
|
437
543
|
workspaceAgentChatSummaries,
|
|
438
544
|
worktreeRunPortsPath,
|
|
439
545
|
wrapReviewSkillMarkdown
|
|
440
|
-
} from "./chunk-
|
|
546
|
+
} from "./chunk-46FECFZU.js";
|
|
441
547
|
import {
|
|
442
548
|
STDERR_EVENT_MAX_CHARS,
|
|
443
549
|
STDOUT_EVENT_MAX_CHARS,
|
|
@@ -479,7 +585,7 @@ import {
|
|
|
479
585
|
visibleToolRowDetail,
|
|
480
586
|
withEventParentId,
|
|
481
587
|
withEventsParentId
|
|
482
|
-
} from "./chunk-
|
|
588
|
+
} from "./chunk-OOXRMTU6.js";
|
|
483
589
|
import "./chunk-7H655NEW.js";
|
|
484
590
|
import {
|
|
485
591
|
ABLETIME_OAUTH_CALLBACK_PATH,
|
|
@@ -503,36 +609,10 @@ import {
|
|
|
503
609
|
parseAbleTimeOAuthCallbackUrl,
|
|
504
610
|
resolveAbleTimeOAuthHost,
|
|
505
611
|
startAbleTimeOAuth
|
|
506
|
-
} from "./chunk-
|
|
507
|
-
import {
|
|
508
|
-
ableTimeDependencyTarget,
|
|
509
|
-
ableTimeStateIsClosed,
|
|
510
|
-
commentAbleTimeTask,
|
|
511
|
-
createAbleTimeTask,
|
|
512
|
-
downloadAbleTimeAttachment,
|
|
513
|
-
downloadAuthenticatedFile,
|
|
514
|
-
ensureAbleTimeTask,
|
|
515
|
-
extractHttpUrls,
|
|
516
|
-
getAbleTimeOrientation,
|
|
517
|
-
getAbleTimeTask,
|
|
518
|
-
hostMatches,
|
|
519
|
-
issueAttachmentForAbleTimeTask,
|
|
520
|
-
issueAttachmentWorktreePath,
|
|
521
|
-
listAbleTimeAssignedIssues,
|
|
522
|
-
listAbleTimeProjects,
|
|
523
|
-
listAbleTimeTasks,
|
|
524
|
-
looksLikeHttpUrl,
|
|
525
|
-
mapAbleTimeTask,
|
|
526
|
-
mergeIssueAttachments,
|
|
527
|
-
searchAbleTimeTasks,
|
|
528
|
-
taskUrl,
|
|
529
|
-
toAbleTimeIssueInfo,
|
|
530
|
-
updateAbleTimeTask,
|
|
531
|
-
verifyAbleTimeConnection
|
|
532
|
-
} from "./chunk-7PVT73H6.js";
|
|
612
|
+
} from "./chunk-RZVOX2MT.js";
|
|
533
613
|
import {
|
|
534
614
|
looksLikeAbleTimeUlid
|
|
535
|
-
} from "./chunk-
|
|
615
|
+
} from "./chunk-KXRF6RNL.js";
|
|
536
616
|
import {
|
|
537
617
|
ABLETIME_MCP_PATH,
|
|
538
618
|
ABLETIME_MCP_PM_PATH,
|
|
@@ -553,41 +633,7 @@ import {
|
|
|
553
633
|
normalizeAbleTimeCredential,
|
|
554
634
|
normalizeAbleTimeHost,
|
|
555
635
|
rewriteAbleTimeError
|
|
556
|
-
} from "./chunk-
|
|
557
|
-
import {
|
|
558
|
-
clearOptionalCliPresenceCache,
|
|
559
|
-
detectOptionalServiceClis,
|
|
560
|
-
installOptionalServiceCli,
|
|
561
|
-
optionalCliPresence
|
|
562
|
-
} from "./chunk-74OPNF7U.js";
|
|
563
|
-
import {
|
|
564
|
-
OPTIONAL_SERVICES,
|
|
565
|
-
OPTIONAL_SERVICE_IDS,
|
|
566
|
-
connectOptionalService,
|
|
567
|
-
connectedOptionalServices,
|
|
568
|
-
disconnectOptionalService,
|
|
569
|
-
formatOptionalServicesDirective,
|
|
570
|
-
formatOptionalServicesDirectiveForTurn,
|
|
571
|
-
formatOptionalServicesReminder,
|
|
572
|
-
formatOptionalServicesReminderForTurn,
|
|
573
|
-
isOptionalServiceId,
|
|
574
|
-
normalizeServiceOrigin,
|
|
575
|
-
optionalServiceConnected,
|
|
576
|
-
optionalServiceSpec,
|
|
577
|
-
verifyOptionalService
|
|
578
|
-
} from "./chunk-INBUV7HY.js";
|
|
579
|
-
import {
|
|
580
|
-
getAgentSetupInfo,
|
|
581
|
-
installAgent,
|
|
582
|
-
installNpmGlobalPackage,
|
|
583
|
-
listAgentSetupInfo,
|
|
584
|
-
loginAgent,
|
|
585
|
-
loginManagedAccount,
|
|
586
|
-
openInSystemTerminal,
|
|
587
|
-
prepareTerminalCommand,
|
|
588
|
-
resolveLoginCommand,
|
|
589
|
-
whichOnPath
|
|
590
|
-
} from "./chunk-5GDJSFCT.js";
|
|
636
|
+
} from "./chunk-C7HUCBX7.js";
|
|
591
637
|
import {
|
|
592
638
|
CLOUD_COORDINATOR_BUSY_REPLY,
|
|
593
639
|
CLOUD_COORDINATOR_CANCELED_REPLY,
|
|
@@ -640,7 +686,7 @@ import {
|
|
|
640
686
|
stageBuffersAsAttachments,
|
|
641
687
|
syncWorkspacesFromThreads,
|
|
642
688
|
takenTeamSlugsForOrchestration
|
|
643
|
-
} from "./chunk-
|
|
689
|
+
} from "./chunk-FWVUGLBG.js";
|
|
644
690
|
import {
|
|
645
691
|
GITHUB_PR_BODY_MAX_CHARS,
|
|
646
692
|
GITHUB_PR_BODY_SAFE_CHARS,
|
|
@@ -729,7 +775,7 @@ import {
|
|
|
729
775
|
suggestSlug,
|
|
730
776
|
switchWorktreeBranch,
|
|
731
777
|
warmGithubAgentAuth
|
|
732
|
-
} from "./chunk-
|
|
778
|
+
} from "./chunk-KECHK3ZT.js";
|
|
733
779
|
import {
|
|
734
780
|
FAMOUS_SOCCER_TEAMS,
|
|
735
781
|
allocateTeamName,
|
|
@@ -764,7 +810,7 @@ import {
|
|
|
764
810
|
worktreeNameFromPath,
|
|
765
811
|
worktreeSlugForTicket,
|
|
766
812
|
writeThread
|
|
767
|
-
} from "./chunk-
|
|
813
|
+
} from "./chunk-ZSHS46V5.js";
|
|
768
814
|
import "./chunk-QKHEVSB5.js";
|
|
769
815
|
import {
|
|
770
816
|
BRIGHTSY_OAUTH_CLIENT_ID,
|
|
@@ -802,7 +848,7 @@ import {
|
|
|
802
848
|
import {
|
|
803
849
|
clipMcpToolResult,
|
|
804
850
|
cursorToolErrorText
|
|
805
|
-
} from "./chunk-
|
|
851
|
+
} from "./chunk-5YCKATG6.js";
|
|
806
852
|
import "./chunk-AXQ4ZWHK.js";
|
|
807
853
|
import {
|
|
808
854
|
AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
@@ -820,8 +866,8 @@ import {
|
|
|
820
866
|
trustSystemCertificates,
|
|
821
867
|
turnCostUsdFromCursorUsage,
|
|
822
868
|
withMaxOldSpaceSize
|
|
823
|
-
} from "./chunk-
|
|
824
|
-
import "./chunk-
|
|
869
|
+
} from "./chunk-NURZOXGN.js";
|
|
870
|
+
import "./chunk-OPXEJJBM.js";
|
|
825
871
|
import {
|
|
826
872
|
ATTACHMENTS_DIR,
|
|
827
873
|
CLI_DIR,
|
|
@@ -870,7 +916,7 @@ import {
|
|
|
870
916
|
rpcNotify,
|
|
871
917
|
rpcResult,
|
|
872
918
|
writeAgentRuntimeMetadata
|
|
873
|
-
} from "./chunk-
|
|
919
|
+
} from "./chunk-SUZOPJZC.js";
|
|
874
920
|
import {
|
|
875
921
|
AGENT_DONE_SOUNDS,
|
|
876
922
|
BAKED_REMOTE_RELAY_URL,
|
|
@@ -1012,6 +1058,7 @@ import {
|
|
|
1012
1058
|
showCostEnabled,
|
|
1013
1059
|
slackAppLevelToken,
|
|
1014
1060
|
slackListenEnabled,
|
|
1061
|
+
subtractUsage,
|
|
1015
1062
|
sumUsageList,
|
|
1016
1063
|
toPublicAppSettings,
|
|
1017
1064
|
totalTokens,
|
|
@@ -1030,7 +1077,7 @@ import {
|
|
|
1030
1077
|
withTurnScopedCost,
|
|
1031
1078
|
writeSecureJson,
|
|
1032
1079
|
writeViewerContext
|
|
1033
|
-
} from "./chunk-
|
|
1080
|
+
} from "./chunk-QPSKQP6E.js";
|
|
1034
1081
|
import {
|
|
1035
1082
|
THINKING_EFFORTS,
|
|
1036
1083
|
isThinkingEffort,
|
|
@@ -3691,1615 +3738,232 @@ function issueNumberFromApiUrl(url) {
|
|
|
3691
3738
|
const match = url.match(/\/issues\/(\d+)(?:\b|$)/i);
|
|
3692
3739
|
if (!match) return null;
|
|
3693
3740
|
const number = Number(match[1]);
|
|
3694
|
-
return Number.isFinite(number) && number > 0 ? number : null;
|
|
3695
|
-
}
|
|
3696
|
-
var GITHUB_ISSUE_COMMENTS_PAGE_SIZE = 100;
|
|
3697
|
-
var GITHUB_ISSUE_COMMENTS_MAX_PAGES = 5;
|
|
3698
|
-
function parseGitHubIssueCommentNode(item, allowed) {
|
|
3699
|
-
const rec = item && typeof item === "object" ? item : null;
|
|
3700
|
-
if (!rec) return null;
|
|
3701
|
-
const issueUrl = typeof rec.issue_url === "string" ? rec.issue_url : "";
|
|
3702
|
-
const number = issueNumberFromApiUrl(issueUrl);
|
|
3703
|
-
if (number == null) return null;
|
|
3704
|
-
if (allowed && !allowed.has(number)) return null;
|
|
3705
|
-
const body = previewIssueCommentBody(typeof rec.body === "string" ? rec.body : "");
|
|
3706
|
-
const user = rec.user && typeof rec.user === "object" ? String(rec.user.login ?? "").trim() : "";
|
|
3707
|
-
const createdAt = typeof rec.created_at === "string" ? rec.created_at : typeof rec.createdAt === "string" ? rec.createdAt : void 0;
|
|
3708
|
-
const url = typeof rec.html_url === "string" ? rec.html_url : void 0;
|
|
3709
|
-
return {
|
|
3710
|
-
identifier: `#${number}`,
|
|
3711
|
-
...user ? { author: user } : {},
|
|
3712
|
-
...createdAt ? { createdAt } : {},
|
|
3713
|
-
body,
|
|
3714
|
-
...url ? { url } : {}
|
|
3715
|
-
};
|
|
3716
|
-
}
|
|
3717
|
-
async function listGitHubIssueCommentsSince(opts) {
|
|
3718
|
-
const limit = Math.max(1, Math.min(250, opts.limit ?? 200));
|
|
3719
|
-
const { cwd, slug } = await resolveGitHubIssueRepo(opts.repoPath);
|
|
3720
|
-
if (!slug) return [];
|
|
3721
|
-
const restrictToIssues = opts.issueIdentifiers != null;
|
|
3722
|
-
const allowed = new Set(
|
|
3723
|
-
(opts.issueIdentifiers ?? []).map((id) => {
|
|
3724
|
-
try {
|
|
3725
|
-
return parseGitHubIssueNumber(id);
|
|
3726
|
-
} catch {
|
|
3727
|
-
return null;
|
|
3728
|
-
}
|
|
3729
|
-
}).filter((n) => n != null)
|
|
3730
|
-
);
|
|
3731
|
-
if (restrictToIssues && allowed.size === 0) return [];
|
|
3732
|
-
const pageSize = GITHUB_ISSUE_COMMENTS_PAGE_SIZE;
|
|
3733
|
-
const out = [];
|
|
3734
|
-
for (let page = 1; page <= GITHUB_ISSUE_COMMENTS_MAX_PAGES; page++) {
|
|
3735
|
-
const path2 = `repos/${slug}/issues/comments?since=${encodeURIComponent(opts.since)}&sort=created&direction=desc&per_page=${pageSize}&page=${page}`;
|
|
3736
|
-
const result = await gh(["api", path2], cwd, { reject: false });
|
|
3737
|
-
if (result.exitCode !== 0 || !result.stdout.trim()) break;
|
|
3738
|
-
let parsed;
|
|
3739
|
-
try {
|
|
3740
|
-
parsed = JSON.parse(result.stdout);
|
|
3741
|
-
} catch {
|
|
3742
|
-
break;
|
|
3743
|
-
}
|
|
3744
|
-
if (!Array.isArray(parsed) || parsed.length === 0) break;
|
|
3745
|
-
for (const item of parsed) {
|
|
3746
|
-
const comment = parseGitHubIssueCommentNode(item, restrictToIssues ? allowed : null);
|
|
3747
|
-
if (!comment) continue;
|
|
3748
|
-
out.push(comment);
|
|
3749
|
-
if (out.length >= limit) return out;
|
|
3750
|
-
}
|
|
3751
|
-
if (parsed.length < pageSize) break;
|
|
3752
|
-
}
|
|
3753
|
-
return out;
|
|
3754
|
-
}
|
|
3755
|
-
async function getGitHubIssue(id, opts) {
|
|
3756
|
-
const number = parseGitHubIssueNumber(id);
|
|
3757
|
-
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
3758
|
-
const stdout = requireGhOk(
|
|
3759
|
-
await gh(
|
|
3760
|
-
[
|
|
3761
|
-
"issue",
|
|
3762
|
-
"view",
|
|
3763
|
-
String(number),
|
|
3764
|
-
...repoArgs,
|
|
3765
|
-
"--json",
|
|
3766
|
-
"number,title,body,url,state,labels,assignees,comments,author"
|
|
3767
|
-
],
|
|
3768
|
-
cwd,
|
|
3769
|
-
{ reject: false }
|
|
3770
|
-
),
|
|
3771
|
-
`GitHub issue ${number}`
|
|
3772
|
-
);
|
|
3773
|
-
let parsed;
|
|
3774
|
-
try {
|
|
3775
|
-
parsed = JSON.parse(stdout);
|
|
3776
|
-
} catch {
|
|
3777
|
-
throw new Error(`GitHub issue ${number}: gh returned non-JSON`);
|
|
3778
|
-
}
|
|
3779
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
3780
|
-
throw new Error(`GitHub issue not found: #${number}`);
|
|
3781
|
-
}
|
|
3782
|
-
const issue = toGitHubIssue(parsed);
|
|
3783
|
-
const projects = await readGitHubIssueProjects(number, cwd, repoArgs);
|
|
3784
|
-
issue.projects = projects.projects;
|
|
3785
|
-
if (projects.error) issue.projectsError = projects.error;
|
|
3786
|
-
return issue;
|
|
3787
|
-
}
|
|
3788
|
-
async function commentGitHubIssue(input, opts) {
|
|
3789
|
-
const number = parseGitHubIssueNumber(input.id);
|
|
3790
|
-
const body = input.body.trim();
|
|
3791
|
-
if (!body) throw new Error("GitHub comment body is required");
|
|
3792
|
-
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
3793
|
-
const stdout = requireGhOk(
|
|
3794
|
-
await gh(
|
|
3795
|
-
["issue", "comment", String(number), ...repoArgs, "--body", body],
|
|
3796
|
-
cwd,
|
|
3797
|
-
{ reject: false }
|
|
3798
|
-
),
|
|
3799
|
-
`GitHub comment on #${number}`
|
|
3800
|
-
);
|
|
3801
|
-
const url = stdout.trim().split(/\s+/).find((part) => /^https?:\/\//i.test(part));
|
|
3802
|
-
return { body, url };
|
|
3803
|
-
}
|
|
3804
|
-
function isGitHubNoneToken(value) {
|
|
3805
|
-
if (value == null) return true;
|
|
3806
|
-
const s = value.trim();
|
|
3807
|
-
return !s || /^(none|clear|unschedule|null)$/i.test(s);
|
|
3808
|
-
}
|
|
3809
|
-
function githubRelationEditFlag(type, remove = false) {
|
|
3810
|
-
const s = type.trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
3811
|
-
if (s === "blockedby" || s === "blocked" || s === "dependson") {
|
|
3812
|
-
return remove ? "--remove-blocked-by" : "--add-blocked-by";
|
|
3813
|
-
}
|
|
3814
|
-
if (s === "blocks" || s === "blocking") {
|
|
3815
|
-
return remove ? "--remove-blocking" : "--add-blocking";
|
|
3816
|
-
}
|
|
3817
|
-
throw new Error(
|
|
3818
|
-
`GitHub relation type must be blocks or blockedBy (got ${type}). Parent is a separate parent= field.`
|
|
3819
|
-
);
|
|
3820
|
-
}
|
|
3821
|
-
function labelsEqual(a, b) {
|
|
3822
|
-
return a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
3823
|
-
}
|
|
3824
|
-
async function updateGitHubIssue(input, opts) {
|
|
3825
|
-
const number = parseGitHubIssueNumber(input.id);
|
|
3826
|
-
const title = input.title?.trim();
|
|
3827
|
-
const body = input.body;
|
|
3828
|
-
const state = input.state?.trim().toLowerCase();
|
|
3829
|
-
const addRelations = input.relations?.length ? input.relations : void 0;
|
|
3830
|
-
const removeRelations = input.removeRelations?.length ? input.removeRelations : void 0;
|
|
3831
|
-
const hasEditFields = Boolean(title) || body !== void 0 || input.labels !== void 0 || input.project !== void 0 || input.parent !== void 0 || Boolean(addRelations) || Boolean(removeRelations);
|
|
3832
|
-
if (!hasEditFields && !state) {
|
|
3833
|
-
throw new Error(
|
|
3834
|
-
"github_update_issue needs at least one of title, body, state, labels, project, parent, relations, removeRelations"
|
|
3835
|
-
);
|
|
3836
|
-
}
|
|
3837
|
-
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
3838
|
-
if (state === "closed" || state === "close") {
|
|
3839
|
-
requireGhOk(
|
|
3840
|
-
await gh(["issue", "close", String(number), ...repoArgs], cwd, { reject: false }),
|
|
3841
|
-
`GitHub close #${number}`
|
|
3842
|
-
);
|
|
3843
|
-
} else if (state === "open" || state === "reopen") {
|
|
3844
|
-
requireGhOk(
|
|
3845
|
-
await gh(["issue", "reopen", String(number), ...repoArgs], cwd, { reject: false }),
|
|
3846
|
-
`GitHub reopen #${number}`
|
|
3847
|
-
);
|
|
3848
|
-
} else if (state) {
|
|
3849
|
-
throw new Error(`GitHub issue state must be open or closed (got ${input.state})`);
|
|
3850
|
-
}
|
|
3851
|
-
const args = ["issue", "edit", String(number), ...repoArgs];
|
|
3852
|
-
if (title) args.push("--title", title);
|
|
3853
|
-
if (body !== void 0) args.push("--body", body);
|
|
3854
|
-
if (input.labels !== void 0) {
|
|
3855
|
-
const existing = await getGitHubIssue(String(number), opts);
|
|
3856
|
-
const desired = input.labels.map((name) => name.trim()).filter(Boolean);
|
|
3857
|
-
const add = desired.filter((name) => !existing.labels.some((cur) => labelsEqual(cur, name)));
|
|
3858
|
-
const remove = existing.labels.filter((cur) => !desired.some((name) => labelsEqual(cur, name)));
|
|
3859
|
-
if (add.length) args.push("--add-label", add.join(","));
|
|
3860
|
-
if (remove.length) args.push("--remove-label", remove.join(","));
|
|
3861
|
-
}
|
|
3862
|
-
if (input.project !== void 0) {
|
|
3863
|
-
const current = await listGitHubIssueProjectNames(number, cwd, repoArgs);
|
|
3864
|
-
if (isGitHubNoneToken(input.project)) {
|
|
3865
|
-
for (const name of current) args.push("--remove-project", name);
|
|
3866
|
-
} else {
|
|
3867
|
-
const wanted = (input.project ?? "").trim();
|
|
3868
|
-
if (!current.some((name) => labelsEqual(name, wanted))) {
|
|
3869
|
-
args.push("--add-project", wanted);
|
|
3870
|
-
}
|
|
3871
|
-
for (const name of current) {
|
|
3872
|
-
if (!labelsEqual(name, wanted)) args.push("--remove-project", name);
|
|
3873
|
-
}
|
|
3874
|
-
}
|
|
3875
|
-
}
|
|
3876
|
-
if (input.parent !== void 0) {
|
|
3877
|
-
if (isGitHubNoneToken(input.parent)) {
|
|
3878
|
-
args.push("--remove-parent");
|
|
3879
|
-
} else {
|
|
3880
|
-
args.push("--parent", String(parseGitHubIssueNumber(input.parent ?? "")));
|
|
3881
|
-
}
|
|
3882
|
-
}
|
|
3883
|
-
for (const rel of removeRelations ?? []) {
|
|
3884
|
-
args.push(githubRelationEditFlag(rel.type, true), String(parseGitHubIssueNumber(rel.issue)));
|
|
3885
|
-
}
|
|
3886
|
-
for (const rel of addRelations ?? []) {
|
|
3887
|
-
args.push(githubRelationEditFlag(rel.type, false), String(parseGitHubIssueNumber(rel.issue)));
|
|
3888
|
-
}
|
|
3889
|
-
const editChanged = args.length > 3 + repoArgs.length;
|
|
3890
|
-
if (editChanged) {
|
|
3891
|
-
requireGhOk(await gh(args, cwd, { reject: false }), `GitHub edit #${number}`);
|
|
3892
|
-
}
|
|
3893
|
-
return getGitHubIssue(String(number), opts);
|
|
3894
|
-
}
|
|
3895
|
-
async function createGitHubIssue(input, opts) {
|
|
3896
|
-
const title = input.title.trim();
|
|
3897
|
-
if (!title) throw new Error("GitHub issue title is required");
|
|
3898
|
-
const parent = input.parent?.trim();
|
|
3899
|
-
const parentNumber = parent ? parseGitHubIssueNumber(parent) : null;
|
|
3900
|
-
const bodyParts = [
|
|
3901
|
-
parentNumber ? `Spin-off of #${parentNumber}.` : null,
|
|
3902
|
-
input.body?.trim() || null
|
|
3903
|
-
].filter(Boolean);
|
|
3904
|
-
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
3905
|
-
const args = ["issue", "create", ...repoArgs, "--title", title];
|
|
3906
|
-
if (bodyParts.length) args.push("--body", bodyParts.join("\n\n"));
|
|
3907
|
-
const created = await gh([...args, "--json", "number,url,title"], cwd, { reject: false });
|
|
3908
|
-
if (created.exitCode === 0 && created.stdout.trim()) {
|
|
3909
|
-
try {
|
|
3910
|
-
const parsed = JSON.parse(created.stdout);
|
|
3911
|
-
if (parsed.number) return getGitHubIssue(String(parsed.number), opts);
|
|
3912
|
-
} catch {
|
|
3913
|
-
}
|
|
3914
|
-
}
|
|
3915
|
-
const fallback = created.exitCode === 0 ? created : await gh(args, cwd, { reject: false });
|
|
3916
|
-
const stdout = requireGhOk(fallback, "GitHub create issue");
|
|
3917
|
-
const url = stdout.trim().match(/https?:\/\/github\.com\/[^/\s]+\/[^/\s]+\/issues\/(\d+)/i);
|
|
3918
|
-
if (url?.[1]) return getGitHubIssue(url[1], opts);
|
|
3919
|
-
throw new Error(`GitHub create issue: could not parse issue from ${stdout.trim() || "empty output"}`);
|
|
3920
|
-
}
|
|
3921
|
-
|
|
3922
|
-
// src/integrations/linear-oauth.ts
|
|
3923
|
-
import { createHash, randomBytes as randomBytes2 } from "crypto";
|
|
3924
|
-
import { createServer } from "http";
|
|
3925
|
-
|
|
3926
|
-
// src/integrations/linear-app.ts
|
|
3927
|
-
var BAKED_LINEAR_CLIENT_ID = "39a15abc7ea5bea17c47f47f85ff5fd0";
|
|
3928
|
-
function hasBakedLinearOAuth() {
|
|
3929
|
-
return Boolean(BAKED_LINEAR_CLIENT_ID.trim());
|
|
3930
|
-
}
|
|
3931
|
-
|
|
3932
|
-
// src/integrations/linear-oauth.ts
|
|
3933
|
-
var LINEAR_OAUTH_PORT = 19848;
|
|
3934
|
-
var LINEAR_OAUTH_REDIRECT = `http://127.0.0.1:${LINEAR_OAUTH_PORT}/callback`;
|
|
3935
|
-
var LINEAR_AUTHORIZE = "https://linear.app/oauth/authorize";
|
|
3936
|
-
var LINEAR_TOKEN = "https://api.linear.app/oauth/token";
|
|
3937
|
-
var LINEAR_REVOKE = "https://api.linear.app/oauth/revoke";
|
|
3938
|
-
var LINEAR_GRAPHQL = "https://api.linear.app/graphql";
|
|
3939
|
-
var LINEAR_OAUTH_SCOPES = "read,write";
|
|
3940
|
-
var REFRESH_SKEW_MS = 5 * 6e4;
|
|
3941
|
-
function linearOAuthCredentials() {
|
|
3942
|
-
const settings = loadAppSettings();
|
|
3943
|
-
const clientId = process.env.SIDEBOARD_LINEAR_CLIENT_ID?.trim() || settings.integrations.linearClientId?.trim() || BAKED_LINEAR_CLIENT_ID.trim();
|
|
3944
|
-
const clientSecret = process.env.SIDEBOARD_LINEAR_CLIENT_SECRET?.trim() || settings.integrations.linearClientSecret?.trim() || "";
|
|
3945
|
-
if (!clientId) {
|
|
3946
|
-
throw new Error(
|
|
3947
|
-
`Linear browser sign-in needs a Linear OAuth app Client ID. Create one at linear.app/settings/api/applications/new with callback ${LINEAR_OAUTH_REDIRECT}, then set SIDEBOARD_LINEAR_CLIENT_ID (PKCE does not require a secret). You can still paste a personal API key.`
|
|
3948
|
-
);
|
|
3949
|
-
}
|
|
3950
|
-
return { clientId, clientSecret };
|
|
3951
|
-
}
|
|
3952
|
-
function createLinearPkce() {
|
|
3953
|
-
const verifier = randomBytes2(32).toString("base64url");
|
|
3954
|
-
const challenge = createHash("sha256").update(verifier).digest("base64url");
|
|
3955
|
-
return { verifier, challenge };
|
|
3956
|
-
}
|
|
3957
|
-
function linearOAuthAuthorizeUrl(clientId, state, codeChallenge) {
|
|
3958
|
-
const params = new URLSearchParams({
|
|
3959
|
-
client_id: clientId,
|
|
3960
|
-
redirect_uri: LINEAR_OAUTH_REDIRECT,
|
|
3961
|
-
response_type: "code",
|
|
3962
|
-
scope: LINEAR_OAUTH_SCOPES,
|
|
3963
|
-
state,
|
|
3964
|
-
prompt: "consent",
|
|
3965
|
-
actor: "user",
|
|
3966
|
-
code_challenge: codeChallenge,
|
|
3967
|
-
code_challenge_method: "S256"
|
|
3968
|
-
});
|
|
3969
|
-
return `${LINEAR_AUTHORIZE}?${params.toString()}`;
|
|
3970
|
-
}
|
|
3971
|
-
function linearAuthorizationHeader(token) {
|
|
3972
|
-
const t = token.trim();
|
|
3973
|
-
if (!t) return t;
|
|
3974
|
-
if (/^bearer\s+/i.test(t)) return t;
|
|
3975
|
-
if (t.startsWith("lin_api_")) return t;
|
|
3976
|
-
return `Bearer ${t}`;
|
|
3977
|
-
}
|
|
3978
|
-
function htmlPage(title, body) {
|
|
3979
|
-
return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title>
|
|
3980
|
-
<style>body{font-family:ui-sans-serif,system-ui,sans-serif;padding:48px 24px;max-width:36rem;margin:0 auto;color:#1a1a1a}
|
|
3981
|
-
h1{font-size:1.25rem}p{line-height:1.5;color:#444}</style></head><body>${body}</body></html>`;
|
|
3982
|
-
}
|
|
3983
|
-
async function exchangeLinearToken(body) {
|
|
3984
|
-
const res = await httpFetch(LINEAR_TOKEN, {
|
|
3985
|
-
method: "POST",
|
|
3986
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
3987
|
-
body
|
|
3988
|
-
});
|
|
3989
|
-
const data = await res.json().catch(() => ({}));
|
|
3990
|
-
if (!res.ok || data.error || !data.access_token?.trim()) {
|
|
3991
|
-
const detail = data.error_description || data.error || `HTTP ${res.status}`;
|
|
3992
|
-
throw new Error(`Linear OAuth token exchange failed: ${detail}`);
|
|
3993
|
-
}
|
|
3994
|
-
return data;
|
|
3995
|
-
}
|
|
3996
|
-
async function persistLinearTokens(data, viewer) {
|
|
3997
|
-
return saveLinearOAuth({
|
|
3998
|
-
accessToken: data.access_token.trim(),
|
|
3999
|
-
refreshToken: data.refresh_token?.trim(),
|
|
4000
|
-
expiresIn: data.expires_in,
|
|
4001
|
-
viewerName: viewer?.name,
|
|
4002
|
-
organizationName: viewer?.organizationName
|
|
4003
|
-
});
|
|
4004
|
-
}
|
|
4005
|
-
async function fetchLinearViewer(accessToken3) {
|
|
4006
|
-
const res = await httpFetch(LINEAR_GRAPHQL, {
|
|
4007
|
-
method: "POST",
|
|
4008
|
-
headers: {
|
|
4009
|
-
"Content-Type": "application/json",
|
|
4010
|
-
Authorization: linearAuthorizationHeader(accessToken3)
|
|
4011
|
-
},
|
|
4012
|
-
body: JSON.stringify({
|
|
4013
|
-
query: "{ viewer { name organization { name } } }"
|
|
4014
|
-
})
|
|
4015
|
-
});
|
|
4016
|
-
if (!res.ok) return {};
|
|
4017
|
-
const json = await res.json().catch(() => ({}));
|
|
4018
|
-
const viewer = json.data?.viewer;
|
|
4019
|
-
return {
|
|
4020
|
-
name: viewer?.name?.trim() || void 0,
|
|
4021
|
-
organizationName: viewer?.organization?.name?.trim() || void 0
|
|
4022
|
-
};
|
|
4023
|
-
}
|
|
4024
|
-
var refreshInFlight = null;
|
|
4025
|
-
async function refreshLinearAccessToken(refreshToken) {
|
|
4026
|
-
const { clientId, clientSecret } = linearOAuthCredentials();
|
|
4027
|
-
const body = new URLSearchParams({
|
|
4028
|
-
grant_type: "refresh_token",
|
|
4029
|
-
refresh_token: refreshToken,
|
|
4030
|
-
client_id: clientId
|
|
4031
|
-
});
|
|
4032
|
-
if (clientSecret) body.set("client_secret", clientSecret);
|
|
4033
|
-
try {
|
|
4034
|
-
const data = await exchangeLinearToken(body);
|
|
4035
|
-
await persistLinearTokens(data);
|
|
4036
|
-
return data.access_token.trim();
|
|
4037
|
-
} catch (err) {
|
|
4038
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
4039
|
-
if (/invalid_grant|invalid_token|unauthorized/i.test(message)) {
|
|
4040
|
-
disconnectLinearConnection();
|
|
4041
|
-
throw new Error("Linear sign-in expired \u2014 connect again in Account settings");
|
|
4042
|
-
}
|
|
4043
|
-
throw err;
|
|
4044
|
-
}
|
|
4045
|
-
}
|
|
4046
|
-
async function getLinearAuthToken(settings = loadAppSettings()) {
|
|
4047
|
-
const access = settings.integrations.linearAccessToken?.trim() || "";
|
|
4048
|
-
const refresh = settings.integrations.linearRefreshToken?.trim() || "";
|
|
4049
|
-
const expiresAt = settings.integrations.linearTokenExpiresAt ?? 0;
|
|
4050
|
-
const apiKey = settings.integrations.linearApiKey?.trim() || "";
|
|
4051
|
-
if (access && (!refresh || Date.now() < expiresAt - REFRESH_SKEW_MS)) {
|
|
4052
|
-
return access;
|
|
4053
|
-
}
|
|
4054
|
-
if (refresh) {
|
|
4055
|
-
if (!refreshInFlight) {
|
|
4056
|
-
refreshInFlight = refreshLinearAccessToken(refresh).finally(() => {
|
|
4057
|
-
refreshInFlight = null;
|
|
4058
|
-
});
|
|
4059
|
-
}
|
|
4060
|
-
return refreshInFlight.catch((err) => {
|
|
4061
|
-
if (apiKey) return apiKey;
|
|
4062
|
-
throw err;
|
|
4063
|
-
});
|
|
4064
|
-
}
|
|
4065
|
-
return apiKey || null;
|
|
4066
|
-
}
|
|
4067
|
-
var LINEAR_OAUTH_CANCELLED = "Linear sign-in cancelled";
|
|
4068
|
-
var LinearOAuthCancelledError = class extends Error {
|
|
4069
|
-
constructor(message = LINEAR_OAUTH_CANCELLED) {
|
|
4070
|
-
super(message);
|
|
4071
|
-
this.name = "LinearOAuthCancelledError";
|
|
4072
|
-
}
|
|
4073
|
-
};
|
|
4074
|
-
function isLinearOAuthCancelled(err) {
|
|
4075
|
-
if (err instanceof LinearOAuthCancelledError) return true;
|
|
4076
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
4077
|
-
if (message.includes(LINEAR_OAUTH_CANCELLED)) return true;
|
|
4078
|
-
return err instanceof Error && err.name === "LinearOAuthCancelledError";
|
|
4079
|
-
}
|
|
4080
|
-
async function startLinearOAuth(opts) {
|
|
4081
|
-
if (opts?.signal?.aborted) {
|
|
4082
|
-
throw new LinearOAuthCancelledError();
|
|
4083
|
-
}
|
|
4084
|
-
const { clientId, clientSecret } = linearOAuthCredentials();
|
|
4085
|
-
const state = randomBytes2(16).toString("hex");
|
|
4086
|
-
const pkce = createLinearPkce();
|
|
4087
|
-
const authorizeUrl = linearOAuthAuthorizeUrl(clientId, state, pkce.challenge);
|
|
4088
|
-
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
4089
|
-
const code = await new Promise((resolve, reject) => {
|
|
4090
|
-
let settled = false;
|
|
4091
|
-
const finish = (err, value) => {
|
|
4092
|
-
if (settled) return;
|
|
4093
|
-
settled = true;
|
|
4094
|
-
cleanup();
|
|
4095
|
-
if (err) reject(err);
|
|
4096
|
-
else resolve(value);
|
|
4097
|
-
};
|
|
4098
|
-
const server = createServer((req, res) => {
|
|
4099
|
-
try {
|
|
4100
|
-
const url = new URL(req.url || "/", `http://127.0.0.1:${LINEAR_OAUTH_PORT}`);
|
|
4101
|
-
if (url.pathname !== "/callback") {
|
|
4102
|
-
res.writeHead(404);
|
|
4103
|
-
res.end("Not found");
|
|
4104
|
-
return;
|
|
4105
|
-
}
|
|
4106
|
-
const err = url.searchParams.get("error");
|
|
4107
|
-
const gotState = url.searchParams.get("state");
|
|
4108
|
-
const gotCode = url.searchParams.get("code");
|
|
4109
|
-
if (err) {
|
|
4110
|
-
res.writeHead(400, { "Content-Type": "text/html" });
|
|
4111
|
-
res.end(htmlPage("Linear", `<h1>Authorization cancelled</h1><p>${err}</p>`));
|
|
4112
|
-
finish(new LinearOAuthCancelledError(`Linear OAuth: ${err}`));
|
|
4113
|
-
return;
|
|
4114
|
-
}
|
|
4115
|
-
if (gotState !== state || !gotCode) {
|
|
4116
|
-
res.writeHead(400, { "Content-Type": "text/html" });
|
|
4117
|
-
res.end(htmlPage("Linear", "<h1>Invalid callback</h1><p>State or code missing.</p>"));
|
|
4118
|
-
finish(new Error("Linear OAuth callback was invalid"));
|
|
4119
|
-
return;
|
|
4120
|
-
}
|
|
4121
|
-
res.writeHead(200, { "Content-Type": "text/html" });
|
|
4122
|
-
res.end(
|
|
4123
|
-
htmlPage(
|
|
4124
|
-
"Linear connected",
|
|
4125
|
-
"<h1>Linear connected</h1><p>You can close this tab and return to Sideboard.</p>"
|
|
4126
|
-
)
|
|
4127
|
-
);
|
|
4128
|
-
finish(void 0, gotCode);
|
|
4129
|
-
} catch (e) {
|
|
4130
|
-
finish(e instanceof Error ? e : new Error(String(e)));
|
|
4131
|
-
}
|
|
4132
|
-
});
|
|
4133
|
-
const timer = setTimeout(() => {
|
|
4134
|
-
finish(new Error("Linear sign-in timed out \u2014 try again"));
|
|
4135
|
-
}, timeoutMs);
|
|
4136
|
-
const onAbort = () => finish(new LinearOAuthCancelledError());
|
|
4137
|
-
const cleanup = () => {
|
|
4138
|
-
clearTimeout(timer);
|
|
4139
|
-
opts?.signal?.removeEventListener("abort", onAbort);
|
|
4140
|
-
server.close();
|
|
4141
|
-
};
|
|
4142
|
-
opts?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
4143
|
-
if (opts?.signal?.aborted) {
|
|
4144
|
-
onAbort();
|
|
4145
|
-
return;
|
|
4146
|
-
}
|
|
4147
|
-
server.on("error", (e) => {
|
|
4148
|
-
finish(
|
|
4149
|
-
e instanceof Error && e.code === "EADDRINUSE" ? new Error(
|
|
4150
|
-
`Port ${LINEAR_OAUTH_PORT} is in use. Close whatever is bound there, or paste a Linear API key instead.`
|
|
4151
|
-
) : e instanceof Error ? e : new Error(String(e))
|
|
4152
|
-
);
|
|
4153
|
-
});
|
|
4154
|
-
server.listen(LINEAR_OAUTH_PORT, "127.0.0.1", () => {
|
|
4155
|
-
void Promise.resolve(opts?.openUrl?.(authorizeUrl)).catch((e) => {
|
|
4156
|
-
finish(e instanceof Error ? e : new Error(String(e)));
|
|
4157
|
-
});
|
|
4158
|
-
});
|
|
4159
|
-
});
|
|
4160
|
-
const body = new URLSearchParams({
|
|
4161
|
-
grant_type: "authorization_code",
|
|
4162
|
-
code,
|
|
4163
|
-
redirect_uri: LINEAR_OAUTH_REDIRECT,
|
|
4164
|
-
client_id: clientId,
|
|
4165
|
-
code_verifier: pkce.verifier
|
|
4166
|
-
});
|
|
4167
|
-
if (clientSecret) body.set("client_secret", clientSecret);
|
|
4168
|
-
const data = await exchangeLinearToken(body);
|
|
4169
|
-
const viewer = await fetchLinearViewer(data.access_token.trim()).catch(() => ({}));
|
|
4170
|
-
return persistLinearTokens(data, viewer);
|
|
4171
|
-
}
|
|
4172
|
-
async function revokeLinearToken(token, tokenTypeHint) {
|
|
4173
|
-
const body = new URLSearchParams({
|
|
4174
|
-
token,
|
|
4175
|
-
token_type_hint: tokenTypeHint
|
|
4176
|
-
});
|
|
4177
|
-
await httpFetch(LINEAR_REVOKE, {
|
|
4178
|
-
method: "POST",
|
|
4179
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
4180
|
-
body
|
|
4181
|
-
}).catch(() => void 0);
|
|
4182
|
-
}
|
|
4183
|
-
async function disconnectLinear() {
|
|
4184
|
-
const settings = loadAppSettings();
|
|
4185
|
-
const access = settings.integrations.linearAccessToken?.trim();
|
|
4186
|
-
const refresh = settings.integrations.linearRefreshToken?.trim();
|
|
4187
|
-
if (access) await revokeLinearToken(access, "access_token");
|
|
4188
|
-
if (refresh) await revokeLinearToken(refresh, "refresh_token");
|
|
4189
|
-
return disconnectLinearConnection();
|
|
4190
|
-
}
|
|
4191
|
-
|
|
4192
|
-
// src/integrations/linear-attachments.ts
|
|
4193
|
-
var LINEAR_GRAPHQL2 = "https://api.linear.app/graphql";
|
|
4194
|
-
var LINEAR_FILE_HOSTS = ["linear.app", "uploads.linear.app", "linearusercontent.com"];
|
|
4195
|
-
var LINEAR_LINK_SOURCE_TYPES = /* @__PURE__ */ new Set([
|
|
4196
|
-
"github",
|
|
4197
|
-
"githubPullRequest",
|
|
4198
|
-
"gitlab",
|
|
4199
|
-
"figma",
|
|
4200
|
-
"slack",
|
|
4201
|
-
"sentry",
|
|
4202
|
-
"zendesk",
|
|
4203
|
-
"intercom",
|
|
4204
|
-
"front"
|
|
4205
|
-
]);
|
|
4206
|
-
var ATTACHMENT_QUERY = `
|
|
4207
|
-
query SideboardAttachment($id: String!) {
|
|
4208
|
-
attachment(id: $id) {
|
|
4209
|
-
id
|
|
4210
|
-
title
|
|
4211
|
-
url
|
|
4212
|
-
subtitle
|
|
4213
|
-
sourceType
|
|
4214
|
-
}
|
|
4215
|
-
}
|
|
4216
|
-
`;
|
|
4217
|
-
function mapLinearAttachments(node) {
|
|
4218
|
-
const listed = [];
|
|
4219
|
-
for (const attachment of node.attachments?.nodes ?? []) {
|
|
4220
|
-
const id = String(attachment.id ?? "");
|
|
4221
|
-
const url = String(attachment.url ?? "").trim();
|
|
4222
|
-
if (!id && !url) continue;
|
|
4223
|
-
listed.push({
|
|
4224
|
-
id,
|
|
4225
|
-
title: String(attachment.title ?? ""),
|
|
4226
|
-
url,
|
|
4227
|
-
subtitle: attachment.subtitle?.trim() || void 0,
|
|
4228
|
-
sourceType: attachment.sourceType?.trim() || void 0
|
|
4229
|
-
});
|
|
4230
|
-
}
|
|
4231
|
-
const embedded = [
|
|
4232
|
-
...extractHttpUrls(node.description).filter((url) => hostMatches(url, LINEAR_FILE_HOSTS)),
|
|
4233
|
-
...(node.comments?.nodes ?? []).flatMap(
|
|
4234
|
-
(comment) => extractHttpUrls(comment.body).filter((url) => hostMatches(url, LINEAR_FILE_HOSTS))
|
|
4235
|
-
)
|
|
4236
|
-
];
|
|
4237
|
-
const merged = mergeIssueAttachments([
|
|
4238
|
-
...listed.map((item) => ({
|
|
4239
|
-
id: item.id,
|
|
4240
|
-
name: item.title || item.subtitle || "attachment",
|
|
4241
|
-
url: item.url,
|
|
4242
|
-
sourceType: item.sourceType
|
|
4243
|
-
})),
|
|
4244
|
-
...embedded.map((url) => ({
|
|
4245
|
-
id: url,
|
|
4246
|
-
name: url.split("/").pop() || "image",
|
|
4247
|
-
url,
|
|
4248
|
-
sourceType: "upload"
|
|
4249
|
-
}))
|
|
4250
|
-
]);
|
|
4251
|
-
const byUrl = new Map(listed.map((item) => [item.url, item]));
|
|
4252
|
-
return merged.map((item) => {
|
|
4253
|
-
const prior = byUrl.get(item.url);
|
|
4254
|
-
return {
|
|
4255
|
-
id: prior?.id || item.id,
|
|
4256
|
-
title: prior?.title || item.name,
|
|
4257
|
-
url: item.url,
|
|
4258
|
-
subtitle: prior?.subtitle,
|
|
4259
|
-
sourceType: prior?.sourceType || item.sourceType
|
|
4260
|
-
};
|
|
4261
|
-
});
|
|
4262
|
-
}
|
|
4263
|
-
function assertLinearFileUrl(url, sourceType) {
|
|
4264
|
-
const kind = sourceType?.trim();
|
|
4265
|
-
if (kind && LINEAR_LINK_SOURCE_TYPES.has(kind)) {
|
|
4266
|
-
throw new Error(
|
|
4267
|
-
`Linear attachment is a ${kind} link, not a file. Open the URL in the browser instead of downloading it.`
|
|
4268
|
-
);
|
|
4269
|
-
}
|
|
4270
|
-
if (!hostMatches(url, LINEAR_FILE_HOSTS)) {
|
|
4271
|
-
throw new Error(
|
|
4272
|
-
`Linear attachment URL is not a Linear upload (${url}). Only uploads.linear.app / linear.app file hosts can be downloaded with the Linear token.`
|
|
4273
|
-
);
|
|
4274
|
-
}
|
|
4275
|
-
}
|
|
4276
|
-
async function requireLinearToken(apiKey) {
|
|
4277
|
-
const token = (apiKey ?? await getLinearAuthToken())?.trim();
|
|
4278
|
-
if (!token) {
|
|
4279
|
-
throw new Error("Linear is not connected \u2014 sign in from Account settings");
|
|
4280
|
-
}
|
|
4281
|
-
return token;
|
|
4282
|
-
}
|
|
4283
|
-
async function downloadLinearAttachment(input, opts) {
|
|
4284
|
-
const id = input.id.trim();
|
|
4285
|
-
if (!id) throw new Error("Linear attachment id or URL is required");
|
|
4286
|
-
const apiKey = await requireLinearToken(opts?.apiKey);
|
|
4287
|
-
let url = id;
|
|
4288
|
-
let name = input.name?.trim();
|
|
4289
|
-
let sourceType;
|
|
4290
|
-
if (!looksLikeHttpUrl(id)) {
|
|
4291
|
-
const res = await httpFetch(LINEAR_GRAPHQL2, {
|
|
4292
|
-
method: "POST",
|
|
4293
|
-
headers: {
|
|
4294
|
-
"Content-Type": "application/json",
|
|
4295
|
-
Authorization: linearAuthorizationHeader(apiKey)
|
|
4296
|
-
},
|
|
4297
|
-
body: JSON.stringify({ query: ATTACHMENT_QUERY, variables: { id } })
|
|
4298
|
-
});
|
|
4299
|
-
if (!res.ok) {
|
|
4300
|
-
throw new Error(`Linear API error ${res.status} looking up attachment ${id}`);
|
|
4301
|
-
}
|
|
4302
|
-
const json = await res.json();
|
|
4303
|
-
if (json.errors?.length) {
|
|
4304
|
-
throw new Error(json.errors.map((e) => e.message ?? "Linear error").join("; "));
|
|
4305
|
-
}
|
|
4306
|
-
const attachment = json.data?.attachment;
|
|
4307
|
-
if (!attachment?.url?.trim()) {
|
|
4308
|
-
throw new Error(`Linear attachment not found: ${id}`);
|
|
4309
|
-
}
|
|
4310
|
-
url = attachment.url.trim();
|
|
4311
|
-
name = name || attachment.title?.trim() || attachment.subtitle?.trim() || void 0;
|
|
4312
|
-
sourceType = attachment.sourceType?.trim() || void 0;
|
|
4313
|
-
}
|
|
4314
|
-
assertLinearFileUrl(url, sourceType);
|
|
4315
|
-
return downloadAuthenticatedFile({
|
|
4316
|
-
url,
|
|
4317
|
-
headers: { Authorization: linearAuthorizationHeader(apiKey) },
|
|
4318
|
-
worktreePath: issueAttachmentWorktreePath(opts?.destPath),
|
|
4319
|
-
name
|
|
4320
|
-
});
|
|
4321
|
-
}
|
|
4322
|
-
|
|
4323
|
-
// src/integrations/linear.ts
|
|
4324
|
-
var LINEAR_GRAPHQL3 = "https://api.linear.app/graphql";
|
|
4325
|
-
var LIST_ISSUE_FIELDS = `
|
|
4326
|
-
id
|
|
4327
|
-
identifier
|
|
4328
|
-
title
|
|
4329
|
-
url
|
|
4330
|
-
createdAt
|
|
4331
|
-
updatedAt
|
|
4332
|
-
assignee { id name }
|
|
4333
|
-
team { id key }
|
|
4334
|
-
labels(first: 10) { nodes { name } }
|
|
4335
|
-
cycle { id name number startsAt endsAt completedAt }
|
|
4336
|
-
project { id name }
|
|
4337
|
-
`;
|
|
4338
|
-
var ISSUE_REF_FIELDS = `
|
|
4339
|
-
id
|
|
4340
|
-
identifier
|
|
4341
|
-
title
|
|
4342
|
-
url
|
|
4343
|
-
`;
|
|
4344
|
-
var ISSUE_FIELDS = `
|
|
4345
|
-
id
|
|
4346
|
-
identifier
|
|
4347
|
-
title
|
|
4348
|
-
description
|
|
4349
|
-
url
|
|
4350
|
-
priority
|
|
4351
|
-
estimate
|
|
4352
|
-
dueDate
|
|
4353
|
-
branchName
|
|
4354
|
-
createdAt
|
|
4355
|
-
updatedAt
|
|
4356
|
-
completedAt
|
|
4357
|
-
state { id name type }
|
|
4358
|
-
assignee { id name }
|
|
4359
|
-
creator { id name }
|
|
4360
|
-
team { id key name }
|
|
4361
|
-
labels(first: 50) { nodes { name } }
|
|
4362
|
-
cycle { id name number startsAt endsAt completedAt }
|
|
4363
|
-
project { id name }
|
|
4364
|
-
parent { ${ISSUE_REF_FIELDS} }
|
|
4365
|
-
children(first: 25) { nodes { ${ISSUE_REF_FIELDS} } }
|
|
4366
|
-
relations(first: 25) {
|
|
4367
|
-
nodes {
|
|
4368
|
-
id
|
|
4369
|
-
type
|
|
4370
|
-
relatedIssue { ${ISSUE_REF_FIELDS} }
|
|
4371
|
-
}
|
|
4372
|
-
}
|
|
4373
|
-
inverseRelations(first: 25) {
|
|
4374
|
-
nodes {
|
|
4375
|
-
id
|
|
4376
|
-
type
|
|
4377
|
-
issue { ${ISSUE_REF_FIELDS} }
|
|
4378
|
-
}
|
|
4379
|
-
}
|
|
4380
|
-
comments(first: 50) {
|
|
4381
|
-
nodes {
|
|
4382
|
-
id
|
|
4383
|
-
body
|
|
4384
|
-
url
|
|
4385
|
-
createdAt
|
|
4386
|
-
updatedAt
|
|
4387
|
-
user { id name }
|
|
4388
|
-
}
|
|
4389
|
-
}
|
|
4390
|
-
attachments(first: 20) {
|
|
4391
|
-
nodes { id title url subtitle sourceType }
|
|
4392
|
-
}
|
|
4393
|
-
`;
|
|
4394
|
-
var ASSIGNED_ISSUES_QUERY = `
|
|
4395
|
-
query SideboardAssignedIssues($first: Int!, $filter: IssueFilter) {
|
|
4396
|
-
viewer {
|
|
4397
|
-
id
|
|
4398
|
-
name
|
|
4399
|
-
assignedIssues(
|
|
4400
|
-
first: $first
|
|
4401
|
-
orderBy: updatedAt
|
|
4402
|
-
filter: $filter
|
|
4403
|
-
) {
|
|
4404
|
-
nodes { ${LIST_ISSUE_FIELDS} }
|
|
4405
|
-
}
|
|
4406
|
-
}
|
|
4407
|
-
}
|
|
4408
|
-
`;
|
|
4409
|
-
var COMMENTS_SINCE_QUERY = `
|
|
4410
|
-
query SideboardCommentsSince($first: Int!, $filter: CommentFilter) {
|
|
4411
|
-
viewer { id name }
|
|
4412
|
-
comments(first: $first, orderBy: createdAt, filter: $filter) {
|
|
4413
|
-
nodes {
|
|
4414
|
-
id
|
|
4415
|
-
body
|
|
4416
|
-
url
|
|
4417
|
-
createdAt
|
|
4418
|
-
user { id name }
|
|
4419
|
-
issue { identifier title url }
|
|
4420
|
-
}
|
|
4421
|
-
}
|
|
4422
|
-
}
|
|
4423
|
-
`;
|
|
4424
|
-
var ISSUES_QUERY = `
|
|
4425
|
-
query SideboardIssues($first: Int!, $filter: IssueFilter) {
|
|
4426
|
-
viewer { id name }
|
|
4427
|
-
issues(first: $first, orderBy: updatedAt, filter: $filter) {
|
|
4428
|
-
nodes { ${LIST_ISSUE_FIELDS} }
|
|
4429
|
-
}
|
|
4430
|
-
}
|
|
4431
|
-
`;
|
|
4432
|
-
var SEARCH_ISSUES_QUERY = `
|
|
4433
|
-
query SideboardSearchIssues($term: String!, $first: Int!, $filter: IssueFilter) {
|
|
4434
|
-
viewer { id name }
|
|
4435
|
-
searchIssues(term: $term, first: $first, includeArchived: false, filter: $filter) {
|
|
4436
|
-
nodes { ${LIST_ISSUE_FIELDS} }
|
|
4437
|
-
}
|
|
4438
|
-
}
|
|
4439
|
-
`;
|
|
4440
|
-
var TEAMS_QUERY = `
|
|
4441
|
-
query SideboardTeams {
|
|
4442
|
-
viewer { id name }
|
|
4443
|
-
teams(first: 50) {
|
|
4444
|
-
nodes {
|
|
4445
|
-
id
|
|
4446
|
-
key
|
|
4447
|
-
name
|
|
4448
|
-
states(first: 50) { nodes { id name type } }
|
|
4449
|
-
activeCycle { id name number startsAt endsAt completedAt }
|
|
4450
|
-
}
|
|
4451
|
-
}
|
|
4452
|
-
}
|
|
4453
|
-
`;
|
|
4454
|
-
var TEAM_CYCLES_QUERY = `
|
|
4455
|
-
query SideboardTeamCycles($id: String!) {
|
|
4456
|
-
team(id: $id) {
|
|
4457
|
-
id
|
|
4458
|
-
key
|
|
4459
|
-
activeCycle { id name number startsAt endsAt completedAt }
|
|
4460
|
-
cycles(first: 40) {
|
|
4461
|
-
nodes { id name number startsAt endsAt completedAt }
|
|
4462
|
-
}
|
|
4463
|
-
}
|
|
4464
|
-
}
|
|
4465
|
-
`;
|
|
4466
|
-
var ISSUE_QUERY = `
|
|
4467
|
-
query SideboardIssue($id: String!) {
|
|
4468
|
-
issue(id: $id) { ${ISSUE_FIELDS} }
|
|
4469
|
-
}
|
|
4470
|
-
`;
|
|
4471
|
-
var ISSUE_CREATE = `
|
|
4472
|
-
mutation SideboardIssueCreate($input: IssueCreateInput!) {
|
|
4473
|
-
issueCreate(input: $input) {
|
|
4474
|
-
success
|
|
4475
|
-
issue { ${ISSUE_FIELDS} }
|
|
4476
|
-
}
|
|
4477
|
-
}
|
|
4478
|
-
`;
|
|
4479
|
-
var ISSUE_UPDATE = `
|
|
4480
|
-
mutation SideboardIssueUpdate($id: String!, $input: IssueUpdateInput!) {
|
|
4481
|
-
issueUpdate(id: $id, input: $input) {
|
|
4482
|
-
success
|
|
4483
|
-
issue { ${ISSUE_FIELDS} }
|
|
4484
|
-
}
|
|
4485
|
-
}
|
|
4486
|
-
`;
|
|
4487
|
-
var COMMENT_CREATE = `
|
|
4488
|
-
mutation SideboardCommentCreate($input: CommentCreateInput!) {
|
|
4489
|
-
commentCreate(input: $input) {
|
|
4490
|
-
success
|
|
4491
|
-
comment { id body url }
|
|
4492
|
-
}
|
|
4493
|
-
}
|
|
4494
|
-
`;
|
|
4495
|
-
var LABELS_QUERY = `
|
|
4496
|
-
query SideboardLabels($first: Int!) {
|
|
4497
|
-
issueLabels(first: $first) {
|
|
4498
|
-
nodes { id name }
|
|
4499
|
-
}
|
|
4500
|
-
}
|
|
4501
|
-
`;
|
|
4502
|
-
var TEAM_LABELS_QUERY = `
|
|
4503
|
-
query SideboardTeamLabels($id: String!, $first: Int!, $after: String) {
|
|
4504
|
-
team(id: $id) {
|
|
4505
|
-
labels(first: $first, after: $after) {
|
|
4506
|
-
nodes { id name }
|
|
4507
|
-
pageInfo { hasNextPage endCursor }
|
|
4508
|
-
}
|
|
4509
|
-
}
|
|
4510
|
-
}
|
|
4511
|
-
`;
|
|
4512
|
-
var PROJECTS_QUERY = `
|
|
4513
|
-
query SideboardProjects($first: Int!, $filter: ProjectFilter, $after: String) {
|
|
4514
|
-
projects(first: $first, filter: $filter, after: $after) {
|
|
4515
|
-
nodes { id name slugId }
|
|
4516
|
-
pageInfo { hasNextPage endCursor }
|
|
4517
|
-
}
|
|
4518
|
-
}
|
|
4519
|
-
`;
|
|
4520
|
-
var ISSUE_RELATION_CREATE = `
|
|
4521
|
-
mutation SideboardIssueRelationCreate($input: IssueRelationCreateInput!) {
|
|
4522
|
-
issueRelationCreate(input: $input) {
|
|
4523
|
-
success
|
|
4524
|
-
issueRelation { id type }
|
|
4525
|
-
}
|
|
4526
|
-
}
|
|
4527
|
-
`;
|
|
4528
|
-
var ISSUE_RELATION_DELETE = `
|
|
4529
|
-
mutation SideboardIssueRelationDelete($id: String!) {
|
|
4530
|
-
issueRelationDelete(id: $id) {
|
|
4531
|
-
success
|
|
4532
|
-
}
|
|
4533
|
-
}
|
|
4534
|
-
`;
|
|
4535
|
-
function rewriteLinearError(message) {
|
|
4536
|
-
if (/scope|permission|not (authorized|allowed)|unauthorized|insufficient/i.test(message)) {
|
|
4537
|
-
return `${message} \u2014 Disconnect and Connect Linear in Account settings to grant write access.`;
|
|
4538
|
-
}
|
|
4539
|
-
return message;
|
|
4540
|
-
}
|
|
4541
|
-
async function requireLinearToken2(apiKey) {
|
|
4542
|
-
const token = (apiKey ?? await getLinearAuthToken())?.trim();
|
|
4543
|
-
if (!token) {
|
|
4544
|
-
throw new Error("Linear is not connected \u2014 sign in from Account settings");
|
|
4545
|
-
}
|
|
4546
|
-
return token;
|
|
4547
|
-
}
|
|
4548
|
-
async function linearGraphql(query, variables, opts) {
|
|
4549
|
-
const apiKey = await requireLinearToken2(opts?.apiKey);
|
|
4550
|
-
const res = await httpFetch(LINEAR_GRAPHQL3, {
|
|
4551
|
-
method: "POST",
|
|
4552
|
-
headers: {
|
|
4553
|
-
"Content-Type": "application/json",
|
|
4554
|
-
Authorization: linearAuthorizationHeader(apiKey)
|
|
4555
|
-
},
|
|
4556
|
-
body: JSON.stringify({ query, variables })
|
|
4557
|
-
});
|
|
4558
|
-
if (!res.ok) {
|
|
4559
|
-
const body = await res.text().catch(() => "");
|
|
4560
|
-
throw new Error(
|
|
4561
|
-
rewriteLinearError(
|
|
4562
|
-
`Linear API error ${res.status}${body ? `: ${body.slice(0, 200)}` : ""}`
|
|
4563
|
-
)
|
|
4564
|
-
);
|
|
4565
|
-
}
|
|
4566
|
-
const json = await res.json();
|
|
4567
|
-
if (json.errors?.length) {
|
|
4568
|
-
throw new Error(
|
|
4569
|
-
rewriteLinearError(json.errors.map((e) => e.message ?? "Linear error").join("; "))
|
|
4570
|
-
);
|
|
4571
|
-
}
|
|
4572
|
-
if (json.data == null) {
|
|
4573
|
-
throw new Error("Linear API returned no data");
|
|
4574
|
-
}
|
|
4575
|
-
return json.data;
|
|
4576
|
-
}
|
|
4577
|
-
function mapState(node) {
|
|
4578
|
-
if (!node?.id) return void 0;
|
|
4579
|
-
return {
|
|
4580
|
-
id: String(node.id),
|
|
4581
|
-
name: String(node.name ?? ""),
|
|
4582
|
-
type: String(node.type ?? "")
|
|
4583
|
-
};
|
|
4584
|
-
}
|
|
4585
|
-
function linearCycleIsActive(cycle, now = Date.now()) {
|
|
4586
|
-
if (!cycle) return false;
|
|
4587
|
-
if (cycle.completedAt) return false;
|
|
4588
|
-
const start = cycle.startsAt ? Date.parse(cycle.startsAt) : Number.NaN;
|
|
4589
|
-
const end = cycle.endsAt ? Date.parse(cycle.endsAt) : Number.NaN;
|
|
4590
|
-
if (Number.isFinite(start) && now < start) return false;
|
|
4591
|
-
if (Number.isFinite(end) && now > end) return false;
|
|
4592
|
-
return true;
|
|
4593
|
-
}
|
|
4594
|
-
function cycleDisplayName(node) {
|
|
4595
|
-
return String(node?.name ?? (node?.number != null ? `Cycle ${node.number}` : "")).trim();
|
|
4596
|
-
}
|
|
4597
|
-
function mapNamedCycle(node) {
|
|
4598
|
-
if (!node?.id) return null;
|
|
4599
|
-
const name = cycleDisplayName(node);
|
|
4600
|
-
if (!name) return null;
|
|
4601
|
-
return {
|
|
4602
|
-
id: String(node.id),
|
|
4603
|
-
name,
|
|
4604
|
-
number: typeof node.number === "number" ? node.number : void 0,
|
|
4605
|
-
isActive: linearCycleIsActive(node)
|
|
4606
|
-
};
|
|
4607
|
-
}
|
|
4608
|
-
function mapCycle(node) {
|
|
4609
|
-
if (!node?.name && node?.number == null) return null;
|
|
4610
|
-
const name = cycleDisplayName(node);
|
|
4611
|
-
if (!name) return null;
|
|
4612
|
-
return {
|
|
4613
|
-
...node.id ? { id: String(node.id) } : {},
|
|
4614
|
-
name,
|
|
4615
|
-
number: typeof node.number === "number" ? node.number : void 0,
|
|
4616
|
-
isActive: linearCycleIsActive(node)
|
|
4617
|
-
};
|
|
4618
|
-
}
|
|
4619
|
-
function mapUser(node) {
|
|
4620
|
-
if (!node?.id && !node?.name) return void 0;
|
|
4621
|
-
return {
|
|
4622
|
-
id: String(node.id ?? ""),
|
|
4623
|
-
name: String(node.name ?? "")
|
|
4624
|
-
};
|
|
4625
|
-
}
|
|
4626
|
-
function mapIssueRef(node) {
|
|
4627
|
-
if (!node?.id && !node?.identifier) return void 0;
|
|
4628
|
-
return {
|
|
4629
|
-
id: String(node.id ?? node.identifier ?? ""),
|
|
4630
|
-
identifier: String(node.identifier ?? node.id ?? ""),
|
|
4631
|
-
title: String(node.title ?? ""),
|
|
4632
|
-
url: String(node.url ?? "")
|
|
4633
|
-
};
|
|
4634
|
-
}
|
|
4635
|
-
function flipLinearRelationType(type) {
|
|
4636
|
-
switch (type) {
|
|
4637
|
-
case "blocks":
|
|
4638
|
-
return "blockedBy";
|
|
4639
|
-
case "blockedBy":
|
|
4640
|
-
return "blocks";
|
|
4641
|
-
case "duplicate":
|
|
4642
|
-
return "duplicateOf";
|
|
4643
|
-
case "duplicateOf":
|
|
4644
|
-
return "duplicate";
|
|
4645
|
-
default:
|
|
4646
|
-
return type;
|
|
4647
|
-
}
|
|
4648
|
-
}
|
|
4649
|
-
function mapRelations(node) {
|
|
4650
|
-
const out = [];
|
|
4651
|
-
for (const rel of node.relations?.nodes ?? []) {
|
|
4652
|
-
const issue = mapIssueRef(rel.relatedIssue);
|
|
4653
|
-
if (!issue) continue;
|
|
4654
|
-
const id = rel.id?.trim();
|
|
4655
|
-
out.push({ type: String(rel.type ?? "related"), issue, ...id ? { id } : {} });
|
|
4656
|
-
}
|
|
4657
|
-
for (const rel of node.inverseRelations?.nodes ?? []) {
|
|
4658
|
-
const issue = mapIssueRef(rel.issue);
|
|
4659
|
-
if (!issue) continue;
|
|
4660
|
-
const id = rel.id?.trim();
|
|
4661
|
-
out.push({
|
|
4662
|
-
type: flipLinearRelationType(String(rel.type ?? "related")),
|
|
4663
|
-
issue,
|
|
4664
|
-
...id ? { id } : {}
|
|
4665
|
-
});
|
|
4666
|
-
}
|
|
4667
|
-
return out;
|
|
4668
|
-
}
|
|
4669
|
-
function mapComments2(node) {
|
|
4670
|
-
const out = [];
|
|
4671
|
-
for (const comment of node.comments?.nodes ?? []) {
|
|
4672
|
-
const id = String(comment.id ?? "");
|
|
4673
|
-
const body = String(comment.body ?? "");
|
|
4674
|
-
if (!id && !body) continue;
|
|
4675
|
-
out.push({
|
|
4676
|
-
id,
|
|
4677
|
-
body,
|
|
4678
|
-
url: comment.url?.trim() || void 0,
|
|
4679
|
-
createdAt: comment.createdAt?.trim() || void 0,
|
|
4680
|
-
updatedAt: comment.updatedAt?.trim() || void 0,
|
|
4681
|
-
user: mapUser(comment.user)
|
|
4682
|
-
});
|
|
4683
|
-
}
|
|
4684
|
-
return out;
|
|
4685
|
-
}
|
|
4686
|
-
function mapIssue(node) {
|
|
4687
|
-
const team = node.team;
|
|
4688
|
-
return {
|
|
4689
|
-
id: String(node.id ?? node.identifier ?? ""),
|
|
4690
|
-
identifier: String(node.identifier ?? node.id ?? ""),
|
|
4691
|
-
title: String(node.title ?? ""),
|
|
4692
|
-
url: String(node.url ?? ""),
|
|
4693
|
-
description: node.description?.trim() || void 0,
|
|
4694
|
-
priority: typeof node.priority === "number" ? node.priority : void 0,
|
|
4695
|
-
estimate: typeof node.estimate === "number" ? node.estimate : void 0,
|
|
4696
|
-
dueDate: node.dueDate?.trim() || void 0,
|
|
4697
|
-
branchName: node.branchName?.trim() || void 0,
|
|
4698
|
-
createdAt: node.createdAt?.trim() || void 0,
|
|
4699
|
-
updatedAt: node.updatedAt?.trim() || void 0,
|
|
4700
|
-
completedAt: node.completedAt?.trim() || void 0,
|
|
4701
|
-
state: mapState(node.state),
|
|
4702
|
-
assignee: mapUser(node.assignee),
|
|
4703
|
-
creator: mapUser(node.creator),
|
|
4704
|
-
team: team?.id ? {
|
|
4705
|
-
id: String(team.id),
|
|
4706
|
-
key: String(team.key ?? ""),
|
|
4707
|
-
name: String(team.name ?? ""),
|
|
4708
|
-
states: (team.states?.nodes ?? []).flatMap((s) => {
|
|
4709
|
-
const state = mapState(s);
|
|
4710
|
-
return state ? [state] : [];
|
|
4711
|
-
})
|
|
4712
|
-
} : void 0,
|
|
4713
|
-
labels: (node.labels?.nodes ?? []).map((l) => l.name).filter((n) => Boolean(n)),
|
|
4714
|
-
cycle: mapCycle(node.cycle),
|
|
4715
|
-
project: node.project?.id ? { id: String(node.project.id), name: String(node.project.name ?? "") } : void 0,
|
|
4716
|
-
parent: mapIssueRef(node.parent),
|
|
4717
|
-
children: (node.children?.nodes ?? []).flatMap((child) => {
|
|
4718
|
-
const ref = mapIssueRef(child);
|
|
4719
|
-
return ref ? [ref] : [];
|
|
4720
|
-
}),
|
|
4721
|
-
relations: mapRelations(node),
|
|
4722
|
-
comments: mapComments2(node),
|
|
4723
|
-
attachments: mapLinearAttachments(node)
|
|
4724
|
-
};
|
|
4725
|
-
}
|
|
4726
|
-
function toIssueInfo(issue) {
|
|
4727
|
-
return {
|
|
4728
|
-
id: issue.id,
|
|
4729
|
-
identifier: issue.identifier,
|
|
4730
|
-
title: issue.title,
|
|
4731
|
-
url: issue.url,
|
|
4732
|
-
labels: issue.labels,
|
|
4733
|
-
provider: "linear",
|
|
4734
|
-
assignee: issue.assignee?.name,
|
|
4735
|
-
assignees: issue.assignee?.name ? [issue.assignee.name] : void 0,
|
|
4736
|
-
cycle: issue.cycle ?? null,
|
|
4737
|
-
teamKey: issue.team?.key || void 0,
|
|
4738
|
-
...issue.project ? { projects: [issue.project] } : {},
|
|
4739
|
-
...issue.createdAt ? { createdAt: issue.createdAt } : {},
|
|
4740
|
-
...issue.updatedAt ? { updatedAt: issue.updatedAt } : {}
|
|
4741
|
-
};
|
|
4742
|
-
}
|
|
4743
|
-
function mapTeam(node) {
|
|
4744
|
-
const cycles = (node.cycles?.nodes ?? []).flatMap((c) => {
|
|
4745
|
-
const cycle = mapNamedCycle(c);
|
|
4746
|
-
return cycle ? [cycle] : [];
|
|
4747
|
-
});
|
|
4748
|
-
return {
|
|
4749
|
-
id: String(node.id ?? ""),
|
|
4750
|
-
key: String(node.key ?? ""),
|
|
4751
|
-
name: String(node.name ?? ""),
|
|
4752
|
-
states: (node.states?.nodes ?? []).flatMap((s) => {
|
|
4753
|
-
const state = mapState(s);
|
|
4754
|
-
return state ? [state] : [];
|
|
4755
|
-
}),
|
|
4756
|
-
activeCycle: mapNamedCycle(node.activeCycle),
|
|
4757
|
-
...cycles.length ? { cycles } : {}
|
|
4758
|
-
};
|
|
4759
|
-
}
|
|
4760
|
-
function resolveLinearTeam(teams, team) {
|
|
4761
|
-
const t = team.trim();
|
|
4762
|
-
if (!t) throw new Error("Linear team is required (id, key, or name from linear_list_teams)");
|
|
4763
|
-
const lower = t.toLowerCase();
|
|
4764
|
-
const found = teams.find(
|
|
4765
|
-
(x) => x.id === t || x.key.toLowerCase() === lower || x.name.toLowerCase() === lower
|
|
4766
|
-
);
|
|
4767
|
-
if (!found) {
|
|
4768
|
-
const keys = teams.map((x) => x.key).filter(Boolean).join(", ") || "(none)";
|
|
4769
|
-
throw new Error(`Linear team not found: ${team}. Known keys: ${keys}`);
|
|
4770
|
-
}
|
|
4771
|
-
return found;
|
|
4772
|
-
}
|
|
4773
|
-
function resolveLinearState(team, state) {
|
|
4774
|
-
const s = state.trim();
|
|
4775
|
-
if (!s) throw new Error("Linear state is empty");
|
|
4776
|
-
const lower = s.toLowerCase();
|
|
4777
|
-
const found = team.states.find((x) => x.id === s) || team.states.find((x) => x.name.toLowerCase() === lower) || team.states.find((x) => x.type.toLowerCase() === lower);
|
|
4778
|
-
if (!found) {
|
|
4779
|
-
const available = team.states.map((x) => `${x.name} (${x.type})`).join(", ") || "(none)";
|
|
4780
|
-
throw new Error(`Linear state not found on ${team.key}: ${state}. Available: ${available}`);
|
|
4781
|
-
}
|
|
4782
|
-
return found;
|
|
4783
|
-
}
|
|
4784
|
-
function resolveLinearCycle(team, cycle) {
|
|
4785
|
-
if (cycle == null) return null;
|
|
4786
|
-
const s = cycle.trim();
|
|
4787
|
-
if (!s || /^(none|clear|unschedule|null)$/i.test(s)) return null;
|
|
4788
|
-
if (/^(current|active|this)$/i.test(s)) {
|
|
4789
|
-
const active = team.activeCycle ?? team.cycles?.find((c) => c.isActive);
|
|
4790
|
-
if (!active?.id) {
|
|
4791
|
-
throw new Error(`Linear team ${team.key} has no active cycle`);
|
|
4792
|
-
}
|
|
4793
|
-
return active.id;
|
|
4794
|
-
}
|
|
4795
|
-
const lower = s.toLowerCase();
|
|
4796
|
-
const asNumber = Number.parseInt(s, 10);
|
|
4797
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4798
|
-
const cycles = [team.activeCycle, ...team.cycles ?? []].filter(
|
|
4799
|
-
(c) => {
|
|
4800
|
-
if (!c?.id || seen.has(c.id)) return false;
|
|
4801
|
-
seen.add(c.id);
|
|
4802
|
-
return true;
|
|
4803
|
-
}
|
|
4804
|
-
);
|
|
4805
|
-
const found = cycles.find((c) => c.id === s) || cycles.find((c) => c.name.toLowerCase() === lower) || (Number.isInteger(asNumber) ? cycles.find((c) => c.number === asNumber) : void 0);
|
|
4806
|
-
if (!found) {
|
|
4807
|
-
const available = cycles.map((c) => c.number != null ? `${c.name} (${c.number})` : c.name).join(", ") || "(none)";
|
|
4808
|
-
throw new Error(`Linear cycle not found on ${team.key}: ${cycle}. Available: ${available}`);
|
|
4809
|
-
}
|
|
4810
|
-
return found.id;
|
|
4811
|
-
}
|
|
4812
|
-
var LINEAR_NONE_RE = /^(none|clear|unschedule|null)$/i;
|
|
4813
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
4814
|
-
function isLinearNoneToken(value) {
|
|
4815
|
-
if (value == null) return true;
|
|
4816
|
-
const s = value.trim();
|
|
4817
|
-
return !s || LINEAR_NONE_RE.test(s);
|
|
4818
|
-
}
|
|
4819
|
-
function resolveLinearLabel(labels, name) {
|
|
4820
|
-
const s = name.trim();
|
|
4821
|
-
if (!s) throw new Error("Linear label is empty");
|
|
4822
|
-
const lower = s.toLowerCase();
|
|
4823
|
-
const found = labels.find((x) => x.id === s) || labels.find((x) => x.name.toLowerCase() === lower);
|
|
4824
|
-
if (!found) {
|
|
4825
|
-
const available = labels.map((x) => x.name).filter(Boolean).slice(0, 20).join(", ") || "(none)";
|
|
4826
|
-
throw new Error(`Linear label not found: ${name}. Available: ${available}`);
|
|
4827
|
-
}
|
|
4828
|
-
return found;
|
|
4829
|
-
}
|
|
4830
|
-
function resolveLinearProject(projects, project) {
|
|
4831
|
-
if (isLinearNoneToken(project)) return null;
|
|
4832
|
-
const s = (project ?? "").trim();
|
|
4833
|
-
const lower = s.toLowerCase();
|
|
4834
|
-
const found = projects.find((x) => x.id === s) || projects.find((x) => x.name.toLowerCase() === lower) || projects.find((x) => x.slugId?.toLowerCase() === lower);
|
|
4835
|
-
if (!found) {
|
|
4836
|
-
const available = projects.map((x) => x.name).filter(Boolean).slice(0, 20).join(", ") || "(none)";
|
|
4837
|
-
throw new Error(`Linear project not found: ${project}. Available: ${available}`);
|
|
4838
|
-
}
|
|
4839
|
-
return found.id;
|
|
4840
|
-
}
|
|
4841
|
-
function normalizeLinearRelationType(type) {
|
|
4842
|
-
const s = type.trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
4843
|
-
if (s === "blocks" || s === "block") return "blocks";
|
|
4844
|
-
if (s === "blockedby" || s === "blocked") return "blockedBy";
|
|
4845
|
-
if (s === "related" || s === "relates" || s === "relatesto") return "related";
|
|
4846
|
-
if (s === "duplicate" || s === "dup") return "duplicate";
|
|
4847
|
-
if (s === "duplicateof" || s === "original") return "duplicateOf";
|
|
4848
|
-
if (s === "similar") return "similar";
|
|
4849
|
-
throw new Error(
|
|
4850
|
-
`Linear relation type must be blocks, blockedBy, related, duplicate, or duplicateOf (got ${type})`
|
|
4851
|
-
);
|
|
4852
|
-
}
|
|
4853
|
-
function linearRelationCreateInput(thisIssueId, type, relatedIssueId) {
|
|
4854
|
-
const view = normalizeLinearRelationType(type);
|
|
4855
|
-
if (view === "blockedBy") {
|
|
4856
|
-
return { issueId: relatedIssueId, relatedIssueId: thisIssueId, type: "blocks" };
|
|
4857
|
-
}
|
|
4858
|
-
if (view === "duplicateOf") {
|
|
4859
|
-
return { issueId: relatedIssueId, relatedIssueId: thisIssueId, type: "duplicate" };
|
|
4860
|
-
}
|
|
4861
|
-
return { issueId: thisIssueId, relatedIssueId, type: view };
|
|
4862
|
-
}
|
|
4863
|
-
function relationMatches(rel, type, issueRef) {
|
|
4864
|
-
if (rel.type.toLowerCase() !== type.toLowerCase()) return false;
|
|
4865
|
-
const ref = issueRef.trim().toLowerCase();
|
|
4866
|
-
return rel.issue.id.toLowerCase() === ref || rel.issue.identifier.toLowerCase() === ref;
|
|
4867
|
-
}
|
|
4868
|
-
function formatRelationList(relations) {
|
|
4869
|
-
if (!relations.length) return "(none)";
|
|
4870
|
-
return relations.map((r) => `${r.type} ${r.issue.identifier || r.issue.id}`).join(", ");
|
|
4871
|
-
}
|
|
4872
|
-
function mapLabelNodes(nodes) {
|
|
4873
|
-
return (nodes ?? []).flatMap((node) => {
|
|
4874
|
-
const id = String(node.id ?? "").trim();
|
|
4875
|
-
const name = String(node.name ?? "").trim();
|
|
4876
|
-
if (!id) return [];
|
|
4877
|
-
return [{ id, name }];
|
|
4878
|
-
});
|
|
4879
|
-
}
|
|
4880
|
-
async function listLinearWorkspaceLabels(opts) {
|
|
4881
|
-
const json = await linearGraphql(LABELS_QUERY, { first: 250 }, opts);
|
|
4882
|
-
return mapLabelNodes(json.issueLabels?.nodes);
|
|
4883
|
-
}
|
|
4884
|
-
async function listLinearLabels(opts) {
|
|
4885
|
-
const teamId = opts?.teamId?.trim();
|
|
4886
|
-
if (teamId) {
|
|
4887
|
-
const out = [];
|
|
4888
|
-
let after;
|
|
4889
|
-
for (let page = 0; page < 4; page++) {
|
|
4890
|
-
const json = await linearGraphql(
|
|
4891
|
-
TEAM_LABELS_QUERY,
|
|
4892
|
-
after ? { id: teamId, first: 250, after } : { id: teamId, first: 250 },
|
|
4893
|
-
opts
|
|
4894
|
-
);
|
|
4895
|
-
const conn = json.team?.labels;
|
|
4896
|
-
out.push(...mapLabelNodes(conn?.nodes));
|
|
4897
|
-
if (!conn?.pageInfo?.hasNextPage || !conn.pageInfo.endCursor) break;
|
|
4898
|
-
after = conn.pageInfo.endCursor;
|
|
4899
|
-
}
|
|
4900
|
-
if (out.length) return out;
|
|
4901
|
-
}
|
|
4902
|
-
return listLinearWorkspaceLabels(opts);
|
|
4903
|
-
}
|
|
4904
|
-
async function queryLinearProjects(filter, opts) {
|
|
4905
|
-
const out = [];
|
|
4906
|
-
let after;
|
|
4907
|
-
for (let page = 0; page < 4; page++) {
|
|
4908
|
-
const json = await linearGraphql(PROJECTS_QUERY, { first: 50, filter: filter ?? {}, ...after ? { after } : {} }, opts);
|
|
4909
|
-
for (const node of json.projects?.nodes ?? []) {
|
|
4910
|
-
const id = String(node.id ?? "").trim();
|
|
4911
|
-
if (!id) continue;
|
|
4912
|
-
out.push({ id, name: String(node.name ?? ""), ...node.slugId ? { slugId: String(node.slugId) } : {} });
|
|
4913
|
-
}
|
|
4914
|
-
const pageInfo = json.projects?.pageInfo;
|
|
4915
|
-
if (!pageInfo?.hasNextPage || !pageInfo.endCursor) break;
|
|
4916
|
-
after = pageInfo.endCursor;
|
|
4917
|
-
}
|
|
4918
|
-
return out;
|
|
4919
|
-
}
|
|
4920
|
-
function listLinearProjects(opts) {
|
|
4921
|
-
return queryLinearProjects(void 0, opts);
|
|
4922
|
-
}
|
|
4923
|
-
async function resolveLinearProjectId(project, opts) {
|
|
4924
|
-
if (isLinearNoneToken(project)) return null;
|
|
4925
|
-
const s = (project ?? "").trim();
|
|
4926
|
-
const filter = UUID_RE.test(s) ? { id: { eq: s } } : { name: { eqIgnoreCase: s } };
|
|
4927
|
-
const matched = await queryLinearProjects(filter, opts);
|
|
4928
|
-
try {
|
|
4929
|
-
return resolveLinearProject(matched, s);
|
|
4930
|
-
} catch {
|
|
4931
|
-
if (UUID_RE.test(s)) return s;
|
|
4932
|
-
const listed = await queryLinearProjects(void 0, opts);
|
|
4933
|
-
return resolveLinearProject(listed, s);
|
|
4934
|
-
}
|
|
4935
|
-
}
|
|
4936
|
-
async function resolveLinearLabelIds(names, opts) {
|
|
4937
|
-
if (names.length === 0) return [];
|
|
4938
|
-
const labels = await listLinearLabels(opts);
|
|
4939
|
-
return names.map((name) => {
|
|
4940
|
-
const s = name.trim();
|
|
4941
|
-
try {
|
|
4942
|
-
return resolveLinearLabel(labels, s).id;
|
|
4943
|
-
} catch (error) {
|
|
4944
|
-
if (UUID_RE.test(s)) return s;
|
|
4945
|
-
throw error;
|
|
4946
|
-
}
|
|
4947
|
-
});
|
|
4948
|
-
}
|
|
4949
|
-
async function applyLinearIssueRelations(issue, add, remove, opts) {
|
|
4950
|
-
const current = [...issue.relations];
|
|
4951
|
-
for (const rel of remove ?? []) {
|
|
4952
|
-
const type = normalizeLinearRelationType(rel.type);
|
|
4953
|
-
const issueRef = rel.issue.trim();
|
|
4954
|
-
if (!issueRef) throw new Error("Linear removeRelations.issue is required (uuid or ENG-123)");
|
|
4955
|
-
const match = current.find((r) => relationMatches(r, type, issueRef));
|
|
4956
|
-
if (!match?.id) {
|
|
4957
|
-
throw new Error(
|
|
4958
|
-
`Linear relation not found: ${type} ${issueRef}. Existing: ${formatRelationList(current)}`
|
|
4959
|
-
);
|
|
4960
|
-
}
|
|
4961
|
-
const json = await linearGraphql(ISSUE_RELATION_DELETE, { id: match.id }, opts);
|
|
4962
|
-
if (!json.issueRelationDelete?.success) {
|
|
4963
|
-
throw new Error(`Linear issueRelationDelete failed (${type} ${issueRef})`);
|
|
4964
|
-
}
|
|
4965
|
-
const idx = current.indexOf(match);
|
|
4966
|
-
if (idx >= 0) current.splice(idx, 1);
|
|
4967
|
-
}
|
|
4968
|
-
for (const rel of add ?? []) {
|
|
4969
|
-
const type = normalizeLinearRelationType(rel.type);
|
|
4970
|
-
const issueRef = rel.issue.trim();
|
|
4971
|
-
if (!issueRef) throw new Error("Linear relations.issue is required (uuid or ENG-123)");
|
|
4972
|
-
if (current.some((r) => relationMatches(r, type, issueRef))) continue;
|
|
4973
|
-
const related = await getLinearIssue(issueRef, opts);
|
|
4974
|
-
const input = linearRelationCreateInput(issue.id, type, related.id);
|
|
4975
|
-
const json = await linearGraphql(ISSUE_RELATION_CREATE, { input }, opts);
|
|
4976
|
-
if (!json.issueRelationCreate?.success) {
|
|
4977
|
-
throw new Error(`Linear issueRelationCreate failed (${type} ${related.identifier})`);
|
|
4978
|
-
}
|
|
4979
|
-
current.push({ type, issue: related, id: void 0 });
|
|
4980
|
-
}
|
|
4981
|
-
}
|
|
4982
|
-
function normalizePriority(priority) {
|
|
4983
|
-
if (priority == null) return void 0;
|
|
4984
|
-
if (!Number.isInteger(priority) || priority < 0 || priority > 4) {
|
|
4985
|
-
throw new Error("Linear priority must be 0 (none), 1 (urgent), 2 (high), 3 (medium), or 4 (low)");
|
|
4986
|
-
}
|
|
4987
|
-
return priority;
|
|
4988
|
-
}
|
|
4989
|
-
function assigneeFilterKey(assignee) {
|
|
4990
|
-
return (assignee ?? "").trim();
|
|
4991
|
-
}
|
|
4992
|
-
function applyLinearIssueQueryFilter(filter, query) {
|
|
4993
|
-
const term = query?.trim();
|
|
4994
|
-
if (!term) return;
|
|
4995
|
-
const or = [
|
|
4996
|
-
{ title: { containsIgnoreCase: term } },
|
|
4997
|
-
{ description: { containsIgnoreCase: term } }
|
|
4998
|
-
];
|
|
4999
|
-
const identifierMatch = term.match(/^[A-Za-z][\w]*-(\d+)$/);
|
|
5000
|
-
const asNumber = identifierMatch ? Number(identifierMatch[1]) : /^\d+$/.test(term) ? Number(term) : NaN;
|
|
5001
|
-
if (Number.isFinite(asNumber) && asNumber > 0) {
|
|
5002
|
-
or.push({ number: { eq: asNumber } });
|
|
5003
|
-
}
|
|
5004
|
-
filter.or = or;
|
|
5005
|
-
}
|
|
5006
|
-
function buildLinearIssueFilter(input) {
|
|
5007
|
-
const filter = {
|
|
5008
|
-
state: { type: { nin: ["completed", "canceled"] } }
|
|
5009
|
-
};
|
|
5010
|
-
const since = input?.updatedSince?.trim();
|
|
5011
|
-
if (since) filter.updatedAt = { gte: since };
|
|
5012
|
-
applyLinearIssueQueryFilter(filter, input?.query);
|
|
5013
|
-
const raw = assigneeFilterKey(input?.assignee) || "me";
|
|
5014
|
-
const key = raw.toLowerCase();
|
|
5015
|
-
if (key === "all" || key === "*") return filter;
|
|
5016
|
-
if (key === "unassigned" || key === "none" || key === "null") {
|
|
5017
|
-
filter.assignee = { null: true };
|
|
5018
|
-
return filter;
|
|
5019
|
-
}
|
|
5020
|
-
if (key === "me" || key === "@me") {
|
|
5021
|
-
filter.assignee = { isMe: { eq: true } };
|
|
5022
|
-
return filter;
|
|
5023
|
-
}
|
|
5024
|
-
if (UUID_RE.test(raw)) {
|
|
5025
|
-
filter.assignee = { id: { eq: raw } };
|
|
5026
|
-
return filter;
|
|
5027
|
-
}
|
|
5028
|
-
filter.assignee = { name: { eqIgnoreCase: raw } };
|
|
5029
|
-
return filter;
|
|
5030
|
-
}
|
|
5031
|
-
function isAssignedToMe(assignee) {
|
|
5032
|
-
const key = assignee.toLowerCase();
|
|
5033
|
-
return key === "me" || key === "@me";
|
|
3741
|
+
return Number.isFinite(number) && number > 0 ? number : null;
|
|
5034
3742
|
}
|
|
5035
|
-
|
|
3743
|
+
var GITHUB_ISSUE_COMMENTS_PAGE_SIZE = 100;
|
|
3744
|
+
var GITHUB_ISSUE_COMMENTS_MAX_PAGES = 5;
|
|
3745
|
+
function parseGitHubIssueCommentNode(item, allowed) {
|
|
3746
|
+
const rec = item && typeof item === "object" ? item : null;
|
|
3747
|
+
if (!rec) return null;
|
|
3748
|
+
const issueUrl = typeof rec.issue_url === "string" ? rec.issue_url : "";
|
|
3749
|
+
const number = issueNumberFromApiUrl(issueUrl);
|
|
3750
|
+
if (number == null) return null;
|
|
3751
|
+
if (allowed && !allowed.has(number)) return null;
|
|
3752
|
+
const body = previewIssueCommentBody(typeof rec.body === "string" ? rec.body : "");
|
|
3753
|
+
const user = rec.user && typeof rec.user === "object" ? String(rec.user.login ?? "").trim() : "";
|
|
3754
|
+
const createdAt = typeof rec.created_at === "string" ? rec.created_at : typeof rec.createdAt === "string" ? rec.createdAt : void 0;
|
|
3755
|
+
const url = typeof rec.html_url === "string" ? rec.html_url : void 0;
|
|
5036
3756
|
return {
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
3757
|
+
identifier: `#${number}`,
|
|
3758
|
+
...user ? { author: user } : {},
|
|
3759
|
+
...createdAt ? { createdAt } : {},
|
|
3760
|
+
body,
|
|
3761
|
+
...url ? { url } : {}
|
|
5042
3762
|
};
|
|
5043
3763
|
}
|
|
5044
|
-
async function
|
|
5045
|
-
const
|
|
5046
|
-
const
|
|
5047
|
-
|
|
5048
|
-
const filter = buildLinearIssueFilter({
|
|
5049
|
-
assignee,
|
|
5050
|
-
updatedSince: opts?.updatedSince
|
|
5051
|
-
});
|
|
5052
|
-
if (query) {
|
|
5053
|
-
const json2 = await linearGraphql(
|
|
5054
|
-
SEARCH_ISSUES_QUERY,
|
|
5055
|
-
{ term: query, first, filter },
|
|
5056
|
-
opts
|
|
5057
|
-
);
|
|
5058
|
-
return listedLinearIssues(json2, json2.searchIssues?.nodes);
|
|
5059
|
-
}
|
|
5060
|
-
if (isAssignedToMe(assignee)) {
|
|
5061
|
-
const json2 = await linearGraphql(
|
|
5062
|
-
ASSIGNED_ISSUES_QUERY,
|
|
5063
|
-
{ first, filter },
|
|
5064
|
-
opts
|
|
5065
|
-
);
|
|
5066
|
-
return listedLinearIssues(json2, json2.viewer?.assignedIssues?.nodes);
|
|
5067
|
-
}
|
|
5068
|
-
const json = await linearGraphql(
|
|
5069
|
-
ISSUES_QUERY,
|
|
5070
|
-
{ first, filter },
|
|
5071
|
-
opts
|
|
5072
|
-
);
|
|
5073
|
-
return listedLinearIssues(json, json.issues?.nodes);
|
|
5074
|
-
}
|
|
5075
|
-
async function listLinearCommentsSince(opts) {
|
|
5076
|
-
const first = Math.max(1, Math.min(250, opts.limit ?? 200));
|
|
5077
|
-
const query = opts.query?.trim() ?? "";
|
|
3764
|
+
async function listGitHubIssueCommentsSince(opts) {
|
|
3765
|
+
const limit = Math.max(1, Math.min(250, opts.limit ?? 200));
|
|
3766
|
+
const { cwd, slug } = await resolveGitHubIssueRepo(opts.repoPath);
|
|
3767
|
+
if (!slug) return [];
|
|
5078
3768
|
const restrictToIssues = opts.issueIdentifiers != null;
|
|
5079
3769
|
const allowed = new Set(
|
|
5080
|
-
(opts.issueIdentifiers ?? []).map((id) =>
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
COMMENTS_SINCE_QUERY,
|
|
5086
|
-
{
|
|
5087
|
-
first,
|
|
5088
|
-
filter: {
|
|
5089
|
-
createdAt: { gte: opts.since },
|
|
5090
|
-
issue: buildLinearIssueFilter({
|
|
5091
|
-
assignee,
|
|
5092
|
-
// Search hits may match identifier/comments — do not also require
|
|
5093
|
-
// title/description contains when an allowlist is already applied.
|
|
5094
|
-
query: restrictToIssues ? void 0 : query || void 0
|
|
5095
|
-
})
|
|
3770
|
+
(opts.issueIdentifiers ?? []).map((id) => {
|
|
3771
|
+
try {
|
|
3772
|
+
return parseGitHubIssueNumber(id);
|
|
3773
|
+
} catch {
|
|
3774
|
+
return null;
|
|
5096
3775
|
}
|
|
5097
|
-
}
|
|
5098
|
-
opts
|
|
3776
|
+
}).filter((n) => n != null)
|
|
5099
3777
|
);
|
|
3778
|
+
if (restrictToIssues && allowed.size === 0) return [];
|
|
3779
|
+
const pageSize = GITHUB_ISSUE_COMMENTS_PAGE_SIZE;
|
|
5100
3780
|
const out = [];
|
|
5101
|
-
for (
|
|
5102
|
-
const
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
3781
|
+
for (let page = 1; page <= GITHUB_ISSUE_COMMENTS_MAX_PAGES; page++) {
|
|
3782
|
+
const path2 = `repos/${slug}/issues/comments?since=${encodeURIComponent(opts.since)}&sort=created&direction=desc&per_page=${pageSize}&page=${page}`;
|
|
3783
|
+
const result = await gh(["api", path2], cwd, { reject: false });
|
|
3784
|
+
if (result.exitCode !== 0 || !result.stdout.trim()) break;
|
|
3785
|
+
let parsed;
|
|
3786
|
+
try {
|
|
3787
|
+
parsed = JSON.parse(result.stdout);
|
|
3788
|
+
} catch {
|
|
3789
|
+
break;
|
|
3790
|
+
}
|
|
3791
|
+
if (!Array.isArray(parsed) || parsed.length === 0) break;
|
|
3792
|
+
for (const item of parsed) {
|
|
3793
|
+
const comment = parseGitHubIssueCommentNode(item, restrictToIssues ? allowed : null);
|
|
3794
|
+
if (!comment) continue;
|
|
3795
|
+
out.push(comment);
|
|
3796
|
+
if (out.length >= limit) return out;
|
|
3797
|
+
}
|
|
3798
|
+
if (parsed.length < pageSize) break;
|
|
5114
3799
|
}
|
|
5115
3800
|
return out;
|
|
5116
3801
|
}
|
|
5117
|
-
async function
|
|
5118
|
-
|
|
5119
|
-
}
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
3802
|
+
async function getGitHubIssue(id, opts) {
|
|
3803
|
+
const number = parseGitHubIssueNumber(id);
|
|
3804
|
+
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
3805
|
+
const stdout = requireGhOk(
|
|
3806
|
+
await gh(
|
|
3807
|
+
[
|
|
3808
|
+
"issue",
|
|
3809
|
+
"view",
|
|
3810
|
+
String(number),
|
|
3811
|
+
...repoArgs,
|
|
3812
|
+
"--json",
|
|
3813
|
+
"number,title,body,url,state,labels,assignees,comments,author"
|
|
3814
|
+
],
|
|
3815
|
+
cwd,
|
|
3816
|
+
{ reject: false }
|
|
3817
|
+
),
|
|
3818
|
+
`GitHub issue ${number}`
|
|
3819
|
+
);
|
|
3820
|
+
let parsed;
|
|
3821
|
+
try {
|
|
3822
|
+
parsed = JSON.parse(stdout);
|
|
3823
|
+
} catch {
|
|
3824
|
+
throw new Error(`GitHub issue ${number}: gh returned non-JSON`);
|
|
5137
3825
|
}
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
3826
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
3827
|
+
throw new Error(`GitHub issue not found: #${number}`);
|
|
3828
|
+
}
|
|
3829
|
+
const issue = toGitHubIssue(parsed);
|
|
3830
|
+
const projects = await readGitHubIssueProjects(number, cwd, repoArgs);
|
|
3831
|
+
issue.projects = projects.projects;
|
|
3832
|
+
if (projects.error) issue.projectsError = projects.error;
|
|
3833
|
+
return issue;
|
|
5145
3834
|
}
|
|
5146
|
-
async function
|
|
5147
|
-
const
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
3835
|
+
async function commentGitHubIssue(input, opts) {
|
|
3836
|
+
const number = parseGitHubIssueNumber(input.id);
|
|
3837
|
+
const body = input.body.trim();
|
|
3838
|
+
if (!body) throw new Error("GitHub comment body is required");
|
|
3839
|
+
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
3840
|
+
const stdout = requireGhOk(
|
|
3841
|
+
await gh(
|
|
3842
|
+
["issue", "comment", String(number), ...repoArgs, "--body", body],
|
|
3843
|
+
cwd,
|
|
3844
|
+
{ reject: false }
|
|
3845
|
+
),
|
|
3846
|
+
`GitHub comment on #${number}`
|
|
5153
3847
|
);
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
}
|
|
5157
|
-
return mapIssue(json.issue);
|
|
3848
|
+
const url = stdout.trim().split(/\s+/).find((part) => /^https?:\/\//i.test(part));
|
|
3849
|
+
return { body, url };
|
|
5158
3850
|
}
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
const description = input.description?.trim();
|
|
5169
|
-
if (description) mutationInput.description = description;
|
|
5170
|
-
if (input.state?.trim()) {
|
|
5171
|
-
mutationInput.stateId = resolveLinearState(team, input.state).id;
|
|
5172
|
-
}
|
|
5173
|
-
const parentRef = input.parent?.trim();
|
|
5174
|
-
if (parentRef) {
|
|
5175
|
-
const parent = await getLinearIssue(parentRef, opts);
|
|
5176
|
-
mutationInput.parentId = parent.id;
|
|
5177
|
-
}
|
|
5178
|
-
const assignee = input.assignee === void 0 ? void 0 : input.assignee?.trim() || null;
|
|
5179
|
-
if (assignee === "me") mutationInput.assigneeId = viewer.id;
|
|
5180
|
-
else if (assignee) mutationInput.assigneeId = assignee;
|
|
5181
|
-
const priority = normalizePriority(input.priority);
|
|
5182
|
-
if (priority != null) mutationInput.priority = priority;
|
|
5183
|
-
const json = await linearGraphql(ISSUE_CREATE, { input: mutationInput }, opts);
|
|
5184
|
-
if (!json.issueCreate?.success || !json.issueCreate.issue) {
|
|
5185
|
-
throw new Error("Linear issueCreate failed");
|
|
5186
|
-
}
|
|
5187
|
-
return mapIssue(json.issueCreate.issue);
|
|
5188
|
-
}
|
|
5189
|
-
async function updateLinearIssue(input, opts) {
|
|
5190
|
-
const issueId = input.id.trim();
|
|
5191
|
-
if (!issueId) throw new Error("Linear issue id is required (uuid or ENG-123)");
|
|
5192
|
-
const mutationInput = {};
|
|
5193
|
-
if (input.title !== void 0) {
|
|
5194
|
-
const title = input.title.trim();
|
|
5195
|
-
if (!title) throw new Error("Linear issue title cannot be empty");
|
|
5196
|
-
mutationInput.title = title;
|
|
3851
|
+
function isGitHubNoneToken(value) {
|
|
3852
|
+
if (value == null) return true;
|
|
3853
|
+
const s = value.trim();
|
|
3854
|
+
return !s || /^(none|clear|unschedule|null)$/i.test(s);
|
|
3855
|
+
}
|
|
3856
|
+
function githubRelationEditFlag(type, remove = false) {
|
|
3857
|
+
const s = type.trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
3858
|
+
if (s === "blockedby" || s === "blocked" || s === "dependson") {
|
|
3859
|
+
return remove ? "--remove-blocked-by" : "--add-blocked-by";
|
|
5197
3860
|
}
|
|
5198
|
-
if (
|
|
5199
|
-
|
|
3861
|
+
if (s === "blocks" || s === "blocking") {
|
|
3862
|
+
return remove ? "--remove-blocking" : "--add-blocking";
|
|
5200
3863
|
}
|
|
3864
|
+
throw new Error(
|
|
3865
|
+
`GitHub relation type must be blocks or blockedBy (got ${type}). Parent is a separate parent= field.`
|
|
3866
|
+
);
|
|
3867
|
+
}
|
|
3868
|
+
function labelsEqual(a, b) {
|
|
3869
|
+
return a.trim().toLowerCase() === b.trim().toLowerCase();
|
|
3870
|
+
}
|
|
3871
|
+
async function updateGitHubIssue(input, opts) {
|
|
3872
|
+
const number = parseGitHubIssueNumber(input.id);
|
|
3873
|
+
const title = input.title?.trim();
|
|
3874
|
+
const body = input.body;
|
|
3875
|
+
const state = input.state?.trim().toLowerCase();
|
|
5201
3876
|
const addRelations = input.relations?.length ? input.relations : void 0;
|
|
5202
3877
|
const removeRelations = input.removeRelations?.length ? input.removeRelations : void 0;
|
|
5203
|
-
const
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
const teamRef = existing?.team?.key || existing?.team?.id;
|
|
5208
|
-
const team = teamRef ? resolveLinearTeam(teams, teamRef) : void 0;
|
|
5209
|
-
if (!team?.states.length) {
|
|
5210
|
-
throw new Error(`Linear issue ${existing?.identifier ?? issueId} has no workflow states to resolve "${input.state}"`);
|
|
5211
|
-
}
|
|
5212
|
-
mutationInput.stateId = resolveLinearState(team, input.state).id;
|
|
5213
|
-
}
|
|
5214
|
-
if (input.cycle !== void 0) {
|
|
5215
|
-
const teamId = existing?.team?.id;
|
|
5216
|
-
if (!teamId) {
|
|
5217
|
-
throw new Error(`Linear issue ${existing?.identifier ?? issueId} has no team to resolve a cycle`);
|
|
5218
|
-
}
|
|
5219
|
-
const cycles = await listLinearTeamCycles(teamId, opts);
|
|
5220
|
-
mutationInput.cycleId = resolveLinearCycle(
|
|
5221
|
-
{ ...cycles, key: cycles.key || existing?.team?.key || teamId },
|
|
5222
|
-
input.cycle
|
|
3878
|
+
const hasEditFields = Boolean(title) || body !== void 0 || input.labels !== void 0 || input.project !== void 0 || input.parent !== void 0 || Boolean(addRelations) || Boolean(removeRelations);
|
|
3879
|
+
if (!hasEditFields && !state) {
|
|
3880
|
+
throw new Error(
|
|
3881
|
+
"github_update_issue needs at least one of title, body, state, labels, project, parent, relations, removeRelations"
|
|
5223
3882
|
);
|
|
5224
3883
|
}
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
3884
|
+
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
3885
|
+
if (state === "closed" || state === "close") {
|
|
3886
|
+
requireGhOk(
|
|
3887
|
+
await gh(["issue", "close", String(number), ...repoArgs], cwd, { reject: false }),
|
|
3888
|
+
`GitHub close #${number}`
|
|
3889
|
+
);
|
|
3890
|
+
} else if (state === "open" || state === "reopen") {
|
|
3891
|
+
requireGhOk(
|
|
3892
|
+
await gh(["issue", "reopen", String(number), ...repoArgs], cwd, { reject: false }),
|
|
3893
|
+
`GitHub reopen #${number}`
|
|
3894
|
+
);
|
|
3895
|
+
} else if (state) {
|
|
3896
|
+
throw new Error(`GitHub issue state must be open or closed (got ${input.state})`);
|
|
5236
3897
|
}
|
|
3898
|
+
const args = ["issue", "edit", String(number), ...repoArgs];
|
|
3899
|
+
if (title) args.push("--title", title);
|
|
3900
|
+
if (body !== void 0) args.push("--body", body);
|
|
5237
3901
|
if (input.labels !== void 0) {
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
3902
|
+
const existing = await getGitHubIssue(String(number), opts);
|
|
3903
|
+
const desired = input.labels.map((name) => name.trim()).filter(Boolean);
|
|
3904
|
+
const add = desired.filter((name) => !existing.labels.some((cur) => labelsEqual(cur, name)));
|
|
3905
|
+
const remove = existing.labels.filter((cur) => !desired.some((name) => labelsEqual(cur, name)));
|
|
3906
|
+
if (add.length) args.push("--add-label", add.join(","));
|
|
3907
|
+
if (remove.length) args.push("--remove-label", remove.join(","));
|
|
5242
3908
|
}
|
|
5243
3909
|
if (input.project !== void 0) {
|
|
5244
|
-
|
|
3910
|
+
const current = await listGitHubIssueProjectNames(number, cwd, repoArgs);
|
|
3911
|
+
if (isGitHubNoneToken(input.project)) {
|
|
3912
|
+
for (const name of current) args.push("--remove-project", name);
|
|
3913
|
+
} else {
|
|
3914
|
+
const wanted = (input.project ?? "").trim();
|
|
3915
|
+
if (!current.some((name) => labelsEqual(name, wanted))) {
|
|
3916
|
+
args.push("--add-project", wanted);
|
|
3917
|
+
}
|
|
3918
|
+
for (const name of current) {
|
|
3919
|
+
if (!labelsEqual(name, wanted)) args.push("--remove-project", name);
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
5245
3922
|
}
|
|
5246
3923
|
if (input.parent !== void 0) {
|
|
5247
|
-
if (
|
|
5248
|
-
|
|
3924
|
+
if (isGitHubNoneToken(input.parent)) {
|
|
3925
|
+
args.push("--remove-parent");
|
|
5249
3926
|
} else {
|
|
5250
|
-
|
|
5251
|
-
mutationInput.parentId = parent.id;
|
|
3927
|
+
args.push("--parent", String(parseGitHubIssueNumber(input.parent ?? "")));
|
|
5252
3928
|
}
|
|
5253
3929
|
}
|
|
5254
|
-
const
|
|
5255
|
-
|
|
5256
|
-
if (!hasIssueFields && !hasRelationWrites) {
|
|
5257
|
-
throw new Error(
|
|
5258
|
-
"linear_update_issue needs at least one of title, description, state, assignee, priority, cycle, labels, project, parent, relations, removeRelations"
|
|
5259
|
-
);
|
|
5260
|
-
}
|
|
5261
|
-
let updated;
|
|
5262
|
-
if (hasIssueFields) {
|
|
5263
|
-
const json = await linearGraphql(ISSUE_UPDATE, { id: issueId, input: mutationInput }, opts);
|
|
5264
|
-
if (!json.issueUpdate?.success || !json.issueUpdate.issue) {
|
|
5265
|
-
throw new Error("Linear issueUpdate failed");
|
|
5266
|
-
}
|
|
5267
|
-
updated = mapIssue(json.issueUpdate.issue);
|
|
3930
|
+
for (const rel of removeRelations ?? []) {
|
|
3931
|
+
args.push(githubRelationEditFlag(rel.type, true), String(parseGitHubIssueNumber(rel.issue)));
|
|
5268
3932
|
}
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
if (!base) {
|
|
5272
|
-
throw new Error(`Linear issue not found: ${issueId}`);
|
|
5273
|
-
}
|
|
5274
|
-
await applyLinearIssueRelations(base, addRelations, removeRelations, opts);
|
|
5275
|
-
return getLinearIssue(base.id || issueId, opts);
|
|
3933
|
+
for (const rel of addRelations ?? []) {
|
|
3934
|
+
args.push(githubRelationEditFlag(rel.type, false), String(parseGitHubIssueNumber(rel.issue)));
|
|
5276
3935
|
}
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
const issueId = input.id.trim();
|
|
5281
|
-
const body = input.body.trim();
|
|
5282
|
-
if (!issueId) throw new Error("Linear issue id is required (uuid or ENG-123)");
|
|
5283
|
-
if (!body) throw new Error("Linear comment body is required");
|
|
5284
|
-
const json = await linearGraphql(COMMENT_CREATE, { input: { issueId, body } }, opts);
|
|
5285
|
-
if (!json.commentCreate?.success || !json.commentCreate.comment?.id) {
|
|
5286
|
-
throw new Error("Linear commentCreate failed");
|
|
3936
|
+
const editChanged = args.length > 3 + repoArgs.length;
|
|
3937
|
+
if (editChanged) {
|
|
3938
|
+
requireGhOk(await gh(args, cwd, { reject: false }), `GitHub edit #${number}`);
|
|
5287
3939
|
}
|
|
5288
|
-
return
|
|
5289
|
-
id: String(json.commentCreate.comment.id),
|
|
5290
|
-
body: String(json.commentCreate.comment.body ?? body),
|
|
5291
|
-
url: json.commentCreate.comment.url?.trim() || void 0
|
|
5292
|
-
};
|
|
3940
|
+
return getGitHubIssue(String(number), opts);
|
|
5293
3941
|
}
|
|
5294
|
-
async function
|
|
5295
|
-
const
|
|
5296
|
-
if (!
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
3942
|
+
async function createGitHubIssue(input, opts) {
|
|
3943
|
+
const title = input.title.trim();
|
|
3944
|
+
if (!title) throw new Error("GitHub issue title is required");
|
|
3945
|
+
const parent = input.parent?.trim();
|
|
3946
|
+
const parentNumber = parent ? parseGitHubIssueNumber(parent) : null;
|
|
3947
|
+
const bodyParts = [
|
|
3948
|
+
parentNumber ? `Spin-off of #${parentNumber}.` : null,
|
|
3949
|
+
input.body?.trim() || null
|
|
3950
|
+
].filter(Boolean);
|
|
3951
|
+
const { cwd, repoArgs } = await resolveGitHubIssueRepo(opts?.repoPath);
|
|
3952
|
+
const args = ["issue", "create", ...repoArgs, "--title", title];
|
|
3953
|
+
if (bodyParts.length) args.push("--body", bodyParts.join("\n\n"));
|
|
3954
|
+
const created = await gh([...args, "--json", "number,url,title"], cwd, { reject: false });
|
|
3955
|
+
if (created.exitCode === 0 && created.stdout.trim()) {
|
|
3956
|
+
try {
|
|
3957
|
+
const parsed = JSON.parse(created.stdout);
|
|
3958
|
+
if (parsed.number) return getGitHubIssue(String(parsed.number), opts);
|
|
3959
|
+
} catch {
|
|
3960
|
+
}
|
|
5302
3961
|
}
|
|
3962
|
+
const fallback = created.exitCode === 0 ? created : await gh(args, cwd, { reject: false });
|
|
3963
|
+
const stdout = requireGhOk(fallback, "GitHub create issue");
|
|
3964
|
+
const url = stdout.trim().match(/https?:\/\/github\.com\/[^/\s]+\/[^/\s]+\/issues\/(\d+)/i);
|
|
3965
|
+
if (url?.[1]) return getGitHubIssue(url[1], opts);
|
|
3966
|
+
throw new Error(`GitHub create issue: could not parse issue from ${stdout.trim() || "empty output"}`);
|
|
5303
3967
|
}
|
|
5304
3968
|
|
|
5305
3969
|
// src/integrations/issues.ts
|
|
@@ -6524,7 +5188,7 @@ function runRemoteHost(opts) {
|
|
|
6524
5188
|
}
|
|
6525
5189
|
|
|
6526
5190
|
// src/relay/server.ts
|
|
6527
|
-
import { createServer
|
|
5191
|
+
import { createServer } from "http";
|
|
6528
5192
|
import { WebSocketServer } from "ws";
|
|
6529
5193
|
|
|
6530
5194
|
// src/slack/oauth-redirect.ts
|
|
@@ -6933,7 +5597,7 @@ function connectSession(ws, opts, log) {
|
|
|
6933
5597
|
}
|
|
6934
5598
|
|
|
6935
5599
|
// src/slack/relay-protocol.ts
|
|
6936
|
-
import { randomBytes as
|
|
5600
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
6937
5601
|
var SLACK_RELAY_PING_INTERVAL_MS = 2e4;
|
|
6938
5602
|
var SLACK_RELAY_PONG_TIMEOUT_MS = 15e3;
|
|
6939
5603
|
function slackRelaySessionKey(teamId, userId, deviceId) {
|
|
@@ -6943,7 +5607,7 @@ function slackRelayUserKey(teamId, userId) {
|
|
|
6943
5607
|
return `${teamId.trim()}:${userId.trim()}`;
|
|
6944
5608
|
}
|
|
6945
5609
|
function newSlackRelayEventId() {
|
|
6946
|
-
return
|
|
5610
|
+
return randomBytes2(12).toString("hex");
|
|
6947
5611
|
}
|
|
6948
5612
|
function parseSlackRelayClientMessage(raw) {
|
|
6949
5613
|
try {
|
|
@@ -7471,7 +6135,7 @@ async function tryServeStatic(req, res, root) {
|
|
|
7471
6135
|
}
|
|
7472
6136
|
|
|
7473
6137
|
// src/remote/hub.ts
|
|
7474
|
-
import { randomBytes as
|
|
6138
|
+
import { randomBytes as randomBytes3 } from "crypto";
|
|
7475
6139
|
var CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
7476
6140
|
var CODE_TTL_MS = 10 * 60 * 1e3;
|
|
7477
6141
|
function send(socket, msg) {
|
|
@@ -7481,7 +6145,7 @@ function send(socket, msg) {
|
|
|
7481
6145
|
}
|
|
7482
6146
|
}
|
|
7483
6147
|
function newCode() {
|
|
7484
|
-
const bytes =
|
|
6148
|
+
const bytes = randomBytes3(6);
|
|
7485
6149
|
let code = "";
|
|
7486
6150
|
for (let i = 0; i < 6; i++) code += CODE_ALPHABET[bytes[i] % CODE_ALPHABET.length];
|
|
7487
6151
|
return code;
|
|
@@ -7752,7 +6416,7 @@ var RemoteHub = class {
|
|
|
7752
6416
|
host.phone = null;
|
|
7753
6417
|
}
|
|
7754
6418
|
const created = sessionToken == null;
|
|
7755
|
-
const token = sessionToken ??
|
|
6419
|
+
const token = sessionToken ?? randomBytes3(24).toString("hex");
|
|
7756
6420
|
if (host.sessionToken && host.sessionToken !== token) {
|
|
7757
6421
|
this.phonesByToken.delete(host.sessionToken);
|
|
7758
6422
|
}
|
|
@@ -7802,7 +6466,7 @@ var RemoteHub = class {
|
|
|
7802
6466
|
};
|
|
7803
6467
|
|
|
7804
6468
|
// src/relay/accounts.ts
|
|
7805
|
-
import { createHash
|
|
6469
|
+
import { createHash, randomBytes as randomBytes4, randomUUID as randomUUID3, timingSafeEqual } from "crypto";
|
|
7806
6470
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, realpathSync } from "fs";
|
|
7807
6471
|
import { createRequire } from "module";
|
|
7808
6472
|
import { dirname, join as join3 } from "path";
|
|
@@ -7822,7 +6486,7 @@ function openDatabase(filePath) {
|
|
|
7822
6486
|
return db;
|
|
7823
6487
|
}
|
|
7824
6488
|
function sha2562(value) {
|
|
7825
|
-
return
|
|
6489
|
+
return createHash("sha256").update(value).digest("hex");
|
|
7826
6490
|
}
|
|
7827
6491
|
function sameHash(a, b) {
|
|
7828
6492
|
const left = Buffer.from(a);
|
|
@@ -8054,7 +6718,7 @@ var RelayAccountStore = class {
|
|
|
8054
6718
|
).run(issuer, subject, accountId, email, verified ? 1 : 0);
|
|
8055
6719
|
}
|
|
8056
6720
|
mintCredential(accountId) {
|
|
8057
|
-
const credential =
|
|
6721
|
+
const credential = randomBytes4(32).toString("hex");
|
|
8058
6722
|
this.db.prepare("INSERT INTO credentials (token_hash, account_id, created_at) VALUES (?, ?, ?)").run(sha2562(credential), accountId, Date.now());
|
|
8059
6723
|
return credential;
|
|
8060
6724
|
}
|
|
@@ -8142,7 +6806,7 @@ function cursorGitHostEntry() {
|
|
|
8142
6806
|
}
|
|
8143
6807
|
|
|
8144
6808
|
// src/relay/account-entire.ts
|
|
8145
|
-
import { createHash as
|
|
6809
|
+
import { createHash as createHash2, randomBytes as randomBytes5 } from "crypto";
|
|
8146
6810
|
var ENTIRE_AUTHORIZE_ORIGIN = "https://auth.entire.io";
|
|
8147
6811
|
var ENTIRE_DEFAULT_ISSUER = "https://us.auth.entire.io";
|
|
8148
6812
|
var ISSUER_SUFFIX = ".auth.entire.io";
|
|
@@ -8165,10 +6829,10 @@ function entireAccountIssuer(value) {
|
|
|
8165
6829
|
return `https://${host}`;
|
|
8166
6830
|
}
|
|
8167
6831
|
function newEntireCodeVerifier() {
|
|
8168
|
-
return
|
|
6832
|
+
return randomBytes5(32).toString("base64url");
|
|
8169
6833
|
}
|
|
8170
6834
|
function entireCodeChallenge(verifier) {
|
|
8171
|
-
return
|
|
6835
|
+
return createHash2("sha256").update(verifier).digest("base64url");
|
|
8172
6836
|
}
|
|
8173
6837
|
function entireAuthorizeUrl(clientId, redirectUri, state, codeChallenge) {
|
|
8174
6838
|
const url = new URL("/authorize", ENTIRE_AUTHORIZE_ORIGIN);
|
|
@@ -8754,7 +7418,7 @@ async function startRelayServer(opts) {
|
|
|
8754
7418
|
sendJson2(res, 200, { ok: true, ...value.payload });
|
|
8755
7419
|
return true;
|
|
8756
7420
|
};
|
|
8757
|
-
const httpServer =
|
|
7421
|
+
const httpServer = createServer((req, res) => {
|
|
8758
7422
|
void (async () => {
|
|
8759
7423
|
const reqUrl = req.url || "/";
|
|
8760
7424
|
if (accountStore && await handleRelayAccountHttp({
|
|
@@ -9020,7 +7684,7 @@ async function startHeadlessHost(opts) {
|
|
|
9020
7684
|
};
|
|
9021
7685
|
const refreshRunning = async () => {
|
|
9022
7686
|
try {
|
|
9023
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
7687
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-7FVDBE7Y.js");
|
|
9024
7688
|
agentsRunning = getOrchestrator2().getRuntime().running;
|
|
9025
7689
|
} catch {
|
|
9026
7690
|
agentsRunning = 0;
|
|
@@ -11163,7 +9827,7 @@ async function handleJobStop(params, _ctx) {
|
|
|
11163
9827
|
return stopDetachedJob(jobCwd(rec), id, reason ? { reason } : void 0);
|
|
11164
9828
|
}
|
|
11165
9829
|
async function defaultTurnWait(ref, timeoutMs) {
|
|
11166
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
9830
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-7FVDBE7Y.js");
|
|
11167
9831
|
const orch = getOrchestrator2();
|
|
11168
9832
|
const thread = await orch.waitForTurn(ref, timeoutMs, {
|
|
11169
9833
|
resolveIfStillRunning: true
|
|
@@ -11516,7 +10180,7 @@ async function startMcpServer() {
|
|
|
11516
10180
|
);
|
|
11517
10181
|
}
|
|
11518
10182
|
try {
|
|
11519
|
-
const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-
|
|
10183
|
+
const { maxConcurrentAgents: maxConcurrentAgents2 } = await import("./app-settings-RXBH7KY4.js");
|
|
11520
10184
|
orch.setMaxConcurrent(maxConcurrentAgents2());
|
|
11521
10185
|
} catch {
|
|
11522
10186
|
}
|
|
@@ -12064,7 +10728,7 @@ async function startMcpServer() {
|
|
|
12064
10728
|
if (!worktreeProfile) {
|
|
12065
10729
|
registerConnectedIssueVendorTools(server);
|
|
12066
10730
|
const { getCaffeinateHold: getCaffeinateHold2, setCaffeinateHold: setCaffeinateHold2 } = await import("./caffeinate-hold-ZNUEZGHB.js");
|
|
12067
|
-
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-
|
|
10731
|
+
const { resolveNewThreadOptions: resolveNewThreadOptions2, resolveThreadDefaults: resolveThreadDefaults2 } = await import("./app-settings-RXBH7KY4.js");
|
|
12068
10732
|
const accountDefaults = resolveThreadDefaults2();
|
|
12069
10733
|
const accountDefaultsHint = `Account defaults: agent=${accountDefaults.agent}, model=${accountDefaults.model?.trim() || "Auto"}, effort=${accountDefaults.effort}`;
|
|
12070
10734
|
server.tool(
|
|
@@ -12748,8 +11412,8 @@ async function startMcpServer() {
|
|
|
12748
11412
|
}
|
|
12749
11413
|
|
|
12750
11414
|
// src/agent-rpc/server.ts
|
|
12751
|
-
import { randomBytes as
|
|
12752
|
-
import { createServer as
|
|
11415
|
+
import { randomBytes as randomBytes6, randomUUID as randomUUID8, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
11416
|
+
import { createServer as createServer2 } from "http";
|
|
12753
11417
|
import { WebSocketServer as WebSocketServer2 } from "ws";
|
|
12754
11418
|
function tokensEqual(a, b) {
|
|
12755
11419
|
const left = Buffer.from(a);
|
|
@@ -12788,7 +11452,7 @@ var AgentRpcServer = class {
|
|
|
12788
11452
|
async listen(opts = {}) {
|
|
12789
11453
|
const host = opts.host ?? "127.0.0.1";
|
|
12790
11454
|
const port = opts.port ?? 0;
|
|
12791
|
-
const authToken =
|
|
11455
|
+
const authToken = randomBytes6(24).toString("base64url");
|
|
12792
11456
|
this.meta = {
|
|
12793
11457
|
runtimeId: randomUUID8(),
|
|
12794
11458
|
pid: process.pid,
|
|
@@ -12797,7 +11461,7 @@ var AgentRpcServer = class {
|
|
|
12797
11461
|
startedAt: Date.now(),
|
|
12798
11462
|
transports: []
|
|
12799
11463
|
};
|
|
12800
|
-
const http =
|
|
11464
|
+
const http = createServer2((_req, res) => {
|
|
12801
11465
|
res.statusCode = 404;
|
|
12802
11466
|
res.end();
|
|
12803
11467
|
});
|
|
@@ -13641,7 +12305,7 @@ function slackTokenFor(ws, kind = "read") {
|
|
|
13641
12305
|
}
|
|
13642
12306
|
|
|
13643
12307
|
// src/slack/oauth.ts
|
|
13644
|
-
import { randomBytes as
|
|
12308
|
+
import { randomBytes as randomBytes7 } from "crypto";
|
|
13645
12309
|
var SLACK_BOT_SCOPES = [
|
|
13646
12310
|
"app_mentions:read",
|
|
13647
12311
|
"channels:history",
|
|
@@ -13734,7 +12398,7 @@ async function startSlackOAuth(opts) {
|
|
|
13734
12398
|
throw new SlackOAuthCancelledError();
|
|
13735
12399
|
}
|
|
13736
12400
|
const { clientId } = slackOAuthCredentials();
|
|
13737
|
-
const state =
|
|
12401
|
+
const state = randomBytes7(16).toString("hex");
|
|
13738
12402
|
const authorizeUrl = slackOAuthAuthorizeUrl(clientId, state);
|
|
13739
12403
|
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
13740
12404
|
const pollIntervalMs = opts?.pollIntervalMs ?? 400;
|
|
@@ -14627,7 +13291,7 @@ function resolveSlackListenMode(opts) {
|
|
|
14627
13291
|
}
|
|
14628
13292
|
|
|
14629
13293
|
// src/relay/account-login.ts
|
|
14630
|
-
import { randomBytes as
|
|
13294
|
+
import { randomBytes as randomBytes8 } from "crypto";
|
|
14631
13295
|
var RemoteAccountLoginCancelled = class extends Error {
|
|
14632
13296
|
constructor() {
|
|
14633
13297
|
super("Sign-in cancelled");
|
|
@@ -14693,7 +13357,7 @@ async function readJson3(res, url) {
|
|
|
14693
13357
|
async function startRemoteAccountLogin(opts) {
|
|
14694
13358
|
if (opts.signal?.aborted) throw new RemoteAccountLoginCancelled();
|
|
14695
13359
|
const origin = (opts.origin ?? remoteAccountHttpOrigin()).replace(/\/+$/, "");
|
|
14696
|
-
const state =
|
|
13360
|
+
const state = randomBytes8(16).toString("hex");
|
|
14697
13361
|
const timeoutMs = opts.timeoutMs ?? 5 * 6e4;
|
|
14698
13362
|
const pollIntervalMs = opts.pollIntervalMs ?? 400;
|
|
14699
13363
|
const linkToken = opts.linkToken?.trim() ?? "";
|
|
@@ -14831,7 +13495,7 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
14831
13495
|
await continueOnReply(threadId, prompt);
|
|
14832
13496
|
return;
|
|
14833
13497
|
}
|
|
14834
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
13498
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-7FVDBE7Y.js");
|
|
14835
13499
|
await getOrchestrator2().send(threadId, prompt);
|
|
14836
13500
|
} catch {
|
|
14837
13501
|
}
|
|
@@ -16115,6 +14779,7 @@ export {
|
|
|
16115
14779
|
stripBrightsyNdjsonNoise,
|
|
16116
14780
|
stripNestedElectronEnv,
|
|
16117
14781
|
submitPrStack,
|
|
14782
|
+
subtractUsage,
|
|
16118
14783
|
suggestSlug,
|
|
16119
14784
|
sumUsageList,
|
|
16120
14785
|
summarizeConversation,
|