@straiffi/archon 1.4.6 → 1.4.8

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.
Files changed (147) hide show
  1. package/dist/client/assets/GitHubActionsDialog-ln1X9xDf.js +1 -0
  2. package/dist/client/assets/TestsDialog-Cp3Da2W0.js +5 -0
  3. package/dist/client/assets/{arc-4YfDy8uK.js → arc-CnuHRbeq.js} +1 -1
  4. package/dist/client/assets/architecture-7EHR7CIX-tSrokdFP.js +1 -0
  5. package/dist/client/assets/{architectureDiagram-3BPJPVTR-B0ybRTUn.js → architectureDiagram-3BPJPVTR-CFEtd9pl.js} +1 -1
  6. package/dist/client/assets/{blockDiagram-GPEHLZMM-DrrQouTh.js → blockDiagram-GPEHLZMM-9UsBOlSn.js} +1 -1
  7. package/dist/client/assets/{c4Diagram-AAUBKEIU-BJdqjAbL.js → c4Diagram-AAUBKEIU--TsvWn6X.js} +1 -1
  8. package/dist/client/assets/channel-BjRPoR7Z.js +1 -0
  9. package/dist/client/assets/{chunk-2J33WTMH-Dh6TqsDY.js → chunk-2J33WTMH-B3FphDWz.js} +1 -1
  10. package/dist/client/assets/{chunk-3OPIFGDE-BI6L9trS.js → chunk-3OPIFGDE-CLbUIPKK.js} +1 -1
  11. package/dist/client/assets/{chunk-4BX2VUAB-B3mVWXS4.js → chunk-4BX2VUAB-_WBpCe0m.js} +1 -1
  12. package/dist/client/assets/{chunk-4EGX6M5U-DTB6Um9X.js → chunk-4EGX6M5U-BBQHxMeA.js} +1 -1
  13. package/dist/client/assets/{chunk-55IACEB6-BB-0ajVZ.js → chunk-55IACEB6-B-d-YzxI.js} +1 -1
  14. package/dist/client/assets/{chunk-5DO6E6H7-DtX6GYFY.js → chunk-5DO6E6H7-BA-oSVa-.js} +1 -1
  15. package/dist/client/assets/{chunk-5ZQYHXKU-mt5W2CGF.js → chunk-5ZQYHXKU-CUHKY5eH.js} +1 -1
  16. package/dist/client/assets/{chunk-727SXJPM-D7IvpFAq.js → chunk-727SXJPM-DfKJ4Ud0.js} +1 -1
  17. package/dist/client/assets/{chunk-AQP2D5EJ-CjusndWN.js → chunk-AQP2D5EJ-B0oLVoZs.js} +1 -1
  18. package/dist/client/assets/{chunk-BR22UD5L-CkJbcXZG.js → chunk-BR22UD5L-prWX503j.js} +1 -1
  19. package/dist/client/assets/{chunk-BSJP7CBP-X4eyoSqE.js → chunk-BSJP7CBP-CNtf2uaW.js} +1 -1
  20. package/dist/client/assets/{chunk-CSCIHK7Q-BmXd8_5X.js → chunk-CSCIHK7Q-Czl8GWBW.js} +2 -2
  21. package/dist/client/assets/{chunk-FHYWG6QK-Cb5GzaCb.js → chunk-FHYWG6QK-BQ8n0J0S.js} +1 -1
  22. package/dist/client/assets/{chunk-FMBD7UC4-mGY2D2YB.js → chunk-FMBD7UC4-CWQN4V4s.js} +1 -1
  23. package/dist/client/assets/{chunk-KSCS5N6A-DJIqk8ji.js → chunk-KSCS5N6A-pOOnuun_.js} +1 -1
  24. package/dist/client/assets/{chunk-L5ZTLDWV-CRVFCsvJ.js → chunk-L5ZTLDWV-DzRVeTqf.js} +1 -1
  25. package/dist/client/assets/{chunk-LZXEDZCA-CRla8IXQ.js → chunk-LZXEDZCA-MVhzvLVp.js} +2 -2
  26. package/dist/client/assets/{chunk-MPE355IW-WT7jTCZh.js → chunk-MPE355IW-gHN8AuU_.js} +1 -1
  27. package/dist/client/assets/{chunk-MZUSXYTE-CClXfOSj.js → chunk-MZUSXYTE-Clzaz1kK.js} +1 -1
  28. package/dist/client/assets/{chunk-N66VUXT2-Bpe4yprw.js → chunk-N66VUXT2-CkkAOkdt.js} +1 -1
  29. package/dist/client/assets/{chunk-ND2GUHAM-BYAb7NsA.js → chunk-ND2GUHAM-BeNiy3nE.js} +1 -1
  30. package/dist/client/assets/{chunk-NZK2D7GU-DUI0mwZm.js → chunk-NZK2D7GU-CnENvQ9d.js} +1 -1
  31. package/dist/client/assets/{chunk-O5CBEL6O-BX5VWTmZ.js → chunk-O5CBEL6O-CkqDMX_f.js} +1 -1
  32. package/dist/client/assets/{chunk-PUPMXCY4-Dh45WL5s.js → chunk-PUPMXCY4-DAdQFv8j.js} +1 -1
  33. package/dist/client/assets/chunk-QZHKN3VN-biOr9EeQ.js +1 -0
  34. package/dist/client/assets/{chunk-UIBZB4QT-Cp2xSKlH.js → chunk-UIBZB4QT-BRR84OYN.js} +1 -1
  35. package/dist/client/assets/{chunk-WCWK7LTN-BOt5FFIu.js → chunk-WCWK7LTN-CQU7Fs_W.js} +1 -1
  36. package/dist/client/assets/chunk-WU5MYG2G-sJ18xVVM.js +1 -0
  37. package/dist/client/assets/{chunk-XPW4576I-YiT9fJFY.js → chunk-XPW4576I-DafcxlUi.js} +1 -1
  38. package/dist/client/assets/classDiagram-4FO5ZUOK-BjzBLL5f.js +1 -0
  39. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-BDLW6NLv.js +1 -0
  40. package/dist/client/assets/{cose-bilkent-S5V4N54A-CtC2zeMr.js → cose-bilkent-S5V4N54A-DhJHP9hr.js} +1 -1
  41. package/dist/client/assets/{dagre-BM42HDAG-CqGwgHpm.js → dagre-BM42HDAG-BWnmF2aL.js} +1 -1
  42. package/dist/client/assets/{dagre-5AiD43Uu.js → dagre-CdwzCXQr.js} +1 -1
  43. package/dist/client/assets/{diagram-2AECGRRQ-W2M_p8Yv.js → diagram-2AECGRRQ-CASRQI5Y.js} +1 -1
  44. package/dist/client/assets/{diagram-5GNKFQAL-Cl9Hh0jb.js → diagram-5GNKFQAL-CY8fXRrV.js} +1 -1
  45. package/dist/client/assets/{diagram-KO2AKTUF-Cn0-x5HI.js → diagram-KO2AKTUF-vtm-gqIm.js} +1 -1
  46. package/dist/client/assets/{diagram-LMA3HP47-BH2_JbrM.js → diagram-LMA3HP47-Ck-iA5q1.js} +1 -1
  47. package/dist/client/assets/{diagram-OG6HWLK6-DuSmDdz3.js → diagram-OG6HWLK6-ALBEY2Nn.js} +1 -1
  48. package/dist/client/assets/{erDiagram-TEJ5UH35-DMHLFeQs.js → erDiagram-TEJ5UH35-BAHPzYR7.js} +1 -1
  49. package/dist/client/assets/eventmodeling-FCH6USID-DapHLQIh.js +1 -0
  50. package/dist/client/assets/{flowDiagram-I6XJVG4X-G6q9UKR3.js → flowDiagram-I6XJVG4X-pUYj1KCo.js} +1 -1
  51. package/dist/client/assets/{ganttDiagram-6RSMTGT7-hhWmmnFi.js → ganttDiagram-6RSMTGT7-zw-EKZcX.js} +1 -1
  52. package/dist/client/assets/gitGraph-WXDBUCRP-BTBN_hfb.js +1 -0
  53. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CvmZJ3zf.js → gitGraphDiagram-PVQCEYII-DmPxQKXO.js} +1 -1
  54. package/dist/client/assets/index-BokL1E46.css +2 -0
  55. package/dist/client/assets/index-WeECH1eZ.js +260 -0
  56. package/dist/client/assets/info-J43DQDTF-CgiMJYKF.js +1 -0
  57. package/dist/client/assets/{infoDiagram-5YYISTIA-BIes94lQ.js → infoDiagram-5YYISTIA-D0zuFK8l.js} +1 -1
  58. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-1sUDbjPm.js → ishikawaDiagram-YF4QCWOH-BoNe_Ie6.js} +1 -1
  59. package/dist/client/assets/{journeyDiagram-JHISSGLW-BEEP6Na-.js → journeyDiagram-JHISSGLW-ByAP1uL0.js} +1 -1
  60. package/dist/client/assets/{kanban-definition-UN3LZRKU-CPFego8B.js → kanban-definition-UN3LZRKU-CGXOqdl0.js} +1 -1
  61. package/dist/client/assets/{line-BdjuNfVr.js → line-BZ3JWBtG.js} +1 -1
  62. package/dist/client/assets/{linear-BYVhmsFj.js → linear-DFIW-KnM.js} +1 -1
  63. package/dist/client/assets/{mermaid-parser.core-ep6OPAQm.js → mermaid-parser.core-DuHIifHL.js} +2 -2
  64. package/dist/client/assets/{mermaid.core-CgiGEZhy.js → mermaid.core-Co8EqKSU.js} +3 -3
  65. package/dist/client/assets/{mindmap-definition-RKZ34NQL-0hOE50-8.js → mindmap-definition-RKZ34NQL-Cv3-5Q_b.js} +1 -1
  66. package/dist/client/assets/{ordinal-DIg8h6NI.js → ordinal-C1FS1Sfe.js} +1 -1
  67. package/dist/client/assets/packet-YPE3B663-Bnwqjena.js +1 -0
  68. package/dist/client/assets/pie-LRSECV5Y-Ctj0ef-4.js +1 -0
  69. package/dist/client/assets/{pieDiagram-4H26LBE5-BEgccEEE.js → pieDiagram-4H26LBE5-DziVyTJT.js} +1 -1
  70. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-CvDyAdJI.js → quadrantDiagram-W4KKPZXB-DjNtV4An.js} +1 -1
  71. package/dist/client/assets/radar-GUYGQ44K-DE0H_saz.js +1 -0
  72. package/dist/client/assets/{requirementDiagram-4Y6WPE33-DSmztQBO.js → requirementDiagram-4Y6WPE33-Cs3npD-z.js} +1 -1
  73. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-DgDUHUAw.js → sankeyDiagram-5OEKKPKP-DxepDJwc.js} +1 -1
  74. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BYaYgN0W.js → sequenceDiagram-3UESZ5HK-DIPg-NEj.js} +1 -1
  75. package/dist/client/assets/{stateDiagram-AJRCARHV-CqU_4LpV.js → stateDiagram-AJRCARHV-DUA-Ko9b.js} +1 -1
  76. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-D6OlcyEp.js +1 -0
  77. package/dist/client/assets/{timeline-definition-PNZ67QCA-BTYLNo8J.js → timeline-definition-PNZ67QCA-wvi8pREg.js} +1 -1
  78. package/dist/client/assets/treeView-BLDUP644-CSAZL2OH.js +1 -0
  79. package/dist/client/assets/treemap-LRROVOQU-tYm4XKfP.js +1 -0
  80. package/dist/client/assets/{vennDiagram-CIIHVFJN-C-MV8-cy.js → vennDiagram-CIIHVFJN-CabtIweB.js} +1 -1
  81. package/dist/client/assets/wardley-L42UT6IY-DdiEFppm.js +1 -0
  82. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-BOZ5g5Fn.js → wardleyDiagram-YWT4CUSO-BiOWydTY.js} +1 -1
  83. package/dist/client/assets/{xychartDiagram-2RQKCTM6-6wYwr3vn.js → xychartDiagram-2RQKCTM6-sWfRgDRl.js} +1 -1
  84. package/dist/client/index.html +2 -2
  85. package/dist/server/db.js +114 -9
  86. package/dist/server/db.js.map +1 -1
  87. package/dist/server/index.js +409 -5
  88. package/dist/server/index.js.map +1 -1
  89. package/dist/server/lib/attachments.js +36 -26
  90. package/dist/server/lib/attachments.js.map +1 -1
  91. package/dist/server/lib/chatOperations.js +6 -0
  92. package/dist/server/lib/chatOperations.js.map +1 -1
  93. package/dist/server/lib/chats.js +2 -2
  94. package/dist/server/lib/chats.js.map +1 -1
  95. package/dist/server/lib/integrations/assignedIssues.js +21 -9
  96. package/dist/server/lib/integrations/assignedIssues.js.map +1 -1
  97. package/dist/server/lib/integrations/github-issues.js +107 -12
  98. package/dist/server/lib/integrations/github-issues.js.map +1 -1
  99. package/dist/server/lib/integrations/github.js +10 -0
  100. package/dist/server/lib/integrations/github.js.map +1 -1
  101. package/dist/server/lib/integrations/githubActions.js +266 -0
  102. package/dist/server/lib/integrations/githubActions.js.map +1 -0
  103. package/dist/server/lib/integrations/githubActionsSessions.js +168 -0
  104. package/dist/server/lib/integrations/githubActionsSessions.js.map +1 -0
  105. package/dist/server/lib/integrations/importedIssueImages.js +210 -0
  106. package/dist/server/lib/integrations/importedIssueImages.js.map +1 -0
  107. package/dist/server/lib/integrations/jira.js +132 -21
  108. package/dist/server/lib/integrations/jira.js.map +1 -1
  109. package/dist/server/lib/specOperations.js +11 -0
  110. package/dist/server/lib/specOperations.js.map +1 -1
  111. package/dist/server/lib/specs.js +2 -7
  112. package/dist/server/lib/specs.js.map +1 -1
  113. package/dist/server/lib/ticketWorkflowOperations.js +6 -0
  114. package/dist/server/lib/ticketWorkflowOperations.js.map +1 -1
  115. package/dist/server/lib/trackedExternalIssues.js +33 -0
  116. package/dist/server/lib/trackedExternalIssues.js.map +1 -0
  117. package/package.json +2 -1
  118. package/dist/client/assets/TestsDialog-Vy9cdwzA.js +0 -5
  119. package/dist/client/assets/architecture-7EHR7CIX-B0ldQ5d_.js +0 -1
  120. package/dist/client/assets/channel-B3iXJSit.js +0 -1
  121. package/dist/client/assets/chunk-QZHKN3VN-U_l6-b5-.js +0 -1
  122. package/dist/client/assets/chunk-WU5MYG2G-xHcJhj1C.js +0 -1
  123. package/dist/client/assets/classDiagram-4FO5ZUOK-BvskNpH1.js +0 -1
  124. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-B1-Afdak.js +0 -1
  125. package/dist/client/assets/eventmodeling-FCH6USID-BczyucFk.js +0 -1
  126. package/dist/client/assets/gitGraph-WXDBUCRP-x5mlGyzr.js +0 -1
  127. package/dist/client/assets/index-CT3aYWb0.js +0 -260
  128. package/dist/client/assets/index-D6gWjd6J.css +0 -2
  129. package/dist/client/assets/info-J43DQDTF-BsM_Qb70.js +0 -1
  130. package/dist/client/assets/packet-YPE3B663-DeVIMHV5.js +0 -1
  131. package/dist/client/assets/pie-LRSECV5Y-ByRF4_cj.js +0 -1
  132. package/dist/client/assets/radar-GUYGQ44K-XEreg8aR.js +0 -1
  133. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-C_J0aecM.js +0 -1
  134. package/dist/client/assets/treeView-BLDUP644-D85P2Ji3.js +0 -1
  135. package/dist/client/assets/treemap-LRROVOQU-BnXG5utQ.js +0 -1
  136. package/dist/client/assets/wardley-L42UT6IY-Ct9qHtJP.js +0 -1
  137. /package/dist/client/assets/{array-DOVTz2Mq.js → array-B57E4cSs.js} +0 -0
  138. /package/dist/client/assets/{chunk-NNHCCRGN-C4ci-Gd6.js → chunk-NNHCCRGN-DRSAMe5H.js} +0 -0
  139. /package/dist/client/assets/{cytoscape.esm-DnmRtSj9.js → cytoscape.esm-CVYSm6AJ.js} +0 -0
  140. /package/dist/client/assets/{defaultLocale-Ck2Xxk-C.js → defaultLocale-6WHJ3B09.js} +0 -0
  141. /package/dist/client/assets/{dist-BQjeO--f.js → dist-By2zKKTF.js} +0 -0
  142. /package/dist/client/assets/{graphlib-CMVU3iJd.js → graphlib-B0DVs0bw.js} +0 -0
  143. /package/dist/client/assets/{init-Bft5Ffpj.js → init-DiYCGS1R.js} +0 -0
  144. /package/dist/client/assets/{katex-Bpz5kWH9.js → katex-bgJu_0Fy.js} +0 -0
  145. /package/dist/client/assets/{path-DfRbCp9y.js → path-DL_QDy2X.js} +0 -0
  146. /package/dist/client/assets/{rough.esm-DCvqWubr.js → rough.esm-Bbn_-PMU.js} +0 -0
  147. /package/dist/client/assets/{src-BcaL9nCM.js → src-Bi_byJUe.js} +0 -0
@@ -10,7 +10,7 @@ import { adjectives, animals, uniqueNamesGenerator } from 'unique-names-generato
10
10
  import db from './db.js';
11
11
  import { deleteChatSession, getChatSession, listChatSessions, updateChatSessionMode } from './lib/chats.js';
12
12
  import { deleteSpecSession, getCreatedTicketIdsForSnapshot, getSpecSession, getSpecSnapshot, listSpecSessions } from './lib/specs.js';
13
- import { createChatAttachment, createSpecAttachment, deleteChatAttachment, deleteSpecSessionAttachment, getChatAttachmentById, getChatAttachmentForSession, getChatAttachmentLinkedMessageId, getSpecAttachmentForSession, getSpecAttachmentLinkedMessageId, MAX_CHAT_ATTACHMENTS_PER_TURN, MAX_CHAT_ATTACHMENT_SIZE_BYTES, MAX_SPEC_ATTACHMENTS_PER_TURN, MAX_SPEC_ATTACHMENT_SIZE_BYTES, readChatAttachmentContent, readSpecAttachmentContent, validateImageAttachment, } from './lib/attachments.js';
13
+ import { createChatAttachment, createSpecAttachment, getAttachmentById, deleteChatAttachment, deleteSpecSessionAttachment, getChatAttachmentForSession, getChatAttachmentLinkedMessageId, getSpecAttachmentForSession, getSpecAttachmentLinkedMessageId, MAX_CHAT_ATTACHMENTS_PER_TURN, MAX_CHAT_ATTACHMENT_SIZE_BYTES, MAX_SPEC_ATTACHMENTS_PER_TURN, MAX_SPEC_ATTACHMENT_SIZE_BYTES, readChatAttachmentContent, readSpecAttachmentContent, validateImageAttachment, } from './lib/attachments.js';
14
14
  import { createProjectChatSession, resolveChatMode, resolveChatTool, stopProjectChatSessionResponse, submitProjectChatSessionMessage, } from './lib/chatOperations.js';
15
15
  import { createProjectSpecSession, deleteProjectSpecSession, stopProjectSpecSessionResponse, submitProjectSpecSessionMessage, updateProjectSpecSessionMode, } from './lib/specOperations.js';
16
16
  import { buildProjectSpecReviewRevisionDraft, getProjectSpecReviewFindings, startProjectSpecReview, stopProjectSpecReview, } from './lib/specReviewOperations.js';
@@ -49,8 +49,11 @@ import { shutdownRealtimeServer } from './lib/shutdown.js';
49
49
  import { resolveClientBuildPaths, shouldServeClientApp, STATIC_CLIENT_DIRECTORY_OPTIONS, STATIC_CLIENT_INDEX_OPTIONS, } from './lib/staticClient.js';
50
50
  import { deleteIntegrationConnection, getGitHubConnectionConfig, getJiraConnectionConfig, listIntegrationConnections, upsertIntegrationConnection, } from './lib/integrations/index.js';
51
51
  import { createGitHubPullRequest, createOrFindGitHubPullRequest, findOpenGitHubPullRequest, findOpenGitHubPullRequestByHeadBranch, getGitHubPullRequestComments, getGitHubPullRequestReviews, getGitHubPullRequest, GitHubApiError, hasGitHubRemoteBranch, resolveGitHubRepository, validateGitHubConnection, } from './lib/integrations/github.js';
52
- import { importJiraIssue, JiraApiError, validateJiraConnection } from './lib/integrations/jira.js';
53
- import { importGitHubIssue } from './lib/integrations/github-issues.js';
52
+ import { importJiraIssue, JiraApiError, prepareJiraIssueImport, validateJiraConnection } from './lib/integrations/jira.js';
53
+ import { importGitHubIssue, prepareGitHubIssueImport } from './lib/integrations/github-issues.js';
54
+ import { buildImportedIssueSessionTitle, importProviderHostedIssueImages } from './lib/integrations/importedIssueImages.js';
55
+ import { dispatchGitHubWorkflow, getGitHubWorkflowDispatchConfig, getGitHubWorkflowRun, getGitHubWorkflowRunJobs, listGitHubWorkflows, listGitHubWorkflowRuns, } from './lib/integrations/githubActions.js';
56
+ import { clearAllGitHubActionsSessions, getActiveTrackedRunCount, getTrackedGitHubActionsRuns, trackGitHubActionsRun, } from './lib/integrations/githubActionsSessions.js';
54
57
  import { PlanningReferenceError, resolveExternalPlanningDescription } from './lib/integrations/planning.js';
55
58
  import { refreshIfNeeded as refreshAssignedIssues, clearCachedAssignedIssues } from './lib/integrations/assignedIssuesCache.js';
56
59
  import { buildCommitMessagePrompt, normalizeGeneratedCommitMessage } from './lib/commitMessage.js';
@@ -2066,7 +2069,7 @@ app.post('/projects/:id/chat-sessions/:sessionId/attachments', (req, res) => {
2066
2069
  }
2067
2070
  catch (uploadError) {
2068
2071
  for (const attachmentId of createdAttachmentIds) {
2069
- const attachment = getChatAttachmentById(attachmentId);
2072
+ const attachment = getAttachmentById(attachmentId);
2070
2073
  if (!attachment) {
2071
2074
  continue;
2072
2075
  }
@@ -2351,7 +2354,7 @@ app.post('/projects/:id/spec-sessions/:sessionId/attachments', (req, res) => {
2351
2354
  }
2352
2355
  catch (uploadError) {
2353
2356
  for (const attachmentId of createdAttachmentIds) {
2354
- const attachment = getChatAttachmentById(attachmentId);
2357
+ const attachment = getAttachmentById(attachmentId);
2355
2358
  if (!attachment) {
2356
2359
  continue;
2357
2360
  }
@@ -3578,6 +3581,76 @@ app.get('/integrations', (req, res) => {
3578
3581
  }
3579
3582
  return res.json({ connections: listIntegrationConnections(result.project.id) });
3580
3583
  });
3584
+ const NOOP_BROADCASTER = {
3585
+ emit: () => undefined,
3586
+ };
3587
+ const buildSpecImportSessionPayload = (payload, draft) => {
3588
+ return {
3589
+ tool: payload?.tool,
3590
+ mode: payload?.mode,
3591
+ model: payload?.model,
3592
+ variant: payload?.variant,
3593
+ skills: payload?.skills,
3594
+ title: buildImportedIssueSessionTitle(draft),
3595
+ external_source: draft.external_source,
3596
+ };
3597
+ };
3598
+ const prepareSpecImportResponse = async ({ projectId, payload, preparedDraft, importOptions, broadcaster, }) => {
3599
+ const draft = {
3600
+ title: preparedDraft.title,
3601
+ description: preparedDraft.description,
3602
+ external_source: preparedDraft.external_source,
3603
+ };
3604
+ const imported = await importProviderHostedIssueImages({
3605
+ ...importOptions,
3606
+ draft,
3607
+ imageCandidates: preparedDraft.image_candidates,
3608
+ });
3609
+ if (imported.importedImages.length === 0) {
3610
+ return {
3611
+ draft,
3612
+ prompt_text: imported.promptText,
3613
+ external_source: draft.external_source,
3614
+ spec_session: null,
3615
+ imported_attachments: [],
3616
+ import_notice: imported.notice,
3617
+ };
3618
+ }
3619
+ const createResult = createProjectSpecSession({
3620
+ projectId,
3621
+ payload: buildSpecImportSessionPayload(payload, draft),
3622
+ broadcaster: NOOP_BROADCASTER,
3623
+ });
3624
+ if (!createResult.ok) {
3625
+ throw new HttpError(createResult.status, createResult.error);
3626
+ }
3627
+ if (!createResult.value) {
3628
+ throw new Error('Unable to create the imported spec session.');
3629
+ }
3630
+ const specSession = createResult.value;
3631
+ try {
3632
+ const importedAttachments = imported.importedImages.map(image => createSpecAttachment({
3633
+ specSessionId: specSession.id,
3634
+ originalName: image.originalName,
3635
+ mimeType: image.mimeType,
3636
+ buffer: image.buffer,
3637
+ projectId,
3638
+ }));
3639
+ broadcaster.emit('spec-session:created', specSession);
3640
+ return {
3641
+ draft,
3642
+ prompt_text: imported.promptText,
3643
+ external_source: draft.external_source,
3644
+ spec_session: specSession,
3645
+ imported_attachments: importedAttachments,
3646
+ import_notice: imported.notice,
3647
+ };
3648
+ }
3649
+ catch (error) {
3650
+ deleteSpecSession(specSession.id);
3651
+ throw error;
3652
+ }
3653
+ };
3581
3654
  app.post('/integrations/github/connect', async (req, res) => {
3582
3655
  const result = getRequiredProject(req);
3583
3656
  if ('error' in result) {
@@ -3749,6 +3822,77 @@ app.post('/integrations/jira/resolve/issue', async (req, res) => {
3749
3822
  return res.status(500).json({ error: getErrorMessage(error) || 'Unable to import the Jira issue.' });
3750
3823
  }
3751
3824
  });
3825
+ app.post('/integrations/jira/prepare-spec-import', async (req, res) => {
3826
+ const result = getRequiredProject(req);
3827
+ if ('error' in result) {
3828
+ return res.status(400).json({ error: result.error });
3829
+ }
3830
+ const jiraConfig = getJiraConnectionConfig(result.project.id);
3831
+ if (!jiraConfig) {
3832
+ return res.status(400).json({ error: 'Jira is not connected for this project.' });
3833
+ }
3834
+ const issue = asTrimmedString(req.body?.issue);
3835
+ if (!issue) {
3836
+ return res.status(400).json({ error: 'issue is required' });
3837
+ }
3838
+ try {
3839
+ const prepared = await prepareJiraIssueImport(jiraConfig, issue);
3840
+ const response = await prepareSpecImportResponse({
3841
+ projectId: result.project.id,
3842
+ payload: req.body,
3843
+ preparedDraft: prepared,
3844
+ importOptions: { provider: 'jira', config: jiraConfig },
3845
+ broadcaster: io,
3846
+ });
3847
+ return res.json(response);
3848
+ }
3849
+ catch (error) {
3850
+ console.warn(`[integrations] jira spec import failed project=${result.project.id} issue=${issue}`, error);
3851
+ if (error instanceof JiraApiError) {
3852
+ return res.status(error.status).json({ error: error.publicMessage });
3853
+ }
3854
+ if (error instanceof HttpError) {
3855
+ return res.status(error.status).json({ error: error.message });
3856
+ }
3857
+ return res.status(500).json({ error: getErrorMessage(error) || 'Unable to prepare the Jira issue for spec import.' });
3858
+ }
3859
+ });
3860
+ app.post('/integrations/jira/prepare/spec-import', async (req, res) => {
3861
+ const result = getRequiredProject(req);
3862
+ if ('error' in result) {
3863
+ return res.status(400).json({ error: result.error });
3864
+ }
3865
+ const jiraConfig = getJiraConnectionConfig(result.project.id);
3866
+ if (!jiraConfig) {
3867
+ return res.status(400).json({ error: 'Jira is not connected for this project.' });
3868
+ }
3869
+ const issue = asTrimmedString(req.body?.issue);
3870
+ if (!issue) {
3871
+ return res.status(400).json({ error: 'issue is required' });
3872
+ }
3873
+ console.info(`[integrations] jira prepare spec import project=${result.project.id} issue=${issue}`);
3874
+ try {
3875
+ const prepared = await prepareJiraIssueImport(jiraConfig, issue);
3876
+ const response = await prepareSpecImportResponse({
3877
+ projectId: result.project.id,
3878
+ payload: req.body,
3879
+ preparedDraft: prepared,
3880
+ importOptions: { provider: 'jira', config: jiraConfig },
3881
+ broadcaster: io,
3882
+ });
3883
+ return res.json(response);
3884
+ }
3885
+ catch (error) {
3886
+ console.warn(`[integrations] jira prepare spec import failed project=${result.project.id} issue=${issue}`, error);
3887
+ if (error instanceof JiraApiError) {
3888
+ return res.status(error.status).json({ error: error.publicMessage });
3889
+ }
3890
+ if (error instanceof HttpError) {
3891
+ return res.status(error.status).json({ error: error.message });
3892
+ }
3893
+ return res.status(500).json({ error: getErrorMessage(error) || 'Unable to prepare the Jira issue for spec import.' });
3894
+ }
3895
+ });
3752
3896
  app.post('/integrations/github/resolve/issue', async (req, res) => {
3753
3897
  const result = getRequiredProject(req);
3754
3898
  if ('error' in result) {
@@ -3778,6 +3922,265 @@ app.post('/integrations/github/resolve/issue', async (req, res) => {
3778
3922
  return res.status(500).json({ error: getErrorMessage(error) || 'Unable to import the GitHub issue.' });
3779
3923
  }
3780
3924
  });
3925
+ app.post('/integrations/github/prepare-spec-import', async (req, res) => {
3926
+ const result = getRequiredProject(req);
3927
+ if ('error' in result) {
3928
+ return res.status(400).json({ error: result.error });
3929
+ }
3930
+ const githubConfig = getGitHubConnectionConfig(result.project.id);
3931
+ if (!githubConfig) {
3932
+ return res.status(400).json({ error: 'GitHub is not connected for this project.' });
3933
+ }
3934
+ const issue = asTrimmedString(req.body?.issue);
3935
+ if (!issue) {
3936
+ return res.status(400).json({ error: 'issue is required' });
3937
+ }
3938
+ try {
3939
+ const prepared = await prepareGitHubIssueImport(githubConfig, issue);
3940
+ const response = await prepareSpecImportResponse({
3941
+ projectId: result.project.id,
3942
+ payload: req.body,
3943
+ preparedDraft: prepared,
3944
+ importOptions: { provider: 'github', config: githubConfig },
3945
+ broadcaster: io,
3946
+ });
3947
+ return res.json(response);
3948
+ }
3949
+ catch (error) {
3950
+ console.warn(`[integrations] github spec import failed project=${result.project.id} issue=${issue}`, error);
3951
+ if (error instanceof GitHubApiError) {
3952
+ return res.status(error.status).json({ error: error.publicMessage });
3953
+ }
3954
+ if (error instanceof HttpError) {
3955
+ return res.status(error.status).json({ error: error.message });
3956
+ }
3957
+ return res.status(500).json({ error: getErrorMessage(error) || 'Unable to prepare the GitHub issue for spec import.' });
3958
+ }
3959
+ });
3960
+ app.post('/integrations/github/prepare/spec-import', async (req, res) => {
3961
+ const result = getRequiredProject(req);
3962
+ if ('error' in result) {
3963
+ return res.status(400).json({ error: result.error });
3964
+ }
3965
+ const githubConfig = getGitHubConnectionConfig(result.project.id);
3966
+ if (!githubConfig) {
3967
+ return res.status(400).json({ error: 'GitHub is not connected for this project.' });
3968
+ }
3969
+ const issue = asTrimmedString(req.body?.issue);
3970
+ if (!issue) {
3971
+ return res.status(400).json({ error: 'issue is required' });
3972
+ }
3973
+ console.info(`[integrations] github prepare spec import project=${result.project.id} issue=${issue}`);
3974
+ try {
3975
+ const prepared = await prepareGitHubIssueImport(githubConfig, issue);
3976
+ const response = await prepareSpecImportResponse({
3977
+ projectId: result.project.id,
3978
+ payload: req.body,
3979
+ preparedDraft: prepared,
3980
+ importOptions: { provider: 'github', config: githubConfig },
3981
+ broadcaster: io,
3982
+ });
3983
+ return res.json(response);
3984
+ }
3985
+ catch (error) {
3986
+ console.warn(`[integrations] github prepare spec import failed project=${result.project.id} issue=${issue}`, error);
3987
+ if (error instanceof GitHubApiError) {
3988
+ return res.status(error.status).json({ error: error.publicMessage });
3989
+ }
3990
+ if (error instanceof HttpError) {
3991
+ return res.status(error.status).json({ error: error.message });
3992
+ }
3993
+ return res.status(500).json({ error: getErrorMessage(error) || 'Unable to prepare the GitHub issue for spec import.' });
3994
+ }
3995
+ });
3996
+ app.get('/integrations/github/actions/workflows', async (req, res) => {
3997
+ const result = getRequiredProject(req);
3998
+ if ('error' in result) {
3999
+ return res.status(400).json({ error: result.error });
4000
+ }
4001
+ const githubConfig = getGitHubConnectionConfig(result.project.id);
4002
+ if (!githubConfig) {
4003
+ return res.status(400).json({ error: 'GitHub is not connected for this project.' });
4004
+ }
4005
+ try {
4006
+ const workflows = await listGitHubWorkflows(githubConfig);
4007
+ return res.json({ workflows });
4008
+ }
4009
+ catch (error) {
4010
+ console.warn(`[integrations] github actions workflows failed project=${result.project.id}`, error);
4011
+ if (error instanceof GitHubApiError) {
4012
+ return res.status(error.status).json({ error: error.publicMessage });
4013
+ }
4014
+ return res.status(502).json({ error: 'Unable to load GitHub Actions workflows right now.' });
4015
+ }
4016
+ });
4017
+ app.get('/integrations/github/actions/workflows/:id/inputs', async (req, res) => {
4018
+ const result = getRequiredProject(req);
4019
+ if ('error' in result) {
4020
+ return res.status(400).json({ error: result.error });
4021
+ }
4022
+ const githubConfig = getGitHubConnectionConfig(result.project.id);
4023
+ if (!githubConfig) {
4024
+ return res.status(400).json({ error: 'GitHub is not connected for this project.' });
4025
+ }
4026
+ const workflowIdParam = asTrimmedString(req.params.id);
4027
+ const workflowPath = asTrimmedString(req.query.path);
4028
+ if (!workflowPath) {
4029
+ return res.status(400).json({ error: 'path query parameter is required' });
4030
+ }
4031
+ try {
4032
+ const workflows = await listGitHubWorkflows(githubConfig);
4033
+ const workflow = workflows.find(candidate => String(candidate.id) === workflowIdParam);
4034
+ if (!workflow) {
4035
+ return res.status(404).json({ error: 'GitHub Actions workflow not found.' });
4036
+ }
4037
+ const dispatchConfig = await getGitHubWorkflowDispatchConfig(githubConfig, workflowPath);
4038
+ return res.json({ inputs: dispatchConfig.inputs, dispatchable: dispatchConfig.dispatchable });
4039
+ }
4040
+ catch (error) {
4041
+ console.warn(`[integrations] github actions inputs failed project=${result.project.id} workflow=${workflowIdParam}`, error);
4042
+ if (error instanceof GitHubApiError) {
4043
+ return res.status(error.status).json({ error: error.publicMessage });
4044
+ }
4045
+ return res.status(502).json({ error: 'Unable to load the workflow inputs right now.' });
4046
+ }
4047
+ });
4048
+ app.post('/integrations/github/actions/workflows/:id/dispatch', async (req, res) => {
4049
+ const result = getRequiredProject(req);
4050
+ if ('error' in result) {
4051
+ return res.status(400).json({ error: result.error });
4052
+ }
4053
+ const githubConfig = getGitHubConnectionConfig(result.project.id);
4054
+ if (!githubConfig) {
4055
+ return res.status(400).json({ error: 'GitHub is not connected for this project.' });
4056
+ }
4057
+ const workflowIdParam = asTrimmedString(req.params.id);
4058
+ const workflowId = Number(workflowIdParam);
4059
+ if (!workflowId || !Number.isFinite(workflowId)) {
4060
+ return res.status(400).json({ error: 'workflow id must be a number' });
4061
+ }
4062
+ const ref = asTrimmedString(req.body?.ref);
4063
+ if (!ref) {
4064
+ return res.status(400).json({ error: 'ref is required' });
4065
+ }
4066
+ const rawInputs = req.body?.inputs;
4067
+ const inputs = {};
4068
+ if (rawInputs && typeof rawInputs === 'object' && !Array.isArray(rawInputs)) {
4069
+ for (const [key, value] of Object.entries(rawInputs)) {
4070
+ if (typeof value === 'string' || typeof value === 'boolean') {
4071
+ inputs[key] = value;
4072
+ }
4073
+ else if (typeof value === 'number') {
4074
+ inputs[key] = String(value);
4075
+ }
4076
+ }
4077
+ }
4078
+ console.info(`[integrations] github actions dispatch project=${result.project.id} workflow=${workflowId} ref=${ref}`);
4079
+ try {
4080
+ const dispatchedAt = Date.now();
4081
+ await dispatchGitHubWorkflow(githubConfig, workflowId, { ref, inputs });
4082
+ const trackedRun = trackGitHubActionsRun({
4083
+ io,
4084
+ projectId: result.project.id,
4085
+ workflowId,
4086
+ config: githubConfig,
4087
+ dispatchedAt,
4088
+ });
4089
+ return res.status(202).json({ tracking_key: trackedRun.tracking_key, dispatched: true });
4090
+ }
4091
+ catch (error) {
4092
+ console.warn(`[integrations] github actions dispatch failed project=${result.project.id} workflow=${workflowId}`, error);
4093
+ if (error instanceof GitHubApiError) {
4094
+ return res.status(error.status).json({ error: error.publicMessage });
4095
+ }
4096
+ return res.status(502).json({ error: 'Unable to dispatch the workflow right now.' });
4097
+ }
4098
+ });
4099
+ app.get('/integrations/github/actions/runs', async (req, res) => {
4100
+ const result = getRequiredProject(req);
4101
+ if ('error' in result) {
4102
+ return res.status(400).json({ error: result.error });
4103
+ }
4104
+ const githubConfig = getGitHubConnectionConfig(result.project.id);
4105
+ if (!githubConfig) {
4106
+ return res.status(400).json({ error: 'GitHub is not connected for this project.' });
4107
+ }
4108
+ try {
4109
+ const [recentRuns, trackedRuns] = await Promise.all([
4110
+ listGitHubWorkflowRuns(githubConfig).catch(error => {
4111
+ console.warn(`[integrations] github actions runs list failed project=${result.project.id}`, error);
4112
+ return [];
4113
+ }),
4114
+ Promise.resolve(getTrackedGitHubActionsRuns(result.project.id)),
4115
+ ]);
4116
+ const trackedByRunId = new Map();
4117
+ for (const tracked of trackedRuns) {
4118
+ if (tracked.run) {
4119
+ trackedByRunId.set(tracked.run.id, tracked);
4120
+ }
4121
+ }
4122
+ const recentById = new Map(recentRuns.map(run => [run.id, run]));
4123
+ const mergedRecent = recentRuns.map(run => trackedByRunId.get(run.id)?.run ?? run);
4124
+ const trackedOnly = trackedRuns
4125
+ .map(tracked => tracked.run)
4126
+ .filter((run) => Boolean(run) && !recentById.has(run.id));
4127
+ const seenRunIds = new Set();
4128
+ const runs = [...trackedOnly, ...mergedRecent].filter(run => {
4129
+ if (seenRunIds.has(run.id)) {
4130
+ return false;
4131
+ }
4132
+ seenRunIds.add(run.id);
4133
+ return true;
4134
+ });
4135
+ return res.json({ runs, tracked_runs: trackedRuns });
4136
+ }
4137
+ catch (error) {
4138
+ console.warn(`[integrations] github actions runs failed project=${result.project.id}`, error);
4139
+ if (error instanceof GitHubApiError) {
4140
+ return res.status(error.status).json({ error: error.publicMessage });
4141
+ }
4142
+ return res.status(502).json({ error: 'Unable to load GitHub Actions runs right now.' });
4143
+ }
4144
+ });
4145
+ app.get('/integrations/github/actions/active-count', (req, res) => {
4146
+ const result = getRequiredProject(req);
4147
+ if ('error' in result) {
4148
+ return res.status(400).json({ error: result.error });
4149
+ }
4150
+ const githubConfig = getGitHubConnectionConfig(result.project.id);
4151
+ if (!githubConfig) {
4152
+ return res.status(400).json({ error: 'GitHub is not connected for this project.' });
4153
+ }
4154
+ return res.json({ activeCount: getActiveTrackedRunCount(result.project.id) });
4155
+ });
4156
+ app.get('/integrations/github/actions/runs/:id', async (req, res) => {
4157
+ const result = getRequiredProject(req);
4158
+ if ('error' in result) {
4159
+ return res.status(400).json({ error: result.error });
4160
+ }
4161
+ const githubConfig = getGitHubConnectionConfig(result.project.id);
4162
+ if (!githubConfig) {
4163
+ return res.status(400).json({ error: 'GitHub is not connected for this project.' });
4164
+ }
4165
+ const runId = Number(asTrimmedString(req.params.id));
4166
+ if (!runId || !Number.isFinite(runId)) {
4167
+ return res.status(400).json({ error: 'run id must be a number' });
4168
+ }
4169
+ try {
4170
+ const [run, jobs] = await Promise.all([
4171
+ getGitHubWorkflowRun(githubConfig, runId),
4172
+ getGitHubWorkflowRunJobs(githubConfig, runId),
4173
+ ]);
4174
+ return res.json({ run: { ...run, jobs } });
4175
+ }
4176
+ catch (error) {
4177
+ console.warn(`[integrations] github actions run failed project=${result.project.id} run=${runId}`, error);
4178
+ if (error instanceof GitHubApiError) {
4179
+ return res.status(error.status).json({ error: error.publicMessage });
4180
+ }
4181
+ return res.status(502).json({ error: 'Unable to load the GitHub Actions run right now.' });
4182
+ }
4183
+ });
3781
4184
  const ASSIGNED_ISSUES_CACHE_TTL_MS = 60 * 1000;
3782
4185
  app.get('/integrations/assigned', async (req, res) => {
3783
4186
  const result = getRequiredProject(req);
@@ -6052,6 +6455,7 @@ export const startServer = ({ port = DEFAULT_PORT, host } = {}) => {
6052
6455
  export const shutdownServer = async (signal) => {
6053
6456
  await stopMobileAccess();
6054
6457
  await clearAllTestSessions(io);
6458
+ await clearAllGitHubActionsSessions();
6055
6459
  await shutdownRealtimeServer({
6056
6460
  signal,
6057
6461
  io,