@opentig/cli 0.2.3 → 0.2.4
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/LICENSE +1 -1
- package/README.md +14 -14
- package/dist/bin.mjs +25 -10
- package/dist/chunks/{cli-L6lvCZdX.mjs → cli-gyjD1t0O.mjs} +611 -268
- package/dist/chunks/network-settings-CdFeKFRB.mjs +142 -0
- package/dist/chunks/{server-Cz0gvydn.mjs → server-DMk8-Xxt.mjs} +557 -105
- package/dist/chunks/{service-manager-Jyx-MWo-.mjs → service-manager-BxSmV_8Z.mjs} +88 -14
- package/dist/client/assets/App-Bfw_TX8q.js +132 -0
- package/dist/client/assets/CreatePullRequestDialog-DyPd6SKH.js +2 -0
- package/dist/client/assets/DiffWorkspace-DCo89dF1.js +7 -0
- package/dist/client/assets/EditableFileViewer-Dotntfij.js +1 -0
- package/dist/client/assets/EditableFileViewer-svutjHZv.css +1 -0
- package/dist/client/assets/ExternalLink-jdtpN_yL.js +1 -0
- package/dist/client/assets/{File-BPxtFxHC.js → File-DHnVtIhS.js} +1 -1
- package/dist/client/assets/FilesView-D6f1hPVy.js +1 -0
- package/dist/client/assets/GitHubAvatar-B6vyoVlh.js +1 -0
- package/dist/client/assets/HistoryView-D9RhzCPY.js +1 -0
- package/dist/client/assets/HtmlFileViewer-Bwmj5umg.js +2 -0
- package/dist/client/assets/{IconAlertTriangle-C1OX2bTH.js → IconAlertTriangle-B9iDJKvP.js} +1 -1
- package/dist/client/assets/IconCheck-C-G-IA13.js +1 -0
- package/dist/client/assets/IconChevronDown-DU5i40lD.js +1 -0
- package/dist/client/assets/{IconEdit-BP5Emwzw.js → IconEdit-CZe-EJxX.js} +1 -1
- package/dist/client/assets/{IconFolderSymlink-BNWI0eXB.js → IconFolderSymlink-DkLEuUb6.js} +1 -1
- package/dist/client/assets/IconGitMerge-D9KEj1cE.js +1 -0
- package/dist/client/assets/IconGitPullRequest-ClaUqyVa.js +1 -0
- package/dist/client/assets/{IconLoader4-Bk6G4Bls.js → IconLoader4-D2VN7u1V.js} +1 -1
- package/dist/client/assets/{IconPlus-DUFNSK_i.js → IconPlus-DJCoD3wt.js} +1 -1
- package/dist/client/assets/{IconSearch-ZL3YVslZ.js → IconSearch-1VQ4P4kW.js} +1 -1
- package/dist/client/assets/{IconSelector-BGwY84R8.js → IconSelector-BVMU1PEt.js} +1 -1
- package/dist/client/assets/ImageFileViewer-3izbFVTw.css +1 -0
- package/dist/client/assets/ImageFileViewer-CdFLl_in.js +1 -0
- package/dist/client/assets/LinkedMarkdown-Bnc2OA63.js +1 -0
- package/dist/client/assets/LinkedText-BD6K3d1t.js +1 -0
- package/dist/client/assets/LocalRefsDialog-Bl-fddap.js +1 -0
- package/dist/client/assets/MarkdownFileViewer-NUAoQmv5.js +2 -0
- package/dist/client/assets/PairingPage-CqrxpC3_.js +1 -0
- package/dist/client/assets/PierreDiffViewer-CDfQ32e0.js +1 -0
- package/dist/client/assets/{PierreEditBoundary-Bt0q7SXc.js → PierreEditBoundary-DAdYNYZ3.js} +1 -1
- package/dist/client/assets/{PierreWorkerPool-DMFigp0u.js → PierreWorkerPool-mIDzTMB9.js} +3 -3
- package/dist/client/assets/PullRequestDiff-n1NZFHKN.js +1 -0
- package/dist/client/assets/PullRequestViewer-u89zl169.js +2 -0
- package/dist/client/assets/PullRequestsView-D6BqvB4E.js +1 -0
- package/dist/client/assets/QuickOpenDialog-BbEifGRi.js +1 -0
- package/dist/client/assets/RepositoryProjectsDialog-EGAsXL-Z.js +1 -0
- package/dist/client/assets/{SearchView-BYz1K1Ta.js → SearchView-BMz7qOdc.js} +1 -1
- package/dist/client/assets/SettingsDialog-DmD7HvhX.js +9 -0
- package/dist/client/assets/Viewer-CDj_vo6w.js +3 -0
- package/dist/client/assets/{abnfDiagram-YUKMZFIV-CpKAN_8n.js → abnfDiagram-YUKMZFIV-It6tW-4v.js} +1 -1
- package/dist/client/assets/{architecture-WOLXFQ4H-DrizVopY.js → architecture-WOLXFQ4H-CaOaChND.js} +1 -1
- package/dist/client/assets/{architectureDiagram-47P4ROYG-gvUijB3H.js → architectureDiagram-47P4ROYG-COtncecA.js} +1 -1
- package/dist/client/assets/{blockDiagram-E7TT5TSB-CdcXmnf_.js → blockDiagram-E7TT5TSB-NU4wFrfP.js} +1 -1
- package/dist/client/assets/{c4Diagram-CRA5TL53-DtoD1jKt.js → c4Diagram-CRA5TL53-0xzM4S8q.js} +1 -1
- package/dist/client/assets/channel-QED-4ACa.js +1 -0
- package/dist/client/assets/checkbox-Dh4T0bCe.js +1 -0
- package/dist/client/assets/{chunk-2BW5OAIV-Bxqf2dFQ.js → chunk-2BW5OAIV-BQMxlSNI.js} +1 -1
- package/dist/client/assets/{chunk-3YJQHVM4-BhzXJiq0.js → chunk-3YJQHVM4-CdP15KtA.js} +1 -1
- package/dist/client/assets/{chunk-4EA7E6EY-CvEILf8c.js → chunk-4EA7E6EY-xRPCCZLU.js} +1 -1
- package/dist/client/assets/{chunk-7TKQ45FW-pTEdzVz3.js → chunk-7TKQ45FW-Cr_UAObV.js} +1 -1
- package/dist/client/assets/{chunk-AW2ZBBNX-LVROkcXk.js → chunk-AW2ZBBNX-DBqqxF2h.js} +1 -1
- package/dist/client/assets/{chunk-DBDB3WZW-FCGrB-q8.js → chunk-DBDB3WZW-BonEIRP2.js} +1 -1
- package/dist/client/assets/{chunk-E2ZNV5FY-B3rGXPn-.js → chunk-E2ZNV5FY-DnKvxSiA.js} +1 -1
- package/dist/client/assets/{chunk-EU5HNXII-D6a_Ly_W.js → chunk-EU5HNXII-DLK0K3sR.js} +1 -1
- package/dist/client/assets/{chunk-FVRAUYC3-B5hy0Ket.js → chunk-FVRAUYC3-DmjKoYAh.js} +1 -1
- package/dist/client/assets/{chunk-HJ2JQQFS-CKHFz1Wl.js → chunk-HJ2JQQFS-AF4phse0.js} +1 -1
- package/dist/client/assets/{chunk-HTAEGDNF-CtwDuO27.js → chunk-HTAEGDNF-CG4pBnUZ.js} +1 -1
- package/dist/client/assets/{chunk-KQW6MTUR-BN3uxLYf.js → chunk-KQW6MTUR-Dd87yj5r.js} +1 -1
- package/dist/client/assets/{chunk-NTY3LDVX-CfXSvyIC.js → chunk-NTY3LDVX-OolU2PaP.js} +1 -1
- package/dist/client/assets/{chunk-VPRB5NB3-LAggcSji.js → chunk-VPRB5NB3-Dnr3edxV.js} +3 -3
- package/dist/client/assets/{chunk-XC4XBNZT-BksIPWQU.js → chunk-XC4XBNZT-CIqGRxkD.js} +1 -1
- package/dist/client/assets/{classDiagram-v2-K4WV4PDN-DH78Maqm.js → classDiagram-v2-K4WV4PDN-CPGawm8n.js} +1 -1
- package/dist/client/assets/createReactComponent-Ctub7J3a.js +1 -0
- package/dist/client/assets/{cynefin-EF2NZ3EQ-DozKlDV1.js → cynefin-EF2NZ3EQ-OA2R9WCH.js} +1 -1
- package/dist/client/assets/{cynefinDiagram-3GCD6N5R-A1yS7NKN.js → cynefinDiagram-3GCD6N5R-DqCVtoKt.js} +1 -1
- package/dist/client/assets/{dagre-W4DXFKR2-DRJ1mmbe.js → dagre-W4DXFKR2-kc3-tkAQ.js} +1 -1
- package/dist/client/assets/{diagram-2UJZ2QOL-B1sI2b2o.js → diagram-2UJZ2QOL-DoBZKT3s.js} +1 -1
- package/dist/client/assets/{diagram-OVF4WLC6-DLE1o4-P.js → diagram-OVF4WLC6-DGkyha_F.js} +1 -1
- package/dist/client/assets/{diagram-PFPMY2P6-UtIEsv14.js → diagram-PFPMY2P6-DpRDDgdV.js} +1 -1
- package/dist/client/assets/{diagram-UMYRVEAY-BwTyzVap.js → diagram-UMYRVEAY-DlsfhYv3.js} +1 -1
- package/dist/client/assets/{diagram-YEKJPTXX-DNPUQqu0.js → diagram-YEKJPTXX-BIhRZzBM.js} +1 -1
- package/dist/client/assets/{diagram-ZIFT7M5P-ClXykQ3I.js → diagram-ZIFT7M5P-mweJcgBZ.js} +1 -1
- package/dist/client/assets/diff-parse.worker-C1IFRYY9.js +13 -0
- package/dist/client/assets/{ebnfDiagram-VR2GEFS7-Dm8VAviu.js → ebnfDiagram-VR2GEFS7-Dn5zyIbZ.js} +1 -1
- package/dist/client/assets/{elk-IJKZMXRS-CEEoPARn.js → elk-IJKZMXRS-eBPlf1-o.js} +1 -1
- package/dist/client/assets/{erDiagram-O2IAPWRE-CaDE4obX.js → erDiagram-O2IAPWRE-WEE519yZ.js} +1 -1
- package/dist/client/assets/{eventmodeling-K75KTNOO-PjYgMkPC.js → eventmodeling-K75KTNOO-BgE_VmNy.js} +1 -1
- package/dist/client/assets/external-url-IUS7RmYD.js +1 -0
- package/dist/client/assets/flowDiagram-OXPTDLAJ-CHQYfkMg.js +1 -0
- package/dist/client/assets/{ganttDiagram-R7TSEDQI-CmkBarBi.js → ganttDiagram-R7TSEDQI-D_1KyJZU.js} +1 -1
- package/dist/client/assets/{gitGraph-VSP46ZUC-DeotTdHj.js → gitGraph-VSP46ZUC-CEbT7DA4.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-XJZIOB7I-uV1hPaZT.js → gitGraphDiagram-XJZIOB7I-QQyYc1gM.js} +1 -1
- package/dist/client/assets/index-DsKvWsTs.js +47 -0
- package/dist/client/assets/index-kyWCnbkb.css +2 -0
- package/dist/client/assets/{info-OHQRW6UA-BQDsGvSl.js → info-OHQRW6UA-ChHAnveo.js} +1 -1
- package/dist/client/assets/{infoDiagram-5W2HQ5XZ-CDfEPsgZ.js → infoDiagram-5W2HQ5XZ-BaxAE7UA.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-K3B6WC7H-Bb7PQGe6.js → ishikawaDiagram-K3B6WC7H-vhUflBE_.js} +1 -1
- package/dist/client/assets/{journeyDiagram-COXZFDF6-BDythglw.js → journeyDiagram-COXZFDF6-C8WWosVa.js} +1 -1
- package/dist/client/assets/{kanban-definition-P3RFRI5V-CvY5kXhh.js → kanban-definition-P3RFRI5V-CU6W9Yi3.js} +1 -1
- package/dist/client/assets/katex-CQfTPFh0.js +1 -0
- package/dist/client/assets/{line-99p2yFnB.js → line-Axq4vVX-.js} +1 -1
- package/dist/client/assets/{mermaid-parser.core-BjZYtoJ8.js → mermaid-parser.core-DNeaCX12.js} +3 -3
- package/dist/client/assets/{mermaid.core-DRcXnUox.js → mermaid.core-Dtt7rXw0.js} +4 -4
- package/dist/client/assets/{mindmap-definition-LZFPQGKD-t9LoCpnW.js → mindmap-definition-LZFPQGKD-D_8ZqQot.js} +1 -1
- package/dist/client/assets/{packet-JDAUHWVQ-BlR-5V7K.js → packet-JDAUHWVQ-6z7ujmGF.js} +1 -1
- package/dist/client/assets/{pegDiagram-BGZESJAR-CWeGI-T9.js → pegDiagram-BGZESJAR-3ujyk-bt.js} +1 -1
- package/dist/client/assets/{pie-XZMESJXO-BkvYHWgl.js → pie-XZMESJXO-DvpLy2DB.js} +1 -1
- package/dist/client/assets/{pieDiagram-CAPJLFHJ-CY5PPIr3.js → pieDiagram-CAPJLFHJ-BPMPWyc7.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-GDMTTRAM-kqj-z271.js → quadrantDiagram-GDMTTRAM-BVUCR_lf.js} +1 -1
- package/dist/client/assets/{radar-ABXABTNO-DuEtYatv.js → radar-ABXABTNO-lu14X99R.js} +1 -1
- package/dist/client/assets/{railroad-I3PHUGI6-CVlnj2j9.js → railroad-I3PHUGI6-DI_WtDmt.js} +1 -1
- package/dist/client/assets/{railroad-abnf-LNEFI6M7-9Qiczw4d.js → railroad-abnf-LNEFI6M7-CmWAKZ2T.js} +1 -1
- package/dist/client/assets/{railroad-ebnf-DWYD2UWJ-BNL7TMEg.js → railroad-ebnf-DWYD2UWJ-BWKFU_UH.js} +1 -1
- package/dist/client/assets/{railroad-peg-JR7BVNR7-DdpRxgk6.js → railroad-peg-JR7BVNR7-9wkgzxMB.js} +1 -1
- package/dist/client/assets/{railroadDiagram-SM67HX2B-CDXvYNqW.js → railroadDiagram-SM67HX2B-4JnRbxl_.js} +1 -1
- package/dist/client/assets/{render-markdown-DF9Fy7MD.js → render-markdown-CQi2yKYQ.js} +2 -2
- package/dist/client/assets/{requirementDiagram-X7JNWC4A-BDdhqnZA.js → requirementDiagram-X7JNWC4A-CVJiZZsi.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-UM26HJRW-Rkyk5oEf.js → sankeyDiagram-UM26HJRW-BmlWQdkv.js} +1 -1
- package/dist/client/assets/select-Dq3KDX2O.js +1 -0
- package/dist/client/assets/{sequenceDiagram-ZO4K6R2Y-Coj9ap7h.js → sequenceDiagram-ZO4K6R2Y-zUx3LmPj.js} +1 -1
- package/dist/client/assets/{stateDiagram-v2-TBUQTH76-BucWlahR.js → stateDiagram-v2-TBUQTH76-o_ci-2bu.js} +1 -1
- package/dist/client/assets/{swimlanes-N4OXWK64-tEECcFP9.js → swimlanes-N4OXWK64-BlI0GC5-.js} +1 -1
- package/dist/client/assets/swimlanesDiagram-K3J5GTZL-BEeJLN8d.js +8 -0
- package/dist/client/assets/tanstack-CVAt_k_T.js +1 -0
- package/dist/client/assets/{timeline-definition-YOQAKGHF-XvIbGO4d.js → timeline-definition-YOQAKGHF-DLnvF__o.js} +1 -1
- package/dist/client/assets/{treeView-D4JQ5SDB-DuhJpfXe.js → treeView-D4JQ5SDB-DPuMLQhB.js} +1 -1
- package/dist/client/assets/{treemap-SAJKECNS-CLiUWfZh.js → treemap-SAJKECNS-DjxqxJ8-.js} +1 -1
- package/dist/client/assets/{usecaseDiagram-VIAY4XPW-BuwkpV9w.js → usecaseDiagram-VIAY4XPW-BtnFNltN.js} +1 -1
- package/dist/client/assets/utils-D0T3A-kS.js +1 -0
- package/dist/client/assets/{vennDiagram-BLWOH2XV-kfza5Rxq.js → vennDiagram-BLWOH2XV-CAiF2FKz.js} +1 -1
- package/dist/client/assets/viewer-tabs-DkkqN9MD.js +1 -0
- package/dist/client/assets/{wardley-7MLQ67FV-CcbU_Ykg.js → wardley-7MLQ67FV-DAWySamq.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-YMQ3BMBF-DnxlEg3a.js → wardleyDiagram-YMQ3BMBF-DiBCclEL.js} +1 -1
- package/dist/client/assets/{xychartDiagram-TAQBALBS-BzgtwtvK.js → xychartDiagram-TAQBALBS-B0NgfXz6.js} +1 -1
- package/dist/client/index.html +6 -4
- package/dist/dev.mjs +6 -6
- package/dist/manifest.json +1 -1
- package/dist/server.mjs +1 -1
- package/dist/service-update.mjs +11 -3
- package/dist/utility.mjs +25 -4
- package/package.json +4 -4
- package/dist/client/assets/App-DEIZhWJG.js +0 -132
- package/dist/client/assets/CreatePullRequestDialog-BceOUbYK.js +0 -2
- package/dist/client/assets/DiffWorkspace-BMtV4MMc.js +0 -6
- package/dist/client/assets/EditableFileViewer-B44TSuFu.css +0 -1
- package/dist/client/assets/EditableFileViewer-BE3AsnVa.js +0 -1
- package/dist/client/assets/FilesView-DoVhRfi1.js +0 -1
- package/dist/client/assets/GitHubAvatar-RiePq1Yf.js +0 -1
- package/dist/client/assets/HistoryView-Dl8uqmKe.js +0 -1
- package/dist/client/assets/HtmlFileViewer-xfjY1Swc.js +0 -2
- package/dist/client/assets/IconCheck-BPhxw_LS.js +0 -1
- package/dist/client/assets/IconChevronDown-C3FplJL8.js +0 -1
- package/dist/client/assets/ImageFileViewer-BJBOmpsf.css +0 -1
- package/dist/client/assets/ImageFileViewer-DXaIo7_d.js +0 -1
- package/dist/client/assets/LocalRefsDialog-C5J3kgTe.js +0 -1
- package/dist/client/assets/MarkdownFileViewer-18mvm0f2.js +0 -2
- package/dist/client/assets/PairingPage-BEUj3t0V.js +0 -1
- package/dist/client/assets/PierreDiffViewer-p29h7lW_.js +0 -1
- package/dist/client/assets/PullRequestDiff-C17JA4ko.js +0 -1
- package/dist/client/assets/PullRequestViewer-DaMtkESD.js +0 -2
- package/dist/client/assets/PullRequestsView-44f03VLC.js +0 -1
- package/dist/client/assets/QuickOpenDialog-Bi-L68PU.js +0 -1
- package/dist/client/assets/RepositoryProjectsDialog-DFGn74fe.js +0 -1
- package/dist/client/assets/SettingsDialog-CruoIiyN.js +0 -9
- package/dist/client/assets/Viewer-3WKpwRzo.js +0 -3
- package/dist/client/assets/channel-BqKQxg-1.js +0 -1
- package/dist/client/assets/checkbox-MDl2t9_3.js +0 -1
- package/dist/client/assets/createReactComponent-BfrD_oQK.js +0 -1
- package/dist/client/assets/flowDiagram-OXPTDLAJ-DSokwYKA.js +0 -1
- package/dist/client/assets/index-CoZCJqTQ.js +0 -47
- package/dist/client/assets/index-DUERTTRS.css +0 -2
- package/dist/client/assets/katex-C6e6EqXB.js +0 -1
- package/dist/client/assets/select-pZpCy7qn.js +0 -1
- package/dist/client/assets/swimlanesDiagram-K3J5GTZL-DEFKR6Vf.js +0 -8
- package/dist/client/assets/tanstack-CYi5knVJ.js +0 -1
- package/dist/client/assets/viewer-tabs-MMNrJqGE.js +0 -1
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { a as normalizePairingOrigin, c as redactSensitiveText, i as loadStartupNetwork, n as applyNetworkPatch, o as backupPathFor, r as isLoopbackHost, s as writeFileAtomically } from "./network-settings-CdFeKFRB.mjs";
|
|
1
2
|
import process$1, { execArgv, execPath, hrtime, platform } from "node:process";
|
|
2
|
-
import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
|
|
3
|
-
import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
4
|
-
import fs, { appendFileSync, closeSync, constants, createReadStream, createWriteStream, openSync, readFileSync, readSync, statSync, watch, writeFileSync } from "node:fs";
|
|
5
3
|
import { access, appendFile, chmod, cp, link, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
6
4
|
import path, { delimiter, dirname, extname, isAbsolute, join, resolve } from "node:path";
|
|
5
|
+
import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
6
|
+
import fs, { appendFileSync, closeSync, constants, createReadStream, createWriteStream, openSync, readFileSync, readSync, statSync, watch, writeFileSync } from "node:fs";
|
|
7
7
|
import os, { constants as constants$1, homedir, networkInterfaces } from "node:os";
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
|
-
import {
|
|
9
|
+
import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
|
|
10
|
+
import { aborted, callbackify, debuglog, inspect, promisify, stripVTControlCharacters, types } from "node:util";
|
|
10
11
|
import { StringDecoder } from "node:string_decoder";
|
|
11
12
|
import tty from "node:tty";
|
|
12
13
|
import { scheduler, setImmediate, setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
@@ -12931,26 +12932,6 @@ function refine(fn, _params = {}) {
|
|
|
12931
12932
|
function superRefine(fn, params) {
|
|
12932
12933
|
return /* @__PURE__ */ _superRefine(fn, params);
|
|
12933
12934
|
}
|
|
12934
|
-
//#endregion
|
|
12935
|
-
//#region ../../src/shared/redaction.ts
|
|
12936
|
-
/** Removes credential-shaped values before text reaches logs or clients. */
|
|
12937
|
-
function redactSensitiveText(value) {
|
|
12938
|
-
return value.replace(/\b(?:github_pat_|gh[opusr]_)[A-Za-z0-9_]{16,}/g, "[redacted]").replace(/\bauthorization\s*[:=]\s*(?:bearer|basic)\s+[^\s,;]+/gi, "authorization=[redacted]").replace(/\b(authorization|cookie|set-cookie)\s*[:=]\s*[^\s,;]+/gi, "$1=[redacted]").replace(/\b(token|secret|password)\s*[:=]\s*[^\s,;]+/gi, "$1=[redacted]").replace(/https?:\/\/[^\s"'<>]+/gi, (candidate) => redactUrl(candidate));
|
|
12939
|
-
}
|
|
12940
|
-
function redactUrl(candidate) {
|
|
12941
|
-
try {
|
|
12942
|
-
const url = new URL(candidate);
|
|
12943
|
-
if (url.username || url.password) {
|
|
12944
|
-
url.username = "[redacted]";
|
|
12945
|
-
url.password = "";
|
|
12946
|
-
}
|
|
12947
|
-
url.search = "";
|
|
12948
|
-
url.hash = "";
|
|
12949
|
-
return url.toString();
|
|
12950
|
-
} catch {
|
|
12951
|
-
return "[redacted-url]";
|
|
12952
|
-
}
|
|
12953
|
-
}
|
|
12954
12935
|
var EMPTY_AI_USAGE = {
|
|
12955
12936
|
inputTokens: null,
|
|
12956
12937
|
outputTokens: null,
|
|
@@ -13104,34 +13085,6 @@ function flattenWhitespace$1(value) {
|
|
|
13104
13085
|
return flattened.replace(/ {2,}/g, " ").trim();
|
|
13105
13086
|
}
|
|
13106
13087
|
//#endregion
|
|
13107
|
-
//#region ../../src/main/persistence/atomicWrite.ts
|
|
13108
|
-
function backupPathFor(filePath) {
|
|
13109
|
-
return `${filePath}.bak`;
|
|
13110
|
-
}
|
|
13111
|
-
async function writeFileAtomically(filePath, contents, options = {}) {
|
|
13112
|
-
if (options.parseJson) JSON.parse(contents);
|
|
13113
|
-
const directory = path.dirname(filePath);
|
|
13114
|
-
await mkdir(directory, {
|
|
13115
|
-
recursive: true,
|
|
13116
|
-
...options.directoryMode === void 0 ? {} : { mode: options.directoryMode }
|
|
13117
|
-
});
|
|
13118
|
-
const temporary = `${filePath}.${randomUUID()}.tmp`;
|
|
13119
|
-
try {
|
|
13120
|
-
const handle = await open(temporary, "wx", 384);
|
|
13121
|
-
try {
|
|
13122
|
-
await handle.writeFile(contents, "utf8");
|
|
13123
|
-
await handle.sync();
|
|
13124
|
-
} finally {
|
|
13125
|
-
await handle.close();
|
|
13126
|
-
}
|
|
13127
|
-
await rename(temporary, filePath);
|
|
13128
|
-
await chmod(filePath, 384);
|
|
13129
|
-
} catch (error) {
|
|
13130
|
-
await rm(temporary, { force: true });
|
|
13131
|
-
throw error;
|
|
13132
|
-
}
|
|
13133
|
-
}
|
|
13134
|
-
//#endregion
|
|
13135
13088
|
//#region ../../src/main/persistence/AiLogStore.ts
|
|
13136
13089
|
/**
|
|
13137
13090
|
* Records a run without ever letting the attempt reach the caller. Diagnostics
|
|
@@ -17348,6 +17301,64 @@ function isGitHubHttp(value) {
|
|
|
17348
17301
|
}
|
|
17349
17302
|
}
|
|
17350
17303
|
//#endregion
|
|
17304
|
+
//#region ../../src/main/git/ReadSingleFlight.ts
|
|
17305
|
+
/** Shares pending attempts only; invalidated results/errors get at most two retries. */
|
|
17306
|
+
var ReadSingleFlight = class {
|
|
17307
|
+
generations = /* @__PURE__ */ new Map();
|
|
17308
|
+
invalidate(scope) {
|
|
17309
|
+
const generation = this.generations.get(scope);
|
|
17310
|
+
if (generation) generation.invalidated = true;
|
|
17311
|
+
this.generations.delete(scope);
|
|
17312
|
+
}
|
|
17313
|
+
clear() {
|
|
17314
|
+
for (const scope of this.generations.keys()) this.invalidate(scope);
|
|
17315
|
+
}
|
|
17316
|
+
async run(scope, key, read, retryInvalidated = true) {
|
|
17317
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
17318
|
+
const { generation, promise } = this.pending(scope, key, read);
|
|
17319
|
+
try {
|
|
17320
|
+
const value = await promise;
|
|
17321
|
+
if (!generation.invalidated || !retryInvalidated) return value;
|
|
17322
|
+
} catch (error) {
|
|
17323
|
+
if (!generation.invalidated || !retryInvalidated) throw error;
|
|
17324
|
+
}
|
|
17325
|
+
}
|
|
17326
|
+
throw new Error("The repository changed repeatedly during the read. Try refreshing again.");
|
|
17327
|
+
}
|
|
17328
|
+
pending(scope, key, read) {
|
|
17329
|
+
let generation = this.generations.get(scope);
|
|
17330
|
+
if (!generation) {
|
|
17331
|
+
generation = {
|
|
17332
|
+
invalidated: false,
|
|
17333
|
+
flights: /* @__PURE__ */ new Map()
|
|
17334
|
+
};
|
|
17335
|
+
this.generations.set(scope, generation);
|
|
17336
|
+
}
|
|
17337
|
+
const existing = generation.flights.get(key);
|
|
17338
|
+
if (existing) return {
|
|
17339
|
+
generation,
|
|
17340
|
+
promise: existing
|
|
17341
|
+
};
|
|
17342
|
+
const current = generation;
|
|
17343
|
+
const release = () => {
|
|
17344
|
+
if (current.flights.get(key) === promise) current.flights.delete(key);
|
|
17345
|
+
if (!current.flights.size && this.generations.get(scope) === current) this.generations.delete(scope);
|
|
17346
|
+
};
|
|
17347
|
+
const promise = Promise.resolve().then(read).then((value) => {
|
|
17348
|
+
release();
|
|
17349
|
+
return value;
|
|
17350
|
+
}, (error) => {
|
|
17351
|
+
release();
|
|
17352
|
+
throw error;
|
|
17353
|
+
});
|
|
17354
|
+
current.flights.set(key, promise);
|
|
17355
|
+
return {
|
|
17356
|
+
generation,
|
|
17357
|
+
promise
|
|
17358
|
+
};
|
|
17359
|
+
}
|
|
17360
|
+
};
|
|
17361
|
+
//#endregion
|
|
17351
17362
|
//#region ../../src/main/git/GitProcess.ts
|
|
17352
17363
|
/**
|
|
17353
17364
|
* Two spellings of the same directory must share one queue. Collapsing case and
|
|
@@ -17358,6 +17369,9 @@ function normalizeLockKey(value) {
|
|
|
17358
17369
|
return process.platform === "win32" ? unified.toLowerCase() : unified;
|
|
17359
17370
|
}
|
|
17360
17371
|
var GitProcess = class {
|
|
17372
|
+
reads = new ReadSingleFlight();
|
|
17373
|
+
repositoryScopes = /* @__PURE__ */ new Map();
|
|
17374
|
+
invalidationListeners = /* @__PURE__ */ new Set();
|
|
17361
17375
|
writeQueues = /* @__PURE__ */ new Map();
|
|
17362
17376
|
activeChildren = /* @__PURE__ */ new Set();
|
|
17363
17377
|
activeControllers = /* @__PURE__ */ new Set();
|
|
@@ -17369,6 +17383,32 @@ var GitProcess = class {
|
|
|
17369
17383
|
withNetworkContext(cwd, task) {
|
|
17370
17384
|
return this.networkScope ? this.networkScope(cwd, task) : task();
|
|
17371
17385
|
}
|
|
17386
|
+
registerRepository(cwd, commonDir) {
|
|
17387
|
+
const key = normalizeLockKey(path.resolve(cwd));
|
|
17388
|
+
const scope = normalizeLockKey(path.resolve(commonDir));
|
|
17389
|
+
if (this.repositoryScopes.get(key) === scope) return;
|
|
17390
|
+
this.invalidate(cwd);
|
|
17391
|
+
this.repositoryScopes.set(key, scope);
|
|
17392
|
+
}
|
|
17393
|
+
unregisterRepository(cwd) {
|
|
17394
|
+
this.invalidate(cwd);
|
|
17395
|
+
this.repositoryScopes.delete(normalizeLockKey(path.resolve(cwd)));
|
|
17396
|
+
}
|
|
17397
|
+
readScope(cwd) {
|
|
17398
|
+
const key = normalizeLockKey(path.resolve(cwd));
|
|
17399
|
+
return this.repositoryScopes.get(key) ?? key;
|
|
17400
|
+
}
|
|
17401
|
+
onInvalidate(listener) {
|
|
17402
|
+
this.invalidationListeners.add(listener);
|
|
17403
|
+
}
|
|
17404
|
+
invalidate(cwd) {
|
|
17405
|
+
const scope = this.readScope(cwd);
|
|
17406
|
+
this.reads.invalidate(scope);
|
|
17407
|
+
for (const listener of this.invalidationListeners) listener(scope);
|
|
17408
|
+
}
|
|
17409
|
+
shareRead(cwd, key, read, retryInvalidated = true) {
|
|
17410
|
+
return this.reads.run(this.readScope(cwd), JSON.stringify([path.resolve(cwd), key]), read, retryInvalidated);
|
|
17411
|
+
}
|
|
17372
17412
|
run(cwd, args, options) {
|
|
17373
17413
|
let index = 0;
|
|
17374
17414
|
while (args[index] === "-c") index += 2;
|
|
@@ -17376,13 +17416,28 @@ var GitProcess = class {
|
|
|
17376
17416
|
"fetch",
|
|
17377
17417
|
"push",
|
|
17378
17418
|
"ls-remote"
|
|
17379
|
-
].includes(args[index] ?? "")) return this.network(cwd, args, options, (next, settings) => this.runDirect(cwd, next, settings));
|
|
17419
|
+
].includes(args[index] ?? "")) return this.network(cwd, args, options, (next, settings) => this.runDirect(cwd, next, settings, false));
|
|
17380
17420
|
return this.runDirect(cwd, args, options);
|
|
17381
17421
|
}
|
|
17382
|
-
runDirect(cwd, args, options) {
|
|
17422
|
+
runDirect(cwd, args, options, retryInvalidated = true) {
|
|
17383
17423
|
const globalArgs = ["--no-pager"];
|
|
17384
17424
|
if (options.readOnly) globalArgs.push("--no-optional-locks");
|
|
17385
|
-
|
|
17425
|
+
const execute = () => this.spawnGit(cwd, [...globalArgs, ...args], options);
|
|
17426
|
+
if (options.readOnly) {
|
|
17427
|
+
if ("signal" in options && options.signal != null || "onStderr" in options && options.onStderr != null) return execute();
|
|
17428
|
+
const key = createHash("sha256").update(JSON.stringify([
|
|
17429
|
+
args,
|
|
17430
|
+
options,
|
|
17431
|
+
this.authenticationKey?.(cwd)
|
|
17432
|
+
])).digest("hex");
|
|
17433
|
+
return this.shareRead(cwd, `git:${key}`, execute, retryInvalidated).then((output) => ({
|
|
17434
|
+
...output,
|
|
17435
|
+
stdout: Buffer.from(output.stdout),
|
|
17436
|
+
stderr: Buffer.from(output.stderr)
|
|
17437
|
+
}));
|
|
17438
|
+
}
|
|
17439
|
+
this.invalidate(cwd);
|
|
17440
|
+
return execute().finally(() => this.invalidate(cwd));
|
|
17386
17441
|
}
|
|
17387
17442
|
/**
|
|
17388
17443
|
* `lockKey` serializes against a queue other than `cwd` without changing the
|
|
@@ -17406,9 +17461,11 @@ var GitProcess = class {
|
|
|
17406
17461
|
const queued = previous.then(() => current);
|
|
17407
17462
|
this.writeQueues.set(key, queued);
|
|
17408
17463
|
await previous;
|
|
17464
|
+
this.invalidate(rawKey);
|
|
17409
17465
|
try {
|
|
17410
17466
|
return await task();
|
|
17411
17467
|
} finally {
|
|
17468
|
+
this.invalidate(rawKey);
|
|
17412
17469
|
release();
|
|
17413
17470
|
if (this.writeQueues.get(key) === queued) this.writeQueues.delete(key);
|
|
17414
17471
|
}
|
|
@@ -20525,6 +20582,7 @@ var RepositoryService = class {
|
|
|
20525
20582
|
async preparePath(selectedPath) {
|
|
20526
20583
|
const repository = await this.inspectPath(selectedPath);
|
|
20527
20584
|
this.repositories.set(repository.id, repository);
|
|
20585
|
+
this.git.registerRepository(repository.path, repository.commonDir);
|
|
20528
20586
|
return repository;
|
|
20529
20587
|
}
|
|
20530
20588
|
async completeSetup(id, validate = () => void 0) {
|
|
@@ -20541,6 +20599,7 @@ var RepositoryService = class {
|
|
|
20541
20599
|
operation: "relocate-repository",
|
|
20542
20600
|
message: "The recent repository no longer exists."
|
|
20543
20601
|
});
|
|
20602
|
+
const previous = this.get(id);
|
|
20544
20603
|
const repository = await this.inspectPath(selectedPath);
|
|
20545
20604
|
if (repository.id !== id && this.settings.recentRepositories.some((item) => item.id === repository.id)) throw new GitOperationError({
|
|
20546
20605
|
code: "INVALID_ARGUMENT",
|
|
@@ -20548,14 +20607,19 @@ var RepositoryService = class {
|
|
|
20548
20607
|
message: "That folder is already in OpenTig. Remove the old entry instead, or choose a different location."
|
|
20549
20608
|
});
|
|
20550
20609
|
await this.settings.relocateRepository(id, repository);
|
|
20610
|
+
this.git.unregisterRepository(previous.path);
|
|
20551
20611
|
this.repositories.delete(id);
|
|
20552
20612
|
this.repositories.set(repository.id, repository);
|
|
20613
|
+
this.git.registerRepository(repository.path, repository.commonDir);
|
|
20553
20614
|
return repository;
|
|
20554
20615
|
}
|
|
20555
20616
|
async forget(repositoryKey) {
|
|
20556
20617
|
const key = normalizeRepositoryKey(repositoryKey);
|
|
20557
20618
|
const organization = await this.settings.forgetRepository(key);
|
|
20558
|
-
for (const [id, repository] of this.repositories) if (normalizeRepositoryKey(repository.commonDir) === key)
|
|
20619
|
+
for (const [id, repository] of this.repositories) if (normalizeRepositoryKey(repository.commonDir) === key) {
|
|
20620
|
+
this.git.unregisterRepository(repository.path);
|
|
20621
|
+
this.repositories.delete(id);
|
|
20622
|
+
}
|
|
20559
20623
|
return organization;
|
|
20560
20624
|
}
|
|
20561
20625
|
recent(id) {
|
|
@@ -20602,6 +20666,7 @@ var RepositoryService = class {
|
|
|
20602
20666
|
commonDir: recent.commonDir
|
|
20603
20667
|
};
|
|
20604
20668
|
this.repositories.set(id, hydrated);
|
|
20669
|
+
this.git.registerRepository(hydrated.path, hydrated.commonDir);
|
|
20605
20670
|
return hydrated;
|
|
20606
20671
|
}
|
|
20607
20672
|
throw new GitOperationError({
|
|
@@ -20614,6 +20679,16 @@ var RepositoryService = class {
|
|
|
20614
20679
|
return this.settings.recentRepositories;
|
|
20615
20680
|
}
|
|
20616
20681
|
async status(id, includeStats = true) {
|
|
20682
|
+
const repository = this.get(id);
|
|
20683
|
+
return structuredClone(await this.git.shareRead(repository.path, `status:${includeStats}`, () => this.readStatus(id, includeStats)));
|
|
20684
|
+
}
|
|
20685
|
+
invalidate(id) {
|
|
20686
|
+
this.git.invalidate(this.get(id).path);
|
|
20687
|
+
}
|
|
20688
|
+
invalidateIfKnown(id) {
|
|
20689
|
+
if (typeof id === "string" && (this.repositories.has(id) || this.recent(id))) this.invalidate(id);
|
|
20690
|
+
}
|
|
20691
|
+
async readStatus(id, includeStats) {
|
|
20617
20692
|
const repository = this.get(id);
|
|
20618
20693
|
const status = parseStatus((await this.git.run(repository.path, [
|
|
20619
20694
|
"status",
|
|
@@ -20643,7 +20718,10 @@ var RepositoryService = class {
|
|
|
20643
20718
|
*/
|
|
20644
20719
|
async forgetWorktree(worktreePath) {
|
|
20645
20720
|
const target = normalizeWorktreePath$1(worktreePath);
|
|
20646
|
-
for (const [id, repository] of this.repositories) if (normalizeWorktreePath$1(repository.path) === target)
|
|
20721
|
+
for (const [id, repository] of this.repositories) if (normalizeWorktreePath$1(repository.path) === target) {
|
|
20722
|
+
this.git.unregisterRepository(repository.path);
|
|
20723
|
+
this.repositories.delete(id);
|
|
20724
|
+
}
|
|
20647
20725
|
return this.settings.forgetWorktreePath(worktreePath);
|
|
20648
20726
|
}
|
|
20649
20727
|
/**
|
|
@@ -21851,6 +21929,7 @@ var GitHubService = class {
|
|
|
21851
21929
|
runner;
|
|
21852
21930
|
git;
|
|
21853
21931
|
repositories;
|
|
21932
|
+
reads = new ReadSingleFlight();
|
|
21854
21933
|
gitAccess;
|
|
21855
21934
|
accounts;
|
|
21856
21935
|
constructor(resolver, runner, git, repositories, accounts) {
|
|
@@ -21858,9 +21937,37 @@ var GitHubService = class {
|
|
|
21858
21937
|
this.git = git;
|
|
21859
21938
|
this.repositories = repositories;
|
|
21860
21939
|
this.accounts = accounts ?? new GitHubAccountsService(resolver, runner, git);
|
|
21940
|
+
this.git.onInvalidate((scope) => this.reads.invalidate(scope));
|
|
21941
|
+
}
|
|
21942
|
+
invalidate(repositoryId) {
|
|
21943
|
+
if (repositoryId) this.reads.invalidate(this.git.readScope(this.repositories.get(repositoryId).path));
|
|
21944
|
+
else this.reads.clear();
|
|
21945
|
+
}
|
|
21946
|
+
async shareRead(repositoryId, resource, read) {
|
|
21947
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
21948
|
+
const repository = this.repositories.get(repositoryId);
|
|
21949
|
+
const revision = this.accounts.authRevision;
|
|
21950
|
+
const key = JSON.stringify([
|
|
21951
|
+
repository.path,
|
|
21952
|
+
repository.commonDir,
|
|
21953
|
+
revision,
|
|
21954
|
+
this.accounts.revisionFor(repository),
|
|
21955
|
+
...resource
|
|
21956
|
+
]);
|
|
21957
|
+
try {
|
|
21958
|
+
const value = await this.reads.run(this.git.readScope(repository.path), key, read);
|
|
21959
|
+
if (revision === this.accounts.authRevision) return structuredClone(value);
|
|
21960
|
+
} catch (error) {
|
|
21961
|
+
if (revision === this.accounts.authRevision) throw error;
|
|
21962
|
+
}
|
|
21963
|
+
}
|
|
21964
|
+
throw new Error("The GitHub account changed repeatedly during the read. Try refreshing again.");
|
|
21861
21965
|
}
|
|
21862
21966
|
async status(forceRefresh = false) {
|
|
21863
|
-
if (forceRefresh)
|
|
21967
|
+
if (forceRefresh) {
|
|
21968
|
+
this.invalidate();
|
|
21969
|
+
this.accounts.refreshAuthentication();
|
|
21970
|
+
}
|
|
21864
21971
|
const detected = await this.accounts.detect(forceRefresh);
|
|
21865
21972
|
return {
|
|
21866
21973
|
installed: !!detected.candidate,
|
|
@@ -21871,6 +21978,7 @@ var GitHubService = class {
|
|
|
21871
21978
|
};
|
|
21872
21979
|
}
|
|
21873
21980
|
async repositoryAccount(repositoryId, forceRefresh = false) {
|
|
21981
|
+
if (forceRefresh) this.invalidate(repositoryId);
|
|
21874
21982
|
const repository = this.repositories.get(repositoryId);
|
|
21875
21983
|
const info = await this.repositoryInfo(repositoryId);
|
|
21876
21984
|
const remote = await this.remote(repositoryId);
|
|
@@ -21911,9 +22019,23 @@ var GitHubService = class {
|
|
|
21911
22019
|
return "";
|
|
21912
22020
|
}
|
|
21913
22021
|
}
|
|
21914
|
-
|
|
22022
|
+
repositoryInfo(repositoryId) {
|
|
22023
|
+
return this.shareRead(repositoryId, [], () => this.readRepositoryInfo(repositoryId));
|
|
22024
|
+
}
|
|
22025
|
+
async readRepositoryInfo(repositoryId) {
|
|
22026
|
+
return (await this.routing(repositoryId)).info;
|
|
22027
|
+
}
|
|
22028
|
+
routing(repositoryId) {
|
|
22029
|
+
return this.shareRead(repositoryId, ["routing"], async () => {
|
|
22030
|
+
const remote = await this.remote(repositoryId);
|
|
22031
|
+
return {
|
|
22032
|
+
remote,
|
|
22033
|
+
info: await this.infoForRemote(remote)
|
|
22034
|
+
};
|
|
22035
|
+
});
|
|
22036
|
+
}
|
|
22037
|
+
async infoForRemote(url) {
|
|
21915
22038
|
try {
|
|
21916
|
-
const url = await this.remote(repositoryId);
|
|
21917
22039
|
let parsed = parseGitHubRemote(url);
|
|
21918
22040
|
if (!parsed) {
|
|
21919
22041
|
const ssh = parseSshRemote(url);
|
|
@@ -21946,9 +22068,12 @@ var GitHubService = class {
|
|
|
21946
22068
|
};
|
|
21947
22069
|
}
|
|
21948
22070
|
}
|
|
21949
|
-
|
|
22071
|
+
listPullRequests(repositoryId, states) {
|
|
22072
|
+
return this.shareRead(repositoryId, ["list", [...states].sort()], () => this.readListPullRequests(repositoryId, states));
|
|
22073
|
+
}
|
|
22074
|
+
async readListPullRequests(repositoryId, states) {
|
|
21950
22075
|
const { repository, nameWithOwner, auth, executable } = await this.requireGitHub(repositoryId, "gh-pr-list");
|
|
21951
|
-
const results = await Promise.all(states.map((state) => this.
|
|
22076
|
+
const results = await Promise.all(states.map((state) => this.runGhRead([
|
|
21952
22077
|
"pr",
|
|
21953
22078
|
"list",
|
|
21954
22079
|
"-R",
|
|
@@ -21969,11 +22094,14 @@ var GitHubService = class {
|
|
|
21969
22094
|
})));
|
|
21970
22095
|
return this.withStackMembership(repository.path, nameWithOwner, auth, executable, selectPullRequestsNewestFirst(results.flatMap((result) => parsePullRequestList(result.stdout)), states));
|
|
21971
22096
|
}
|
|
21972
|
-
|
|
22097
|
+
findPullRequestForBranch(repositoryId, branchName) {
|
|
22098
|
+
return this.shareRead(repositoryId, ["branch", branchName], () => this.readFindPullRequestForBranch(repositoryId, branchName));
|
|
22099
|
+
}
|
|
22100
|
+
async readFindPullRequestForBranch(repositoryId, branchName) {
|
|
21973
22101
|
const operation = "gh-pr-for-branch";
|
|
21974
22102
|
const { repository, nameWithOwner, auth, executable } = await this.requireGitHub(repositoryId, operation);
|
|
21975
22103
|
const branch = validateRef(branchName, operation);
|
|
21976
|
-
const openPull = sortPullRequestsNewestFirst(parseBranchPullRequestList((await this.
|
|
22104
|
+
const openPull = sortPullRequestsNewestFirst(parseBranchPullRequestList((await this.runGhRead([
|
|
21977
22105
|
"pr",
|
|
21978
22106
|
"list",
|
|
21979
22107
|
"-R",
|
|
@@ -21994,8 +22122,8 @@ var GitHubService = class {
|
|
|
21994
22122
|
timeoutMs: NETWORK_TIMEOUT_MS,
|
|
21995
22123
|
maxOutputBytes: 1048576
|
|
21996
22124
|
})).stdout, nameWithOwner, branch)).find((pull) => pull.state === "OPEN");
|
|
21997
|
-
if (openPull) return openPull;
|
|
21998
|
-
|
|
22125
|
+
if (openPull) return (await this.withStackMembership(repository.path, nameWithOwner, auth, executable, [openPull]))[0];
|
|
22126
|
+
const pull = sortPullRequestsNewestFirst(parseBranchPullRequestList((await this.runGhRead([
|
|
21999
22127
|
"pr",
|
|
22000
22128
|
"list",
|
|
22001
22129
|
"-R",
|
|
@@ -22015,11 +22143,15 @@ var GitHubService = class {
|
|
|
22015
22143
|
operation,
|
|
22016
22144
|
timeoutMs: NETWORK_TIMEOUT_MS,
|
|
22017
22145
|
maxOutputBytes: 1048576
|
|
22018
|
-
})).stdout, nameWithOwner, branch))[0]
|
|
22146
|
+
})).stdout, nameWithOwner, branch))[0];
|
|
22147
|
+
return pull ? (await this.withStackMembership(repository.path, nameWithOwner, auth, executable, [pull]))[0] : null;
|
|
22148
|
+
}
|
|
22149
|
+
getPullRequest(repositoryId, prNumber) {
|
|
22150
|
+
return this.shareRead(repositoryId, ["details", prNumber], () => this.readGetPullRequest(repositoryId, prNumber));
|
|
22019
22151
|
}
|
|
22020
|
-
async
|
|
22152
|
+
async readGetPullRequest(repositoryId, prNumber) {
|
|
22021
22153
|
const { repository, nameWithOwner, auth, executable } = await this.requireGitHub(repositoryId, "gh-pr-view");
|
|
22022
|
-
const details = parsePullRequestDetails((await this.
|
|
22154
|
+
const details = parsePullRequestDetails((await this.runGhRead([
|
|
22023
22155
|
"pr",
|
|
22024
22156
|
"view",
|
|
22025
22157
|
String(prNumber),
|
|
@@ -22044,7 +22176,7 @@ var GitHubService = class {
|
|
|
22044
22176
|
const batch = pulls.slice(start, start + 25);
|
|
22045
22177
|
try {
|
|
22046
22178
|
const numbers = batch.map((pr) => pr.number);
|
|
22047
|
-
const memberships = parseStackMemberships((await this.
|
|
22179
|
+
const memberships = parseStackMemberships((await this.runGhRead([
|
|
22048
22180
|
"api",
|
|
22049
22181
|
"graphql",
|
|
22050
22182
|
"-f",
|
|
@@ -22074,7 +22206,10 @@ var GitHubService = class {
|
|
|
22074
22206
|
}
|
|
22075
22207
|
return enriched;
|
|
22076
22208
|
}
|
|
22077
|
-
|
|
22209
|
+
getPullRequestStack(repositoryId, prNumber) {
|
|
22210
|
+
return this.shareRead(repositoryId, ["stack", prNumber], () => this.readGetPullRequestStack(repositoryId, prNumber));
|
|
22211
|
+
}
|
|
22212
|
+
async readGetPullRequestStack(repositoryId, prNumber) {
|
|
22078
22213
|
const operation = "gh-pr-stack";
|
|
22079
22214
|
const { repository, nameWithOwner, auth, executable } = await this.requireGitHub(repositoryId, operation);
|
|
22080
22215
|
const options = {
|
|
@@ -22085,7 +22220,7 @@ var GitHubService = class {
|
|
|
22085
22220
|
timeoutMs: NETWORK_TIMEOUT_MS,
|
|
22086
22221
|
maxOutputBytes: 4194304
|
|
22087
22222
|
};
|
|
22088
|
-
const read = async (endpoint) => this.
|
|
22223
|
+
const read = async (endpoint) => this.runGhRead(["api", endpoint], options);
|
|
22089
22224
|
let listing;
|
|
22090
22225
|
try {
|
|
22091
22226
|
listing = await read(`repos/${nameWithOwner}/stacks?pull_request=${prNumber}`);
|
|
@@ -22103,9 +22238,12 @@ var GitHubService = class {
|
|
|
22103
22238
|
});
|
|
22104
22239
|
return complete;
|
|
22105
22240
|
}
|
|
22106
|
-
|
|
22241
|
+
getPullRequestDiff(repositoryId, prNumber) {
|
|
22242
|
+
return this.shareRead(repositoryId, ["diff", prNumber], () => this.readGetPullRequestDiff(repositoryId, prNumber));
|
|
22243
|
+
}
|
|
22244
|
+
async readGetPullRequestDiff(repositoryId, prNumber) {
|
|
22107
22245
|
const { repository, nameWithOwner, auth, executable } = await this.requireGitHub(repositoryId, "gh-pr-diff");
|
|
22108
|
-
const patch = (await this.
|
|
22246
|
+
const patch = (await this.runGhRead([
|
|
22109
22247
|
"pr",
|
|
22110
22248
|
"diff",
|
|
22111
22249
|
String(prNumber),
|
|
@@ -22128,7 +22266,10 @@ var GitHubService = class {
|
|
|
22128
22266
|
lineCount: patch ? patch.split("\n").length : 0
|
|
22129
22267
|
};
|
|
22130
22268
|
}
|
|
22131
|
-
|
|
22269
|
+
getPullRequestCommitDiff(repositoryId, oid) {
|
|
22270
|
+
return this.shareRead(repositoryId, ["commit-diff", oid], () => this.readGetPullRequestCommitDiff(repositoryId, oid));
|
|
22271
|
+
}
|
|
22272
|
+
async readGetPullRequestCommitDiff(repositoryId, oid) {
|
|
22132
22273
|
const operation = "gh-pr-commit-diff";
|
|
22133
22274
|
const { repository, nameWithOwner, auth, executable } = await this.requireGitHub(repositoryId, operation);
|
|
22134
22275
|
if (!/^[0-9a-f]{40,64}$/i.test(oid)) throw new GhOperationError({
|
|
@@ -22136,7 +22277,7 @@ var GitHubService = class {
|
|
|
22136
22277
|
operation,
|
|
22137
22278
|
message: "The commit is not valid."
|
|
22138
22279
|
});
|
|
22139
|
-
const patch = (await this.
|
|
22280
|
+
const patch = (await this.runGhRead([
|
|
22140
22281
|
"api",
|
|
22141
22282
|
`repos/${nameWithOwner}/commits/${oid}`,
|
|
22142
22283
|
"--header",
|
|
@@ -22215,14 +22356,21 @@ var GitHubService = class {
|
|
|
22215
22356
|
];
|
|
22216
22357
|
if (input.draft) args.push("--draft");
|
|
22217
22358
|
verifyAccount();
|
|
22218
|
-
|
|
22219
|
-
|
|
22220
|
-
|
|
22221
|
-
|
|
22222
|
-
|
|
22223
|
-
|
|
22224
|
-
|
|
22225
|
-
|
|
22359
|
+
this.invalidate(input.repositoryId);
|
|
22360
|
+
let result;
|
|
22361
|
+
try {
|
|
22362
|
+
result = await this.runGh(args, {
|
|
22363
|
+
auth,
|
|
22364
|
+
executable,
|
|
22365
|
+
cwd: repository.path,
|
|
22366
|
+
operation,
|
|
22367
|
+
timeoutMs: 12e4,
|
|
22368
|
+
maxOutputBytes: 1048576
|
|
22369
|
+
});
|
|
22370
|
+
} finally {
|
|
22371
|
+
this.invalidate(input.repositoryId);
|
|
22372
|
+
}
|
|
22373
|
+
const created = parseCreatedPullRequestUrl(result.stdout);
|
|
22226
22374
|
if (!created) throw new GhOperationError({
|
|
22227
22375
|
code: "GH_INVALID_OUTPUT",
|
|
22228
22376
|
operation,
|
|
@@ -22238,13 +22386,12 @@ var GitHubService = class {
|
|
|
22238
22386
|
}
|
|
22239
22387
|
async requireGitHub(repositoryId, operation) {
|
|
22240
22388
|
const repository = this.repositories.get(repositoryId);
|
|
22241
|
-
const info = await this.
|
|
22389
|
+
const { remote, info } = await this.routing(repositoryId);
|
|
22242
22390
|
if (!info.isGitHub || !info.nameWithOwner) throw new GhOperationError({
|
|
22243
22391
|
code: "GH_NOT_GITHUB_REPO",
|
|
22244
22392
|
operation,
|
|
22245
22393
|
message: "The origin remote does not point to a GitHub repository."
|
|
22246
22394
|
});
|
|
22247
|
-
const remote = await this.remote(repositoryId);
|
|
22248
22395
|
const authenticated = await this.accounts.authenticate(repository, remote, info.nameWithOwner, operation);
|
|
22249
22396
|
return {
|
|
22250
22397
|
repository,
|
|
@@ -22252,6 +22399,10 @@ var GitHubService = class {
|
|
|
22252
22399
|
...authenticated
|
|
22253
22400
|
};
|
|
22254
22401
|
}
|
|
22402
|
+
runGhRead(args, options) {
|
|
22403
|
+
const key = createHash("sha256").update(JSON.stringify([args, options])).digest("hex");
|
|
22404
|
+
return this.reads.run(this.git.readScope(options.cwd), `command:${key}`, () => this.runGh(args, options), false);
|
|
22405
|
+
}
|
|
22255
22406
|
async runGh(args, options) {
|
|
22256
22407
|
const result = await this.run(options.executable, args, options);
|
|
22257
22408
|
if (result.exitCode !== 0) throw classifyFailure({
|
|
@@ -22975,6 +23126,11 @@ function normalizeRemoteFetchIntervalSeconds(value) {
|
|
|
22975
23126
|
const stepped = Math.round(rounded / 5) * 5;
|
|
22976
23127
|
return Math.min(300, Math.max(5, stepped));
|
|
22977
23128
|
}
|
|
23129
|
+
//#endregion
|
|
23130
|
+
//#region ../../src/main/window/desktop-window-size.ts
|
|
23131
|
+
/** Smallest restored desktop width that keeps the toolbar on one row.
|
|
23132
|
+
* Renderer wrapping starts at max-width 1100px in styles/mobile.css. */
|
|
23133
|
+
var DESKTOP_MIN_WINDOW_WIDTH = 1104;
|
|
22978
23134
|
var defaults = {
|
|
22979
23135
|
version: 2,
|
|
22980
23136
|
githubAccounts: {},
|
|
@@ -23041,6 +23197,7 @@ var SettingsStore = class {
|
|
|
23041
23197
|
loaded = false;
|
|
23042
23198
|
repositoryAiWriteVersions = /* @__PURE__ */ new Map();
|
|
23043
23199
|
pendingWrite = Promise.resolve();
|
|
23200
|
+
persistedSnapshot = null;
|
|
23044
23201
|
backgroundTimers = /* @__PURE__ */ new Map();
|
|
23045
23202
|
backgroundDirty = /* @__PURE__ */ new Set();
|
|
23046
23203
|
lastBackgroundWriteError = null;
|
|
@@ -23075,6 +23232,7 @@ var SettingsStore = class {
|
|
|
23075
23232
|
}
|
|
23076
23233
|
const backup = await readJsonDocument(backupPathFor(this.filePath));
|
|
23077
23234
|
if (!backup.ok || backup.raw !== snapshot) await writeFileAtomically(backupPathFor(this.filePath), snapshot, { parseJson: true });
|
|
23235
|
+
this.persistedSnapshot = snapshot;
|
|
23078
23236
|
}
|
|
23079
23237
|
get recentRepositories() {
|
|
23080
23238
|
return [...this.data.recentRepositories];
|
|
@@ -23455,8 +23613,11 @@ var SettingsStore = class {
|
|
|
23455
23613
|
enqueueWrite() {
|
|
23456
23614
|
const snapshot = this.snapshot();
|
|
23457
23615
|
const write = this.pendingWrite.then(async () => {
|
|
23616
|
+
if (snapshot === this.persistedSnapshot) return;
|
|
23617
|
+
this.persistedSnapshot = null;
|
|
23458
23618
|
await writeFileAtomically(this.filePath, snapshot, { parseJson: true });
|
|
23459
23619
|
await writeFileAtomically(backupPathFor(this.filePath), snapshot, { parseJson: true });
|
|
23620
|
+
this.persistedSnapshot = snapshot;
|
|
23460
23621
|
});
|
|
23461
23622
|
this.pendingWrite = write.catch(() => void 0);
|
|
23462
23623
|
return write;
|
|
@@ -23529,7 +23690,7 @@ function validate(value, defaultDoubleControlShortcutEnabled) {
|
|
|
23529
23690
|
const parsedBounds = windowBoundsRecordSchema.safeParse(input.windowBounds);
|
|
23530
23691
|
const bounds = parsedBounds.success ? parsedBounds.data : null;
|
|
23531
23692
|
const windowBounds = bounds && typeof bounds.width === "number" && Number.isFinite(bounds.width) && typeof bounds.height === "number" && Number.isFinite(bounds.height) ? {
|
|
23532
|
-
width: Math.max(
|
|
23693
|
+
width: Math.max(DESKTOP_MIN_WINDOW_WIDTH, bounds.width),
|
|
23533
23694
|
height: Math.max(600, bounds.height),
|
|
23534
23695
|
...typeof bounds.x === "number" && Number.isFinite(bounds.x) ? { x: bounds.x } : {},
|
|
23535
23696
|
...typeof bounds.y === "number" && Number.isFinite(bounds.y) ? { y: bounds.y } : {}
|
|
@@ -23777,6 +23938,7 @@ var OpenTigRuntime = class {
|
|
|
23777
23938
|
}
|
|
23778
23939
|
publishRepositoryChange(repositoryId, scope) {
|
|
23779
23940
|
if (this.closePromise) return;
|
|
23941
|
+
this.services.repositories.invalidateIfKnown(repositoryId);
|
|
23780
23942
|
const elapsed = Date.now() - this.lastNotifiedAt;
|
|
23781
23943
|
if (elapsed < 1e3) {
|
|
23782
23944
|
this.trailingChange = this.trailingChange?.repositoryId === repositoryId ? {
|
|
@@ -24989,11 +25151,26 @@ function orderingIndexArg(value, operation) {
|
|
|
24989
25151
|
//#region ../../src/main/runtime/registerServerCommands.ts
|
|
24990
25152
|
function registerServerCommands(registry, services, host) {
|
|
24991
25153
|
const serverDefinitions = new Map(Object.values(OPEN_TIG_SERVER_COMMANDS).map((definition) => [definition.command, definition]));
|
|
25154
|
+
const repositoryMutations = new Set(Object.entries(OPEN_TIG_SERVER_COMMANDS).filter(([method, definition]) => definition.mutation && [
|
|
25155
|
+
"repository.",
|
|
25156
|
+
"index.",
|
|
25157
|
+
"commits.",
|
|
25158
|
+
"refs."
|
|
25159
|
+
].some((prefix) => method.startsWith(prefix))).map(([, definition]) => definition.command));
|
|
24992
25160
|
const handleWithContext = (channel, operation, handler) => {
|
|
24993
25161
|
const definition = serverDefinitions.get(channel);
|
|
24994
25162
|
if (!definition) throw new Error(`Not a server command: ${channel}`);
|
|
24995
25163
|
if (definition.operation !== operation) throw new Error(`Operation mismatch for ${channel}: ${operation}`);
|
|
24996
|
-
registry.registerHandler(definition, (context, args) =>
|
|
25164
|
+
registry.registerHandler(definition, async (context, args) => {
|
|
25165
|
+
const first = args[0];
|
|
25166
|
+
const repositoryId = typeof first === "object" && first !== null && "repositoryId" in first ? first.repositoryId : first;
|
|
25167
|
+
if (repositoryMutations.has(channel)) services.repositories.invalidateIfKnown(repositoryId);
|
|
25168
|
+
try {
|
|
25169
|
+
return await handler(context, ...args);
|
|
25170
|
+
} finally {
|
|
25171
|
+
if (repositoryMutations.has(channel)) services.repositories.invalidateIfKnown(repositoryId);
|
|
25172
|
+
}
|
|
25173
|
+
});
|
|
24997
25174
|
};
|
|
24998
25175
|
const handle = (channel, operation, handler) => {
|
|
24999
25176
|
handleWithContext(channel, operation, (_context, ...args) => handler(...args));
|
|
@@ -25470,6 +25647,8 @@ function clipboardImageArg(value) {
|
|
|
25470
25647
|
//#endregion
|
|
25471
25648
|
//#region ../../src/shared/server-protocol.ts
|
|
25472
25649
|
var OPEN_TIG_PROTOCOL_VERSION = { protocolVersion: 4 }.protocolVersion;
|
|
25650
|
+
/** UTF-8 JSON bytes, including the envelope and escaped strings. */
|
|
25651
|
+
var OPEN_TIG_MAX_SERVER_MESSAGE_BYTES = 4194304;
|
|
25473
25652
|
//#endregion
|
|
25474
25653
|
//#region ../../src/shared/application-profile.ts
|
|
25475
25654
|
function applicationName(profile) {
|
|
@@ -26327,7 +26506,11 @@ async function handleRequest(context, request, response) {
|
|
|
26327
26506
|
listeningOnLan: !isLoopbackAddress(listener.host),
|
|
26328
26507
|
listenerHost: listener.host,
|
|
26329
26508
|
actualPort: listener.port,
|
|
26330
|
-
ready: context.isReady()
|
|
26509
|
+
ready: context.isReady(),
|
|
26510
|
+
...context.webAccess ? {
|
|
26511
|
+
configuration: context.webAccess.getStatus(),
|
|
26512
|
+
recoveryCommand: context.webAccess.getStatus().recoveryCommand
|
|
26513
|
+
} : {}
|
|
26331
26514
|
});
|
|
26332
26515
|
}
|
|
26333
26516
|
if (method === "GET" && rawPath === "/api/auth/sessions") {
|
|
@@ -26343,9 +26526,20 @@ async function handleRequest(context, request, response) {
|
|
|
26343
26526
|
};
|
|
26344
26527
|
}) });
|
|
26345
26528
|
}
|
|
26346
|
-
if (method === "POST" && rawPath === "/api/admin/pair") return
|
|
26529
|
+
if (method === "POST" && (rawPath === "/api/admin/pair" || rawPath === "/api/admin/status")) return handleLocalAdminRequest(context, request, response, rawPath === "/api/admin/status");
|
|
26347
26530
|
if (method === "POST" && rawPath.startsWith("/api/")) {
|
|
26348
26531
|
if (!isAllowedOrigin(request)) return sendJson(response, 403, { error: "Forbidden origin." });
|
|
26532
|
+
if (rawPath === "/api/auth/web-access") {
|
|
26533
|
+
if (!context.auth.authenticate(request.headers)) return sendJson(response, 401, { error: "Authentication required." });
|
|
26534
|
+
if (!context.webAccess) return sendJson(response, 503, { error: "Web access settings are unavailable." });
|
|
26535
|
+
const body = await readJsonObject(request, response);
|
|
26536
|
+
if (!body) return;
|
|
26537
|
+
try {
|
|
26538
|
+
return sendJson(response, 200, await context.webAccess.update(body), { Connection: "close" });
|
|
26539
|
+
} catch (error) {
|
|
26540
|
+
return sendJson(response, 400, { error: error instanceof Error ? error.message : "Could not change web access." }, { Connection: "close" });
|
|
26541
|
+
}
|
|
26542
|
+
}
|
|
26349
26543
|
if (rawPath === "/api/html-preview") return handleHtmlPreview(context, request, response);
|
|
26350
26544
|
if (rawPath === "/api/auth/pairing-link") {
|
|
26351
26545
|
if (!context.auth.authenticate(request.headers)) return sendJson(response, 401, { error: "Authentication required." });
|
|
@@ -26354,7 +26548,17 @@ async function handleRequest(context, request, response) {
|
|
|
26354
26548
|
code: "WEB_ACCESS_DISABLED"
|
|
26355
26549
|
});
|
|
26356
26550
|
if (!context.isReady()) return sendJson(response, 503, { error: "Server is not ready." });
|
|
26357
|
-
const
|
|
26551
|
+
const body = request.headers["content-length"] && request.headers["content-length"] !== "0" || request.headers["transfer-encoding"] ? await readJsonObject(request, response) : {};
|
|
26552
|
+
if (!body) return;
|
|
26553
|
+
let endpoint;
|
|
26554
|
+
try {
|
|
26555
|
+
endpoint = body.endpoint === void 0 ? request.headers.origin : normalizePairingOrigin(typeof body.endpoint === "string" ? body.endpoint : "");
|
|
26556
|
+
} catch {
|
|
26557
|
+
return sendJson(response, 400, { error: "Enter a valid pairing endpoint." });
|
|
26558
|
+
}
|
|
26559
|
+
const status = context.webAccess?.getStatus();
|
|
26560
|
+
if (!endpoint || endpoint !== request.headers.origin && !status?.pairingEndpoints.includes(endpoint)) return sendJson(response, 400, { error: "Select an active pairing endpoint or save its domain." });
|
|
26561
|
+
const url = new URL("/pair", endpoint);
|
|
26358
26562
|
const pairing = context.auth.createPairingToken();
|
|
26359
26563
|
url.hash = new URLSearchParams({ token: pairing.token }).toString();
|
|
26360
26564
|
return sendJson(response, 200, {
|
|
@@ -26519,14 +26723,20 @@ function isLoopbackAddress(value) {
|
|
|
26519
26723
|
const normalized = value.startsWith("::ffff:") ? value.slice(7) : value;
|
|
26520
26724
|
return normalized === "::1" || normalized.startsWith("127.");
|
|
26521
26725
|
}
|
|
26522
|
-
async function
|
|
26726
|
+
async function handleLocalAdminRequest(context, request, response, statusOnly = false) {
|
|
26523
26727
|
const admin = context.admin;
|
|
26524
26728
|
const suppliedToken = request.headers["x-opentig-admin"];
|
|
26525
26729
|
if (!admin || !isLocalAddress(request.socket.remoteAddress) || typeof suppliedToken !== "string" || !sameSecret(admin.token, suppliedToken)) return sendJson(response, 404, { error: "Not found." });
|
|
26526
26730
|
const body = await readJsonObject(request, response);
|
|
26527
26731
|
if (!body) return;
|
|
26528
|
-
if (body.instanceId !== admin.instanceId
|
|
26529
|
-
|
|
26732
|
+
if (body.instanceId !== admin.instanceId) return sendJson(response, 409, { error: "OpenTig server identity mismatch." });
|
|
26733
|
+
if (statusOnly) return sendJson(response, context.isReady() ? 200 : 503, {
|
|
26734
|
+
status: context.isReady() ? "ready" : "not-ready",
|
|
26735
|
+
...context.identity,
|
|
26736
|
+
instanceId: admin.instanceId,
|
|
26737
|
+
pid: process.pid
|
|
26738
|
+
});
|
|
26739
|
+
const origin = typeof body.publicOrigin === "string" ? normalizePublicOrigin(body.publicOrigin) : null;
|
|
26530
26740
|
if (!origin) return sendJson(response, 400, { error: "Invalid public origin." });
|
|
26531
26741
|
if (!context.auth.descriptor().browserAccessEnabled) return sendJson(response, 403, {
|
|
26532
26742
|
error: "Web access is disabled.",
|
|
@@ -26684,6 +26894,64 @@ function redactOperationResult(result) {
|
|
|
26684
26894
|
};
|
|
26685
26895
|
}
|
|
26686
26896
|
//#endregion
|
|
26897
|
+
//#region src/bounded-json.ts
|
|
26898
|
+
/** Stop JSON.stringify before it builds an oversized wire message. */
|
|
26899
|
+
var JsonByteLimitError = class extends Error {};
|
|
26900
|
+
function stringifyBounded(value, maxBytes) {
|
|
26901
|
+
let bytes = 0;
|
|
26902
|
+
let root = true;
|
|
26903
|
+
const populated = /* @__PURE__ */ new WeakSet();
|
|
26904
|
+
const add = (count) => {
|
|
26905
|
+
bytes += count;
|
|
26906
|
+
if (bytes > maxBytes) throw new JsonByteLimitError("JSON byte limit exceeded.");
|
|
26907
|
+
};
|
|
26908
|
+
const text = JSON.stringify(value, function(key, entry) {
|
|
26909
|
+
if (typeof entry === "object" && entry !== null && types.isBoxedPrimitive(entry)) {
|
|
26910
|
+
if (types.isNumberObject(entry)) entry = +entry;
|
|
26911
|
+
else if (types.isStringObject(entry)) entry = String(entry);
|
|
26912
|
+
else if (types.isBooleanObject(entry)) entry = Boolean.prototype.valueOf.call(entry);
|
|
26913
|
+
}
|
|
26914
|
+
const omitted = entry === void 0 || typeof entry === "function" || typeof entry === "symbol";
|
|
26915
|
+
const isRoot = root;
|
|
26916
|
+
root = false;
|
|
26917
|
+
if (omitted && !Array.isArray(this)) return entry;
|
|
26918
|
+
if (!isRoot) {
|
|
26919
|
+
if (populated.has(this)) add(1);
|
|
26920
|
+
else populated.add(this);
|
|
26921
|
+
if (!Array.isArray(this)) add(stringBytes(key, maxBytes - bytes) + 1);
|
|
26922
|
+
}
|
|
26923
|
+
if (typeof entry === "string") add(stringBytes(entry, maxBytes - bytes));
|
|
26924
|
+
else if (entry === null || omitted) add(4);
|
|
26925
|
+
else if (typeof entry === "number") add(Number.isFinite(entry) ? String(entry).length : 4);
|
|
26926
|
+
else if (typeof entry === "boolean") add(entry ? 4 : 5);
|
|
26927
|
+
else if (typeof entry === "object") {
|
|
26928
|
+
add(2);
|
|
26929
|
+
populated.delete(entry);
|
|
26930
|
+
}
|
|
26931
|
+
return entry;
|
|
26932
|
+
});
|
|
26933
|
+
if (text === void 0) throw new TypeError("Expected a JSON value.");
|
|
26934
|
+
return {
|
|
26935
|
+
text,
|
|
26936
|
+
bytes
|
|
26937
|
+
};
|
|
26938
|
+
}
|
|
26939
|
+
function stringBytes(value, remaining) {
|
|
26940
|
+
let bytes = Buffer.byteLength(value) + 2;
|
|
26941
|
+
if (bytes > remaining) throw new JsonByteLimitError("JSON byte limit exceeded.");
|
|
26942
|
+
if (!/[\u0000-\u001f"\\\ud800-\udfff]/.test(value)) return bytes;
|
|
26943
|
+
bytes = 2;
|
|
26944
|
+
for (let start = 0; start < value.length;) {
|
|
26945
|
+
let end = Math.min(start + 16384, value.length);
|
|
26946
|
+
const previous = value.charCodeAt(end - 1), next = value.charCodeAt(end);
|
|
26947
|
+
if (previous >= 55296 && previous <= 56319 && next >= 56320 && next <= 57343) end++;
|
|
26948
|
+
bytes += Buffer.byteLength(JSON.stringify(value.slice(start, end))) - 2;
|
|
26949
|
+
if (bytes > remaining) throw new JsonByteLimitError("JSON byte limit exceeded.");
|
|
26950
|
+
start = end;
|
|
26951
|
+
}
|
|
26952
|
+
return bytes;
|
|
26953
|
+
}
|
|
26954
|
+
//#endregion
|
|
26687
26955
|
//#region src/websocket.ts
|
|
26688
26956
|
var NORMAL_FRAME_LIMIT = 1048576;
|
|
26689
26957
|
var CONTENT_FRAME_LIMIT = 9437184;
|
|
@@ -26706,6 +26974,7 @@ var OpenTigWebSocketTransport = class {
|
|
|
26706
26974
|
connectionLimit;
|
|
26707
26975
|
requestRateLimit;
|
|
26708
26976
|
requestRateWindowMs;
|
|
26977
|
+
measureTransport = process.env.OPENTIG_PERF_LOG === "1";
|
|
26709
26978
|
heartbeat;
|
|
26710
26979
|
upgradeHandler;
|
|
26711
26980
|
closePromise = null;
|
|
@@ -26746,6 +27015,10 @@ var OpenTigWebSocketTransport = class {
|
|
|
26746
27015
|
this.closePromise ??= this.closeTransport();
|
|
26747
27016
|
return this.closePromise;
|
|
26748
27017
|
}
|
|
27018
|
+
/** Keep authentication and runtime alive while clients reconnect to a new bind. */
|
|
27019
|
+
reconnectClients() {
|
|
27020
|
+
for (const socket of this.clients.keys()) socket.terminate();
|
|
27021
|
+
}
|
|
26749
27022
|
upgrade(request, socket, head) {
|
|
26750
27023
|
if ((request.url ?? "").split(/[?#]/, 1)[0] !== "/ws") return rejectUpgrade(socket, 404, "Not Found");
|
|
26751
27024
|
if (!isAllowedOrigin(request)) return rejectUpgrade(socket, 403, "Forbidden");
|
|
@@ -26806,6 +27079,7 @@ var OpenTigWebSocketTransport = class {
|
|
|
26806
27079
|
if (bytes.byteLength > frameLimit(input.command)) return this.sendProtocolError(socket, input.id, "Command request exceeded the transport limit.");
|
|
26807
27080
|
if (input.command === IPC.repositoryReadImage) return this.sendProtocolError(socket, input.id, "Image previews use authenticated HTTP.");
|
|
26808
27081
|
const controller = new AbortController();
|
|
27082
|
+
const startedAt = this.measureTransport ? performance.now() : void 0;
|
|
26809
27083
|
state.requests.set(input.id, controller);
|
|
26810
27084
|
const execution = this.options.registry.execute(state.sessionId, input.command, input.args, { signal: controller.signal });
|
|
26811
27085
|
this.pendingOperations.add(execution);
|
|
@@ -26823,6 +27097,10 @@ var OpenTigWebSocketTransport = class {
|
|
|
26823
27097
|
});
|
|
26824
27098
|
} finally {
|
|
26825
27099
|
state.requests.delete(input.id);
|
|
27100
|
+
if (startedAt !== void 0) this.measure("request", startedAt, {
|
|
27101
|
+
requestBytes: bytes.byteLength,
|
|
27102
|
+
operation: commandDefinition(input.command)?.operation ?? "unknown"
|
|
27103
|
+
});
|
|
26826
27104
|
}
|
|
26827
27105
|
}
|
|
26828
27106
|
sendProtocolError(socket, id, message) {
|
|
@@ -26840,21 +27118,93 @@ var OpenTigWebSocketTransport = class {
|
|
|
26840
27118
|
});
|
|
26841
27119
|
}
|
|
26842
27120
|
broadcast(message) {
|
|
26843
|
-
|
|
27121
|
+
let serialized;
|
|
27122
|
+
for (const socket of this.clients.keys()) {
|
|
27123
|
+
if (!this.canSend(socket)) continue;
|
|
27124
|
+
if (serialized === void 0) serialized = this.serialize(message);
|
|
27125
|
+
this.sendSerialized(socket, serialized);
|
|
27126
|
+
}
|
|
26844
27127
|
}
|
|
26845
27128
|
send(socket, message) {
|
|
26846
|
-
if (socket.
|
|
27129
|
+
if (this.canSend(socket)) this.sendSerialized(socket, this.serialize(message));
|
|
27130
|
+
}
|
|
27131
|
+
canSend(socket) {
|
|
27132
|
+
if (socket.readyState !== WebSocket.OPEN) return false;
|
|
26847
27133
|
const state = this.clients.get(socket);
|
|
26848
|
-
if (state
|
|
26849
|
-
|
|
26850
|
-
|
|
27134
|
+
if (!state || !this.options.auth.hasSession(state.sessionId)) {
|
|
27135
|
+
socket.close(1008, "Session expired or revoked");
|
|
27136
|
+
return false;
|
|
27137
|
+
}
|
|
27138
|
+
return true;
|
|
27139
|
+
}
|
|
27140
|
+
serialize(message) {
|
|
27141
|
+
const startedAt = this.measureTransport ? performance.now() : void 0;
|
|
27142
|
+
let serialized = null;
|
|
27143
|
+
let outcome = "accepted";
|
|
27144
|
+
try {
|
|
27145
|
+
serialized = stringifyBounded(message, OPEN_TIG_MAX_SERVER_MESSAGE_BYTES);
|
|
27146
|
+
} catch (error) {
|
|
27147
|
+
if (!(error instanceof JsonByteLimitError)) throw error;
|
|
27148
|
+
outcome = "message-too-large";
|
|
27149
|
+
if (message.type === "result") {
|
|
27150
|
+
outcome = "response-too-large";
|
|
27151
|
+
serialized = stringifyBounded({
|
|
27152
|
+
type: "result",
|
|
27153
|
+
id: message.id,
|
|
27154
|
+
result: {
|
|
27155
|
+
ok: false,
|
|
27156
|
+
error: {
|
|
27157
|
+
code: "RESPONSE_TOO_LARGE",
|
|
27158
|
+
operation: "server-protocol",
|
|
27159
|
+
message: "Response exceeded the 4 MiB transport limit. Request a smaller scope.",
|
|
27160
|
+
maxBytes: OPEN_TIG_MAX_SERVER_MESSAGE_BYTES
|
|
27161
|
+
}
|
|
27162
|
+
}
|
|
27163
|
+
}, OPEN_TIG_MAX_SERVER_MESSAGE_BYTES);
|
|
27164
|
+
}
|
|
27165
|
+
}
|
|
27166
|
+
if (startedAt !== void 0) this.measure("serialize", startedAt, {
|
|
27167
|
+
bytes: serialized?.bytes ?? 0,
|
|
27168
|
+
outcome,
|
|
27169
|
+
maxBytes: OPEN_TIG_MAX_SERVER_MESSAGE_BYTES
|
|
27170
|
+
});
|
|
27171
|
+
return serialized;
|
|
27172
|
+
}
|
|
27173
|
+
sendSerialized(socket, serialized) {
|
|
27174
|
+
if (!this.canSend(socket)) return;
|
|
27175
|
+
if (!serialized) {
|
|
27176
|
+
socket.close(1009, "Server message too large");
|
|
27177
|
+
return;
|
|
27178
|
+
}
|
|
27179
|
+
const startedAt = this.measureTransport ? performance.now() : void 0;
|
|
27180
|
+
const bufferedBytes = socket.bufferedAmount;
|
|
27181
|
+
if (bufferedBytes + serialized.bytes > MAX_BUFFERED_SEND_BYTES) {
|
|
27182
|
+
if (startedAt !== void 0) this.measure("send", startedAt, {
|
|
27183
|
+
bytes: serialized.bytes,
|
|
27184
|
+
bufferedBytes,
|
|
27185
|
+
outcome: "backpressure"
|
|
27186
|
+
});
|
|
26851
27187
|
socket.close(1013, "Client is too slow");
|
|
26852
27188
|
return;
|
|
26853
27189
|
}
|
|
26854
|
-
socket.send(serialized, (error) => {
|
|
27190
|
+
socket.send(serialized.text, (error) => {
|
|
27191
|
+
if (startedAt !== void 0) this.measure("send", startedAt, {
|
|
27192
|
+
bytes: serialized.bytes,
|
|
27193
|
+
bufferedBytes,
|
|
27194
|
+
outcome: error ? "failed" : "written"
|
|
27195
|
+
});
|
|
26855
27196
|
if (error) this.options.logger("warn", "WebSocket send failed.");
|
|
26856
27197
|
});
|
|
26857
27198
|
}
|
|
27199
|
+
measure(kind, startedAt, data) {
|
|
27200
|
+
try {
|
|
27201
|
+
this.options.logger("info", `WebSocket performance ${JSON.stringify({
|
|
27202
|
+
kind,
|
|
27203
|
+
durationMs: performance.now() - startedAt,
|
|
27204
|
+
...data
|
|
27205
|
+
})}`);
|
|
27206
|
+
} catch {}
|
|
27207
|
+
}
|
|
26858
27208
|
checkHeartbeats() {
|
|
26859
27209
|
for (const [socket, state] of this.clients) {
|
|
26860
27210
|
if (!this.options.auth.hasSession(state.sessionId)) {
|
|
@@ -26944,8 +27294,11 @@ var OpenTigServer = class {
|
|
|
26944
27294
|
ready = false;
|
|
26945
27295
|
startPromise = null;
|
|
26946
27296
|
stopPromise = null;
|
|
27297
|
+
networkMutation = Promise.resolve();
|
|
27298
|
+
network;
|
|
26947
27299
|
constructor(options) {
|
|
26948
27300
|
this.options = options;
|
|
27301
|
+
this.network = options.network;
|
|
26949
27302
|
this.logger = options.logger ?? (() => void 0);
|
|
26950
27303
|
this.httpServer = createServer$1(createOpenTigHttpHandler({
|
|
26951
27304
|
runtime: options.runtime,
|
|
@@ -26954,6 +27307,10 @@ var OpenTigServer = class {
|
|
|
26954
27307
|
cancelUpdate: () => options.registry.resumeAfterUpdate(),
|
|
26955
27308
|
clientRoot: options.clientRoot,
|
|
26956
27309
|
auth: options.auth,
|
|
27310
|
+
webAccess: {
|
|
27311
|
+
getStatus: () => this.getWebAccessStatus(),
|
|
27312
|
+
update: (patch) => this.updateWebAccess(patch)
|
|
27313
|
+
},
|
|
26957
27314
|
identity: options.identity,
|
|
26958
27315
|
mode: options.mode ?? "desktop",
|
|
26959
27316
|
isReady: () => this.ready,
|
|
@@ -27003,6 +27360,74 @@ var OpenTigServer = class {
|
|
|
27003
27360
|
browserAccessEnabled: this.options.auth.descriptor().browserAccessEnabled
|
|
27004
27361
|
};
|
|
27005
27362
|
}
|
|
27363
|
+
getWebAccessStatus() {
|
|
27364
|
+
const address = this.httpServer.address();
|
|
27365
|
+
const port = address?.port ?? this.network.listenerPort;
|
|
27366
|
+
const host = address?.address ?? this.network.listenerHost;
|
|
27367
|
+
const localEndpoint = `http://${formatHost(host === "0.0.0.0" ? "127.0.0.1" : host === "::" ? "::1" : host)}:${port}`;
|
|
27368
|
+
const endpoints = Object.values(networkInterfaces()).flat().filter((entry) => entry && !entry.internal && !entry.address.startsWith("169.254.") && !entry.address.startsWith("fe80:") && (host.includes(":") || entry.family === "IPv4")).map((entry) => `http://${formatHost(entry.address)}:${port}`);
|
|
27369
|
+
const networkEndpoints = [...new Set(endpoints)].sort();
|
|
27370
|
+
const listeningOnLan = !isLoopbackHost(host);
|
|
27371
|
+
return {
|
|
27372
|
+
webAccessEnabled: this.options.auth.descriptor().browserAccessEnabled,
|
|
27373
|
+
lanAccessEnabled: this.network.lanAccessEnabled,
|
|
27374
|
+
publicOrigin: this.network.publicOrigin,
|
|
27375
|
+
listeningOnLan,
|
|
27376
|
+
listenerHost: host,
|
|
27377
|
+
actualPort: port,
|
|
27378
|
+
serverState: this.ready ? "ready" : "restarting",
|
|
27379
|
+
localEndpoint,
|
|
27380
|
+
networkEndpoints,
|
|
27381
|
+
pairingEndpoints: this.network.webAccessEnabled ? [.../* @__PURE__ */ new Set([
|
|
27382
|
+
localEndpoint,
|
|
27383
|
+
...listeningOnLan ? networkEndpoints.filter((endpoint) => host === "0.0.0.0" || host === "::" || new URL(endpoint).hostname.replace(/[[\]]/g, "") === host) : [],
|
|
27384
|
+
...this.network.publicOrigin ? [this.network.publicOrigin] : []
|
|
27385
|
+
])] : [],
|
|
27386
|
+
connectedSessionCount: this.webSockets.connectedSessionCount,
|
|
27387
|
+
restartError: null,
|
|
27388
|
+
...this.options.recoveryCommand ? { recoveryCommand: this.options.recoveryCommand } : {}
|
|
27389
|
+
};
|
|
27390
|
+
}
|
|
27391
|
+
updateWebAccess(patch) {
|
|
27392
|
+
const operation = this.networkMutation.then(async () => {
|
|
27393
|
+
if (!this.ready || this.stopPromise) throw new Error("Server is not ready.");
|
|
27394
|
+
const previous = this.network;
|
|
27395
|
+
const next = applyNetworkPatch(previous, patch);
|
|
27396
|
+
const rebind = next.listenerHost !== previous.listenerHost || next.listenerPort !== previous.listenerPort;
|
|
27397
|
+
if (rebind && !this.options.registry.pauseForUpdate()) throw new Error("Wait for running operations to finish before changing the listener.");
|
|
27398
|
+
try {
|
|
27399
|
+
if (rebind) await this.rebind(next);
|
|
27400
|
+
try {
|
|
27401
|
+
await this.options.networkSettings.save(next);
|
|
27402
|
+
await this.options.onNetworkChanged?.(next);
|
|
27403
|
+
} catch (error) {
|
|
27404
|
+
if (rebind) await this.rebind(previous);
|
|
27405
|
+
await this.options.networkSettings.save(previous);
|
|
27406
|
+
await this.options.onNetworkChanged?.(previous);
|
|
27407
|
+
throw error;
|
|
27408
|
+
}
|
|
27409
|
+
this.network = next;
|
|
27410
|
+
await this.setBrowserAccessEnabled(next.webAccessEnabled);
|
|
27411
|
+
if (rebind) this.webSockets.reconnectClients();
|
|
27412
|
+
return this.getWebAccessStatus();
|
|
27413
|
+
} finally {
|
|
27414
|
+
if (rebind) this.options.registry.resumeAfterUpdate();
|
|
27415
|
+
}
|
|
27416
|
+
});
|
|
27417
|
+
this.networkMutation = operation.catch(() => void 0);
|
|
27418
|
+
return operation;
|
|
27419
|
+
}
|
|
27420
|
+
async rebind(config) {
|
|
27421
|
+
const previous = this.network;
|
|
27422
|
+
this.httpServer.close();
|
|
27423
|
+
this.httpServer.closeIdleConnections();
|
|
27424
|
+
try {
|
|
27425
|
+
await this.bind(config.listenerHost, config.listenerPort);
|
|
27426
|
+
} catch (error) {
|
|
27427
|
+
await this.bind(previous.listenerHost, previous.listenerPort);
|
|
27428
|
+
throw new Error("Could not use that listening address or port. The previous listener has been restored.", { cause: error });
|
|
27429
|
+
}
|
|
27430
|
+
}
|
|
27006
27431
|
async setBrowserAccessEnabled(enabled) {
|
|
27007
27432
|
if (!this.ready) throw new Error("Server is not ready.");
|
|
27008
27433
|
const pending = this.options.auth.setBrowserAccessEnabled(enabled);
|
|
@@ -27023,8 +27448,17 @@ var OpenTigServer = class {
|
|
|
27023
27448
|
return this.stopPromise;
|
|
27024
27449
|
}
|
|
27025
27450
|
listen() {
|
|
27026
|
-
|
|
27027
|
-
|
|
27451
|
+
return this.bind(this.network.listenerHost, this.network.listenerPort).then((address) => {
|
|
27452
|
+
this.network = {
|
|
27453
|
+
...this.network,
|
|
27454
|
+
listenerHost: address.host,
|
|
27455
|
+
listenerPort: address.port,
|
|
27456
|
+
lanAccessEnabled: this.network.listenerHost === address.host ? this.network.lanAccessEnabled : !isLoopbackHost(address.host)
|
|
27457
|
+
};
|
|
27458
|
+
return address;
|
|
27459
|
+
});
|
|
27460
|
+
}
|
|
27461
|
+
bind(host, port) {
|
|
27028
27462
|
return new Promise((resolve, reject) => {
|
|
27029
27463
|
const onError = (error) => reject(error);
|
|
27030
27464
|
this.httpServer.once("error", onError);
|
|
@@ -27043,6 +27477,7 @@ var OpenTigServer = class {
|
|
|
27043
27477
|
});
|
|
27044
27478
|
}
|
|
27045
27479
|
async stopServer() {
|
|
27480
|
+
await this.networkMutation;
|
|
27046
27481
|
this.ready = false;
|
|
27047
27482
|
await this.webSockets.close();
|
|
27048
27483
|
if (this.httpServer.listening) await new Promise((resolve, reject) => {
|
|
@@ -27069,10 +27504,11 @@ function formatHost(host) {
|
|
|
27069
27504
|
*/
|
|
27070
27505
|
async function runOpenTigServer(config) {
|
|
27071
27506
|
if (config.profile !== void 0 && config.profile !== "production" && config.profile !== "dev") throw new Error("Invalid application profile.");
|
|
27507
|
+
const { networkSettings, network } = await loadStartupNetwork(config);
|
|
27072
27508
|
let transport = null;
|
|
27073
27509
|
const auth = await OpenTigSessionAuth.open({
|
|
27074
27510
|
source: config.auth,
|
|
27075
|
-
browserAccessEnabled:
|
|
27511
|
+
browserAccessEnabled: network.webAccessEnabled,
|
|
27076
27512
|
...config.profile ? { profile: config.profile } : {},
|
|
27077
27513
|
dataDirectory: config.serverDataPath ?? path.join(path.dirname(config.settingsPath), "server"),
|
|
27078
27514
|
...config.secureCookies === void 0 ? {} : { secureCookies: config.secureCookies }
|
|
@@ -27101,6 +27537,10 @@ async function runOpenTigServer(config) {
|
|
|
27101
27537
|
registerServerCommands(registry, runtime.services, headlessHost);
|
|
27102
27538
|
registry.setProblemLog(runtime.services.problems);
|
|
27103
27539
|
transport = new OpenTigServer({
|
|
27540
|
+
network,
|
|
27541
|
+
networkSettings,
|
|
27542
|
+
...config.recoveryCommand ? { recoveryCommand: config.recoveryCommand } : {},
|
|
27543
|
+
...config.onNetworkChanged ? { onNetworkChanged: config.onNetworkChanged } : {},
|
|
27104
27544
|
runtime,
|
|
27105
27545
|
registry,
|
|
27106
27546
|
...config.updates ? { updates: config.updates } : {},
|
|
@@ -27127,7 +27567,19 @@ async function runOpenTigServer(config) {
|
|
|
27127
27567
|
runtime,
|
|
27128
27568
|
clientRoot,
|
|
27129
27569
|
...address,
|
|
27130
|
-
|
|
27570
|
+
get host() {
|
|
27571
|
+
return transport.getWebAccessStatus().listenerHost;
|
|
27572
|
+
},
|
|
27573
|
+
get port() {
|
|
27574
|
+
return transport.getWebAccessStatus().actualPort;
|
|
27575
|
+
},
|
|
27576
|
+
get origin() {
|
|
27577
|
+
const status = transport.getWebAccessStatus();
|
|
27578
|
+
return `http://${status.listenerHost.includes(":") ? `[${status.listenerHost}]` : status.listenerHost}:${status.actualPort}`;
|
|
27579
|
+
},
|
|
27580
|
+
getWebAccessStatus: () => transport.getWebAccessStatus(),
|
|
27581
|
+
updateWebAccess: (patch) => transport.updateWebAccess(patch),
|
|
27582
|
+
createPairingLink: () => pairingLink(transport.getWebAccessStatus().localEndpoint, transport.createPairingToken()),
|
|
27131
27583
|
getStatus: () => transport.getStatus(),
|
|
27132
27584
|
revokeAllSessions: () => transport.revokeAllSessions(),
|
|
27133
27585
|
setBrowserAccessEnabled: (enabled) => transport.setBrowserAccessEnabled(enabled),
|
|
@@ -27172,4 +27624,4 @@ var headlessHost = {
|
|
|
27172
27624
|
revealItem: () => void 0
|
|
27173
27625
|
};
|
|
27174
27626
|
//#endregion
|
|
27175
|
-
export { preferredServerPort as a,
|
|
27627
|
+
export { preferredServerPort as a, applicationName as i, runOpenTigServer as n, normalizeRuntimePlatform as o, OneTimeBootstrapAuthSource as r, resolveServerClientRoot as t };
|