@a9n-shoji/rvw 0.2.2 → 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/CHANGELOG.md +26 -0
- package/README.md +6 -3
- package/dist/cli.mjs +188 -19
- package/dist/cli.mjs.map +4 -4
- package/dist/web/assets/DocumentViewer-B4LdSL1l.js +1560 -0
- package/dist/web/assets/{MarkdownTable-DVJ0_MIC.js → MarkdownTable-CdbKPft2.js} +1 -1
- package/dist/web/assets/WalkthroughViewer-D1LlSQw2.js +1 -0
- package/dist/web/assets/{abnfDiagram-N423BO3Z-6dQzEuPV.js → abnfDiagram-N423BO3Z-CE1nZSC9.js} +1 -1
- package/dist/web/assets/architecture-TIHT7OUA-edLV2Zq-.js +1 -0
- package/dist/web/assets/{architectureDiagram-T3A2C74G-DP7hHV7T.js → architectureDiagram-T3A2C74G-B88qYFUo.js} +1 -1
- package/dist/web/assets/{blockDiagram-VBNYF7ZC-ZbwzVKWP.js → blockDiagram-VBNYF7ZC-BaX0rhbb.js} +1 -1
- package/dist/web/assets/{c4Diagram-5PPSVZJV-HVD9_uV4.js → c4Diagram-5PPSVZJV-C1ppBaEC.js} +1 -1
- package/dist/web/assets/channel-J8FHSJDG.js +1 -0
- package/dist/web/assets/{chunk-2GRJ4B5K-Bkxs2d4J.js → chunk-2GRJ4B5K-D2TTocfh.js} +1 -1
- package/dist/web/assets/{chunk-4I5QYGJK-BJHE6zC_.js → chunk-4I5QYGJK-ikT25rPN.js} +1 -1
- package/dist/web/assets/{chunk-5RXB4S5H-B-52dfCQ.js → chunk-5RXB4S5H-CGlPaulj.js} +1 -1
- package/dist/web/assets/{chunk-6Q2QTUOP-BpD0vQ66.js → chunk-6Q2QTUOP-BuuMeCo2.js} +1 -1
- package/dist/web/assets/{chunk-7Z6QIM7H-BTI1qgAO.js → chunk-7Z6QIM7H-DT3BAZ4L.js} +1 -1
- package/dist/web/assets/{chunk-GF5L2VYU-B8BxoCCx.js → chunk-GF5L2VYU-mIdlxzfr.js} +1 -1
- package/dist/web/assets/{chunk-I66GZJ75-zwi-Xf01.js → chunk-I66GZJ75-CGl1hx4J.js} +1 -1
- package/dist/web/assets/{chunk-JQJVKLGR-D9mKBWUz.js → chunk-JQJVKLGR-BLG6gAfx.js} +1 -1
- package/dist/web/assets/{chunk-KBJHAD2P-Qmtir3qc.js → chunk-KBJHAD2P-D_MPzBJx.js} +1 -1
- package/dist/web/assets/{chunk-NSK5VX7P-SP2vZdAo.js → chunk-NSK5VX7P-CiYiU8wP.js} +1 -1
- package/dist/web/assets/{chunk-QR6OTTB3-uVDCHurs.js → chunk-QR6OTTB3-BK8Tnvcc.js} +1 -1
- package/dist/web/assets/{chunk-UBXNYLIW-CtNzBWOU.js → chunk-UBXNYLIW-BuzttTFk.js} +1 -1
- package/dist/web/assets/{chunk-W5SLKNZC-BPfDqbmK.js → chunk-W5SLKNZC-UidmFHe2.js} +1 -1
- package/dist/web/assets/{chunk-WRU74C26-BZkEOexq.js → chunk-WRU74C26-nox7WFnh.js} +1 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-CmTWSnH0.js +1 -0
- package/dist/web/assets/classDiagram-v2-OCEON4UE-CmTWSnH0.js +1 -0
- package/dist/web/assets/{cynefin-VYW2F7L2-BRisCyqC.js → cynefin-VYW2F7L2-wDMibS5s.js} +1 -1
- package/dist/web/assets/{cynefinDiagram-MW4NZA55-ClIrmCkB.js → cynefinDiagram-MW4NZA55-CBTrdvOE.js} +1 -1
- package/dist/web/assets/{dagre-VZM6K2ZE-X31uC_JB.js → dagre-VZM6K2ZE-Bbgn4Rr7.js} +1 -1
- package/dist/web/assets/{diagram-7IWD3JNH-DBQFjl0i.js → diagram-7IWD3JNH-Bm9AuXb9.js} +1 -1
- package/dist/web/assets/{diagram-B4RE2ZJO-BZ7934Wg.js → diagram-B4RE2ZJO-D3lZ-r-H.js} +1 -1
- package/dist/web/assets/{diagram-LBJQPF4R-CcJqH9aY.js → diagram-LBJQPF4R-D-GaynX7.js} +1 -1
- package/dist/web/assets/{diagram-Q27KOJAE-DzM7eiil.js → diagram-Q27KOJAE-BeZRiu1d.js} +1 -1
- package/dist/web/assets/{diagram-UB23O5K3-CVCV-d3m.js → diagram-UB23O5K3-C3GP7iMe.js} +1 -1
- package/dist/web/assets/{ebnfDiagram-BXEA7PRR-D8xb9FBp.js → ebnfDiagram-BXEA7PRR-BJWg0NVU.js} +1 -1
- package/dist/web/assets/{erDiagram-JOGREHBK-1l-G_PMG.js → erDiagram-JOGREHBK-XPmhUeB_.js} +1 -1
- package/dist/web/assets/eventmodeling-45OFAUF4-C3c4qddw.js +1 -0
- package/dist/web/assets/flowDiagram-UKHOOZJN-DMA-a1zf.js +1 -0
- package/dist/web/assets/{ganttDiagram-PKOTCBZU-CDJ9a1tp.js → ganttDiagram-PKOTCBZU-CL-xEo-M.js} +1 -1
- package/dist/web/assets/{gitGraph-TEB2WS4Q-DOeTh5wB.js → gitGraph-TEB2WS4Q-BVRyuIx3.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-DS77QQ5N-DYLxc8Nz.js → gitGraphDiagram-DS77QQ5N-BVNIEupW.js} +1 -1
- package/dist/web/assets/{index-9cOaPesh.js → index-D8GJBw23.js} +98 -98
- package/dist/web/assets/{index-kK3hngnz.css → index-DMD2vvQW.css} +1 -1
- package/dist/web/assets/{info-DKCQHKI2-B5u97QuQ.js → info-DKCQHKI2-BKBUVpfc.js} +1 -1
- package/dist/web/assets/{infoDiagram-6WML65LV-zosKW2OD.js → infoDiagram-6WML65LV-_dTFnzSE.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-WSZJBQD7-VYcc_au8.js → ishikawaDiagram-WSZJBQD7-DIRgD4hO.js} +1 -1
- package/dist/web/assets/{journeyDiagram-NVQOT4AX-C_nU8kP4.js → journeyDiagram-NVQOT4AX-bp7nZrfR.js} +1 -1
- package/dist/web/assets/{kanban-definition-27J2QSJJ-DybKaWfx.js → kanban-definition-27J2QSJJ-C6mZIo8S.js} +1 -1
- package/dist/web/assets/{line-DWsGplGj.js → line-DWTym7eJ.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-D_T_3oIQ.js → mermaid-parser.core-CbecsNA1.js} +3 -3
- package/dist/web/assets/{mermaid.core-BoLGzxB5.js → mermaid.core-CdRuLyYI.js} +4 -4
- package/dist/web/assets/{mindmap-definition-FAOFIHXS-CsdpqPIa.js → mindmap-definition-FAOFIHXS-CU2P1z-9.js} +1 -1
- package/dist/web/assets/{packet-7NZHBO7P-CReAEF1h.js → packet-7NZHBO7P-CZsup6Od.js} +1 -1
- package/dist/web/assets/{pegDiagram-VL7TDLO6-CE9-NPMu.js → pegDiagram-VL7TDLO6-DTTyttpe.js} +1 -1
- package/dist/web/assets/{pie-RZYD4A2V-GYixXUka.js → pie-RZYD4A2V-DgrIiWZA.js} +1 -1
- package/dist/web/assets/{pieDiagram-7S7Q4E2Y-CqaAKNg2.js → pieDiagram-7S7Q4E2Y-BBNFyPT1.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-CIZ2JOQS-eRMHJqfj.js → quadrantDiagram-CIZ2JOQS-ejp-Aa2l.js} +1 -1
- package/dist/web/assets/{radar-I7S5WNFK-BwHxP0iK.js → radar-I7S5WNFK-Cp_hfjrI.js} +1 -1
- package/dist/web/assets/{railroad-3IZDKUUU-AHc3O4Oz.js → railroad-3IZDKUUU-Dw-oMal2.js} +1 -1
- package/dist/web/assets/railroad-abnf-AHOZXSZD-BN-ZBhE0.js +1 -0
- package/dist/web/assets/railroad-ebnf-EBAXGLYW-CG97zyI2.js +1 -0
- package/dist/web/assets/railroad-peg-LSFZ7HO6-DHLQ1evQ.js +1 -0
- package/dist/web/assets/{railroadDiagram-AXF67PYL-BhAxCokG.js → railroadDiagram-AXF67PYL-DkBUaj6Z.js} +1 -1
- package/dist/web/assets/{requirementDiagram-LRYGKXZP-BRW-kYhg.js → requirementDiagram-LRYGKXZP-Dff1ex_h.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-W5VNT64P-DfnYLbDJ.js → sankeyDiagram-W5VNT64P-DptSL9Bc.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-SI44F4Z6-WEkIPSg8.js → sequenceDiagram-SI44F4Z6-DEnPcs3U.js} +1 -1
- package/dist/web/assets/{stateDiagram-OKZ733FA-CdN7veIN.js → stateDiagram-OKZ733FA-BwJO1MKA.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-LaSNqvWy.js +1 -0
- package/dist/web/assets/{swimlanes-SLNWSIFB-g9wWLf5Y.js → swimlanes-SLNWSIFB-BosKiwZA.js} +1 -1
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-C5OBtBlv.js +8 -0
- package/dist/web/assets/{timeline-definition-Z64GVDOM-DjkKZIPE.js → timeline-definition-Z64GVDOM-C5isAn4X.js} +1 -1
- package/dist/web/assets/{treeView-QDETBFTQ-WARa9r09.js → treeView-QDETBFTQ-zEyUXRc4.js} +1 -1
- package/dist/web/assets/{treemap-6X3UGDF4-CTFjdb6k.js → treemap-6X3UGDF4-B-X0EQSE.js} +1 -1
- package/dist/web/assets/{vennDiagram-T6HMQDX7-CI9QrBwu.js → vennDiagram-T6HMQDX7-QMdGieCa.js} +1 -1
- package/dist/web/assets/{wardley-OPB4EBWU-Cfo_Nnla.js → wardley-OPB4EBWU-BaeAF8d8.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-T6FBY63Y-CzGJ4fzZ.js → wardleyDiagram-T6FBY63Y-DTQ_uLUq.js} +1 -1
- package/dist/web/assets/{xychartDiagram-ELKLHX3M-BXCiD7gJ.js → xychartDiagram-ELKLHX3M-BMdBWzib.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/skills/rvw-watch-comments/SKILL.md +84 -34
- package/skills/rvw-watch-comments/agents/openai.yaml +2 -2
- package/skills/rvw-watch-comments/scripts/watch-driver.mjs +223 -48
- package/skills/rvw-watch-comments/scripts/watch-state.mjs +31 -9
- package/dist/web/assets/DocumentViewer-ADzspLC4.js +0 -1560
- package/dist/web/assets/WalkthroughViewer-yQibj2tK.js +0 -1
- package/dist/web/assets/architecture-TIHT7OUA-DEoBuEnc.js +0 -1
- package/dist/web/assets/channel-DjgdV-zW.js +0 -1
- package/dist/web/assets/classDiagram-JCYQIIEL-B6Qjam7Y.js +0 -1
- package/dist/web/assets/classDiagram-v2-OCEON4UE-B6Qjam7Y.js +0 -1
- package/dist/web/assets/eventmodeling-45OFAUF4-ndNeUBOZ.js +0 -1
- package/dist/web/assets/flowDiagram-UKHOOZJN-BVxK_6Tu.js +0 -1
- package/dist/web/assets/railroad-abnf-AHOZXSZD-ZKY5olmV.js +0 -1
- package/dist/web/assets/railroad-ebnf-EBAXGLYW-CrG1UCMo.js +0 -1
- package/dist/web/assets/railroad-peg-LSFZ7HO6-CoY6MIbj.js +0 -1
- package/dist/web/assets/stateDiagram-v2-UEYNNEHI-0xn0gbpn.js +0 -1
- package/dist/web/assets/swimlanesDiagram-ULZ7WXOC-YJ3yh_d3.js +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.2.4] - 2026-08-24
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- `rvw-watch-comments`のinvestigate-and-reply専用taskで、同じPR/repositoryの後続batchもworker capacity内で
|
|
13
|
+
並列に調査・返信。保証できる場合は`max-in-flight=8`を目標とし、fix-and-pushを許可したtaskだけ従来の
|
|
14
|
+
writer排他を維持
|
|
15
|
+
|
|
16
|
+
## [0.2.3] - 2026-08-21
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- PR本文へ貼り付けたmodern GitHub user attachment画像を、public/private共通のlocalhost proxyで表示
|
|
21
|
+
- repositoryのPNG、JPEG、GIF、WebP、AVIF、SVGをexact commit固定の全文表示とold/new Split表示で閲覧
|
|
22
|
+
- 画像ファイルのadded、deleted、renamed、画像・非画像間の変更表示とfile-level comment
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- 同じtask stateで`rvw-watch-comments`のwatch driverを複数起動できないようにし、二重監視と重複処理を防止
|
|
27
|
+
|
|
28
|
+
### Security
|
|
29
|
+
|
|
30
|
+
- GitHub attachment proxyを厳密な`https://github.com/user-attachments/assets/<uuid>`だけへ限定し、
|
|
31
|
+
同一origin request、GitHub CLIの既存認証、10 MiB上限、magic-byte判定、`nosniff`、SVG sandbox CSPを適用
|
|
32
|
+
- arbitrary external image、legacy GitHub image host、unknown binary、HTML/JSON error responseは引き続き自動取得しない
|
|
33
|
+
|
|
8
34
|
## [0.2.2] - 2026-08-21
|
|
9
35
|
|
|
10
36
|
### Changed
|
package/README.md
CHANGED
|
@@ -118,8 +118,8 @@ rvw walkthrough publish --stdin --json
|
|
|
118
118
|
構成されます。参照は意味のある複数行range、単行、またはfile全体を指せます。rvwは登録時にcommit、
|
|
119
119
|
path、指定された行範囲を検証します。publishしてもbrowserは開かれず、
|
|
120
120
|
active tabやscroll位置も変わりません。人間がviewerのWalkthroughを開き、必要なreferenceを選んだ
|
|
121
|
-
時だけ、exact sourceがdocument tab
|
|
122
|
-
|
|
121
|
+
時だけ、exact sourceがdocument tabへ開き、行指定があれば範囲全体が強調されます。通常clickは左ペイン、
|
|
122
|
+
`Cmd` / `Ctrl`を押しながら選べば右ペインへ開きます。説明tabは残るため、
|
|
123
123
|
複数のclaimと実装を任意の順序・任意のタイミングで往復できます。
|
|
124
124
|
|
|
125
125
|
Walkthrough本文のinline referenceとMermaid node linkは維持しますが、同じ参照を横や下へ列挙する
|
|
@@ -199,7 +199,10 @@ PRは常にcode/GitHub read-onlyで調査します。同梱driverがcursor resum
|
|
|
199
199
|
batchをclaimすると各threadへ`🔎 確認中です…`をAgent往復なしに即時返信し、
|
|
200
200
|
完了時は同じreplyを最終結果へ編集します。Skill同梱のtask-state toolがrepository外のSQLiteへcursor、
|
|
201
201
|
queue、retry、batch内のthread単位status post、自己返信抑制をtransactionalに保存します。同じthreadへ
|
|
202
|
-
後から返信が追加された場合は新しいstatus post
|
|
202
|
+
後から返信が追加された場合は新しいstatus postを返信するため、以前の回答は書き換えません。親taskは
|
|
203
|
+
起動前にsubagent枠を予約し、driverはその枠数までだけbatchをclaimします。acknowledge済みbatchは大小や
|
|
204
|
+
変更有無にかかわらず同じscheduling turnでfresh subagentへ委譲し、親taskは直接処理しません。lease解放後の
|
|
205
|
+
同一PR follow-upと期限到達したretryはdriverがtask stateから自動的に再開します。
|
|
203
206
|
調査結果、実装内容、test結果が具体的なcodeに基づく場合、Skillは最終replyからexact commitの有用な
|
|
204
207
|
line rangeへ`rvw-ref:` linkを付け、reviewerが根拠へ直接移動できるようにします。
|
|
205
208
|
|
package/dist/cli.mjs
CHANGED
|
@@ -19759,12 +19759,15 @@ import path5 from "node:path";
|
|
|
19759
19759
|
import { TextDecoder as TextDecoder2 } from "node:util";
|
|
19760
19760
|
|
|
19761
19761
|
// src/shared/constants.ts
|
|
19762
|
-
var APP_VERSION = "0.2.
|
|
19762
|
+
var APP_VERSION = "0.2.4";
|
|
19763
19763
|
var PROTOCOL_VERSION = 3;
|
|
19764
19764
|
var VIEWER_ID_HEADER = "x-rvw-viewer-id";
|
|
19765
19765
|
var GIT_OBJECT_ID_PATTERN = /^[0-9a-f]{40,64}$/i;
|
|
19766
19766
|
var MAX_TEXT_DOCUMENT_BYTES = 1024 * 1024;
|
|
19767
19767
|
var MAX_MARKDOWN_ASSET_BYTES = 5 * 1024 * 1024;
|
|
19768
|
+
var MAX_GITHUB_ATTACHMENT_BYTES = 10 * 1024 * 1024;
|
|
19769
|
+
var MAX_GITHUB_ATTACHMENT_STDERR_BYTES = 64 * 1024;
|
|
19770
|
+
var GITHUB_ATTACHMENT_TIMEOUT_MS = 3e4;
|
|
19768
19771
|
var MAX_COMMENT_BODY_BYTES = 64 * 1024;
|
|
19769
19772
|
var MAX_AUTHOR_LABEL_CHARACTERS = 100;
|
|
19770
19773
|
var MAX_IDEMPOTENCY_KEY_CHARACTERS = 200;
|
|
@@ -20414,6 +20417,101 @@ var GitClient = class {
|
|
|
20414
20417
|
}
|
|
20415
20418
|
};
|
|
20416
20419
|
|
|
20420
|
+
// src/shared/image-assets.ts
|
|
20421
|
+
var SUPPORTED_IMAGE_EXTENSIONS = [
|
|
20422
|
+
".png",
|
|
20423
|
+
".jpg",
|
|
20424
|
+
".jpeg",
|
|
20425
|
+
".gif",
|
|
20426
|
+
".webp",
|
|
20427
|
+
".avif",
|
|
20428
|
+
".svg"
|
|
20429
|
+
];
|
|
20430
|
+
var githubAttachmentPathPattern = /^\/user-attachments\/assets\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
20431
|
+
var utf8Fatal2 = new TextDecoder("utf-8", { fatal: true });
|
|
20432
|
+
function hasBytes(bytes, offset, expected) {
|
|
20433
|
+
return expected.every((value, index2) => bytes[offset + index2] === value);
|
|
20434
|
+
}
|
|
20435
|
+
function ascii(bytes, offset, length) {
|
|
20436
|
+
return String.fromCharCode(...bytes.subarray(offset, offset + length));
|
|
20437
|
+
}
|
|
20438
|
+
function uint16Le(bytes, offset) {
|
|
20439
|
+
return (bytes[offset] ?? 0) | (bytes[offset + 1] ?? 0) << 8;
|
|
20440
|
+
}
|
|
20441
|
+
function uint32Be(bytes, offset) {
|
|
20442
|
+
return (bytes[offset] ?? 0) * 16777216 + ((bytes[offset + 1] ?? 0) << 16) + ((bytes[offset + 2] ?? 0) << 8) + (bytes[offset + 3] ?? 0) >>> 0;
|
|
20443
|
+
}
|
|
20444
|
+
function uint32Le(bytes, offset) {
|
|
20445
|
+
return (bytes[offset] ?? 0) + ((bytes[offset + 1] ?? 0) << 8) + ((bytes[offset + 2] ?? 0) << 16) + (bytes[offset + 3] ?? 0) * 16777216 >>> 0;
|
|
20446
|
+
}
|
|
20447
|
+
function isSvg(bytes) {
|
|
20448
|
+
if (bytes.length === 0) return false;
|
|
20449
|
+
let text3;
|
|
20450
|
+
try {
|
|
20451
|
+
text3 = utf8Fatal2.decode(bytes);
|
|
20452
|
+
} catch {
|
|
20453
|
+
return false;
|
|
20454
|
+
}
|
|
20455
|
+
const withoutBom = text3.charCodeAt(0) === 65279 ? text3.slice(1) : text3;
|
|
20456
|
+
let candidate = withoutBom.trimStart();
|
|
20457
|
+
const declaration = /^<\?xml(?:\s[^?]*)?\?>/iu.exec(candidate);
|
|
20458
|
+
if (declaration) candidate = candidate.slice(declaration[0].length).trimStart();
|
|
20459
|
+
while (candidate.startsWith("<!--")) {
|
|
20460
|
+
const commentEnd = candidate.indexOf("-->");
|
|
20461
|
+
if (commentEnd < 0) return false;
|
|
20462
|
+
candidate = candidate.slice(commentEnd + 3).trimStart();
|
|
20463
|
+
}
|
|
20464
|
+
return /^<svg(?:\s|>)/iu.test(candidate);
|
|
20465
|
+
}
|
|
20466
|
+
function isSupportedImagePath(filePath) {
|
|
20467
|
+
const normalized = filePath.toLowerCase();
|
|
20468
|
+
return SUPPORTED_IMAGE_EXTENSIONS.some((extension2) => normalized.endsWith(extension2));
|
|
20469
|
+
}
|
|
20470
|
+
function canonicalGitHubAttachmentUrl(value) {
|
|
20471
|
+
if (!value?.startsWith("https://github.com/")) return null;
|
|
20472
|
+
let parsed;
|
|
20473
|
+
try {
|
|
20474
|
+
parsed = new URL(value);
|
|
20475
|
+
} catch {
|
|
20476
|
+
return null;
|
|
20477
|
+
}
|
|
20478
|
+
if (parsed.protocol !== "https:" || parsed.hostname !== "github.com" || parsed.username !== "" || parsed.password !== "" || parsed.port !== "" || parsed.search !== "" || parsed.hash !== "" || !githubAttachmentPathPattern.test(parsed.pathname)) {
|
|
20479
|
+
return null;
|
|
20480
|
+
}
|
|
20481
|
+
return parsed.href;
|
|
20482
|
+
}
|
|
20483
|
+
function detectImageContentType(bytes) {
|
|
20484
|
+
if (bytes.length >= 33 && hasBytes(bytes, 0, [137, 80, 78, 71, 13, 10, 26, 10]) && ascii(bytes, 12, 4) === "IHDR" && uint32Be(bytes, 16) > 0 && uint32Be(bytes, 20) > 0) {
|
|
20485
|
+
return "image/png";
|
|
20486
|
+
}
|
|
20487
|
+
if (bytes.length >= 10 && hasBytes(bytes, 0, [255, 216, 255]) && hasBytes(bytes, bytes.length - 2, [255, 217])) {
|
|
20488
|
+
return "image/jpeg";
|
|
20489
|
+
}
|
|
20490
|
+
const gifHeader = bytes.length >= 13 ? ascii(bytes, 0, 6) : "";
|
|
20491
|
+
if ((gifHeader === "GIF87a" || gifHeader === "GIF89a") && uint16Le(bytes, 6) > 0 && uint16Le(bytes, 8) > 0) {
|
|
20492
|
+
return "image/gif";
|
|
20493
|
+
}
|
|
20494
|
+
if (bytes.length >= 20 && ascii(bytes, 0, 4) === "RIFF" && ascii(bytes, 8, 4) === "WEBP" && ["VP8 ", "VP8L", "VP8X"].includes(ascii(bytes, 12, 4)) && uint32Le(bytes, 4) >= 12 && uint32Le(bytes, 4) + 8 <= bytes.length) {
|
|
20495
|
+
return "image/webp";
|
|
20496
|
+
}
|
|
20497
|
+
if (bytes.length >= 16 && ascii(bytes, 4, 4) === "ftyp") {
|
|
20498
|
+
const boxSize = uint32Be(bytes, 0);
|
|
20499
|
+
if (boxSize >= 16 && boxSize <= bytes.length) {
|
|
20500
|
+
const brands = [ascii(bytes, 8, 4)];
|
|
20501
|
+
for (let offset = 16; offset + 4 <= boxSize; offset += 4) {
|
|
20502
|
+
brands.push(ascii(bytes, offset, 4));
|
|
20503
|
+
}
|
|
20504
|
+
if (brands.some((brand) => brand === "avif" || brand === "avis")) {
|
|
20505
|
+
return "image/avif";
|
|
20506
|
+
}
|
|
20507
|
+
}
|
|
20508
|
+
}
|
|
20509
|
+
return isSvg(bytes) ? "image/svg+xml" : null;
|
|
20510
|
+
}
|
|
20511
|
+
function imageContentTypeHeader(contentType2) {
|
|
20512
|
+
return contentType2 === "image/svg+xml" ? `${contentType2}; charset=utf-8` : contentType2;
|
|
20513
|
+
}
|
|
20514
|
+
|
|
20417
20515
|
// src/infrastructure/github/github-client.ts
|
|
20418
20516
|
var ghPullRequestSchema = external_exports.object({
|
|
20419
20517
|
author: external_exports.object({ login: external_exports.string().min(1) }).nullable(),
|
|
@@ -20439,6 +20537,10 @@ function parsePullRequestUrl(url2) {
|
|
|
20439
20537
|
return { owner: match2[1], repository: match2[2], number: Number(match2[3]) };
|
|
20440
20538
|
}
|
|
20441
20539
|
var GitHubClient = class {
|
|
20540
|
+
constructor(processRunner = runProcess) {
|
|
20541
|
+
this.processRunner = processRunner;
|
|
20542
|
+
}
|
|
20543
|
+
processRunner;
|
|
20442
20544
|
async doctor() {
|
|
20443
20545
|
const version2 = await runText("gh", ["--version"]);
|
|
20444
20546
|
const auth = await runProcess("gh", ["auth", "status", "--hostname", "github.com"], {
|
|
@@ -20523,6 +20625,31 @@ var GitHubClient = class {
|
|
|
20523
20625
|
isDraft: parsed.data.isDraft
|
|
20524
20626
|
};
|
|
20525
20627
|
}
|
|
20628
|
+
async getAttachment(absoluteUrl) {
|
|
20629
|
+
const canonicalUrl = canonicalGitHubAttachmentUrl(absoluteUrl);
|
|
20630
|
+
if (!canonicalUrl) {
|
|
20631
|
+
throw new RvwError("INVALID_INPUT", "GitHub user attachment URL\u304C\u4E0D\u6B63\u3067\u3059\u3002");
|
|
20632
|
+
}
|
|
20633
|
+
try {
|
|
20634
|
+
const result = await this.processRunner("gh", ["api", canonicalUrl], {
|
|
20635
|
+
timeoutMs: GITHUB_ATTACHMENT_TIMEOUT_MS,
|
|
20636
|
+
maxStdoutBytes: MAX_GITHUB_ATTACHMENT_BYTES,
|
|
20637
|
+
maxStderrBytes: MAX_GITHUB_ATTACHMENT_STDERR_BYTES
|
|
20638
|
+
});
|
|
20639
|
+
return { content: result.stdout, byteLength: result.stdout.byteLength };
|
|
20640
|
+
} catch (error51) {
|
|
20641
|
+
if (error51 instanceof RvwError && error51.code === "PROCESS_OUTPUT_LIMIT" && error51.details?.stream === "stdout") {
|
|
20642
|
+
throw new RvwError("FILE_TOO_LARGE", "GitHub attachment\u306F10 MiB\u4EE5\u4E0B\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002", {
|
|
20643
|
+
status: 413
|
|
20644
|
+
});
|
|
20645
|
+
}
|
|
20646
|
+
throw new RvwError(
|
|
20647
|
+
"GITHUB_ERROR",
|
|
20648
|
+
"GitHub attachment\u3092\u53D6\u5F97\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002gh\u8A8D\u8A3C\u3068\u753B\u50CF\u306E\u95B2\u89A7\u6A29\u9650\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
20649
|
+
{ status: 502 }
|
|
20650
|
+
);
|
|
20651
|
+
}
|
|
20652
|
+
}
|
|
20526
20653
|
};
|
|
20527
20654
|
|
|
20528
20655
|
// src/application/rvw-service.ts
|
|
@@ -28398,6 +28525,21 @@ var RvwService = class {
|
|
|
28398
28525
|
await this.assertCommitAvailable(pullRequest, sourceOid);
|
|
28399
28526
|
return await this.git.readRepositoryAsset(pullRequest.localRepositoryPath, sourceOid, filePath);
|
|
28400
28527
|
}
|
|
28528
|
+
async getGitHubAttachment(pullRequestId, absoluteUrl) {
|
|
28529
|
+
this.getPullRequest(pullRequestId);
|
|
28530
|
+
const canonicalUrl = canonicalGitHubAttachmentUrl(absoluteUrl);
|
|
28531
|
+
if (!canonicalUrl) {
|
|
28532
|
+
throw new RvwError("INVALID_INPUT", "GitHub user attachment URL\u304C\u4E0D\u6B63\u3067\u3059\u3002");
|
|
28533
|
+
}
|
|
28534
|
+
const attachment = await this.github.getAttachment(canonicalUrl);
|
|
28535
|
+
const contentType2 = detectImageContentType(attachment.content);
|
|
28536
|
+
if (!contentType2) {
|
|
28537
|
+
throw new RvwError("UNSUPPORTED_IMAGE", "GitHub attachment\u306F\u5BFE\u5FDC\u753B\u50CF\u5F62\u5F0F\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002", {
|
|
28538
|
+
status: 415
|
|
28539
|
+
});
|
|
28540
|
+
}
|
|
28541
|
+
return { ...attachment, contentType: contentType2 };
|
|
28542
|
+
}
|
|
28401
28543
|
async getDiff(ref) {
|
|
28402
28544
|
const [old, next] = await Promise.all([
|
|
28403
28545
|
ref.old ? this.getDocument(ref.old) : Promise.resolve(null),
|
|
@@ -33257,17 +33399,25 @@ function parseBooleanQuery(value, name) {
|
|
|
33257
33399
|
if (value === "true") return true;
|
|
33258
33400
|
throw new RvwError("INVALID_INPUT", `${name} query\u306Ftrue\u307E\u305F\u306Ffalse\u306B\u3057\u3066\u304F\u3060\u3055\u3044\u3002`);
|
|
33259
33401
|
}
|
|
33260
|
-
function
|
|
33261
|
-
|
|
33262
|
-
|
|
33263
|
-
|
|
33264
|
-
"
|
|
33265
|
-
|
|
33266
|
-
|
|
33267
|
-
|
|
33268
|
-
|
|
33269
|
-
|
|
33270
|
-
|
|
33402
|
+
function repositoryAssetContentType(filePath, content3) {
|
|
33403
|
+
if (!isSupportedImagePath(filePath)) return null;
|
|
33404
|
+
const contentType2 = detectImageContentType(content3);
|
|
33405
|
+
if (!contentType2) {
|
|
33406
|
+
throw new RvwError("UNSUPPORTED_IMAGE", "repository asset\u306F\u5BFE\u5FDC\u753B\u50CF\u5F62\u5F0F\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002", {
|
|
33407
|
+
status: 415
|
|
33408
|
+
});
|
|
33409
|
+
}
|
|
33410
|
+
return contentType2;
|
|
33411
|
+
}
|
|
33412
|
+
function setImageResponseHeaders(setHeader, contentType2) {
|
|
33413
|
+
setHeader("content-type", imageContentTypeHeader(contentType2));
|
|
33414
|
+
setHeader("cache-control", "private, max-age=31536000, immutable");
|
|
33415
|
+
setHeader("x-content-type-options", "nosniff");
|
|
33416
|
+
setHeader("content-disposition", "inline");
|
|
33417
|
+
setHeader("cross-origin-resource-policy", "same-origin");
|
|
33418
|
+
if (contentType2 === "image/svg+xml") {
|
|
33419
|
+
setHeader("content-security-policy", svgAssetContentSecurityPolicy);
|
|
33420
|
+
}
|
|
33271
33421
|
}
|
|
33272
33422
|
function documentRefFromQuery(pullRequestId, query) {
|
|
33273
33423
|
if (query.kind === "pull-request-markdown") {
|
|
@@ -33395,17 +33545,36 @@ function createApp(service, options) {
|
|
|
33395
33545
|
const ref = documentRefFromQuery(context.req.param("id"), context.req.query());
|
|
33396
33546
|
return context.json({ ok: true, document: await service.getDocument(ref) });
|
|
33397
33547
|
});
|
|
33398
|
-
app.
|
|
33548
|
+
app.on(["GET", "HEAD"], "/api/pull-requests/:id/markdown-asset", async (context) => {
|
|
33399
33549
|
const sourceOid = oidQuery(context.req.query("sourceOid"), "sourceOid");
|
|
33400
33550
|
const filePath = requiredQuery(context.req.query("path"), "path");
|
|
33401
33551
|
const asset = await service.getRepositoryAsset(context.req.param("id"), sourceOid, filePath);
|
|
33402
|
-
|
|
33403
|
-
|
|
33404
|
-
|
|
33405
|
-
|
|
33406
|
-
context.header("content-
|
|
33552
|
+
const contentType2 = repositoryAssetContentType(filePath, asset.content);
|
|
33553
|
+
if (contentType2) {
|
|
33554
|
+
setImageResponseHeaders((name, value) => context.header(name, value), contentType2);
|
|
33555
|
+
} else {
|
|
33556
|
+
context.header("content-type", "application/octet-stream");
|
|
33557
|
+
context.header("cache-control", "private, max-age=31536000, immutable");
|
|
33558
|
+
context.header("x-content-type-options", "nosniff");
|
|
33559
|
+
}
|
|
33560
|
+
return context.req.method === "HEAD" ? context.body(null) : context.body(Uint8Array.from(asset.content));
|
|
33561
|
+
});
|
|
33562
|
+
app.get("/api/pull-requests/:id/github-attachment", async (context) => {
|
|
33563
|
+
const fetchSite = context.req.header("sec-fetch-site");
|
|
33564
|
+
const origin = context.req.header("origin");
|
|
33565
|
+
if (fetchSite !== void 0 && fetchSite !== "same-origin" && fetchSite !== "none" || origin !== void 0 && origin !== options.security.expectedOrigin) {
|
|
33566
|
+
throw new RvwError(
|
|
33567
|
+
"INVALID_ORIGIN",
|
|
33568
|
+
"cross-origin attachment request\u306F\u8A31\u53EF\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002",
|
|
33569
|
+
{
|
|
33570
|
+
status: 403
|
|
33571
|
+
}
|
|
33572
|
+
);
|
|
33407
33573
|
}
|
|
33408
|
-
|
|
33574
|
+
const absoluteUrl = requiredQuery(context.req.query("url"), "url");
|
|
33575
|
+
const attachment = await service.getGitHubAttachment(context.req.param("id"), absoluteUrl);
|
|
33576
|
+
setImageResponseHeaders((name, value) => context.header(name, value), attachment.contentType);
|
|
33577
|
+
return context.body(Uint8Array.from(attachment.content));
|
|
33409
33578
|
});
|
|
33410
33579
|
app.get("/api/pull-requests/:id/diff", async (context) => {
|
|
33411
33580
|
const pullRequestId = context.req.param("id");
|