specrails-desktop 2.20.1 → 2.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/client/dist/assets/{ActivityFeedPage-3WSqRYVx.js → ActivityFeedPage-roDyKGuv.js} +1 -1
- package/client/dist/assets/AgentBrowserCapture-BsXnWa4j.js +1 -0
- package/client/dist/assets/{AgentModeCodePane-Cnsea6eZ.js → AgentModeCodePane-o97EJvbJ.js} +2 -2
- package/client/dist/assets/AgentModeJobsPane-CLtpPxPM.js +1 -0
- package/client/dist/assets/AgentsPage-eNSo_ZcC.js +86 -0
- package/client/dist/assets/AnalyticsPage-cAJ60_Ex.js +1 -0
- package/client/dist/assets/{BarChart-C6If656N.js → BarChart-NLtK7xAY.js} +1 -1
- package/client/dist/assets/CodePage-B5rum8ZB.js +2 -0
- package/client/dist/assets/DesktopAnalyticsPage-DSuvG6Tq.js +1 -0
- package/client/dist/assets/DocsDialog-Bo38G3Yc.js +11 -0
- package/client/dist/assets/DocsPage-BNkiBBrC.js +11 -0
- package/client/dist/assets/{ExportDropdown-CezTK0EX.js → ExportDropdown-DCV8PN4-.js} +1 -1
- package/client/dist/assets/IntegrationsPage-BMJOzpPz.js +3 -0
- package/client/dist/assets/InteractiveJobComposer-aLduNs5R.js +19 -0
- package/client/dist/assets/JobDetailModal-kpkUWJIS.js +1 -0
- package/client/dist/assets/JobDetailPage-CFeqlrPU.js +1 -0
- package/client/dist/assets/JobsPage-fuwhXy2I.js +1 -0
- package/client/dist/assets/LoopBuilderPage-DP66322C.js +7 -0
- package/client/dist/assets/LoopPreviewModal-CpmBhE31.js +1 -0
- package/client/dist/assets/LoopsPage-CvQ18VUx.js +1 -0
- package/client/dist/assets/ProjectSettingsDialog-Deq4pwvq.js +1 -0
- package/client/dist/assets/TemplatePreviewModal-DziOj2Ct.js +1 -0
- package/client/dist/assets/agent-Csh4kP2S.js +1 -0
- package/client/dist/assets/agent-Cz_jgQX32.js +1 -0
- package/client/dist/assets/agent-D3vcIvaz2.js +1 -0
- package/client/dist/assets/agent-Dnbt0V4e.js +1 -0
- package/client/dist/assets/agent-HolR7QrZ.js +1 -0
- package/client/dist/assets/agent-Ka0rRWJy.js +1 -0
- package/client/dist/assets/agent-Te3yysMd.js +1 -0
- package/client/dist/assets/agent-vM4_iLDk2.js +1 -0
- package/client/dist/assets/{upload-CRtTj4Co.js → brain-kFJuEDNH.js} +1 -1
- package/client/dist/assets/{browser-DEEO-OSi.js → browser-BPkhILSJ.js} +1 -1
- package/client/dist/assets/{browser-BTivfZKE2.js → browser-BijVYG0Y2.js} +1 -1
- package/client/dist/assets/{browser-bpO3onlS.js → browser-BqLqJVVT.js} +1 -1
- package/client/dist/assets/{browser-DlSXtfu5.js → browser-Cc0iDqUi.js} +1 -1
- package/client/dist/assets/browser-CdS6O1Qb.js +1 -0
- package/client/dist/assets/{browser-BKP6hqQC2.js → browser-Cvg-QHvd2.js} +1 -1
- package/client/dist/assets/{browser-INCUyduh.js → browser-FRv8P02E.js} +1 -1
- package/client/dist/assets/{browser-DlKLfT612.js → browser-rWKj-RR_2.js} +1 -1
- package/client/dist/assets/code-BRcDlze52.js +1 -0
- package/client/dist/assets/code-BdMy3Ffq.js +1 -0
- package/client/dist/assets/code-CeQ0FZpC.js +1 -0
- package/client/dist/assets/code-CqCcRThR.js +1 -0
- package/client/dist/assets/code-DgeHz9CB.js +1 -0
- package/client/dist/assets/code-DzC1rMk12.js +1 -0
- package/client/dist/assets/code-UPVvmAKM2.js +1 -0
- package/client/dist/assets/code-thSEptMx.js +1 -0
- package/client/dist/assets/{common-QjVnq9XP2.js → common-BM3LFLCE2.js} +1 -1
- package/client/dist/assets/{common-CeQUwuHt2.js → common-Bwafujnc2.js} +1 -1
- package/client/dist/assets/{common-Bb64s3Bm.js → common-C-vU-j-I.js} +1 -1
- package/client/dist/assets/{common-KbQuTSGg.js → common-C5HKSbbz.js} +1 -1
- package/client/dist/assets/{common-BPQQkgJZ.js → common-CYaT2yHp.js} +1 -1
- package/client/dist/assets/{common-DPxotRK9.js → common-DhWTkocg.js} +1 -1
- package/client/dist/assets/{common-k5MTOHpn.js → common-DiPTT7V_2.js} +1 -1
- package/client/dist/assets/{common-C8ZD5upH2.js → common-DmqBPwZG.js} +1 -1
- package/client/dist/assets/{cssMode-DsArsSCT.js → cssMode-DBHbkBvI.js} +1 -1
- package/client/dist/assets/dashboard-BaStj_a_.js +1 -0
- package/client/dist/assets/dashboard-Bi6ugu5Q.js +1 -0
- package/client/dist/assets/dashboard-BmqTn3L02.js +1 -0
- package/client/dist/assets/dashboard-CvT8lOCT.js +1 -0
- package/client/dist/assets/dashboard-D7Pmeavq2.js +1 -0
- package/client/dist/assets/dashboard-DAnWdAac2.js +1 -0
- package/client/dist/assets/dashboard-JnYy2WLg.js +1 -0
- package/client/dist/assets/dashboard-L47YsFS_.js +1 -0
- package/client/dist/assets/{dist-js-BQn4yxaw.js → dist-js-BZSiJDbG.js} +1 -1
- package/client/dist/assets/{dist-js-BAWSqVtr.js → dist-js-DKpmIJlQ.js} +1 -1
- package/client/dist/assets/{editor.main-G3Sl9C67.js → editor.main-C9J2ikJK.js} +2 -2
- package/client/dist/assets/{freemarker2-pBzQNgFi.js → freemarker2-J5iIIIK-.js} +1 -1
- package/client/dist/assets/{handlebars-CtBTCnBI.js → handlebars-DSw1l_tw.js} +1 -1
- package/client/dist/assets/{html-Dx3DYBzY.js → html-h2-wE1Lj.js} +1 -1
- package/client/dist/assets/{htmlMode-In7frFOo.js → htmlMode-Cc_ij7lW.js} +1 -1
- package/client/dist/assets/index-DM6heiGa.js +155 -0
- package/client/dist/assets/index-DPX6_xIC.css +2 -0
- package/client/dist/assets/{javascript-BKcn-YkN.js → javascript-C1q3xv6N.js} +1 -1
- package/client/dist/assets/jira-B3613-gj.js +1 -0
- package/client/dist/assets/jira-BHAKtc4B.js +1 -0
- package/client/dist/assets/jira-Be369RIR2.js +1 -0
- package/client/dist/assets/jira-CPpgA-5z2.js +1 -0
- package/client/dist/assets/jira-D5efgwp_.js +1 -0
- package/client/dist/assets/jira-DS7ownDy.js +1 -0
- package/client/dist/assets/jira-bDs7Im2C.js +1 -0
- package/client/dist/assets/jira-uNH1gETL2.js +1 -0
- package/client/dist/assets/jobs-1GV2-4zd2.js +1 -0
- package/client/dist/assets/jobs-26F9gGcE.js +1 -0
- package/client/dist/assets/jobs-BgQzB0Sx2.js +1 -0
- package/client/dist/assets/jobs-BqWui9_f.js +1 -0
- package/client/dist/assets/jobs-CE8qeIYj.js +1 -0
- package/client/dist/assets/jobs-D5sZzgLs.js +1 -0
- package/client/dist/assets/jobs-DzeuASRf.js +1 -0
- package/client/dist/assets/jobs-Wh7dn28q2.js +1 -0
- package/client/dist/assets/{jsonMode-jNREoQ3C.js → jsonMode-CeaCdEhr.js} +1 -1
- package/client/dist/assets/{lib-C-g8QbZl.js → lib-B2nCKzmA.js} +1 -1
- package/client/dist/assets/{liquid-CtRkGqBu.js → liquid-eFsVK0V-.js} +1 -1
- package/client/dist/assets/loops-BvEenWBy.js +1 -0
- package/client/dist/assets/loops-CBiWd9Hf.js +1 -0
- package/client/dist/assets/loops-DNGiYKll2.js +1 -0
- package/client/dist/assets/{loops-Y3FQsUCh.js → loops-DjOtVXRo.js} +1 -1
- package/client/dist/assets/loops-HTHf-xu32.js +1 -0
- package/client/dist/assets/loops-YYV2bwjd.js +1 -0
- package/client/dist/assets/loops-hXgncEpF2.js +1 -0
- package/client/dist/assets/loops-kYbydDyN.js +1 -0
- package/client/dist/assets/{lspLanguageFeatures-Cl3gvD_W.js → lspLanguageFeatures-DT3-bbhn.js} +1 -1
- package/client/dist/assets/{mdx-B_nJldHz.js → mdx-Fuwnh1cX.js} +1 -1
- package/client/dist/assets/{monaco.contribution-PUeaZk8T.js → monaco.contribution-Desq7oq4.js} +2 -2
- package/client/dist/assets/{python-DS1Ce-A6.js → python-BgoEs6Cj.js} +1 -1
- package/client/dist/assets/{razor-L41Qg9OK.js → razor-CM1sPMMq.js} +1 -1
- package/client/dist/assets/{settings-BPq9nCJm.js → settings-BOHOmHkb.js} +1 -1
- package/client/dist/assets/{settings-r8Qq2Gzp2.js → settings-BlEA_7h02.js} +1 -1
- package/client/dist/assets/{settings-C-m3yEZ2.js → settings-BsJMsWGc.js} +1 -1
- package/client/dist/assets/{settings-zZr7NzuB.js → settings-D6qY9_UI.js} +1 -1
- package/client/dist/assets/{settings-VgCsjaYf.js → settings-DZJeXoRV.js} +1 -1
- package/client/dist/assets/settings-Mr7MnJbf.js +1 -0
- package/client/dist/assets/{settings-BWHGtmU12.js → settings-TNga5gIF2.js} +1 -1
- package/client/dist/assets/{settings-DZj7Uqy32.js → settings-lFoaYT-s2.js} +1 -1
- package/client/dist/assets/setup-B590KsTX.js +1 -0
- package/client/dist/assets/setup-BkyGoby6.js +1 -0
- package/client/dist/assets/setup-CEj6uY3-2.js +1 -0
- package/client/dist/assets/setup-CbIerFGf2.js +1 -0
- package/client/dist/assets/setup-CmU6bTCV2.js +1 -0
- package/client/dist/assets/setup-D674kWQH.js +1 -0
- package/client/dist/assets/setup-VNqXyB6Y.js +1 -0
- package/client/dist/assets/setup-iY8vH8pB.js +1 -0
- package/client/dist/assets/specs-C9Sd7BhH2.js +1 -0
- package/client/dist/assets/specs-CBDvEYKv.js +1 -0
- package/client/dist/assets/specs-CIPr9reA2.js +1 -0
- package/client/dist/assets/specs-CWtKcVDC.js +1 -0
- package/client/dist/assets/specs-CmEZY57v.js +1 -0
- package/client/dist/assets/specs-D2clrt2c2.js +1 -0
- package/client/dist/assets/specs-DTplt2XM.js +1 -0
- package/client/dist/assets/specs-PozUNhqb.js +1 -0
- package/client/dist/assets/{tsMode-BC1g186U.js → tsMode-BmT8WogI.js} +1 -1
- package/client/dist/assets/{typescript-BGlRGvJW.js → typescript-CQrQPsoW.js} +1 -1
- package/client/dist/assets/upload-CLAzW1mS.js +1 -0
- package/client/dist/assets/{value-D9aLbxw-.js → value-DSwzyyWR.js} +1 -1
- package/client/dist/assets/{workers-CXcXtO3b.js → workers-U0UXyLUf.js} +1 -1
- package/client/dist/assets/{xml-JdZZaIhJ.js → xml-WRxJs8_I.js} +1 -1
- package/client/dist/assets/{yaml-Cm82BVBt.js → yaml-CjpSTjJU.js} +1 -1
- package/client/dist/index.html +14 -14
- package/docs/customizing.md +2 -2
- package/docs/gemini.md +15 -4
- package/docs/guide/de/insights/3-code-explorer.md +6 -0
- package/docs/guide/de/integrations/3-jira-integration.md +1 -1
- package/docs/guide/de/integrations/6-agent-chat.md +5 -0
- package/docs/guide/de/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/de/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/de/pipeline/3-batch-implement-and-multi-feature.md +9 -5
- package/docs/guide/de/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/de/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/en/insights/3-code-explorer.md +6 -0
- package/docs/guide/en/integrations/3-jira-integration.md +1 -1
- package/docs/guide/en/integrations/6-agent-chat.md +5 -0
- package/docs/guide/en/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/en/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/en/pipeline/3-batch-implement-and-multi-feature.md +10 -4
- package/docs/guide/en/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/en/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/es/insights/3-code-explorer.md +6 -0
- package/docs/guide/es/integrations/3-jira-integration.md +1 -1
- package/docs/guide/es/integrations/6-agent-chat.md +5 -0
- package/docs/guide/es/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/es/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/es/pipeline/3-batch-implement-and-multi-feature.md +9 -5
- package/docs/guide/es/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/es/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/fr/insights/3-code-explorer.md +6 -0
- package/docs/guide/fr/integrations/3-jira-integration.md +1 -1
- package/docs/guide/fr/integrations/6-agent-chat.md +5 -0
- package/docs/guide/fr/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/fr/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/fr/pipeline/3-batch-implement-and-multi-feature.md +9 -5
- package/docs/guide/fr/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/fr/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/it/insights/3-code-explorer.md +6 -0
- package/docs/guide/it/integrations/3-jira-integration.md +1 -1
- package/docs/guide/it/integrations/6-agent-chat.md +5 -0
- package/docs/guide/it/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/it/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/it/pipeline/3-batch-implement-and-multi-feature.md +10 -6
- package/docs/guide/it/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/it/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/ja/insights/3-code-explorer.md +6 -0
- package/docs/guide/ja/integrations/3-jira-integration.md +1 -1
- package/docs/guide/ja/integrations/6-agent-chat.md +5 -0
- package/docs/guide/ja/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/ja/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/ja/pipeline/3-batch-implement-and-multi-feature.md +9 -5
- package/docs/guide/ja/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/ja/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/pt/insights/3-code-explorer.md +6 -0
- package/docs/guide/pt/integrations/3-jira-integration.md +1 -1
- package/docs/guide/pt/integrations/6-agent-chat.md +5 -0
- package/docs/guide/pt/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/pt/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/pt/pipeline/3-batch-implement-and-multi-feature.md +9 -5
- package/docs/guide/pt/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/pt/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/guide/zh/insights/3-code-explorer.md +6 -0
- package/docs/guide/zh/integrations/3-jira-integration.md +1 -1
- package/docs/guide/zh/integrations/6-agent-chat.md +5 -0
- package/docs/guide/zh/pipeline/1-rails-and-jobs.md +6 -6
- package/docs/guide/zh/pipeline/2-the-job-detail-view.md +27 -2
- package/docs/guide/zh/pipeline/3-batch-implement-and-multi-feature.md +10 -6
- package/docs/guide/zh/pipeline/5-the-loop-builder.md +1 -1
- package/docs/guide/zh/specs/1-specs-and-the-backlog.md +3 -2
- package/docs/internals/README.md +5 -0
- package/docs/internals/adding-a-provider.md +15 -3
- package/docs/internals/api-reference.md +6 -5
- package/docs/internals/browser-capture-performance.md +93 -0
- package/docs/internals/code-explorer-story.md +135 -0
- package/docs/internals/gemini-mcp-registration.md +53 -0
- package/docs/internals/interactive-jobs.md +212 -0
- package/docs/internals/loop-step-log-explorer.md +146 -0
- package/docs/internals/safe-pr-review-flow.md +367 -0
- package/docs/mcp.md +1 -1
- package/docs/running-pipelines.md +6 -2
- package/package.json +1 -1
- package/server/dist/agent-chat-manager.js +211 -0
- package/server/dist/agent-chat-registry.js +20 -0
- package/server/dist/agent-chat-router.js +22 -0
- package/server/dist/agent-mcp-config.js +45 -4
- package/server/dist/agent-operator-prompt.js +149 -56
- package/server/dist/agent-store.js +25 -0
- package/server/dist/agent-tier.js +6 -1
- package/server/dist/browser-capture-manager.js +184 -34
- package/server/dist/browser-playwright.js +98 -13
- package/server/dist/code-explorer-router.js +92 -0
- package/server/dist/db.js +102 -0
- package/server/dist/file-provenance.js +54 -1
- package/server/dist/file-story-manager.js +170 -0
- package/server/dist/file-story.js +148 -0
- package/server/dist/file-summary-generator.js +5 -4
- package/server/dist/git-guardrails.js +62 -0
- package/server/dist/index.js +5 -0
- package/server/dist/integration-branch.js +63 -0
- package/server/dist/interactive-job-session.js +226 -18
- package/server/dist/jira/jira-adf.js +9 -0
- package/server/dist/jira/jira-materializer.js +27 -4
- package/server/dist/jira/jira-status-resolver.js +12 -4
- package/server/dist/jira/jira-sync-manager.js +130 -2
- package/server/dist/jira-router.js +1 -1
- package/server/dist/loop-effect.js +12 -0
- package/server/dist/loop-executors.js +92 -22
- package/server/dist/loop-run-manager.js +354 -28
- package/server/dist/mcp/guide.js +18 -7
- package/server/dist/mcp/tools/jira.js +1 -1
- package/server/dist/mcp/tools/jobs.js +14 -5
- package/server/dist/mcp/tools/rails.js +137 -12
- package/server/dist/mcp/tools/specs.js +35 -9
- package/server/dist/mcp/tools/types.js +49 -0
- package/server/dist/mobile/mobile-ws.js +4 -0
- package/server/dist/path-resolver.js +46 -0
- package/server/dist/pr-body.js +240 -0
- package/server/dist/pr-naming.js +210 -0
- package/server/dist/pr-publisher.js +68 -0
- package/server/dist/project-registry.js +77 -17
- package/server/dist/project-router-jobs.js +53 -13
- package/server/dist/project-router-settings.js +33 -0
- package/server/dist/project-router-terminals.js +16 -0
- package/server/dist/project-router-tickets.js +57 -3
- package/server/dist/project-router.js +52 -13
- package/server/dist/queue-manager.js +268 -83
- package/server/dist/rail-isolated-launch.js +324 -34
- package/server/dist/rail-isolation.js +32 -5
- package/server/dist/rail-pr-decision.js +519 -0
- package/server/dist/rail-pr-delivery.js +75 -0
- package/server/dist/rail-pr-store.js +175 -0
- package/server/dist/rail-worktrees-store.js +18 -0
- package/server/dist/rails-router.js +241 -17
- package/server/dist/rails-store.js +78 -1
- package/server/dist/setup-prerequisites.js +105 -0
- package/server/dist/smash-runner.js +3 -1
- package/server/dist/ticket-store.js +7 -3
- package/server/dist/workspace-resolution.js +27 -0
- package/server/dist/worktree-manager.js +42 -7
- package/server/dist/worktree-overlay.js +261 -0
- package/client/dist/assets/AgentBrowserCapture-BlGDZahz.js +0 -1
- package/client/dist/assets/AgentModeJobsPane-CyNR-9BO.js +0 -1
- package/client/dist/assets/AgentsPage-dNsEsY_x.js +0 -86
- package/client/dist/assets/AnalyticsPage-BAxmR0HQ.js +0 -1
- package/client/dist/assets/CodePage-Crc4BBQN.js +0 -2
- package/client/dist/assets/DesktopAnalyticsPage-IXSnbCce.js +0 -1
- package/client/dist/assets/DocsDialog-Bc4hx7oW.js +0 -11
- package/client/dist/assets/DocsPage-bEU4t9c6.js +0 -11
- package/client/dist/assets/IntegrationsPage-wq2CezEL.js +0 -3
- package/client/dist/assets/JobDetailPage-BBcPk3j8.js +0 -1
- package/client/dist/assets/JobsPage-DXrSPlwa.js +0 -1
- package/client/dist/assets/LogViewer-BoYbwvbU.js +0 -16
- package/client/dist/assets/LoopBuilderPage-CiUMAuWz.js +0 -7
- package/client/dist/assets/LoopsPage-Xrp6W5XW.js +0 -1
- package/client/dist/assets/ProjectSettingsDialog-BU98L5JD.js +0 -1
- package/client/dist/assets/agent-B7GdHP3Z.js +0 -1
- package/client/dist/assets/agent-BHmcgoJf.js +0 -1
- package/client/dist/assets/agent-BdY_fii4.js +0 -1
- package/client/dist/assets/agent-C32FOi9v.js +0 -1
- package/client/dist/assets/agent-CiFQxoaZ2.js +0 -1
- package/client/dist/assets/agent-CtAag7V6.js +0 -1
- package/client/dist/assets/agent-DE4Mp1fY2.js +0 -1
- package/client/dist/assets/agent-DK8qovyG2.js +0 -1
- package/client/dist/assets/browser-ZB1afzjZ.js +0 -1
- package/client/dist/assets/code-BFxE1Yt62.js +0 -1
- package/client/dist/assets/code-BefYOm5Q.js +0 -1
- package/client/dist/assets/code-BkXpJt99.js +0 -1
- package/client/dist/assets/code-C6Sq7_t8.js +0 -1
- package/client/dist/assets/code-CGj3w-2N.js +0 -1
- package/client/dist/assets/code-Dfsxc9o82.js +0 -1
- package/client/dist/assets/code-Y5urks5s2.js +0 -1
- package/client/dist/assets/code-wtcs0ivb.js +0 -1
- package/client/dist/assets/dashboard-B8032psZ.js +0 -1
- package/client/dist/assets/dashboard-BHGKtxty2.js +0 -1
- package/client/dist/assets/dashboard-CXnoUgN22.js +0 -1
- package/client/dist/assets/dashboard-Ccyxqy7d.js +0 -1
- package/client/dist/assets/dashboard-CoED_41r.js +0 -1
- package/client/dist/assets/dashboard-Dc5ZAgMN.js +0 -1
- package/client/dist/assets/dashboard-JMY8ueD3.js +0 -1
- package/client/dist/assets/dashboard-tkWr1aHD2.js +0 -1
- package/client/dist/assets/index-BHPbzyg6.css +0 -2
- package/client/dist/assets/index-n2PmJGF6.js +0 -152
- package/client/dist/assets/jira-BcZzX9122.js +0 -1
- package/client/dist/assets/jira-CTYs7LA12.js +0 -1
- package/client/dist/assets/jira-D2nN39e92.js +0 -1
- package/client/dist/assets/jira-DJxmIkyr.js +0 -1
- package/client/dist/assets/jira-DXMoAKWD.js +0 -1
- package/client/dist/assets/jira-IhC1ROIc.js +0 -1
- package/client/dist/assets/jira-Iw575fN8.js +0 -1
- package/client/dist/assets/jira-kbWZY7jH.js +0 -1
- package/client/dist/assets/jobs-BqF79kHz2.js +0 -1
- package/client/dist/assets/jobs-C1XM5urS.js +0 -1
- package/client/dist/assets/jobs-C5UjG0262.js +0 -1
- package/client/dist/assets/jobs-DBfaHTxC.js +0 -1
- package/client/dist/assets/jobs-DM4BDPdg.js +0 -1
- package/client/dist/assets/jobs-Da5OkZmf2.js +0 -1
- package/client/dist/assets/jobs-JJdnuQ03.js +0 -1
- package/client/dist/assets/jobs-SI34ML8I.js +0 -1
- package/client/dist/assets/loops-BR4FmOv12.js +0 -1
- package/client/dist/assets/loops-BgX_uudn.js +0 -1
- package/client/dist/assets/loops-Bpl2j2LP.js +0 -1
- package/client/dist/assets/loops-CQuuMlyZ.js +0 -1
- package/client/dist/assets/loops-DW8hSMdS2.js +0 -1
- package/client/dist/assets/loops-Q2muu8hW2.js +0 -1
- package/client/dist/assets/loops-q7CNDvfK.js +0 -1
- package/client/dist/assets/settings-CY8udi0e.js +0 -1
- package/client/dist/assets/setup-CNMdCHHt2.js +0 -1
- package/client/dist/assets/setup-Cag4XrbL2.js +0 -1
- package/client/dist/assets/setup-D9IHGoWU.js +0 -1
- package/client/dist/assets/setup-Dg6Ba3JT.js +0 -1
- package/client/dist/assets/setup-DyK36RIW.js +0 -1
- package/client/dist/assets/setup-G4ZvDKxX.js +0 -1
- package/client/dist/assets/setup-aTlGxuwi2.js +0 -1
- package/client/dist/assets/setup-kWsoAeHQ.js +0 -1
- package/client/dist/assets/specs-BFSUPa122.js +0 -1
- package/client/dist/assets/specs-BMzip1WY2.js +0 -1
- package/client/dist/assets/specs-BYeAbIEy.js +0 -1
- package/client/dist/assets/specs-C-mtLcXw.js +0 -1
- package/client/dist/assets/specs-CAzpvADd2.js +0 -1
- package/client/dist/assets/specs-CyRIw_IR.js +0 -1
- package/client/dist/assets/specs-DMm7gB_l.js +0 -1
- package/client/dist/assets/specs-DuottM0Z.js +0 -1
- /package/client/dist/assets/{abap-CpHUwCVK.js → abap-D0E4FfVW.js} +0 -0
- /package/client/dist/assets/{activity-DemHQS7l.js → activity--iN4Ad2R2.js} +0 -0
- /package/client/dist/assets/{activity-B_71BWg3.js → activity-6hM_ZSg9.js} +0 -0
- /package/client/dist/assets/{activity-DHv2V5Oi.js → activity-BZNfOdiS.js} +0 -0
- /package/client/dist/assets/{activity-D0rfjQzT2.js → activity-BuVo5sPB.js} +0 -0
- /package/client/dist/assets/{activity-BpTTj_PH.js → activity-CIOLxGvh.js} +0 -0
- /package/client/dist/assets/{activity-D2e62vbV2.js → activity-D2r2h3SE2.js} +0 -0
- /package/client/dist/assets/{activity-Ch-wbr0C2.js → activity-DtxrAUMo2.js} +0 -0
- /package/client/dist/assets/{addon-image-Ba-TY3wr.js → addon-image-CwoZj17F.js} +0 -0
- /package/client/dist/assets/{addon-ligatures-T4ARLiuu.js → addon-ligatures-CWDrED80.js} +0 -0
- /package/client/dist/assets/{addon-webgl-Cd73n8eh.js → addon-webgl-DMbGeI_u.js} +0 -0
- /package/client/dist/assets/{addspec-xwRX_Pnf.js → addspec-BMtLZLW62.js} +0 -0
- /package/client/dist/assets/{addspec-l_zU5gla.js → addspec-CP0HRuxY.js} +0 -0
- /package/client/dist/assets/{addspec-Dio-H5z0.js → addspec-CRuvWn8d.js} +0 -0
- /package/client/dist/assets/{addspec-D6L4ZteP.js → addspec-D-JLDsUV.js} +0 -0
- /package/client/dist/assets/{addspec-CrEm6Fzr2.js → addspec-DfhDbg5c.js} +0 -0
- /package/client/dist/assets/{addspec-B6Ns8KU02.js → addspec-bsNDdqRm2.js} +0 -0
- /package/client/dist/assets/{addspec-qGyH7MHy2.js → addspec-mgPOt5q02.js} +0 -0
- /package/client/dist/assets/{agents-OzxCLVW1.js → agents-78bTZGZh.js} +0 -0
- /package/client/dist/assets/{agents-hv3sqmPm2.js → agents-BLhWCmDS2.js} +0 -0
- /package/client/dist/assets/{agents-CHgCudUV.js → agents-BUdEi3-N.js} +0 -0
- /package/client/dist/assets/{agents-Dumx4JiM2.js → agents-BpoUaoW92.js} +0 -0
- /package/client/dist/assets/{agents-CnaPeSyw.js → agents-CWo-KyAB.js} +0 -0
- /package/client/dist/assets/{agents-Xh-zYHUx2.js → agents-DDtCJ5M02.js} +0 -0
- /package/client/dist/assets/{agents-DuhLEaQ_.js → agents-bvyRLczL.js} +0 -0
- /package/client/dist/assets/{agentstudio-CCthUdzR.js → agentstudio-BMQxc_bz.js} +0 -0
- /package/client/dist/assets/{agentstudio-CcH8Ckql2.js → agentstudio-COZriimI2.js} +0 -0
- /package/client/dist/assets/{agentstudio-Bo91kfUd2.js → agentstudio-CWgXG5W52.js} +0 -0
- /package/client/dist/assets/{agentstudio-VJOa_NVO.js → agentstudio-C_Jq8Tl4.js} +0 -0
- /package/client/dist/assets/{agentstudio-BIxikDf_.js → agentstudio-Dtq3dGYk.js} +0 -0
- /package/client/dist/assets/{agentstudio-Bz8cP8_c.js → agentstudio-jKMTdT70.js} +0 -0
- /package/client/dist/assets/{agentstudio-DARgYg5Z2.js → agentstudio-sI27TpBu2.js} +0 -0
- /package/client/dist/assets/{aiedit-5ZXN4cep2.js → aiedit-6CHdb_iv2.js} +0 -0
- /package/client/dist/assets/{aiedit-B7gAJZVC.js → aiedit-B03pD_Na.js} +0 -0
- /package/client/dist/assets/{aiedit-D92V-kUW.js → aiedit-BtRqRxML.js} +0 -0
- /package/client/dist/assets/{aiedit-D9ualLVJ.js → aiedit-eMN4eQPF.js} +0 -0
- /package/client/dist/assets/{aiedit-h48tJU0V2.js → aiedit-hknZlly82.js} +0 -0
- /package/client/dist/assets/{aiedit-xkA_mR2a2.js → aiedit-igmw-ZoZ2.js} +0 -0
- /package/client/dist/assets/{aiedit-7B2dvCv-.js → aiedit-pc5JDUq1.js} +0 -0
- /package/client/dist/assets/{analytics-slx4X1mu2.js → analytics-BWMUZ2Pq2.js} +0 -0
- /package/client/dist/assets/{analytics-uRNP6yAJ.js → analytics-Bj-SmKHa.js} +0 -0
- /package/client/dist/assets/{analytics-WF1I49ln.js → analytics-C5dpiS0o.js} +0 -0
- /package/client/dist/assets/{analytics-CBvaxOId.js → analytics-CCprAqk3.js} +0 -0
- /package/client/dist/assets/{analytics-BakymH7Z2.js → analytics-CR1TJqUi2.js} +0 -0
- /package/client/dist/assets/{analytics-B7bPj6u_2.js → analytics-DfNdXR0y2.js} +0 -0
- /package/client/dist/assets/{analytics-DZ74aQ54.js → analytics-rmHuJv0V.js} +0 -0
- /package/client/dist/assets/{apex-DlD7oJX6.js → apex-D_X_8J4d.js} +0 -0
- /package/client/dist/assets/{atom-one-dark-BrRN8wBy.js → atom-one-dark-BMDPFwRZ.js} +0 -0
- /package/client/dist/assets/{attachments-C11tOKZ_.js → attachments-BgBNSqQd.js} +0 -0
- /package/client/dist/assets/{attachments-Bnbyj2PN.js → attachments-BjyMmrwE.js} +0 -0
- /package/client/dist/assets/{attachments-CIeLa8l12.js → attachments-C9GTTJsx2.js} +0 -0
- /package/client/dist/assets/{attachments-DslHfa-L.js → attachments-DCZOL1RU.js} +0 -0
- /package/client/dist/assets/{attachments-C-gyrCOb.js → attachments-DL-bPy9E.js} +0 -0
- /package/client/dist/assets/{attachments-Cxrs0pyL2.js → attachments-DPCmkFZu2.js} +0 -0
- /package/client/dist/assets/{attachments-TTPsc0a52.js → attachments-rNn1ekRy2.js} +0 -0
- /package/client/dist/assets/{azcli-CmXFAbc-.js → azcli-PZLnZ3jH.js} +0 -0
- /package/client/dist/assets/{bat-DdNJq4hH.js → bat-KlBOxpO4.js} +0 -0
- /package/client/dist/assets/{bicep-Bppz7WOA.js → bicep-CgG9oF3V.js} +0 -0
- /package/client/dist/assets/{cameligo-0MRMdSmg.js → cameligo-6R45j6Ph.js} +0 -0
- /package/client/dist/assets/{chat-BvHrebKC2.js → chat-BgL3zv6x2.js} +0 -0
- /package/client/dist/assets/{chat-CZyiLJvQ.js → chat-Bh3NdgJs.js} +0 -0
- /package/client/dist/assets/{chat-DKYP7NnC.js → chat-Bom6bqK3.js} +0 -0
- /package/client/dist/assets/{chat-5mdg_YIc2.js → chat-DdwZjT8K2.js} +0 -0
- /package/client/dist/assets/{chat-wkTIy9rX.js → chat-Dnwg-NpQ.js} +0 -0
- /package/client/dist/assets/{chat-BpYxnkr4.js → chat-DsbwJhus.js} +0 -0
- /package/client/dist/assets/{chat-BGax2nV62.js → chat-vylxqR0P2.js} +0 -0
- /package/client/dist/assets/{clojure-B5dKj863.js → clojure-CLAAMO7J.js} +0 -0
- /package/client/dist/assets/{clsx-1dnHRn8W.js → clsx-CNmW0Nip.js} +0 -0
- /package/client/dist/assets/{coffee-DSbWjzk7.js → coffee-AruSygo-.js} +0 -0
- /package/client/dist/assets/{commands-uz_uKT242.js → commands-C4Y3C-PF2.js} +0 -0
- /package/client/dist/assets/{commands-Bb7MnPqn2.js → commands-CSDs9q_2.js} +0 -0
- /package/client/dist/assets/{commands-C_7zZwJf.js → commands-DkU0qliq.js} +0 -0
- /package/client/dist/assets/{commands-D_6B8lfU.js → commands-JWMfGDmr.js} +0 -0
- /package/client/dist/assets/{commands-DBc5llLi2.js → commands-RHOchN4n2.js} +0 -0
- /package/client/dist/assets/{commands-CQkdthjF.js → commands-u-4eEHCN.js} +0 -0
- /package/client/dist/assets/{commands-DhZNhSbl.js → commands-xHDDGfcP2.js} +0 -0
- /package/client/dist/assets/{cpp-SBF8SAh9.js → cpp-DEE5r4Pw.js} +0 -0
- /package/client/dist/assets/{csharp-Lzuq_1t-.js → csharp-BVXJUvbK.js} +0 -0
- /package/client/dist/assets/{csp-BEzxZqi1.js → csp-cXOF0JoV.js} +0 -0
- /package/client/dist/assets/{css-C3D3PKln.js → css-C_zQuAoy.js} +0 -0
- /package/client/dist/assets/{cypher-CQ3dEPmv.js → cypher-t95m_Q-O.js} +0 -0
- /package/client/dist/assets/{dart-DCPmxolI.js → dart-B4r1Tg-e.js} +0 -0
- /package/client/dist/assets/{dockerfile-B_2_aaTO.js → dockerfile-6mt7zHQw.js} +0 -0
- /package/client/dist/assets/{ecl-rADUeXsV.js → ecl-DEY_Fz_Q.js} +0 -0
- /package/client/dist/assets/{editor.api2-T7u8kQWG.js → editor.api2-C3B0tp2x.js} +0 -0
- /package/client/dist/assets/{elixir-DOd-TuJO.js → elixir-R6-q1c5t.js} +0 -0
- /package/client/dist/assets/{explore-BHEhNekP2.js → explore-B7fblXBb2.js} +0 -0
- /package/client/dist/assets/{explore-DAsroA482.js → explore-BHtEpoNN2.js} +0 -0
- /package/client/dist/assets/{explore-UwHHj2ux.js → explore-C0k8HrkN.js} +0 -0
- /package/client/dist/assets/{explore-DaXiW9-6.js → explore-CNBzrEPS.js} +0 -0
- /package/client/dist/assets/{explore-CWlAXx0f2.js → explore-D_mWBYCS2.js} +0 -0
- /package/client/dist/assets/{explore-DG1CtYqT.js → explore-Dahc1lOx.js} +0 -0
- /package/client/dist/assets/{explore-DMSGAnhK.js → explore-UvW1jEMa.js} +0 -0
- /package/client/dist/assets/{flow9-Bf7zg_j0.js → flow9-CNQvHBOt.js} +0 -0
- /package/client/dist/assets/{format-command-BHymDa4r.js → format-command-CuWuRvka.js} +0 -0
- /package/client/dist/assets/{fsharp-BqROZjbA.js → fsharp-vPZkFXwv.js} +0 -0
- /package/client/dist/assets/{go-oQTP5uLe.js → go-KUzBMYJ_.js} +0 -0
- /package/client/dist/assets/{graphql-CSHfTU3q.js → graphql-C3W0rCsD.js} +0 -0
- /package/client/dist/assets/{hcl-CeG-RoFJ.js → hcl-BOOMFSNX.js} +0 -0
- /package/client/dist/assets/{ini-B2Dv4ohc.js → ini-CFat1WJr.js} +0 -0
- /package/client/dist/assets/{integrations-CAF21FbQ.js → integrations-4KZnYECp.js} +0 -0
- /package/client/dist/assets/{integrations-CHCgning2.js → integrations-7W266oqF2.js} +0 -0
- /package/client/dist/assets/{integrations-B-JVE1_D.js → integrations-BNuauK3o.js} +0 -0
- /package/client/dist/assets/{integrations-BAujIDoE.js → integrations-CHy8tnpV.js} +0 -0
- /package/client/dist/assets/{integrations-DS5FFMes.js → integrations-Cl_wD5AI.js} +0 -0
- /package/client/dist/assets/{integrations-axZN4--n2.js → integrations-Cy8nZ_dv2.js} +0 -0
- /package/client/dist/assets/{integrations-CtsOpq2D2.js → integrations-zMqY4VU12.js} +0 -0
- /package/client/dist/assets/{java-aUDDeufb.js → java-Dee-CNAM.js} +0 -0
- /package/client/dist/assets/{julia-C5LbruO5.js → julia-CGNlLYuU.js} +0 -0
- /package/client/dist/assets/{kotlin-C6mOuSKk.js → kotlin-CY7lhknJ.js} +0 -0
- /package/client/dist/assets/{less-D5qC3caW.js → less-D0KN7hfJ.js} +0 -0
- /package/client/dist/assets/{lexon-Dr2La-fj.js → lexon-CuRJufPs.js} +0 -0
- /package/client/dist/assets/{lua-Zy6u5z6Z.js → lua-DHvH62Zf.js} +0 -0
- /package/client/dist/assets/{m3-C7XHeDz_.js → m3-tlthQ8Fo.js} +0 -0
- /package/client/dist/assets/{markdown-PiF7r-2h.js → markdown-Dbx1LgUZ.js} +0 -0
- /package/client/dist/assets/{mcp-B_nWlxZa2.js → mcp-CjVKXAhD2.js} +0 -0
- /package/client/dist/assets/{mcp-B8omp_Cd.js → mcp-Dcl-XxkR.js} +0 -0
- /package/client/dist/assets/{mcp-DTCCrYHz2.js → mcp-DtbxuH3V2.js} +0 -0
- /package/client/dist/assets/{mcp-DytBbASZ.js → mcp-H-gnQUdN.js} +0 -0
- /package/client/dist/assets/{mcp-D_oPS6Zc.js → mcp-N1nzp8oL.js} +0 -0
- /package/client/dist/assets/{mcp-Cb2jtHP72.js → mcp-XJNRfxVp2.js} +0 -0
- /package/client/dist/assets/{mcp-DPqm2C8-.js → mcp-sRmWE8WJ.js} +0 -0
- /package/client/dist/assets/{mips-nn07GiyE.js → mips-D_O3IUz_.js} +0 -0
- /package/client/dist/assets/{msdax-DaKFVMST.js → msdax-C_ZmCnRt.js} +0 -0
- /package/client/dist/assets/{mysql-C6d6xrbU.js → mysql-C-dCEYSX.js} +0 -0
- /package/client/dist/assets/{nav-Cibp2KWF.js → nav-40HzpIFb.js} +0 -0
- /package/client/dist/assets/{nav-DtTnF9xd.js → nav-8yPhBn00.js} +0 -0
- /package/client/dist/assets/{nav-Danf5sRf2.js → nav-B6PkGfTk2.js} +0 -0
- /package/client/dist/assets/{nav-Bqhyfbcj2.js → nav-BHqMLrS72.js} +0 -0
- /package/client/dist/assets/{nav-CQb9y1km.js → nav-CGy4yx3C.js} +0 -0
- /package/client/dist/assets/{nav-BAsYcqdw2.js → nav-CXEDcRdv2.js} +0 -0
- /package/client/dist/assets/{nav-BRrNlZkr.js → nav-EI_OhEAo.js} +0 -0
- /package/client/dist/assets/{objective-c-B6KMtI-k.js → objective-c-DZY9Aqxd.js} +0 -0
- /package/client/dist/assets/{pascal-DYr8MPE7.js → pascal-BVHLXoor.js} +0 -0
- /package/client/dist/assets/{pascaligo-B3pd-jJ_.js → pascaligo-XdG_6u3k.js} +0 -0
- /package/client/dist/assets/{perl-z2xUzoaL.js → perl-b9QV0BcD.js} +0 -0
- /package/client/dist/assets/{pgsql-DUvT-09-.js → pgsql-Du5FDOgT.js} +0 -0
- /package/client/dist/assets/{php-CGyOmJfy.js → php-WJD0z2Z_.js} +0 -0
- /package/client/dist/assets/{pla-CMqgvyD0.js → pla-CbciTf2w.js} +0 -0
- /package/client/dist/assets/{postiats-Dx5LUn4Z.js → postiats-C1QxmvVq.js} +0 -0
- /package/client/dist/assets/{powerquery-DazYxvJ6.js → powerquery-BEYDhPLZ.js} +0 -0
- /package/client/dist/assets/{powershell-B3nwlDka.js → powershell-CfaYw4XL.js} +0 -0
- /package/client/dist/assets/{protobuf-BGuu2u2s.js → protobuf-pwZEDOYa.js} +0 -0
- /package/client/dist/assets/{pug-Dbte1MXA.js → pug-5ftqSPVe.js} +0 -0
- /package/client/dist/assets/{qsharp-DJLwBOvG.js → qsharp-uI6qZMq4.js} +0 -0
- /package/client/dist/assets/{r-DmGBRSHj.js → r-BK54P1EG.js} +0 -0
- /package/client/dist/assets/{redis-VvUYn0a4.js → redis-DI0yd7kN.js} +0 -0
- /package/client/dist/assets/{redshift-6GdDrxg3.js → redshift-Dov26eyI.js} +0 -0
- /package/client/dist/assets/{restructuredtext-CfAB8aRU.js → restructuredtext-QqJ_1dC-.js} +0 -0
- /package/client/dist/assets/{ruby-CIYK6MXY.js → ruby-5WjveeVb.js} +0 -0
- /package/client/dist/assets/{rust-CTHZfjj9.js → rust-BzVSfqFH.js} +0 -0
- /package/client/dist/assets/{sb-CkKTdkz_.js → sb-DMIUfT-3.js} +0 -0
- /package/client/dist/assets/{scala-Bx-Wr9TM.js → scala-CSKA7lqt.js} +0 -0
- /package/client/dist/assets/{scheme-Dcfi6f83.js → scheme-CojOP2Qc.js} +0 -0
- /package/client/dist/assets/{scss-ByL-7PFQ.js → scss-DoBZGJG6.js} +0 -0
- /package/client/dist/assets/{shell-Csnb9Yqd.js → shell-YOaE4udl.js} +0 -0
- /package/client/dist/assets/{solidity-Dbw5-0QP.js → solidity-l3jSyVLu.js} +0 -0
- /package/client/dist/assets/{sophia-ZQ3saZQx.js → sophia-BhSGTaQJ.js} +0 -0
- /package/client/dist/assets/{sparql-BEtOel8u.js → sparql-qhk6QD9w.js} +0 -0
- /package/client/dist/assets/{sql-DGPDbzB7.js → sql-DoO3y21-.js} +0 -0
- /package/client/dist/assets/{st-GeW2MOgh.js → st-ChZAY8oG.js} +0 -0
- /package/client/dist/assets/{swift-BVYTN8jq.js → swift-D290Tr7_.js} +0 -0
- /package/client/dist/assets/{systemverilog-Deg2a8Lr.js → systemverilog-DsxzgJ-o.js} +0 -0
- /package/client/dist/assets/{tcl-6DnLmROF.js → tcl-BKDUHAAR.js} +0 -0
- /package/client/dist/assets/{terminal-CDiOAmBd2.js → terminal-BRm03iFA2.js} +0 -0
- /package/client/dist/assets/{terminal-CJN-mQMc.js → terminal-De2Brd23.js} +0 -0
- /package/client/dist/assets/{terminal-CYB4TiAd.js → terminal-DxQWCqSD2.js} +0 -0
- /package/client/dist/assets/{terminal-DsaDTq5N2.js → terminal-WSAvHXoU.js} +0 -0
- /package/client/dist/assets/{terminal-DTQ_dPqi2.js → terminal-Z7KsDM_n.js} +0 -0
- /package/client/dist/assets/{terminal-58ReNaya.js → terminal-cqfd3EDV.js} +0 -0
- /package/client/dist/assets/{terminal-DawP8xNL.js → terminal-xGbCZJHg.js} +0 -0
- /package/client/dist/assets/{tickets-DoRTUYRT.js → tickets-B7zT4IRf.js} +0 -0
- /package/client/dist/assets/{tickets-DBfFePwk2.js → tickets-BoXom7l8.js} +0 -0
- /package/client/dist/assets/{tickets-DvohTziT2.js → tickets-C3b8cHhd2.js} +0 -0
- /package/client/dist/assets/{tickets-Da15iaRw.js → tickets-CFXnJsYF.js} +0 -0
- /package/client/dist/assets/{tickets-CujsZvkf.js → tickets-CWYFvx1W2.js} +0 -0
- /package/client/dist/assets/{tickets-Df6pee-O2.js → tickets-DTHo9c3C.js} +0 -0
- /package/client/dist/assets/{tickets-CNvIilmP.js → tickets-SuX_HkK4.js} +0 -0
- /package/client/dist/assets/{twig-XbbXW9Yz.js → twig-UpZQaM8G.js} +0 -0
- /package/client/dist/assets/{typespec-CRlV8GhI.js → typespec-DzaKvN8i.js} +0 -0
- /package/client/dist/assets/{vb-BHE2izPH.js → vb-CZpbb7tk.js} +0 -0
- /package/client/dist/assets/{wgsl-Bf5vkfmY.js → wgsl-CNt3OIvl.js} +0 -0
|
@@ -21,6 +21,13 @@ function isTargetClosedError(err) {
|
|
|
21
21
|
const MAX_SESSIONS_PER_PROJECT = 4;
|
|
22
22
|
const DOM_HTML_BYTE_CAP = 100_000;
|
|
23
23
|
const LAST_URL_KEY = 'config.browser_last_url';
|
|
24
|
+
/** Conflation threshold for the screencast fan-out: skip sending a frame to a
|
|
25
|
+
* client whose WS send-buffer already holds this many bytes. A slow consumer
|
|
26
|
+
* otherwise accumulates seconds of stale frames (ever-growing latency), which is
|
|
27
|
+
* what "sluggish" feels like. Every frame is an independent, complete JPEG, so
|
|
28
|
+
* dropping intermediates is lossless — the client simply paints the next fresh
|
|
29
|
+
* frame (newest-wins). `lastFrame` is still always updated. */
|
|
30
|
+
const MAX_CLIENT_BUFFERED_BYTES = 1_000_000;
|
|
24
31
|
/** How far back from capture time to include buffered network requests. */
|
|
25
32
|
const NETWORK_WINDOW_MS = 30_000;
|
|
26
33
|
/**
|
|
@@ -167,13 +174,15 @@ class BrowserCaptureManager {
|
|
|
167
174
|
id,
|
|
168
175
|
projectId: this.projectId,
|
|
169
176
|
page,
|
|
177
|
+
popups: [],
|
|
178
|
+
popupView: false,
|
|
170
179
|
clients: new Set(),
|
|
171
180
|
lastFrame: null,
|
|
172
181
|
url: null,
|
|
173
182
|
title: null,
|
|
174
183
|
viewport: { ...DEFAULT_VIEWPORT },
|
|
175
184
|
createdAt: opts?.createdAtMs ?? this.now(),
|
|
176
|
-
|
|
185
|
+
screencastPage: null,
|
|
177
186
|
screencastDesired: false,
|
|
178
187
|
screencastOp: null,
|
|
179
188
|
closed: false,
|
|
@@ -181,6 +190,18 @@ class BrowserCaptureManager {
|
|
|
181
190
|
this.sessions.set(id, session);
|
|
182
191
|
// The reservation is now realised as a committed session — release it.
|
|
183
192
|
this._reserved--;
|
|
193
|
+
// Adopt popups the page opens (OAuth login windows etc.) as secondary pages
|
|
194
|
+
// of this session, and keep the client's URL bar live on in-page navigation
|
|
195
|
+
// (link clicks / redirects the REST navigate path never sees).
|
|
196
|
+
this.watchPopups(session, page);
|
|
197
|
+
page.onNavigated?.((url) => {
|
|
198
|
+
if (session.closed || this.disposed)
|
|
199
|
+
return;
|
|
200
|
+
session.url = url;
|
|
201
|
+
if (url && url !== 'about:blank')
|
|
202
|
+
this.setLastUrl(url);
|
|
203
|
+
this.broadcastControl(session, { type: 'nav', url, title: session.title });
|
|
204
|
+
});
|
|
184
205
|
// Start capturing the page's network requests before the first navigation so
|
|
185
206
|
// XHR/fetch made during page load are available at capture time. Best-effort:
|
|
186
207
|
// a handle that doesn't support it (or a failure) just yields no network data.
|
|
@@ -188,13 +209,106 @@ class BrowserCaptureManager {
|
|
|
188
209
|
await page.enableNetwork?.();
|
|
189
210
|
}
|
|
190
211
|
catch { /* network capture is best-effort */ }
|
|
212
|
+
// Kick the initial navigation WITHOUT blocking session creation. The old
|
|
213
|
+
// `await page.goto(...)` here held the create POST — and therefore the whole
|
|
214
|
+
// modal spinner — hostage to `domcontentloaded` (up to 30s on a slow site).
|
|
215
|
+
// Returning immediately lets the client attach and start the screencast right
|
|
216
|
+
// away, so the user watches the page paint progressively instead of staring
|
|
217
|
+
// at "opening…". The URL is set optimistically to the target; the resolved
|
|
218
|
+
// URL/title land via the `nav` control broadcast every client already handles.
|
|
191
219
|
const target = opts?.initialUrl?.trim() || this.getLastUrl() || 'about:blank';
|
|
192
|
-
|
|
193
|
-
session
|
|
194
|
-
|
|
220
|
+
session.url = target;
|
|
221
|
+
void this.runInitialNavigation(session, target);
|
|
222
|
+
return this.toMeta(session);
|
|
223
|
+
}
|
|
224
|
+
/** Complete the initial `goto` in the background (see create()). Never throws;
|
|
225
|
+
* a session killed mid-navigation is simply left alone. */
|
|
226
|
+
async runInitialNavigation(s, target) {
|
|
227
|
+
let result;
|
|
228
|
+
try {
|
|
229
|
+
result = await s.page.goto(target);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return; // the page keeps whatever it settled on; nav failures are non-fatal
|
|
233
|
+
}
|
|
234
|
+
if (s.closed || this.disposed)
|
|
235
|
+
return;
|
|
236
|
+
s.url = result.url;
|
|
237
|
+
s.title = result.title;
|
|
195
238
|
if (result.url && result.url !== 'about:blank')
|
|
196
239
|
this.setLastUrl(result.url);
|
|
197
|
-
|
|
240
|
+
this.broadcastControl(s, { type: 'nav', url: result.url, title: result.title });
|
|
241
|
+
}
|
|
242
|
+
// ─── Popup support (OAuth login windows etc.) ───────────────────────────────
|
|
243
|
+
topPopup(s) {
|
|
244
|
+
return s.popups.length > 0 ? s.popups[s.popups.length - 1] : null;
|
|
245
|
+
}
|
|
246
|
+
/** The page the screencast + interactive input should target: the top popup
|
|
247
|
+
* while one is open and being viewed, else the root page. */
|
|
248
|
+
activePage(s) {
|
|
249
|
+
return (s.popupView && this.topPopup(s)) || s.page;
|
|
250
|
+
}
|
|
251
|
+
watchPopups(s, opener) {
|
|
252
|
+
opener.onPopup?.((popup) => this.adoptPopup(s, popup));
|
|
253
|
+
}
|
|
254
|
+
/** Register a popup as a secondary page of the session: inherit the viewport,
|
|
255
|
+
* reroute the screencast + input to it (latest-wins), and auto-return to the
|
|
256
|
+
* opener when it closes (the typical OAuth self-close). */
|
|
257
|
+
adoptPopup(s, popup) {
|
|
258
|
+
if (s.closed || this.disposed) {
|
|
259
|
+
void popup.close().catch(() => { });
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
s.popups.push(popup);
|
|
263
|
+
s.popupView = true;
|
|
264
|
+
// Popups inherit the session viewport so coordinate mapping + screencast
|
|
265
|
+
// dimensions stay identical to the root page.
|
|
266
|
+
void popup.setViewport(s.viewport.width, s.viewport.height).catch(() => { });
|
|
267
|
+
popup.onClose?.(() => this.dropPopup(s, popup));
|
|
268
|
+
popup.onNavigated?.(() => {
|
|
269
|
+
// Keep the "Login window — <origin>" label fresh while the top popup
|
|
270
|
+
// walks its redirect chain.
|
|
271
|
+
if (!s.closed && this.topPopup(s) === popup)
|
|
272
|
+
this.broadcastPopupState(s);
|
|
273
|
+
});
|
|
274
|
+
// Popups can themselves open popups (rare, but some IdPs chain windows).
|
|
275
|
+
this.watchPopups(s, popup);
|
|
276
|
+
this.broadcastPopupState(s);
|
|
277
|
+
void this.applyScreencast(s);
|
|
278
|
+
}
|
|
279
|
+
dropPopup(s, popup) {
|
|
280
|
+
const i = s.popups.indexOf(popup);
|
|
281
|
+
if (i === -1)
|
|
282
|
+
return;
|
|
283
|
+
s.popups.splice(i, 1);
|
|
284
|
+
if (s.popups.length === 0)
|
|
285
|
+
s.popupView = false;
|
|
286
|
+
if (s.closed)
|
|
287
|
+
return;
|
|
288
|
+
this.broadcastPopupState(s);
|
|
289
|
+
// Re-routes the screencast to the next popup down or back to the opener;
|
|
290
|
+
// startScreencast emits a frame immediately, so the switch paints at once.
|
|
291
|
+
void this.applyScreencast(s);
|
|
292
|
+
}
|
|
293
|
+
broadcastPopupState(s) {
|
|
294
|
+
const top = this.topPopup(s);
|
|
295
|
+
this.broadcastControl(s, {
|
|
296
|
+
type: 'popup',
|
|
297
|
+
count: s.popups.length,
|
|
298
|
+
active: s.popupView && top != null,
|
|
299
|
+
url: top ? top.currentUrl() : null,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
/** Switch the viewed page between the root page and the top popup ("back to
|
|
303
|
+
* page" / "show login window"). Returns false for an unknown session. */
|
|
304
|
+
setPopupView(sessionId, target) {
|
|
305
|
+
const s = this.getSession(sessionId);
|
|
306
|
+
if (!s)
|
|
307
|
+
return false;
|
|
308
|
+
s.popupView = target === 'popup' && s.popups.length > 0;
|
|
309
|
+
this.broadcastPopupState(s);
|
|
310
|
+
void this.applyScreencast(s);
|
|
311
|
+
return true;
|
|
198
312
|
}
|
|
199
313
|
// ─── WS attach / detach + screencast fan-out ────────────────────────────────
|
|
200
314
|
async attach(sessionId, ws) {
|
|
@@ -207,6 +321,12 @@ class BrowserCaptureManager {
|
|
|
207
321
|
this.safeSend(ws, JSON.stringify({ type: 'ready', id: s.id, url: s.url, title: s.title, viewport: s.viewport }));
|
|
208
322
|
if (s.lastFrame)
|
|
209
323
|
this.safeSend(ws, s.lastFrame);
|
|
324
|
+
// A (re)joining client must learn about a live popup immediately, or it
|
|
325
|
+
// would render popup frames with the root-page chrome.
|
|
326
|
+
if (s.popups.length > 0) {
|
|
327
|
+
const top = this.topPopup(s);
|
|
328
|
+
this.safeSend(ws, JSON.stringify({ type: 'popup', count: s.popups.length, active: s.popupView && top != null, url: top ? top.currentUrl() : null }));
|
|
329
|
+
}
|
|
210
330
|
s.screencastDesired = true;
|
|
211
331
|
await this.applyScreencast(s);
|
|
212
332
|
return this.toMeta(s);
|
|
@@ -224,45 +344,48 @@ class BrowserCaptureManager {
|
|
|
224
344
|
/**
|
|
225
345
|
* Serialise screencast start/stop transitions on a per-session promise chain so
|
|
226
346
|
* a rapid detach→attach (stop fired async, then start) can't double-initialise
|
|
227
|
-
* the CDP screencast. The chain
|
|
228
|
-
* `
|
|
347
|
+
* the CDP screencast. The chain reconciles the page the screencast runs on
|
|
348
|
+
* (`screencastPage`) to the DESIRED page — the active page (top popup while one
|
|
349
|
+
* is viewed, else the root page) when `screencastDesired`, or none. A popup
|
|
350
|
+
* open/close/focus switch therefore stops the cast on the old page and starts
|
|
351
|
+
* it on the new one; the same frame callback fans out to the same clients.
|
|
229
352
|
*/
|
|
230
353
|
applyScreencast(s) {
|
|
231
354
|
const prev = s.screencastOp ?? Promise.resolve();
|
|
232
355
|
s.screencastOp = prev.then(async () => {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
s.screencasting = false;
|
|
236
|
-
try {
|
|
237
|
-
await s.page.stopScreencast();
|
|
238
|
-
}
|
|
239
|
-
catch { /* ignore */ }
|
|
240
|
-
}
|
|
356
|
+
const desired = !s.closed && s.screencastDesired ? this.activePage(s) : null;
|
|
357
|
+
if (s.screencastPage === desired)
|
|
241
358
|
return;
|
|
359
|
+
if (s.screencastPage) {
|
|
360
|
+
const old = s.screencastPage;
|
|
361
|
+
s.screencastPage = null;
|
|
362
|
+
try {
|
|
363
|
+
await old.stopScreencast();
|
|
364
|
+
}
|
|
365
|
+
catch { /* ignore */ }
|
|
242
366
|
}
|
|
243
|
-
if (
|
|
244
|
-
s.
|
|
245
|
-
await
|
|
367
|
+
if (desired) {
|
|
368
|
+
s.screencastPage = desired;
|
|
369
|
+
await desired.startScreencast((frame) => {
|
|
246
370
|
if (s.closed)
|
|
247
371
|
return;
|
|
248
372
|
s.lastFrame = frame.data;
|
|
249
373
|
for (const client of s.clients) {
|
|
250
|
-
if (client.readyState
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
374
|
+
if (client.readyState !== WS_OPEN)
|
|
375
|
+
continue;
|
|
376
|
+
// Conflate for slow consumers: when the socket's send-buffer is
|
|
377
|
+
// already backed up, skip this frame for that client instead of
|
|
378
|
+
// queueing ever-staler frames behind the backlog (latency, not
|
|
379
|
+
// fps, is what makes a screencast feel sluggish).
|
|
380
|
+
if ((client.bufferedAmount ?? 0) > MAX_CLIENT_BUFFERED_BYTES)
|
|
381
|
+
continue;
|
|
382
|
+
try {
|
|
383
|
+
client.send(frame.data);
|
|
255
384
|
}
|
|
385
|
+
catch { /* drop */ }
|
|
256
386
|
}
|
|
257
387
|
});
|
|
258
388
|
}
|
|
259
|
-
else if (!s.screencastDesired && s.screencasting) {
|
|
260
|
-
s.screencasting = false;
|
|
261
|
-
try {
|
|
262
|
-
await s.page.stopScreencast();
|
|
263
|
-
}
|
|
264
|
-
catch { /* ignore */ }
|
|
265
|
-
}
|
|
266
389
|
}).catch(() => { });
|
|
267
390
|
return s.screencastOp;
|
|
268
391
|
}
|
|
@@ -308,8 +431,20 @@ class BrowserCaptureManager {
|
|
|
308
431
|
width: Math.max(1, Math.round(event.width)),
|
|
309
432
|
height: Math.max(1, Math.round(event.height)),
|
|
310
433
|
};
|
|
434
|
+
// Keep every page of the session (root + popups) at the same viewport so
|
|
435
|
+
// switching between them never re-maps coordinates or resizes the canvas.
|
|
436
|
+
await s.page.dispatchInput(event);
|
|
437
|
+
for (const popup of s.popups) {
|
|
438
|
+
try {
|
|
439
|
+
await popup.dispatchInput(event);
|
|
440
|
+
}
|
|
441
|
+
catch { /* ignore */ }
|
|
442
|
+
}
|
|
443
|
+
return;
|
|
311
444
|
}
|
|
312
|
-
|
|
445
|
+
// Interactive input (mouse/wheel/keys) goes to the page being VIEWED — the
|
|
446
|
+
// top popup during an OAuth login, else the root page.
|
|
447
|
+
await this.activePage(s).dispatchInput(event);
|
|
313
448
|
}
|
|
314
449
|
async navigate(sessionId, action, url) {
|
|
315
450
|
if (this.disposed)
|
|
@@ -414,14 +549,17 @@ class BrowserCaptureManager {
|
|
|
414
549
|
const s = this.getSession(sessionId);
|
|
415
550
|
if (!s)
|
|
416
551
|
return null;
|
|
552
|
+
// Clipboard follows the viewed page: pasting credentials into an OAuth
|
|
553
|
+
// popup must land in the popup, not the opener behind it.
|
|
554
|
+
const page = this.activePage(s);
|
|
417
555
|
if (action === 'paste') {
|
|
418
556
|
if (text)
|
|
419
|
-
await
|
|
557
|
+
await page.insertText?.(text);
|
|
420
558
|
return { text: '' };
|
|
421
559
|
}
|
|
422
|
-
const sel = (await
|
|
560
|
+
const sel = (await page.getSelectionText?.()) ?? '';
|
|
423
561
|
if (action === 'cut' && sel)
|
|
424
|
-
await
|
|
562
|
+
await page.deleteSelection?.();
|
|
425
563
|
return { text: sel };
|
|
426
564
|
}
|
|
427
565
|
// ─── Multi-breakpoint capture ───────────────────────────────────────────────
|
|
@@ -551,6 +689,12 @@ class BrowserCaptureManager {
|
|
|
551
689
|
catch { /* ignore */ }
|
|
552
690
|
}
|
|
553
691
|
s.clients.clear();
|
|
692
|
+
for (const popup of s.popups.splice(0)) {
|
|
693
|
+
try {
|
|
694
|
+
await popup.close();
|
|
695
|
+
}
|
|
696
|
+
catch { /* ignore */ }
|
|
697
|
+
}
|
|
554
698
|
try {
|
|
555
699
|
await s.page.close();
|
|
556
700
|
}
|
|
@@ -577,6 +721,12 @@ class BrowserCaptureManager {
|
|
|
577
721
|
catch { /* ignore */ }
|
|
578
722
|
}
|
|
579
723
|
s.clients.clear();
|
|
724
|
+
for (const popup of s.popups.splice(0)) {
|
|
725
|
+
try {
|
|
726
|
+
await popup.close();
|
|
727
|
+
}
|
|
728
|
+
catch { /* ignore */ }
|
|
729
|
+
}
|
|
580
730
|
try {
|
|
581
731
|
await s.page.close();
|
|
582
732
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
// screencast API; input + screenshot + DOM use Playwright's high-level page API.
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.isNavigableUrl = isNavigableUrl;
|
|
12
|
+
exports.screencastParams = screencastParams;
|
|
12
13
|
exports.normalizeUrl = normalizeUrl;
|
|
13
14
|
exports.chromiumLaunchArgs = chromiumLaunchArgs;
|
|
14
15
|
exports.createPlaywrightLauncher = createPlaywrightLauncher;
|
|
@@ -94,6 +95,30 @@ function isBlockedHost(host) {
|
|
|
94
95
|
}
|
|
95
96
|
return false;
|
|
96
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* CDP `Page.startScreencast` parameters, centralised and env-tunable.
|
|
100
|
+
*
|
|
101
|
+
* - `quality` (JPEG, default 70) can be tuned via
|
|
102
|
+
* `SPECRAILS_BROWSER_SCREENCAST_QUALITY` (clamped to [1, 100]; non-numeric
|
|
103
|
+
* values fall back to the default). Lower = smaller frames = higher fps on
|
|
104
|
+
* slow links; higher = crisper at more bandwidth/decode cost.
|
|
105
|
+
* - `everyNthFrame: 1` — never skip source frames; the client-side pipeline
|
|
106
|
+
* drops stale frames instead (newest-wins), which keeps latency minimal.
|
|
107
|
+
* - `maxWidth`/`maxHeight` are a GUARD, not a scaler: the session viewport is
|
|
108
|
+
* sized to the displayed canvas (fit mode) or a device preset, so frames are
|
|
109
|
+
* already ≤ display size. The caps only prevent a pathological viewport from
|
|
110
|
+
* producing multi-megabyte frames. Chromium preserves aspect when clamping.
|
|
111
|
+
*/
|
|
112
|
+
function screencastParams(env = process.env) {
|
|
113
|
+
let quality = 70;
|
|
114
|
+
const raw = env.SPECRAILS_BROWSER_SCREENCAST_QUALITY;
|
|
115
|
+
if (raw !== undefined && raw !== '') {
|
|
116
|
+
const n = Number(raw);
|
|
117
|
+
if (Number.isFinite(n))
|
|
118
|
+
quality = Math.min(100, Math.max(1, Math.round(n)));
|
|
119
|
+
}
|
|
120
|
+
return { format: 'jpeg', quality, everyNthFrame: 1, maxWidth: 3840, maxHeight: 2400 };
|
|
121
|
+
}
|
|
97
122
|
/**
|
|
98
123
|
* Normalize a user-supplied address into a safe, navigable URL. A bare host
|
|
99
124
|
* (`example.com`) is upgraded to `https://`. The result is then validated by
|
|
@@ -125,6 +150,10 @@ class PlaywrightPageHandle {
|
|
|
125
150
|
netCdp = null;
|
|
126
151
|
netEnabled = false;
|
|
127
152
|
netBuffer = new browser_network_1.NetworkRingBuffer();
|
|
153
|
+
// Last position the page's virtual mouse was moved to. Lets the wheel path skip
|
|
154
|
+
// the redundant `mouse.move` a scroll gesture would otherwise re-issue for
|
|
155
|
+
// every tick at the same cursor position (halves the CDP commands per wheel).
|
|
156
|
+
lastMouse = null;
|
|
128
157
|
constructor(page) {
|
|
129
158
|
this.page = page;
|
|
130
159
|
}
|
|
@@ -187,16 +216,30 @@ class PlaywrightPageHandle {
|
|
|
187
216
|
async dispatchInput(event) {
|
|
188
217
|
try {
|
|
189
218
|
if (event.type === 'mouse') {
|
|
190
|
-
if (event.action === 'move')
|
|
219
|
+
if (event.action === 'move') {
|
|
220
|
+
this.lastMouse = { x: event.x, y: event.y };
|
|
191
221
|
await this.page.mouse.move(event.x, event.y);
|
|
192
|
-
|
|
222
|
+
}
|
|
223
|
+
else if (event.action === 'down') {
|
|
193
224
|
await this.page.mouse.down({ button: event.button ?? 'left', clickCount: event.clickCount ?? 1 });
|
|
194
|
-
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
195
227
|
await this.page.mouse.up({ button: event.button ?? 'left', clickCount: event.clickCount ?? 1 });
|
|
228
|
+
}
|
|
196
229
|
}
|
|
197
230
|
else if (event.type === 'wheel') {
|
|
198
|
-
|
|
199
|
-
|
|
231
|
+
// Scroll fast-path: skip the redundant reposition when the cursor hasn't
|
|
232
|
+
// moved since the last event (the common case mid-gesture), and dispatch
|
|
233
|
+
// move+wheel back-to-back without a sequential await gap — the Playwright
|
|
234
|
+
// driver sends commands in call order, so Chromium still sees move→wheel,
|
|
235
|
+
// but a concurrent wheel can no longer interleave between the two.
|
|
236
|
+
const ops = [];
|
|
237
|
+
if (!this.lastMouse || this.lastMouse.x !== event.x || this.lastMouse.y !== event.y) {
|
|
238
|
+
this.lastMouse = { x: event.x, y: event.y };
|
|
239
|
+
ops.push(this.page.mouse.move(event.x, event.y));
|
|
240
|
+
}
|
|
241
|
+
ops.push(this.page.mouse.wheel(event.deltaX, event.deltaY));
|
|
242
|
+
await Promise.all(ops);
|
|
200
243
|
}
|
|
201
244
|
else if (event.type === 'key') {
|
|
202
245
|
if (event.action === 'down') {
|
|
@@ -227,7 +270,16 @@ class PlaywrightPageHandle {
|
|
|
227
270
|
this.screencastHandler = onFrame;
|
|
228
271
|
const ctx = this.page.context();
|
|
229
272
|
this.cdp = await ctx.newCDPSession(this.page);
|
|
230
|
-
this.cdp.on('Page.screencastFrame',
|
|
273
|
+
this.cdp.on('Page.screencastFrame', (evt) => {
|
|
274
|
+
// Ack FIRST, fire-and-forget: Chromium throttles the screencast on unacked
|
|
275
|
+
// frames, so acking before the fan-out lets it capture+compress the next
|
|
276
|
+
// frame while this one is being delivered (pipeline overlap → higher fps,
|
|
277
|
+
// lower frame age). The frames are independent JPEGs, so there is no
|
|
278
|
+
// ordering hazard in acking early.
|
|
279
|
+
try {
|
|
280
|
+
this.cdp?.send('Page.screencastFrameAck', { sessionId: evt.sessionId })?.catch?.(() => { });
|
|
281
|
+
}
|
|
282
|
+
catch { /* ignore */ }
|
|
231
283
|
try {
|
|
232
284
|
this.screencastHandler?.({
|
|
233
285
|
data: Buffer.from(evt.data, 'base64'),
|
|
@@ -235,14 +287,9 @@ class PlaywrightPageHandle {
|
|
|
235
287
|
height: evt.metadata?.deviceHeight ?? 0,
|
|
236
288
|
});
|
|
237
289
|
}
|
|
238
|
-
|
|
239
|
-
try {
|
|
240
|
-
await this.cdp.send('Page.screencastFrameAck', { sessionId: evt.sessionId });
|
|
241
|
-
}
|
|
242
|
-
catch { /* ignore */ }
|
|
243
|
-
}
|
|
290
|
+
catch { /* never let a handler error kill the CDP session */ }
|
|
244
291
|
});
|
|
245
|
-
await this.cdp.send('Page.startScreencast',
|
|
292
|
+
await this.cdp.send('Page.startScreencast', screencastParams());
|
|
246
293
|
}
|
|
247
294
|
async stopScreencast() {
|
|
248
295
|
this.screencastHandler = null;
|
|
@@ -443,6 +490,44 @@ class PlaywrightPageHandle {
|
|
|
443
490
|
}
|
|
444
491
|
catch { /* ignore */ }
|
|
445
492
|
}
|
|
493
|
+
onPopup(cb) {
|
|
494
|
+
// Playwright fires 'popup' for pages this page opens (window.open,
|
|
495
|
+
// target=_blank, OAuth login windows). The popup lives in the SAME browser
|
|
496
|
+
// context — cookies and the window.opener/postMessage relationship are
|
|
497
|
+
// intact, which is exactly what an OAuth redirect/popup flow needs.
|
|
498
|
+
try {
|
|
499
|
+
this.page.on('popup', (p) => {
|
|
500
|
+
try {
|
|
501
|
+
cb(new PlaywrightPageHandle(p));
|
|
502
|
+
}
|
|
503
|
+
catch { /* never break the event loop */ }
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
catch { /* ignore */ }
|
|
507
|
+
}
|
|
508
|
+
onClose(cb) {
|
|
509
|
+
try {
|
|
510
|
+
this.page.on('close', () => {
|
|
511
|
+
try {
|
|
512
|
+
cb();
|
|
513
|
+
}
|
|
514
|
+
catch { /* ignore */ }
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
catch { /* ignore */ }
|
|
518
|
+
}
|
|
519
|
+
onNavigated(cb) {
|
|
520
|
+
try {
|
|
521
|
+
this.page.on('framenavigated', (frame) => {
|
|
522
|
+
try {
|
|
523
|
+
if (frame === this.page.mainFrame())
|
|
524
|
+
cb(frame.url());
|
|
525
|
+
}
|
|
526
|
+
catch { /* ignore */ }
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
catch { /* ignore */ }
|
|
530
|
+
}
|
|
446
531
|
async getSelectionText() {
|
|
447
532
|
try {
|
|
448
533
|
return await this.page.evaluate(() => window.getSelection()?.toString() ?? '');
|
|
@@ -12,6 +12,7 @@ const feature_flags_1 = require("./feature-flags");
|
|
|
12
12
|
const build_dirs_1 = require("./build-dirs");
|
|
13
13
|
const file_provenance_1 = require("./file-provenance");
|
|
14
14
|
const file_summary_manager_1 = require("./file-summary-manager");
|
|
15
|
+
const file_story_1 = require("./file-story");
|
|
15
16
|
const MAX_TREE_PAGE = 2000;
|
|
16
17
|
const MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
17
18
|
const BINARY_PROBE_BYTES = 8 * 1024;
|
|
@@ -661,6 +662,97 @@ function createCodeExplorerRouter(deps) {
|
|
|
661
662
|
}
|
|
662
663
|
res.json({ summary, summaryStale });
|
|
663
664
|
});
|
|
665
|
+
// Construction story: the chronological list of interventions (specs/jobs)
|
|
666
|
+
// that built this file — provenance rows enriched with diff stats, the AI
|
|
667
|
+
// contribution paragraph when generated, and the spec's live title/status.
|
|
668
|
+
// Same guards as every sibling content endpoint. Works for deleted files too
|
|
669
|
+
// (their story is still worth telling), so no stat/existence requirement.
|
|
670
|
+
router.get('/file/story', (req, res) => {
|
|
671
|
+
const relRaw = req.query.path;
|
|
672
|
+
if (!relRaw || typeof relRaw !== 'string') {
|
|
673
|
+
res.status(400).json({ error: 'path query parameter is required' });
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
const guard = resolveSafePath(deps.projectPath, relRaw);
|
|
677
|
+
if (!guard) {
|
|
678
|
+
res.status(400).json({ error: 'path traversal not allowed' });
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
if (isDeniedRelPath(relRaw)) {
|
|
682
|
+
res.status(403).json({ error: 'path is excluded by the code-explorer deny-list' });
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
const rel = normalizeRel(relRaw);
|
|
686
|
+
if (isGitIgnored(deps.projectPath, rel)) {
|
|
687
|
+
res.status(403).json({ error: 'path is gitignored' });
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
res.json({ path: rel, story: (0, file_story_1.getFileStory)(deps.db, rel, deps.getTicketSpec) });
|
|
691
|
+
});
|
|
692
|
+
// Construction story: generate (budget-gated) the plain-language "what this
|
|
693
|
+
// spec contributed" paragraph for ONE intervention. Awaits the single model
|
|
694
|
+
// turn; the file.story_updated WS event fires too so other open viewers of
|
|
695
|
+
// the same file refresh.
|
|
696
|
+
router.post('/file/story/explain', async (req, res) => {
|
|
697
|
+
const relRaw = req.query.path;
|
|
698
|
+
if (!relRaw || typeof relRaw !== 'string') {
|
|
699
|
+
res.status(400).json({ error: 'path query parameter is required' });
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const guard = resolveSafePath(deps.projectPath, relRaw);
|
|
703
|
+
if (!guard) {
|
|
704
|
+
res.status(400).json({ error: 'path traversal not allowed' });
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
if (isDeniedRelPath(relRaw)) {
|
|
708
|
+
res.status(403).json({ error: 'path is excluded by the code-explorer deny-list' });
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
const rel = normalizeRel(relRaw);
|
|
712
|
+
if (isGitIgnored(deps.projectPath, rel)) {
|
|
713
|
+
res.status(403).json({ error: 'path is gitignored' });
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
if (!deps.fileStoryManager) {
|
|
717
|
+
res.status(404).json({ error: 'story generation not available' });
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
const body = (req.body ?? {});
|
|
721
|
+
const provenanceId = typeof body.provenanceId === 'number' && Number.isInteger(body.provenanceId) && body.provenanceId > 0
|
|
722
|
+
? body.provenanceId
|
|
723
|
+
: null;
|
|
724
|
+
if (provenanceId == null) {
|
|
725
|
+
res.status(400).json({ error: 'provenanceId (positive integer) is required' });
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
try {
|
|
729
|
+
const result = await deps.fileStoryManager.explain({
|
|
730
|
+
projectId: deps.projectId,
|
|
731
|
+
relPath: rel,
|
|
732
|
+
provenanceId,
|
|
733
|
+
overrideBudget: body.overrideBudget === true,
|
|
734
|
+
});
|
|
735
|
+
if (result === 'generated') {
|
|
736
|
+
res.json({ ok: true });
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
if (result === 'skipped:budget') {
|
|
740
|
+
// 200 (not 4xx) so the client's budget-override prompt is reachable
|
|
741
|
+
// (mirrors /file/regenerate-summary).
|
|
742
|
+
res.status(200).json({ skipped: 'budget' });
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
if (result === 'skipped:not-found') {
|
|
746
|
+
res.status(404).json({ error: 'intervention not found for this file' });
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
res.status(500).json({ error: 'story generation failed' });
|
|
750
|
+
}
|
|
751
|
+
catch (err) {
|
|
752
|
+
console.error('[code-explorer-router] story explain failed:', err);
|
|
753
|
+
res.status(500).json({ error: 'story generation failed', message: err.message });
|
|
754
|
+
}
|
|
755
|
+
});
|
|
664
756
|
router.post('/file/regenerate-summary', async (req, res) => {
|
|
665
757
|
const relRaw = req.query.path;
|
|
666
758
|
if (!relRaw || typeof relRaw !== 'string') {
|