gitwarren 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/lib/gitwarren.cjs +51 -8
- package/package.json +3 -2
- package/web/assets/{index-DshVhmgX.js → index-D4qKqNi1.js} +2 -2
- package/web/assets/main-1gX2-1gC.js +115 -0
- package/web/index.html +1 -1
- package/web/assets/main-BlPq7_RT.js +0 -114
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npx gitwarren serve --open
|
|
|
13
13
|
|
|
14
14
|
That serves the review UI on `127.0.0.1:41427`, prints a URL carrying a token
|
|
15
15
|
minted for that launch, and opens it. Add a repository, pick two refs, and
|
|
16
|
-
review the diff. Ctrl-C stops it. Needs Node 22 or newer; on a machine without
|
|
16
|
+
review the diff. Ctrl-C stops it. Needs Node 22.14 or newer; on a machine without
|
|
17
17
|
Node, the Homebrew formula and the install script in the
|
|
18
18
|
[app repository](https://github.com/klarluft/gitwarren-app#the-gitwarren-command-line)
|
|
19
19
|
bring their own.
|
|
@@ -50,6 +50,13 @@ URL an agent hands out, and lands on that review rather than the home screen.
|
|
|
50
50
|
|
|
51
51
|
## Letting an agent in
|
|
52
52
|
|
|
53
|
+
The quickest way is the plugin, which brings the server and a note that teaches
|
|
54
|
+
the agent when to open a review. In Claude Code, `/plugin marketplace add
|
|
55
|
+
klarluft/gitwarren-app` then `/plugin install gitwarren@gitwarren`; in Gemini
|
|
56
|
+
CLI, `gemini extensions install https://github.com/klarluft/gitwarren-app`;
|
|
57
|
+
Cursor, Codex, VS Code and Kiro read the same repository from their plugin
|
|
58
|
+
screens. The plugin finds this install by itself.
|
|
59
|
+
|
|
53
60
|
Every install carries GitWarren's MCP server. What an agent needs is one
|
|
54
61
|
stable command to start it with, and that is `~/.gitwarren/bin/gitwarren-mcp` —
|
|
55
62
|
the same path on every machine, written by `gitwarren serve`, `gitwarren
|
package/lib/gitwarren.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
const node_child_process = require("node:child_process");
|
|
2
3
|
const node_module = require("node:module");
|
|
3
4
|
const fs = require("node:fs");
|
|
4
5
|
const node_path = require("node:path");
|
|
5
6
|
const node_os = require("node:os");
|
|
6
7
|
const Client = require("better-sqlite3");
|
|
7
8
|
const crypto$1 = require("node:crypto");
|
|
8
|
-
const node_child_process = require("node:child_process");
|
|
9
9
|
const node_stream = require("node:stream");
|
|
10
10
|
const require$$0$3 = require("events");
|
|
11
11
|
const require$$1$1 = require("https");
|
|
@@ -10301,7 +10301,7 @@ function cmdQuote(value) {
|
|
|
10301
10301
|
if (value.includes('"')) throw new Error(`a Windows path cannot contain a quote: ${value}`);
|
|
10302
10302
|
return `"${value}"`;
|
|
10303
10303
|
}
|
|
10304
|
-
const APP_VERSION = "0.1.
|
|
10304
|
+
const APP_VERSION = "0.1.13";
|
|
10305
10305
|
const REMOTE_ROOT = "$HOME/.gitwarren";
|
|
10306
10306
|
const runOnHost = (route2, options) => {
|
|
10307
10307
|
switch (route2.kind) {
|
|
@@ -26869,7 +26869,7 @@ Connection: close\r
|
|
|
26869
26869
|
}
|
|
26870
26870
|
};
|
|
26871
26871
|
}
|
|
26872
|
-
const VERSION$1 = "0.1.
|
|
26872
|
+
const VERSION$1 = "0.1.13";
|
|
26873
26873
|
function isWebBuild(directory) {
|
|
26874
26874
|
return fs.existsSync(node_path.join(directory, "index.html")) && !fs.existsSync(node_path.join(directory, "main.ts"));
|
|
26875
26875
|
}
|
|
@@ -26885,7 +26885,7 @@ function resolveWebRoot() {
|
|
|
26885
26885
|
}
|
|
26886
26886
|
return null;
|
|
26887
26887
|
}
|
|
26888
|
-
function describeInstall(linkPort) {
|
|
26888
|
+
function describeInstall(linkPort, servedFor) {
|
|
26889
26889
|
const mcp = describeMcpLaunch();
|
|
26890
26890
|
return {
|
|
26891
26891
|
version: VERSION$1,
|
|
@@ -26895,7 +26895,11 @@ function describeInstall(linkPort) {
|
|
|
26895
26895
|
dataDirectory: getDataDirectory(),
|
|
26896
26896
|
databasePath: getDatabasePath(),
|
|
26897
26897
|
linkPort,
|
|
26898
|
-
mcp
|
|
26898
|
+
mcp,
|
|
26899
|
+
// Spread rather than assigned, so the field is absent - not `undefined` -
|
|
26900
|
+
// in the JSON the other two servers send, which keeps their answers
|
|
26901
|
+
// byte-for-byte what they were.
|
|
26902
|
+
...servedFor === void 0 ? {} : { servedFor }
|
|
26899
26903
|
};
|
|
26900
26904
|
}
|
|
26901
26905
|
function whereTheOwnerIs(owner) {
|
|
@@ -26943,7 +26947,7 @@ function runListen(hooks = {}) {
|
|
|
26943
26947
|
mount: "/",
|
|
26944
26948
|
staticRoot: webRoot,
|
|
26945
26949
|
token,
|
|
26946
|
-
appInfo: () => describeInstall(linkPort),
|
|
26950
|
+
appInfo: () => describeInstall(linkPort, hooks.servedFor),
|
|
26947
26951
|
tailnet: tailnetGate
|
|
26948
26952
|
});
|
|
26949
26953
|
const server = node_http.createServer((request, response) => {
|
|
@@ -27039,6 +27043,11 @@ function runDaemon(argv, hooks = {}) {
|
|
|
27039
27043
|
return true;
|
|
27040
27044
|
}
|
|
27041
27045
|
const MCP_SERVER_NAME = "gitwarren";
|
|
27046
|
+
const PLUGIN_REPOSITORY = "klarluft/gitwarren-app";
|
|
27047
|
+
const PLUGIN_INSTALL = {
|
|
27048
|
+
/** Two commands, typed into Claude Code itself. */
|
|
27049
|
+
claudeCode: [`/plugin marketplace add ${PLUGIN_REPOSITORY}`, "/plugin install gitwarren@gitwarren"]
|
|
27050
|
+
};
|
|
27042
27051
|
function agentSetupPrompt(mcp) {
|
|
27043
27052
|
return `Set up the GitWarren MCP server for yourself. It speaks MCP over stdio and is started with the command ${mcp.command} (no arguments, no environment). Register it under the name "${MCP_SERVER_NAME}" in your own MCP configuration, then call its agent_identity tool to confirm it works.`;
|
|
27044
27053
|
}
|
|
@@ -27197,6 +27206,10 @@ function runAgentSetup(argv) {
|
|
|
27197
27206
|
console.log(snippet.text.trimEnd());
|
|
27198
27207
|
}
|
|
27199
27208
|
}
|
|
27209
|
+
console.error(
|
|
27210
|
+
`
|
|
27211
|
+
[gitwarren] In Claude Code the plugin does this and teaches the agent when to review: ` + PLUGIN_INSTALL.claudeCode.join(", then ")
|
|
27212
|
+
);
|
|
27200
27213
|
const launchers = ensureLaunchers();
|
|
27201
27214
|
if (launchers.created.includes(command)) {
|
|
27202
27215
|
console.error(`
|
|
@@ -27619,7 +27632,7 @@ function runService(argv) {
|
|
|
27619
27632
|
return true;
|
|
27620
27633
|
}
|
|
27621
27634
|
}
|
|
27622
|
-
const VERSION = "0.1.
|
|
27635
|
+
const VERSION = "0.1.13";
|
|
27623
27636
|
const USAGE = `gitwarren - code review for your own git repositories, in a browser tab
|
|
27624
27637
|
|
|
27625
27638
|
Run it now
|
|
@@ -27711,12 +27724,42 @@ function runMcp(argv) {
|
|
|
27711
27724
|
);
|
|
27712
27725
|
return false;
|
|
27713
27726
|
}
|
|
27727
|
+
if (!nativeAddonLoads(server)) return false;
|
|
27714
27728
|
process.removeAllListeners("SIGINT");
|
|
27715
27729
|
process.removeAllListeners("SIGTERM");
|
|
27716
27730
|
if (argv.includes("--serve")) serveBesideMcp();
|
|
27717
27731
|
node_module.createRequire(server)(server);
|
|
27718
27732
|
return true;
|
|
27719
27733
|
}
|
|
27734
|
+
const NODE_API_NEEDED = 10;
|
|
27735
|
+
const NODE_NEEDED = "Node 22.14 or newer, or Node 24";
|
|
27736
|
+
function nativeAddonLoads(server) {
|
|
27737
|
+
if (Number(process.versions.napi) < NODE_API_NEEDED) {
|
|
27738
|
+
console.error(
|
|
27739
|
+
`[gitwarren] Node ${process.versions.node} is too old for GitWarren's SQLite module, which needs ${NODE_NEEDED} (Node-API ${NODE_API_NEEDED}; this one has ${process.versions.napi}). Run this with a newer Node.`
|
|
27740
|
+
);
|
|
27741
|
+
process.exitCode = 1;
|
|
27742
|
+
return false;
|
|
27743
|
+
}
|
|
27744
|
+
let addon;
|
|
27745
|
+
try {
|
|
27746
|
+
addon = node_module.createRequire(server).resolve("better-sqlite3");
|
|
27747
|
+
} catch {
|
|
27748
|
+
return true;
|
|
27749
|
+
}
|
|
27750
|
+
const probe = node_child_process.spawnSync(
|
|
27751
|
+
process.execPath,
|
|
27752
|
+
["-e", "new (require(process.argv[1]))(':memory:').prepare('select 1').get()", addon],
|
|
27753
|
+
{ stdio: ["ignore", "ignore", "pipe"], timeout: 1e4, encoding: "utf8" }
|
|
27754
|
+
);
|
|
27755
|
+
if (probe.status === 0) return true;
|
|
27756
|
+
const how = probe.signal ? `crashed with ${probe.signal}` : probe.error ? `could not be started: ${probe.error.message}` : `exited with ${probe.status}: ${probe.stderr.trim().split("\n").pop() ?? ""}`;
|
|
27757
|
+
console.error(
|
|
27758
|
+
`[gitwarren] the SQLite module at ${addon} ${how} under Node ${process.versions.node}. GitWarren needs ${NODE_NEEDED}. If this Node is newer than that, the install itself is broken: if it came from npx, remove its gitwarren entry under the npm cache's _npx directory and run again.`
|
|
27759
|
+
);
|
|
27760
|
+
process.exitCode = 1;
|
|
27761
|
+
return false;
|
|
27762
|
+
}
|
|
27720
27763
|
function serveBesideMcp() {
|
|
27721
27764
|
const owner = readLiveDaemonRuntime();
|
|
27722
27765
|
if (owner) {
|
|
@@ -27725,7 +27768,7 @@ function serveBesideMcp() {
|
|
|
27725
27768
|
);
|
|
27726
27769
|
return;
|
|
27727
27770
|
}
|
|
27728
|
-
if (!runDaemon(["--listen"], { brief: true })) {
|
|
27771
|
+
if (!runDaemon(["--listen"], { brief: true, servedFor: "agent" })) {
|
|
27729
27772
|
process.exitCode = void 0;
|
|
27730
27773
|
console.error(
|
|
27731
27774
|
"[gitwarren] the review page could not be served, so links will not open until a GitWarren is started on this machine. The MCP server is running regardless."
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitwarren",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "Code review for your own machines and your own agents, served on loopback. No Electron, no account.",
|
|
5
5
|
"keywords": ["code-review", "git", "diff", "mcp", "local-first", "ssh", "wsl", "tailscale", "self-hosted"],
|
|
6
6
|
"homepage": "https://github.com/klarluft/gitwarren-app",
|
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
"bugs": "https://github.com/klarluft/gitwarren-app/issues",
|
|
9
9
|
"license": "GPL-3.0-or-later",
|
|
10
10
|
"author": { "name": "Klarluft B.V.", "email": "contact@klarluft.com", "url": "https://klarluft.com" },
|
|
11
|
+
"mcpName": "io.github.klarluft/gitwarren",
|
|
11
12
|
"type": "module",
|
|
12
13
|
"bin": { "gitwarren": "bin/gitwarren.mjs" },
|
|
13
14
|
"files": ["bin", "lib", "drizzle", "web", "README.md"],
|
|
14
|
-
"engines": { "node": ">=22" },
|
|
15
|
+
"engines": { "node": ">=22.14" },
|
|
15
16
|
"dependencies": { "better-sqlite3": "^13.0.3" }
|
|
16
17
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./main-
|
|
2
|
-
import{a as U,A as B,i as F,b as j,r as H,c as b,e as I,d as D,l as M,p as N,f as q,H as G,h as J,o as x}from"./routes-DFoqib84.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))n(r);new MutationObserver(r=>{for(const i of r)if(i.type==="childList")for(const c of i.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&n(c)}).observe(document,{childList:!0,subtree:!0});function o(r){const i={};return r.integrity&&(i.integrity=r.integrity),r.referrerPolicy&&(i.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?i.credentials="include":r.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function n(r){if(r.ep)return;r.ep=!0;const i=o(r);fetch(r.href,i)}})();const K="modulepreload",V=function(e,t){return new URL(e,t).href},k={},X=function(t,o,n){let r=Promise.resolve();if(o&&o.length>0){let w=function(a){return Promise.all(a.map(p=>Promise.resolve(p).then(h=>({status:"fulfilled",value:h}),h=>({status:"rejected",reason:h}))))};const c=document.getElementsByTagName("link"),f=document.querySelector("meta[property=csp-nonce]"),y=f?.nonce||f?.getAttribute("nonce");r=w(o.map(a=>{if(a=V(a,n),a in k)return;k[a]=!0;const p=a.endsWith(".css"),h=p?'[rel="stylesheet"]':"";if(n)for(let l=c.length-1;l>=0;l--){const u=c[l];if(u.href===a&&(!p||u.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${a}"]${h}`))return;const s=document.createElement("link");if(s.rel=p?"stylesheet":K,p||(s.as="script"),s.crossOrigin="",s.href=a,y&&s.setAttribute("nonce",y),document.head.appendChild(s),p)return new Promise((l,u)=>{s.addEventListener("load",l),s.addEventListener("error",()=>u(new Error(`Unable to preload CSS for ${a}`)))})}))}function i(c){const f=new Event("vite:preloadError",{cancelable:!0});if(f.payload=c,window.dispatchEvent(f),!f.defaultPrevented)throw c}return r.then(c=>{for(const f of c||[])f.status==="rejected"&&i(f.reason);return t().catch(i)})},P="/gitwarren",O={socket:`${P}/socket`,appInfo:`${P}/app-info`,attachments:`${P}/attachments/`};function Y(e,t){const o=U(e);if(o===null)return e;const n=`${O.attachments}${o}`;return t===void 0?n:`${n}?${B}=${encodeURIComponent(t)}`}const $=32768;function z(e){let t="";for(let o=0;o<e.length;o+=$)t+=String.fromCharCode(...e.subarray(o,o+$));return btoa(t)}function A(e){const{params:t}=e;if(typeof t!="object"||t===null)return JSON.stringify(e);let o=null;for(const[n,r]of Object.entries(t)){const i=Q(r);i!==null&&(o??={...t},o[n]=z(i))}return JSON.stringify(o===null?e:{...e,params:o})}function Q(e){return e instanceof ArrayBuffer?new Uint8Array(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):null}const C=[250,500,1e3,2e3,5e3];function Z(){return`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}${O.socket}`}function ee(){let e=null,t=0,o=1;const n=new Map,r=[],i=new Set,c=new Set,f=s=>{for(const l of i)l(s)},y=s=>{e?.readyState===WebSocket.OPEN?e.send(A(s)):r.push(s)},w=()=>{const s=[...n.values()];n.clear();for(const l of s)l.reject(new b("INTERNAL","The connection to GitWarren was lost before this finished. Nothing was retried; try again."))},a=()=>{const s=new WebSocket(Z());e=s,s.addEventListener("open",()=>{t=0;const u=r.splice(0,r.length);for(const d of u)s.send(A(d));f(!0)}),s.addEventListener("message",u=>{let d;try{d=JSON.parse(u.data)}catch{console.error("[carrier] a message from GitWarren was not JSON");return}if(typeof d!="object"||d===null)return;if(j(d)){for(const E of c)E(d);return}const g=d;if(typeof g.id!="number")return;const m=n.get(g.id);m&&(n.delete(g.id),m.resolve(g))});const l=()=>{if(e!==s)return;e=null,w(),f(!1);const u=C[Math.min(t,C.length-1)];t+=1,window.setTimeout(a,u)};s.addEventListener("close",l),s.addEventListener("error",()=>{console.error("[carrier] the connection to GitWarren failed")})};a();const p=new Map,h=(s,l,u)=>{const d=o++;return new Promise((m,E)=>{n.set(d,{resolve:m,reject:E}),y({id:d,method:s,params:l,...u===void 0?{}:{host:u}})}).then(m=>H(m))};return{request(s,l,u){if(!F(s))return h(s,l,u);const d=`${u??""}:${s}:${JSON.stringify(l??null)}`,g=p.get(d);if(g)return g;const m=h(s,l,u).finally(()=>p.delete(d));return p.set(d,m),m},connected:()=>e?.readyState===WebSocket.OPEN,onConnectionChange(s){return i.add(s),()=>i.delete(s)},onEvent(s){return c.add(s),()=>c.delete(s)}}}const R=()=>{};function v(e,t){return Promise.reject(new b("FORBIDDEN",`${e} is not available in a browser tab. ${t}`))}function te(e,t){const o=e.find(n=>n.instanceId===t);if(!o)throw new b("NOT_FOUND",`This GitWarren does not know a host with id ${t}. It may have been removed, or the link may have come from somewhere else.`);return o}async function ne(){const e=await fetch(O.appInfo,{credentials:"same-origin",headers:{Accept:"application/json"}});if(!e.ok)throw new b("INTERNAL",`GitWarren could not describe this install (HTTP ${e.status}).`);return await e.json()}function T(){const e=M();return{editors:e,defaultId:e[0]?.id??null}}const _={state:"unsupported",reason:"GitWarren in a browser is updated by whatever installed it - Homebrew, npm, or the tarball it was unpacked from."};function oe(){return new Promise(e=>{const t=document.createElement("input");t.type="file",t.accept="image/png,image/jpeg,image/gif,image/webp",t.style.position="fixed",t.style.left="-9999px",document.body.append(t);let o=!1;const n=i=>{o||(o=!0,window.removeEventListener("focus",r),t.remove(),e(i))},r=()=>{window.setTimeout(()=>n(t.files?.[0]??null),300)};t.addEventListener("change",()=>n(t.files?.[0]??null)),t.addEventListener("cancel",()=>n(null)),window.addEventListener("focus",r),t.click()})}function re(e){return{capabilities:{pickDirectory:!1,revealPath:!1,openAtLogin:!1},system:{pickDirectory:()=>Promise.resolve(null),revealPath:()=>v("Revealing a folder","Copy the path and open it yourself."),appInfo:ne,editors:()=>Promise.resolve(T()),getOpenAtLogin:()=>Promise.resolve(!1),setOpenAtLogin:()=>v("Starting at login","Run `gitwarren service install` on this machine.")},updates:{getStatus:()=>Promise.resolve(_),check:()=>Promise.resolve(_),installNow:()=>v("Installing an update","Update GitWarren the way you installed it."),subscribe:()=>R},connection:{connected:()=>e.connected(),subscribe:n=>e.onConnectionChange(n)},navigation:{onDeepLink:()=>R},pickAttachment:async n=>{const r=await oe();return r===null?null:await e.request("attachments.ingest",{bytes:await r.arrayBuffer(),originalName:r.name},n)},attachmentSrc:Y,openInEditor:async n=>{const{id:r,path:i,changes:c,line:f,editorId:y,host:w}=n,a=I(y)??I(T().defaultId??void 0);if(!a?.url)return v("Opening a file in an editor","No editor with a URL scheme was chosen for this tab.");if(w!==void 0&&!a.remoteUrl)return v("Opening a file on another machine",`${a.label} has no way to open a file it cannot see. VS Code, Cursor and Windsurf do.`);const p=await e.request("reviews.filePath",{id:r,path:i,changes:c},w),h=w===void 0?void 0:D(te(await e.request("hosts.list",void 0),w));window.location.href=h===void 0?a.url(p,f??1):a.remoteUrl(h,p,f??1)}}}const se="gitwarren",L=`${se}://`,W="review",S="h=";function ie(e){if(!e||e.slice(0,L.length).toLowerCase()!==L)return null;const o=(e.slice(L.length).split(/[?#]/)[0]??"").split("/").filter(Boolean),n=o[0]?.toLowerCase();if(n===W)return N(`#/reviews/${o.slice(1).join("/")}`);if(n!==void 0&&q(n)){const r=o.slice(1);return r.length===0?{name:"repositories",host:n}:r[0]?.toLowerCase()!==W?null:N(`#/h/${n}/reviews/${r.slice(1).join("/")}`)}return null}function ae(e){return e.replace(/^#/,"").startsWith(S)}function ce(e,t){const o=e.replace(/^#/,"");if(!o.startsWith(S))return null;const n=ie(`gitwarren://${o.slice(S.length)}`);if(!n)return G;if(n.host===void 0)return n;if(n.host===t){const{host:r,...i}=n;return i}return n}function le(e){return{request:(t,o,n)=>x(()=>e.request(t,o,n)),onEvent:t=>e.onEvent(t)}}function ue(){const e=ee(),t={carrier:le(e),shell:re(e)};return window.gitwarren=t,t}async function fe(e){if(!ae(window.location.hash))return;const{instanceId:t}=await e.shell.system.appInfo(),o=ce(window.location.hash,t);o&&window.location.replace(J(o))}const de=ue();await fe(de);await X(()=>import("./main-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./main-1gX2-1gC.js","./routes-DFoqib84.js","./main-CPEXHyPM.css"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{a as U,A as B,i as F,b as j,r as H,c as b,e as I,d as D,l as M,p as N,f as q,H as G,h as J,o as x}from"./routes-DFoqib84.js";(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))n(r);new MutationObserver(r=>{for(const i of r)if(i.type==="childList")for(const c of i.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&n(c)}).observe(document,{childList:!0,subtree:!0});function o(r){const i={};return r.integrity&&(i.integrity=r.integrity),r.referrerPolicy&&(i.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?i.credentials="include":r.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function n(r){if(r.ep)return;r.ep=!0;const i=o(r);fetch(r.href,i)}})();const K="modulepreload",V=function(e,t){return new URL(e,t).href},k={},X=function(t,o,n){let r=Promise.resolve();if(o&&o.length>0){let w=function(a){return Promise.all(a.map(p=>Promise.resolve(p).then(h=>({status:"fulfilled",value:h}),h=>({status:"rejected",reason:h}))))};const c=document.getElementsByTagName("link"),f=document.querySelector("meta[property=csp-nonce]"),y=f?.nonce||f?.getAttribute("nonce");r=w(o.map(a=>{if(a=V(a,n),a in k)return;k[a]=!0;const p=a.endsWith(".css"),h=p?'[rel="stylesheet"]':"";if(n)for(let l=c.length-1;l>=0;l--){const u=c[l];if(u.href===a&&(!p||u.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${a}"]${h}`))return;const s=document.createElement("link");if(s.rel=p?"stylesheet":K,p||(s.as="script"),s.crossOrigin="",s.href=a,y&&s.setAttribute("nonce",y),document.head.appendChild(s),p)return new Promise((l,u)=>{s.addEventListener("load",l),s.addEventListener("error",()=>u(new Error(`Unable to preload CSS for ${a}`)))})}))}function i(c){const f=new Event("vite:preloadError",{cancelable:!0});if(f.payload=c,window.dispatchEvent(f),!f.defaultPrevented)throw c}return r.then(c=>{for(const f of c||[])f.status==="rejected"&&i(f.reason);return t().catch(i)})},P="/gitwarren",O={socket:`${P}/socket`,appInfo:`${P}/app-info`,attachments:`${P}/attachments/`};function Y(e,t){const o=U(e);if(o===null)return e;const n=`${O.attachments}${o}`;return t===void 0?n:`${n}?${B}=${encodeURIComponent(t)}`}const $=32768;function z(e){let t="";for(let o=0;o<e.length;o+=$)t+=String.fromCharCode(...e.subarray(o,o+$));return btoa(t)}function A(e){const{params:t}=e;if(typeof t!="object"||t===null)return JSON.stringify(e);let o=null;for(const[n,r]of Object.entries(t)){const i=Q(r);i!==null&&(o??={...t},o[n]=z(i))}return JSON.stringify(o===null?e:{...e,params:o})}function Q(e){return e instanceof ArrayBuffer?new Uint8Array(e):ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):null}const C=[250,500,1e3,2e3,5e3];function Z(){return`${window.location.protocol==="https:"?"wss:":"ws:"}//${window.location.host}${O.socket}`}function ee(){let e=null,t=0,o=1;const n=new Map,r=[],i=new Set,c=new Set,f=s=>{for(const l of i)l(s)},y=s=>{e?.readyState===WebSocket.OPEN?e.send(A(s)):r.push(s)},w=()=>{const s=[...n.values()];n.clear();for(const l of s)l.reject(new b("INTERNAL","The connection to GitWarren was lost before this finished. Nothing was retried; try again."))},a=()=>{const s=new WebSocket(Z());e=s,s.addEventListener("open",()=>{t=0;const u=r.splice(0,r.length);for(const d of u)s.send(A(d));f(!0)}),s.addEventListener("message",u=>{let d;try{d=JSON.parse(u.data)}catch{console.error("[carrier] a message from GitWarren was not JSON");return}if(typeof d!="object"||d===null)return;if(j(d)){for(const E of c)E(d);return}const g=d;if(typeof g.id!="number")return;const m=n.get(g.id);m&&(n.delete(g.id),m.resolve(g))});const l=()=>{if(e!==s)return;e=null,w(),f(!1);const u=C[Math.min(t,C.length-1)];t+=1,window.setTimeout(a,u)};s.addEventListener("close",l),s.addEventListener("error",()=>{console.error("[carrier] the connection to GitWarren failed")})};a();const p=new Map,h=(s,l,u)=>{const d=o++;return new Promise((m,E)=>{n.set(d,{resolve:m,reject:E}),y({id:d,method:s,params:l,...u===void 0?{}:{host:u}})}).then(m=>H(m))};return{request(s,l,u){if(!F(s))return h(s,l,u);const d=`${u??""}:${s}:${JSON.stringify(l??null)}`,g=p.get(d);if(g)return g;const m=h(s,l,u).finally(()=>p.delete(d));return p.set(d,m),m},connected:()=>e?.readyState===WebSocket.OPEN,onConnectionChange(s){return i.add(s),()=>i.delete(s)},onEvent(s){return c.add(s),()=>c.delete(s)}}}const R=()=>{};function v(e,t){return Promise.reject(new b("FORBIDDEN",`${e} is not available in a browser tab. ${t}`))}function te(e,t){const o=e.find(n=>n.instanceId===t);if(!o)throw new b("NOT_FOUND",`This GitWarren does not know a host with id ${t}. It may have been removed, or the link may have come from somewhere else.`);return o}async function ne(){const e=await fetch(O.appInfo,{credentials:"same-origin",headers:{Accept:"application/json"}});if(!e.ok)throw new b("INTERNAL",`GitWarren could not describe this install (HTTP ${e.status}).`);return await e.json()}function T(){const e=M();return{editors:e,defaultId:e[0]?.id??null}}const _={state:"unsupported",reason:"GitWarren in a browser is updated by whatever installed it - Homebrew, npm, or the tarball it was unpacked from."};function oe(){return new Promise(e=>{const t=document.createElement("input");t.type="file",t.accept="image/png,image/jpeg,image/gif,image/webp",t.style.position="fixed",t.style.left="-9999px",document.body.append(t);let o=!1;const n=i=>{o||(o=!0,window.removeEventListener("focus",r),t.remove(),e(i))},r=()=>{window.setTimeout(()=>n(t.files?.[0]??null),300)};t.addEventListener("change",()=>n(t.files?.[0]??null)),t.addEventListener("cancel",()=>n(null)),window.addEventListener("focus",r),t.click()})}function re(e){return{capabilities:{pickDirectory:!1,revealPath:!1,openAtLogin:!1},system:{pickDirectory:()=>Promise.resolve(null),revealPath:()=>v("Revealing a folder","Copy the path and open it yourself."),appInfo:ne,editors:()=>Promise.resolve(T()),getOpenAtLogin:()=>Promise.resolve(!1),setOpenAtLogin:()=>v("Starting at login","Run `gitwarren service install` on this machine.")},updates:{getStatus:()=>Promise.resolve(_),check:()=>Promise.resolve(_),installNow:()=>v("Installing an update","Update GitWarren the way you installed it."),subscribe:()=>R},connection:{connected:()=>e.connected(),subscribe:n=>e.onConnectionChange(n)},navigation:{onDeepLink:()=>R},pickAttachment:async n=>{const r=await oe();return r===null?null:await e.request("attachments.ingest",{bytes:await r.arrayBuffer(),originalName:r.name},n)},attachmentSrc:Y,openInEditor:async n=>{const{id:r,path:i,changes:c,line:f,editorId:y,host:w}=n,a=I(y)??I(T().defaultId??void 0);if(!a?.url)return v("Opening a file in an editor","No editor with a URL scheme was chosen for this tab.");if(w!==void 0&&!a.remoteUrl)return v("Opening a file on another machine",`${a.label} has no way to open a file it cannot see. VS Code, Cursor and Windsurf do.`);const p=await e.request("reviews.filePath",{id:r,path:i,changes:c},w),h=w===void 0?void 0:D(te(await e.request("hosts.list",void 0),w));window.location.href=h===void 0?a.url(p,f??1):a.remoteUrl(h,p,f??1)}}}const se="gitwarren",L=`${se}://`,W="review",S="h=";function ie(e){if(!e||e.slice(0,L.length).toLowerCase()!==L)return null;const o=(e.slice(L.length).split(/[?#]/)[0]??"").split("/").filter(Boolean),n=o[0]?.toLowerCase();if(n===W)return N(`#/reviews/${o.slice(1).join("/")}`);if(n!==void 0&&q(n)){const r=o.slice(1);return r.length===0?{name:"repositories",host:n}:r[0]?.toLowerCase()!==W?null:N(`#/h/${n}/reviews/${r.slice(1).join("/")}`)}return null}function ae(e){return e.replace(/^#/,"").startsWith(S)}function ce(e,t){const o=e.replace(/^#/,"");if(!o.startsWith(S))return null;const n=ie(`gitwarren://${o.slice(S.length)}`);if(!n)return G;if(n.host===void 0)return n;if(n.host===t){const{host:r,...i}=n;return i}return n}function le(e){return{request:(t,o,n)=>x(()=>e.request(t,o,n)),onEvent:t=>e.onEvent(t)}}function ue(){const e=ee(),t={carrier:le(e),shell:re(e)};return window.gitwarren=t,t}async function fe(e){if(!ae(window.location.hash))return;const{instanceId:t}=await e.shell.system.appInfo(),o=ce(window.location.hash,t);o&&window.location.replace(J(o))}const de=ue();await fe(de);await X(()=>import("./main-1gX2-1gC.js"),__vite__mapDeps([0,1,2]),import.meta.url);
|