oh-my-pr 4.5.5 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/dist/index.cjs +2 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -38,6 +38,14 @@ You need:
|
|
|
38
38
|
- GitHub auth via `gh auth login` or `GITHUB_TOKEN`
|
|
39
39
|
- either the `codex` CLI or `claude` CLI installed and authenticated locally
|
|
40
40
|
|
|
41
|
+
Download the desktop app:
|
|
42
|
+
|
|
43
|
+
- [Download for macOS Apple Silicon](https://github.com/yungookim/oh-my-pr/releases/latest/download/oh-my-pr-macos-arm64.dmg)
|
|
44
|
+
|
|
45
|
+
The macOS desktop build is currently not notarized. If macOS blocks the first
|
|
46
|
+
launch, use Control-click or right-click, choose Open, and confirm the app in
|
|
47
|
+
Privacy & Security.
|
|
48
|
+
|
|
41
49
|
Install and launch:
|
|
42
50
|
|
|
43
51
|
```bash
|
|
@@ -97,7 +105,7 @@ oh-my-pr can be used in a few ways:
|
|
|
97
105
|
- server logs page: `http://localhost:5001/logs`
|
|
98
106
|
- MCP server: `oh-my-pr mcp`
|
|
99
107
|
- local REST API: see [LOCAL_API.md](LOCAL_API.md)
|
|
100
|
-
-
|
|
108
|
+
- desktop app downloads: [macOS Apple Silicon DMG](https://github.com/yungookim/oh-my-pr/releases/latest/download/oh-my-pr-macos-arm64.dmg)
|
|
101
109
|
|
|
102
110
|
Local browser and API access work without a dashboard login. To use the web
|
|
103
111
|
dashboard from another machine, set remote web credentials before starting the
|
package/dist/index.cjs
CHANGED
|
@@ -1126,8 +1126,8 @@ data: ${JSON.stringify(e)}
|
|
|
1126
1126
|
|
|
1127
1127
|
`)!==!1}function qne(t,e){let r=e.githubTokens??(e.githubToken!==void 0?[e.githubToken]:void 0);if(r===void 0)return e;let{githubToken:n,...i}=e;return{...i,githubTokens:Bne(t.githubTokens,r)}}function z8(t){let e=t.githubTokens.map(V8);return{...t,githubTokens:e,githubToken:e[0]??""}}async function J8(t,e,r={}){let n=r.runtime??q8(r),i=r.appUpdateChecker??$8();return await n.start(),t.on("close",()=>{n.stop()}),e.get("/api/runtime",async(s,a)=>{a.json(await n.getRuntimeSnapshot())}),e.get("/api/server-logs",(s,a)=>{let o=K0({level:Mne(s.query.level),source:typeof s.query.source=="string"?s.query.source:void 0,since:Dd(s.query.since),search:typeof s.query.search=="string"?s.query.search:void 0,limit:Dd(s.query.limit)??500});a.json({records:o,sources:hP(),latestSeq:o.length>0?o[o.length-1].seq:Dd(s.query.since)??0})}),e.get("/api/server-logs/stream",(s,a)=>{a.statusCode=200,a.setHeader("Content-Type","text/event-stream"),a.setHeader("Cache-Control","no-cache, no-transform"),a.setHeader("Connection","keep-alive"),a.setHeader("Content-Encoding","identity"),a.setHeader("X-Accel-Buffering","no"),a.flushHeaders?.();let o=!1,u=()=>{},c=setInterval(()=>{a.write(`: heartbeat ${Date.now()}
|
|
1128
1128
|
|
|
1129
|
-
`)===!1&&l()},2e4),l=()=>{o||(o=!0,clearInterval(c),u(),a.writableEnded||a.end())},d=f=>{Une(a,f)||l()},p=Dd(s.query.since);if(p!==void 0){let f=K0({since:p,limit:1e3});for(let m of f)if(d(m),o)return}u=bP(d),s.on("close",l),s.on("aborted",l)}),e.get("/api/activities",async(s,a)=>{a.json(await n.listActivities())}),e.delete("/api/activities/failed",async(s,a)=>{try{a.json(await n.clearFailedActivities())}catch(o){Se(a,o)}}),e.post("/api/runtime/drain",async(s,a)=>{try{let o=y.object({enabled:y.boolean(),reason:y.string().optional(),waitForIdle:y.boolean().optional(),timeoutMs:y.number().int().positive().max(6e5).optional()}).parse(s.body),u=await n.setDrainMode(o);if(o.enabled&&o.waitForIdle&&u.drained===!1)return a.status(202).json(u);a.json(u)}catch(o){Se(a,o)}}),e.get("/api/repos",async(s,a)=>{a.json(await n.listRepos())}),e.get("/api/repos/settings",async(s,a)=>{a.json(await n.listRepoSettings())}),e.post("/api/repos",async(s,a)=>{try{let{repo:o}=y.object({repo:y.string().min(1)}).parse(s.body);a.status(201).json(await n.addRepo(o))}catch(o){Se(a,o)}}),e.patch("/api/repos/settings",async(s,a)=>{try{let o=y.object({repo:y.string().min(1),autoCreateReleases:y.boolean().optional(),ownPrsOnly:y.boolean().optional()}).refine(l=>l.autoCreateReleases!==void 0||l.ownPrsOnly!==void 0,"At least one repository setting must be provided").parse(s.body),{repo:u,...c}=o;a.json(await n.updateRepoSettings(u,c))}catch(o){Se(a,o)}}),e.post("/api/repos/sync",async(s,a)=>{try{a.json(await n.syncRepos())}catch(o){Se(a,o)}}),e.post("/api/repos/release",async(s,a)=>{try{let{repo:o}=y.object({repo:y.string().min(1)}).parse(s.body);a.status(201).json(await n.createManualRelease(o))}catch(o){Se(a,o)}}),e.get("/api/prs",async(s,a)=>{a.json(await n.listPRs("active"))}),e.get("/api/prs/archived",async(s,a)=>{a.json(await n.listPRs("archived"))}),e.get("/api/prs/:id",async(s,a)=>{let o=await n.getPR(s.params.id);if(!o)return a.status(404).json({error:"PR not found"});a.json(o)}),e.post("/api/prs",async(s,a)=>{try{a.status(201).json(await n.addPR(s.body?.url))}catch(o){Se(a,o)}}),e.delete("/api/prs/:id",async(s,a)=>{try{a.json(await n.removePR(s.params.id))}catch(o){Se(a,o)}}),e.patch("/api/prs/:id/watch",async(s,a)=>{try{let{enabled:o}=y.object({enabled:y.boolean()}).parse(s.body);a.json(await n.setPRWatchEnabled(s.params.id,o))}catch(o){Se(a,o)}}),e.post("/api/prs/:id/fetch",async(s,a)=>{try{a.json(await n.fetchPRFeedback(s.params.id))}catch(o){Se(a,o)}}),e.post("/api/prs/:id/triage",async(s,a)=>{try{a.json(await n.triagePR(s.params.id))}catch(o){Se(a,o)}}),e.post("/api/prs/:id/apply",async(s,a)=>{try{a.json(await n.applyPR(s.params.id))}catch(o){Se(a,o)}}),e.post("/api/prs/:id/babysit",async(s,a)=>{try{a.json(await n.babysitPR(s.params.id))}catch(o){Se(a,o)}}),e.patch("/api/prs/:id/feedback/:feedbackId",async(s,a)=>{try{let{decision:o}=y.object({decision:y.enum(["accept","reject","flag"])}).parse(s.body);a.json(await n.setFeedbackDecision(s.params.id,s.params.feedbackId,o))}catch(o){Se(a,o)}}),e.post("/api/prs/:id/feedback/:feedbackId/retry",async(s,a)=>{try{a.json(await n.retryFeedback(s.params.id,s.params.feedbackId))}catch(o){Se(a,o)}}),e.get("/api/prs/:id/questions",async(s,a)=>{try{a.json(await n.listPRQuestions(s.params.id))}catch(o){Se(a,o)}}),e.post("/api/prs/:id/questions",async(s,a)=>{try{a.status(201).json(await n.askQuestion(s.params.id,s.body?.question))}catch(o){Se(a,o)}}),e.get("/api/logs",async(s,a)=>{let o=typeof s.query.prId=="string"?s.query.prId:void 0;a.json(await n.listLogs(o))}),e.get("/api/onboarding/status",async(s,a)=>{try{a.json(await n.getOnboardingStatus())}catch(o){Se(a,o)}}),e.post("/api/onboarding/install-review",async(s,a)=>{try{let{repo:o,tool:u}=y.object({repo:y.string().min(1),tool:y.enum(["claude","codex"])}).parse(s.body);a.json(await n.installReviewWorkflow(o,u))}catch(o){Se(a,o)}}),e.get("/api/healing-sessions",async(s,a)=>{try{a.json(await n.listHealingSessions())}catch(o){Se(a,o)}}),e.get("/api/healing-sessions/:id",async(s,a)=>{try{a.json(await n.getHealingSession(s.params.id))}catch(o){Se(a,o)}}),e.get("/api/deployment-healing-sessions",async(s,a)=>{try{let o=typeof s.query.repo=="string"?s.query.repo:void 0;a.json(await n.listDeploymentHealingSessions(o))}catch(o){Se(a,o)}}),e.get("/api/deployment-healing-sessions/:id",async(s,a)=>{try{a.json(await n.getDeploymentHealingSession(s.params.id))}catch(o){Se(a,o)}}),e.get("/api/config",async(s,a)=>{a.json(z8(await n.getConfig()))}),e.get("/api/app-update",async(s,a)=>{try{a.json(await i("4.5.5"))}catch(o){Se(a,o)}}),e.get("/api/changelogs",async(s,a)=>{try{a.json(await n.listSocialChangelogs())}catch(o){Se(a,o)}}),e.get("/api/changelogs/:id",async(s,a)=>{try{a.json(await n.getSocialChangelog(s.params.id))}catch(o){Se(a,o)}}),e.get("/api/releases",async(s,a)=>{try{a.json(await n.listReleaseRuns())}catch(o){Se(a,o)}}),e.get("/api/releases/:id",async(s,a)=>{try{a.json(await n.getReleaseRun(s.params.id))}catch(o){Se(a,o)}}),e.post("/api/releases/:id/retry",async(s,a)=>{try{a.json(await n.retryReleaseRun(s.params.id))}catch(o){Se(a,o)}}),e.patch("/api/config",async(s,a)=>{try{let o=Ll.partial().parse(s.body),u=await n.getConfig();a.json(z8(await n.updateConfig(qne(u,o))))}catch(o){Se(a,o)}}),t}var gO=Me(Eg(),1);var lO=require("node:net"),Qy=Me(aO(),1),pO=require("node:net"),dO=require("node:buffer"),fO=require("node:crypto"),mO=require("node:net");function Cie(t,e=56){if((0,lO.isIPv6)(t)){let r=new Qy.Address6(t);if(r.is4())return r.to4().correctForm();if(e)return`${new Qy.Address6(`${t}/${e}`).startAddress().correctForm()}/${e}`}return t}var Oie=class{constructor(t){this.validations=t,this.previous=new Map,this.current=new Map,this.localKeys=!0}init(t){this.windowMs=t.windowMs,this.validations?.windowMs(this.windowMs),this.interval&&clearInterval(this.interval),this.interval=setInterval(()=>{this.clearExpired()},this.windowMs),this.interval.unref?.()}async get(t){return this.current.get(t)??this.previous.get(t)}async increment(t){let e=this.getClient(t),r=Date.now();return e.resetTime.getTime()<=r&&this.resetClient(e,r),e.totalHits++,e}async decrement(t){let e=this.getClient(t);e.totalHits>0&&e.totalHits--}async resetKey(t){this.current.delete(t),this.previous.delete(t)}async resetAll(){this.current.clear(),this.previous.clear()}shutdown(){clearInterval(this.interval),this.resetAll()}resetClient(t,e=Date.now()){return t.totalHits=0,t.resetTime.setTime(e+this.windowMs),t}getClient(t){if(this.current.has(t))return this.current.get(t);let e;return this.previous.has(t)?(e=this.previous.get(t),this.previous.delete(t)):(e={totalHits:0,resetTime:new Date},this.resetClient(e)),this.current.set(t,e),e}clearExpired(){this.previous=this.current,this.current=new Map}},Iie={warn(...t){console.warn(...t.reverse())},error(...t){console.error(...t.reverse())}},oO=["draft-6","draft-7","draft-8"],zd=(t,e)=>{let r;if(e){let n=Math.ceil((e.getTime()-Date.now())/1e3);r=Math.max(0,n)}else r=Math.ceil(t/1e3);return r},Lie=t=>{let e=(0,fO.createHash)("sha256");e.update(t);let r=e.digest("hex").slice(0,12);return dO.Buffer.from(r).toString("base64")},Die=(t,e)=>{t.headersSent||(t.setHeader("X-RateLimit-Limit",e.limit.toString()),t.setHeader("X-RateLimit-Remaining",e.remaining.toString()),e.resetTime instanceof Date&&(t.setHeader("Date",new Date().toUTCString()),t.setHeader("X-RateLimit-Reset",Math.ceil(e.resetTime.getTime()/1e3).toString())))},Nie=(t,e,r)=>{if(t.headersSent)return;let n=Math.ceil(r/1e3),i=zd(r,e.resetTime);t.setHeader("RateLimit-Policy",`${e.limit};w=${n}`),t.setHeader("RateLimit-Limit",e.limit.toString()),t.setHeader("RateLimit-Remaining",e.remaining.toString()),typeof i=="number"&&t.setHeader("RateLimit-Reset",i.toString())},Fie=(t,e,r)=>{if(t.headersSent)return;let n=Math.ceil(r/1e3),i=zd(r,e.resetTime);t.setHeader("RateLimit-Policy",`${e.limit};w=${n}`),t.setHeader("RateLimit",`limit=${e.limit}, remaining=${e.remaining}, reset=${i}`)},Mie=(t,e,r,n,i)=>{if(t.headersSent)return;let s=Math.ceil(r/1e3),a=zd(r,e.resetTime),o=Lie(i),u=`r=${e.remaining}; t=${a}`,c=`q=${e.limit}; w=${s}; pk=:${o}:`;t.append("RateLimit",`"${n}"; ${u}`),t.append("RateLimit-Policy",`"${n}"; ${c}`)},jie=(t,e,r)=>{if(t.headersSent)return;let n=zd(r,e.resetTime);t.setHeader("Retry-After",n.toString())},Bie=t=>{let e={};for(let r of Object.keys(t)){let n=r;t[n]!==void 0&&(e[n]=t[n])}return e},it=class extends Error{constructor(t,e){let r=`https://express-rate-limit.github.io/${t}/`;super(`${e} See ${r} for more information.`),this.name=this.constructor.name,this.code=t,this.help=r}},$d=class extends it{},uO=new Set,cO=new WeakMap,Uie={enabled:{default:!0},disable(){for(let t of Object.keys(this.enabled))this.enabled[t]=!1},ip(t){if(t===void 0)throw new it("ERR_ERL_UNDEFINED_IP_ADDRESS","An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.");if(!(0,mO.isIP)(t))throw new it("ERR_ERL_INVALID_IP_ADDRESS",`An invalid 'request.ip' (${t}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`)},trustProxy(t){if(t.app.get("trust proxy")===!0)throw new it("ERR_ERL_PERMISSIVE_TRUST_PROXY","The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.")},xForwardedForHeader(t){if(t.headers["x-forwarded-for"]&&t.app.get("trust proxy")===!1)throw new it("ERR_ERL_UNEXPECTED_X_FORWARDED_FOR","The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.")},forwardedHeader(t){if(t.headers.forwarded&&t.ip===t.socket?.remoteAddress)throw new it("ERR_ERL_FORWARDED_HEADER","The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.")},positiveHits(t){if(typeof t!="number"||t<1||t!==Math.round(t))throw new it("ERR_ERL_INVALID_HITS",`The totalHits value returned from the store must be a positive integer, got ${t}`)},unsharedStore(t){if(uO.has(t)){let e=t?.localKeys?"":" (with a unique prefix)";throw new it("ERR_ERL_STORE_REUSE",`A Store instance must not be shared across multiple rate limiters. Create a new instance of ${t.constructor.name}${e} for each limiter instead.`)}uO.add(t)},singleCount(t,e,r){let n=cO.get(t);n||(n=new Map,cO.set(t,n));let i=e.localKeys?e:e.constructor.name,s=n.get(i);s||(s=[],n.set(i,s));let a=`${e.prefix??""}${r}`;if(s.includes(a))throw new it("ERR_ERL_DOUBLE_COUNT",`The hit count for ${r} was incremented more than once for a single request.`);s.push(a)},limit(t){if(t===0)throw new $d("WRN_ERL_MAX_ZERO","Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7")},draftPolliHeaders(t){if(t)throw new $d("WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS","The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.")},onLimitReached(t){if(t)throw new $d("WRN_ERL_DEPRECATED_ON_LIMIT_REACHED","The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7.")},headersDraftVersion(t){if(typeof t!="string"||!oO.includes(t)){let e=oO.join(", ");throw new it("ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION",`standardHeaders: only the following versions of the IETF draft specification are supported: ${e}.`)}},headersResetTime(t){if(!t)throw new it("ERR_ERL_HEADERS_NO_RESET","standardHeaders: 'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.")},knownOptions(t){if(!t)return;let r=Object.keys({windowMs:!0,limit:!0,message:!0,statusCode:!0,legacyHeaders:!0,standardHeaders:!0,identifier:!0,requestPropertyName:!0,skipFailedRequests:!0,skipSuccessfulRequests:!0,keyGenerator:!0,ipv6Subnet:!0,handler:!0,skip:!0,requestWasSuccessful:!0,store:!0,validate:!0,headers:!0,max:!0,passOnStoreError:!0,logger:!0}).concat("draft_polli_ratelimit_headers","delayAfter","delayMs","maxDelayMs");for(let n of Object.keys(t))if(!r.includes(n))throw new it("ERR_ERL_UNKNOWN_OPTION",`Unexpected configuration option: ${n}`)},validationsConfig(){let t=Object.keys(this).filter(e=>!["enabled","disable"].includes(e));t.push("default");for(let e of Object.keys(this.enabled))if(!t.includes(e))throw new it("ERR_ERL_UNKNOWN_VALIDATION",`options.validate.${e} is not recognized. Supported validate options are: ${t.join(", ")}.`)},creationStack(t){let{stack:e}=new Error("express-rate-limit validation check (set options.validate.creationStack=false to disable)");if(e?.includes("Layer.handle [as handle_request]")||e?.includes("Layer.handleRequest"))throw t.localKeys?new it("ERR_ERL_CREATED_IN_REQUEST_HANDLER","express-rate-limit instance should be created at app initialization, not when responding to a request."):new it("ERR_ERL_CREATED_IN_REQUEST_HANDLER","express-rate-limit instance should *usually* be created at app initialization, not when responding to a request.")},ipv6Subnet(t){if(t!==!1&&(!Number.isInteger(t)||t<32||t>64))throw new it("ERR_ERL_IPV6_SUBNET",`Unexpected ipv6Subnet value: ${t}. Expected an integer between 32 and 64 (usually 48-64).`)},ipv6SubnetOrKeyGenerator(t){if(t.ipv6Subnet!==void 0&&t.keyGenerator)throw new it("ERR_ERL_IPV6SUBNET_OR_KEYGENERATOR","Incompatible options: the 'ipv6Subnet' option is ignored when a custom 'keyGenerator' function is also set.")},keyGeneratorIpFallback(t){if(!t)return;let e=t.toString();if((e.includes("req.ip")||e.includes("request.ip"))&&!e.includes("ipKeyGenerator"))throw new it("ERR_ERL_KEY_GEN_IPV6","Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits.")},windowMs(t){if(typeof t!="number"||Number.isNaN(t)||t<1||t>2147483647)throw new it("ERR_ERL_WINDOW_MS",`Invalid windowMs value: ${t}${typeof t!="number"?` (${typeof t})`:""}, must be a number between 1 and 2147483647 when using the default MemoryStore`)}};function qie(t){if(typeof t!="object"||typeof t.error!="function"||typeof t.warn!="function")throw new TypeError("Provided logger does not implement the Logger interface")}var Gie=(t,e)=>{qie(e);let r;typeof t=="boolean"?r={default:t}:r={default:!0,...t};let n={enabled:r};for(let[i,s]of Object.entries(Uie))typeof s=="function"&&(n[i]=(...a)=>{if(r[i]??r.default)try{s.apply(n,a)}catch(o){o instanceof $d?e.warn(o):e.error(o)}});return n},Hie=t=>typeof t.incr=="function"&&typeof t.increment!="function",$ie=t=>{if(!Hie(t))return t;let e=t;class r{async increment(i){return new Promise((s,a)=>{e.incr(i,(o,u,c)=>{o&&a(o),s({totalHits:u,resetTime:c})})})}async decrement(i){return e.decrement(i)}async resetKey(i){return e.resetKey(i)}async resetAll(){if(typeof e.resetAll=="function")return e.resetAll()}}return new r},zie=t=>{let{validations:e,...r}=t;return{...r,validate:e.enabled}},Wie=t=>{let e=Bie(t),r=t.logger??Iie,n=Gie(e?.validate??!0,r);n.validationsConfig(),n.knownOptions(t),n.draftPolliHeaders(e.draft_polli_ratelimit_headers),n.onLimitReached(e.onLimitReached),e.ipv6Subnet!==void 0&&typeof e.ipv6Subnet!="function"&&n.ipv6Subnet(e.ipv6Subnet),n.keyGeneratorIpFallback(e.keyGenerator),n.ipv6SubnetOrKeyGenerator(e);let i=e.standardHeaders??!1;i===!0&&(i="draft-6");let s={windowMs:60*1e3,limit:t.max??5,message:"Too many requests, please try again later.",statusCode:429,legacyHeaders:t.headers??!0,identifier(a,o){let u="",c=s.requestPropertyName,{limit:l}=a[c],d=s.windowMs/1e3,p=s.windowMs/(1e3*60),f=s.windowMs/(1e3*60*60),m=s.windowMs/(1e3*60*60*24);return d<60?u=`${d}sec`:p<60?u=`${p}min`:f<24?u=`${f}hr${f>1?"s":""}`:u=`${m}day${m>1?"s":""}`,`${l}-in-${u}`},requestPropertyName:"rateLimit",skipFailedRequests:!1,skipSuccessfulRequests:!1,requestWasSuccessful:(a,o)=>o.statusCode<400,skip:(a,o)=>!1,async keyGenerator(a,o){n.ip(a.ip),n.trustProxy(a),n.xForwardedForHeader(a),n.forwardedHeader(a);let u=a.ip,c=56;return(0,pO.isIPv6)(u)&&(c=typeof s.ipv6Subnet=="function"?await s.ipv6Subnet(a,o):s.ipv6Subnet,typeof s.ipv6Subnet=="function"&&n.ipv6Subnet(c)),Cie(u,c)},ipv6Subnet:56,async handler(a,o,u,c){o.status(s.statusCode);let l=typeof s.message=="function"?await s.message(a,o):s.message;o.writableEnded||o.send(l)},passOnStoreError:!1,...e,standardHeaders:i,store:$ie(e.store??new Oie(n)),validations:n,logger:r};if(typeof s.store.increment!="function"||typeof s.store.decrement!="function"||typeof s.store.resetKey!="function"||s.store.resetAll!==void 0&&typeof s.store.resetAll!="function"||s.store.init!==void 0&&typeof s.store.init!="function")throw new TypeError("An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface.");return s},Vie=t=>async(e,r,n)=>{try{await Promise.resolve(t(e,r,n)).catch(n)}catch(i){n(i)}},Jie=t=>{let e=Wie(t??{}),r=zie(e);if(e.validations.creationStack(e.store),e.validations.unsharedStore(e.store),typeof e.store.init=="function")try{let s=e.store.init(r);s instanceof Promise&&s.catch(a=>e.logger.error(a,"express-rate-limit: async error during store initialization."))}catch(s){e.logger.error(s,"express-rate-limit: error during store initialization.")}let n=Vie(async(s,a,o)=>{let u=e.skipFailedRequests&&new Promise(S=>a.once("close",S)),c=(e.skipFailedRequests||e.skipSuccessfulRequests)&&new Promise(S=>a.once("finish",S)),l=e.skipFailedRequests&&new Promise(S=>a.once("error",S));if(await e.skip(s,a)){o();return}let p=s,f=await e.keyGenerator(s,a),m=0,h;try{let S=await e.store.increment(f);m=S.totalHits,h=S.resetTime}catch(S){if(e.passOnStoreError){e.logger.error(S,"express-rate-limit: error from store, allowing request without rate-limiting."),o();return}throw S}e.validations.positiveHits(m),e.validations.singleCount(s,e.store,f);let E=await(typeof e.limit=="function"?e.limit(s,a):e.limit);e.validations.limit(E);let R={limit:E,used:m,remaining:Math.max(E-m,0),resetTime:h,key:f};if(Object.defineProperty(R,"current",{configurable:!1,enumerable:!1,value:m}),p[e.requestPropertyName]=R,e.legacyHeaders&&!a.headersSent&&Die(a,R),e.standardHeaders&&!a.headersSent)switch(e.standardHeaders){case"draft-6":{Nie(a,R,e.windowMs);break}case"draft-7":{e.validations.headersResetTime(R.resetTime),Fie(a,R,e.windowMs);break}case"draft-8":{let P=await(typeof e.identifier=="function"?e.identifier(s,a):e.identifier);e.validations.headersResetTime(R.resetTime),Mie(a,R,e.windowMs,P,f);break}default:{e.validations.headersDraftVersion(e.standardHeaders);break}}if(e.skipFailedRequests||e.skipSuccessfulRequests){let S=!1,P=async()=>{S||(await e.store.decrement(f),S=!0)};e.skipFailedRequests&&(c&&c.then(async()=>{await e.requestWasSuccessful(s,a)||await P()}),u&&u.then(async()=>{a.writableEnded||await P()}),l&&l.then(async()=>{await P()})),e.skipSuccessfulRequests&&c&&c.then(async()=>{await e.requestWasSuccessful(s,a)&&await P()})}if(e.validations.disable(),m>E){(e.legacyHeaders||e.standardHeaders)&&jie(a,R,e.windowMs),e.handler(s,a,o,r);return}o()}),i=()=>{throw new Error("The current store does not support the get/getKey method")};return n.resetKey=e.store.resetKey.bind(e.store),n.getKey=typeof e.store.get=="function"?e.store.get.bind(e.store):i,n},Wd=Jie;var hO=Me(require("fs"),1),Zy=Me(require("path"),1),bO=require("url"),Yie={},Kie=Wd({windowMs:60*1e3,limit:600,standardHeaders:!0,legacyHeaders:!1});function Xie(){return typeof __dirname=="string"?__dirname:Zy.default.dirname((0,bO.fileURLToPath)(Yie.url))}function yO(t,e=Xie()){let r=Zy.default.resolve(e,"public");if(!hO.default.existsSync(r))throw new Error(`Could not find the build directory: ${r}, make sure to build the client first`);t.use(gO.default.static(r)),t.use("/{*path}",Kie,(n,i)=>{i.sendFile("index.html",{root:r})})}var dc=Me(require("node:crypto"),1),wv=Me(iI(),1),AI=Me(xI(),1);var dae=new Set(["127.0.0.1","::1","::ffff:127.0.0.1","localhost"]);function _I(t){if(!t)return!1;let e=t.replace(/^\[|\]$/g,"");return dae.has(e)?!0:e.startsWith("::ffff:")?e.slice(7).startsWith("127."):!!e.startsWith("127.")}var yv="OH_MY_PR_WEB_USERNAME",vv="OH_MY_PR_WEB_PASSWORD",fae="OH_MY_PR_SESSION_SECRET",EI="oh-my-pr.sid",SI=720*60*1e3,mae=(0,AI.default)(wv.default);function TI(t){let e=t?.trim();return e||void 0}function gae(t=process.env){return{username:TI(t[yv]),password:t[vv],sessionSecret:TI(t[fae])}}function hae(t){return!t.username||!t.password?null:{username:t.username,password:t.password}}function bae(t){return t.ip??t.socket?.remoteAddress}function PI(t){return _I(bae(t))}function kI(t,e){let r=dc.default.createHash("sha256").update(t).digest(),n=dc.default.createHash("sha256").update(e).digest();return dc.default.timingSafeEqual(r,n)}function bv(t,e){let r=!PI(t),n=t.session?.authenticatedWebUser??null,i=!r||!!(e&&n===e.username);return{requiresLogin:r,loginConfigured:e!==null,authenticated:i,username:i&&e?e.username:null}}function yae(t){return new Promise((e,r)=>{t.session.regenerate(n=>{if(n){r(n);return}e()})})}function vae(t){return new Promise((e,r)=>{t.session.save(n=>{if(n){r(n);return}e()})})}function wae(t){return new Promise((e,r)=>{t.session.destroy(n=>{if(n){r(n);return}e()})})}function RI(t,e){let r=e instanceof Error?e.message:String(e);return new Error(`Web auth ${t} failed: ${r}`,{cause:e})}function CI(t,e=gae()){let r=hae(e),n=e.sessionSecret??dc.default.randomBytes(32).toString("hex");t.use((0,wv.default)({name:EI,secret:n,store:new mae({checkPeriod:SI}),resave:!1,saveUninitialized:!1,cookie:{httpOnly:!0,sameSite:"lax",secure:!0,maxAge:SI}}));let i=Wd({windowMs:300*1e3,limit:20,standardHeaders:!0,legacyHeaders:!1});return t.get("/api/auth/status",(s,a)=>{a.json(bv(s,r))}),t.post("/api/auth/login",i,async(s,a,o)=>{if(!r){a.status(403).json({error:"Remote login is not configured",message:`Set ${yv} and ${vv} before using remote web access.`});return}let u=typeof s.body?.username=="string"?s.body.username:"",c=typeof s.body?.password=="string"?s.body.password:"";if(!kI(u,r.username)||!kI(c,r.password)){a.status(401).json({error:"Invalid credentials",message:"Username or password is incorrect."});return}let l="session regeneration";try{await yae(s),s.session.authenticatedWebUser=r.username,l="session save",await vae(s)}catch(d){o(RI(l,d));return}a.json(bv(s,r))}),t.post("/api/auth/logout",async(s,a,o)=>{try{s.session.authenticatedWebUser&&(await wae(s),a.clearCookie(EI))}catch(u){o(RI("session destroy",u));return}a.json(bv(s,r))}),{apiAccessMiddleware(s,a,o){if(PI(s)){o();return}if(!r){a.status(403).json({error:"Remote access is not configured",message:`Set ${yv} and ${vv} to allow remote dashboard login.`});return}if(s.session?.authenticatedWebUser===r.username){o();return}a.status(401).json({error:"Login required",message:"Sign in to use oh-my-pr from this network address."})}}}var II=require("http");var xae=lr("server"),Nn=(0,Qd.default)(),OI=(0,II.createServer)(Nn);function _ae(t){let e=t?.trim();if(!e||e==="false")return!1;if(e==="true")return!0;let r=Number(e);return Number.isInteger(r)&&r>=0?r:e}Nn.use(Qd.default.json({verify:(t,e,r)=>{t.rawBody=r}}));Nn.use(Qd.default.urlencoded({extended:!1}));Nn.set("trust proxy",_ae(process.env.OH_MY_PR_TRUST_PROXY));var Eae=CI(Nn);Nn.use("/api",Eae.apiAccessMiddleware);function LI(t,e="express"){X0.info({source:e},t)}async function Sae(t){let{default:e}=await import("open");await e(t)}(async()=>{await J8(OI,Nn),Nn.use((e,r,n,i)=>{let s=e,a=s.status||s.statusCode||500,o=s.message||"Internal Server Error";return xae.error({err:e instanceof Error?e.message:String(e),status:a},"Internal Server Error"),n.headersSent?i(e):n.status(a).json({message:o})}),yO(Nn);let t=parseInt(process.env.PORT||"5001",10);OI.listen({port:t,host:"0.0.0.0"},()=>{let e=`http://localhost:${t}`;console.log(`
|
|
1130
|
-
oh-my-pr v4.
|
|
1129
|
+
`)===!1&&l()},2e4),l=()=>{o||(o=!0,clearInterval(c),u(),a.writableEnded||a.end())},d=f=>{Une(a,f)||l()},p=Dd(s.query.since);if(p!==void 0){let f=K0({since:p,limit:1e3});for(let m of f)if(d(m),o)return}u=bP(d),s.on("close",l),s.on("aborted",l)}),e.get("/api/activities",async(s,a)=>{a.json(await n.listActivities())}),e.delete("/api/activities/failed",async(s,a)=>{try{a.json(await n.clearFailedActivities())}catch(o){Se(a,o)}}),e.post("/api/runtime/drain",async(s,a)=>{try{let o=y.object({enabled:y.boolean(),reason:y.string().optional(),waitForIdle:y.boolean().optional(),timeoutMs:y.number().int().positive().max(6e5).optional()}).parse(s.body),u=await n.setDrainMode(o);if(o.enabled&&o.waitForIdle&&u.drained===!1)return a.status(202).json(u);a.json(u)}catch(o){Se(a,o)}}),e.get("/api/repos",async(s,a)=>{a.json(await n.listRepos())}),e.get("/api/repos/settings",async(s,a)=>{a.json(await n.listRepoSettings())}),e.post("/api/repos",async(s,a)=>{try{let{repo:o}=y.object({repo:y.string().min(1)}).parse(s.body);a.status(201).json(await n.addRepo(o))}catch(o){Se(a,o)}}),e.patch("/api/repos/settings",async(s,a)=>{try{let o=y.object({repo:y.string().min(1),autoCreateReleases:y.boolean().optional(),ownPrsOnly:y.boolean().optional()}).refine(l=>l.autoCreateReleases!==void 0||l.ownPrsOnly!==void 0,"At least one repository setting must be provided").parse(s.body),{repo:u,...c}=o;a.json(await n.updateRepoSettings(u,c))}catch(o){Se(a,o)}}),e.post("/api/repos/sync",async(s,a)=>{try{a.json(await n.syncRepos())}catch(o){Se(a,o)}}),e.post("/api/repos/release",async(s,a)=>{try{let{repo:o}=y.object({repo:y.string().min(1)}).parse(s.body);a.status(201).json(await n.createManualRelease(o))}catch(o){Se(a,o)}}),e.get("/api/prs",async(s,a)=>{a.json(await n.listPRs("active"))}),e.get("/api/prs/archived",async(s,a)=>{a.json(await n.listPRs("archived"))}),e.get("/api/prs/:id",async(s,a)=>{let o=await n.getPR(s.params.id);if(!o)return a.status(404).json({error:"PR not found"});a.json(o)}),e.post("/api/prs",async(s,a)=>{try{a.status(201).json(await n.addPR(s.body?.url))}catch(o){Se(a,o)}}),e.delete("/api/prs/:id",async(s,a)=>{try{a.json(await n.removePR(s.params.id))}catch(o){Se(a,o)}}),e.patch("/api/prs/:id/watch",async(s,a)=>{try{let{enabled:o}=y.object({enabled:y.boolean()}).parse(s.body);a.json(await n.setPRWatchEnabled(s.params.id,o))}catch(o){Se(a,o)}}),e.post("/api/prs/:id/fetch",async(s,a)=>{try{a.json(await n.fetchPRFeedback(s.params.id))}catch(o){Se(a,o)}}),e.post("/api/prs/:id/triage",async(s,a)=>{try{a.json(await n.triagePR(s.params.id))}catch(o){Se(a,o)}}),e.post("/api/prs/:id/apply",async(s,a)=>{try{a.json(await n.applyPR(s.params.id))}catch(o){Se(a,o)}}),e.post("/api/prs/:id/babysit",async(s,a)=>{try{a.json(await n.babysitPR(s.params.id))}catch(o){Se(a,o)}}),e.patch("/api/prs/:id/feedback/:feedbackId",async(s,a)=>{try{let{decision:o}=y.object({decision:y.enum(["accept","reject","flag"])}).parse(s.body);a.json(await n.setFeedbackDecision(s.params.id,s.params.feedbackId,o))}catch(o){Se(a,o)}}),e.post("/api/prs/:id/feedback/:feedbackId/retry",async(s,a)=>{try{a.json(await n.retryFeedback(s.params.id,s.params.feedbackId))}catch(o){Se(a,o)}}),e.get("/api/prs/:id/questions",async(s,a)=>{try{a.json(await n.listPRQuestions(s.params.id))}catch(o){Se(a,o)}}),e.post("/api/prs/:id/questions",async(s,a)=>{try{a.status(201).json(await n.askQuestion(s.params.id,s.body?.question))}catch(o){Se(a,o)}}),e.get("/api/logs",async(s,a)=>{let o=typeof s.query.prId=="string"?s.query.prId:void 0;a.json(await n.listLogs(o))}),e.get("/api/onboarding/status",async(s,a)=>{try{a.json(await n.getOnboardingStatus())}catch(o){Se(a,o)}}),e.post("/api/onboarding/install-review",async(s,a)=>{try{let{repo:o,tool:u}=y.object({repo:y.string().min(1),tool:y.enum(["claude","codex"])}).parse(s.body);a.json(await n.installReviewWorkflow(o,u))}catch(o){Se(a,o)}}),e.get("/api/healing-sessions",async(s,a)=>{try{a.json(await n.listHealingSessions())}catch(o){Se(a,o)}}),e.get("/api/healing-sessions/:id",async(s,a)=>{try{a.json(await n.getHealingSession(s.params.id))}catch(o){Se(a,o)}}),e.get("/api/deployment-healing-sessions",async(s,a)=>{try{let o=typeof s.query.repo=="string"?s.query.repo:void 0;a.json(await n.listDeploymentHealingSessions(o))}catch(o){Se(a,o)}}),e.get("/api/deployment-healing-sessions/:id",async(s,a)=>{try{a.json(await n.getDeploymentHealingSession(s.params.id))}catch(o){Se(a,o)}}),e.get("/api/config",async(s,a)=>{a.json(z8(await n.getConfig()))}),e.get("/api/app-update",async(s,a)=>{try{a.json(await i("4.6.0"))}catch(o){Se(a,o)}}),e.get("/api/changelogs",async(s,a)=>{try{a.json(await n.listSocialChangelogs())}catch(o){Se(a,o)}}),e.get("/api/changelogs/:id",async(s,a)=>{try{a.json(await n.getSocialChangelog(s.params.id))}catch(o){Se(a,o)}}),e.get("/api/releases",async(s,a)=>{try{a.json(await n.listReleaseRuns())}catch(o){Se(a,o)}}),e.get("/api/releases/:id",async(s,a)=>{try{a.json(await n.getReleaseRun(s.params.id))}catch(o){Se(a,o)}}),e.post("/api/releases/:id/retry",async(s,a)=>{try{a.json(await n.retryReleaseRun(s.params.id))}catch(o){Se(a,o)}}),e.patch("/api/config",async(s,a)=>{try{let o=Ll.partial().parse(s.body),u=await n.getConfig();a.json(z8(await n.updateConfig(qne(u,o))))}catch(o){Se(a,o)}}),t}var gO=Me(Eg(),1);var lO=require("node:net"),Qy=Me(aO(),1),pO=require("node:net"),dO=require("node:buffer"),fO=require("node:crypto"),mO=require("node:net");function Cie(t,e=56){if((0,lO.isIPv6)(t)){let r=new Qy.Address6(t);if(r.is4())return r.to4().correctForm();if(e)return`${new Qy.Address6(`${t}/${e}`).startAddress().correctForm()}/${e}`}return t}var Oie=class{constructor(t){this.validations=t,this.previous=new Map,this.current=new Map,this.localKeys=!0}init(t){this.windowMs=t.windowMs,this.validations?.windowMs(this.windowMs),this.interval&&clearInterval(this.interval),this.interval=setInterval(()=>{this.clearExpired()},this.windowMs),this.interval.unref?.()}async get(t){return this.current.get(t)??this.previous.get(t)}async increment(t){let e=this.getClient(t),r=Date.now();return e.resetTime.getTime()<=r&&this.resetClient(e,r),e.totalHits++,e}async decrement(t){let e=this.getClient(t);e.totalHits>0&&e.totalHits--}async resetKey(t){this.current.delete(t),this.previous.delete(t)}async resetAll(){this.current.clear(),this.previous.clear()}shutdown(){clearInterval(this.interval),this.resetAll()}resetClient(t,e=Date.now()){return t.totalHits=0,t.resetTime.setTime(e+this.windowMs),t}getClient(t){if(this.current.has(t))return this.current.get(t);let e;return this.previous.has(t)?(e=this.previous.get(t),this.previous.delete(t)):(e={totalHits:0,resetTime:new Date},this.resetClient(e)),this.current.set(t,e),e}clearExpired(){this.previous=this.current,this.current=new Map}},Iie={warn(...t){console.warn(...t.reverse())},error(...t){console.error(...t.reverse())}},oO=["draft-6","draft-7","draft-8"],zd=(t,e)=>{let r;if(e){let n=Math.ceil((e.getTime()-Date.now())/1e3);r=Math.max(0,n)}else r=Math.ceil(t/1e3);return r},Lie=t=>{let e=(0,fO.createHash)("sha256");e.update(t);let r=e.digest("hex").slice(0,12);return dO.Buffer.from(r).toString("base64")},Die=(t,e)=>{t.headersSent||(t.setHeader("X-RateLimit-Limit",e.limit.toString()),t.setHeader("X-RateLimit-Remaining",e.remaining.toString()),e.resetTime instanceof Date&&(t.setHeader("Date",new Date().toUTCString()),t.setHeader("X-RateLimit-Reset",Math.ceil(e.resetTime.getTime()/1e3).toString())))},Nie=(t,e,r)=>{if(t.headersSent)return;let n=Math.ceil(r/1e3),i=zd(r,e.resetTime);t.setHeader("RateLimit-Policy",`${e.limit};w=${n}`),t.setHeader("RateLimit-Limit",e.limit.toString()),t.setHeader("RateLimit-Remaining",e.remaining.toString()),typeof i=="number"&&t.setHeader("RateLimit-Reset",i.toString())},Fie=(t,e,r)=>{if(t.headersSent)return;let n=Math.ceil(r/1e3),i=zd(r,e.resetTime);t.setHeader("RateLimit-Policy",`${e.limit};w=${n}`),t.setHeader("RateLimit",`limit=${e.limit}, remaining=${e.remaining}, reset=${i}`)},Mie=(t,e,r,n,i)=>{if(t.headersSent)return;let s=Math.ceil(r/1e3),a=zd(r,e.resetTime),o=Lie(i),u=`r=${e.remaining}; t=${a}`,c=`q=${e.limit}; w=${s}; pk=:${o}:`;t.append("RateLimit",`"${n}"; ${u}`),t.append("RateLimit-Policy",`"${n}"; ${c}`)},jie=(t,e,r)=>{if(t.headersSent)return;let n=zd(r,e.resetTime);t.setHeader("Retry-After",n.toString())},Bie=t=>{let e={};for(let r of Object.keys(t)){let n=r;t[n]!==void 0&&(e[n]=t[n])}return e},it=class extends Error{constructor(t,e){let r=`https://express-rate-limit.github.io/${t}/`;super(`${e} See ${r} for more information.`),this.name=this.constructor.name,this.code=t,this.help=r}},$d=class extends it{},uO=new Set,cO=new WeakMap,Uie={enabled:{default:!0},disable(){for(let t of Object.keys(this.enabled))this.enabled[t]=!1},ip(t){if(t===void 0)throw new it("ERR_ERL_UNDEFINED_IP_ADDRESS","An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.");if(!(0,mO.isIP)(t))throw new it("ERR_ERL_INVALID_IP_ADDRESS",`An invalid 'request.ip' (${t}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`)},trustProxy(t){if(t.app.get("trust proxy")===!0)throw new it("ERR_ERL_PERMISSIVE_TRUST_PROXY","The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.")},xForwardedForHeader(t){if(t.headers["x-forwarded-for"]&&t.app.get("trust proxy")===!1)throw new it("ERR_ERL_UNEXPECTED_X_FORWARDED_FOR","The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.")},forwardedHeader(t){if(t.headers.forwarded&&t.ip===t.socket?.remoteAddress)throw new it("ERR_ERL_FORWARDED_HEADER","The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.")},positiveHits(t){if(typeof t!="number"||t<1||t!==Math.round(t))throw new it("ERR_ERL_INVALID_HITS",`The totalHits value returned from the store must be a positive integer, got ${t}`)},unsharedStore(t){if(uO.has(t)){let e=t?.localKeys?"":" (with a unique prefix)";throw new it("ERR_ERL_STORE_REUSE",`A Store instance must not be shared across multiple rate limiters. Create a new instance of ${t.constructor.name}${e} for each limiter instead.`)}uO.add(t)},singleCount(t,e,r){let n=cO.get(t);n||(n=new Map,cO.set(t,n));let i=e.localKeys?e:e.constructor.name,s=n.get(i);s||(s=[],n.set(i,s));let a=`${e.prefix??""}${r}`;if(s.includes(a))throw new it("ERR_ERL_DOUBLE_COUNT",`The hit count for ${r} was incremented more than once for a single request.`);s.push(a)},limit(t){if(t===0)throw new $d("WRN_ERL_MAX_ZERO","Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7")},draftPolliHeaders(t){if(t)throw new $d("WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS","The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.")},onLimitReached(t){if(t)throw new $d("WRN_ERL_DEPRECATED_ON_LIMIT_REACHED","The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7.")},headersDraftVersion(t){if(typeof t!="string"||!oO.includes(t)){let e=oO.join(", ");throw new it("ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION",`standardHeaders: only the following versions of the IETF draft specification are supported: ${e}.`)}},headersResetTime(t){if(!t)throw new it("ERR_ERL_HEADERS_NO_RESET","standardHeaders: 'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.")},knownOptions(t){if(!t)return;let r=Object.keys({windowMs:!0,limit:!0,message:!0,statusCode:!0,legacyHeaders:!0,standardHeaders:!0,identifier:!0,requestPropertyName:!0,skipFailedRequests:!0,skipSuccessfulRequests:!0,keyGenerator:!0,ipv6Subnet:!0,handler:!0,skip:!0,requestWasSuccessful:!0,store:!0,validate:!0,headers:!0,max:!0,passOnStoreError:!0,logger:!0}).concat("draft_polli_ratelimit_headers","delayAfter","delayMs","maxDelayMs");for(let n of Object.keys(t))if(!r.includes(n))throw new it("ERR_ERL_UNKNOWN_OPTION",`Unexpected configuration option: ${n}`)},validationsConfig(){let t=Object.keys(this).filter(e=>!["enabled","disable"].includes(e));t.push("default");for(let e of Object.keys(this.enabled))if(!t.includes(e))throw new it("ERR_ERL_UNKNOWN_VALIDATION",`options.validate.${e} is not recognized. Supported validate options are: ${t.join(", ")}.`)},creationStack(t){let{stack:e}=new Error("express-rate-limit validation check (set options.validate.creationStack=false to disable)");if(e?.includes("Layer.handle [as handle_request]")||e?.includes("Layer.handleRequest"))throw t.localKeys?new it("ERR_ERL_CREATED_IN_REQUEST_HANDLER","express-rate-limit instance should be created at app initialization, not when responding to a request."):new it("ERR_ERL_CREATED_IN_REQUEST_HANDLER","express-rate-limit instance should *usually* be created at app initialization, not when responding to a request.")},ipv6Subnet(t){if(t!==!1&&(!Number.isInteger(t)||t<32||t>64))throw new it("ERR_ERL_IPV6_SUBNET",`Unexpected ipv6Subnet value: ${t}. Expected an integer between 32 and 64 (usually 48-64).`)},ipv6SubnetOrKeyGenerator(t){if(t.ipv6Subnet!==void 0&&t.keyGenerator)throw new it("ERR_ERL_IPV6SUBNET_OR_KEYGENERATOR","Incompatible options: the 'ipv6Subnet' option is ignored when a custom 'keyGenerator' function is also set.")},keyGeneratorIpFallback(t){if(!t)return;let e=t.toString();if((e.includes("req.ip")||e.includes("request.ip"))&&!e.includes("ipKeyGenerator"))throw new it("ERR_ERL_KEY_GEN_IPV6","Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits.")},windowMs(t){if(typeof t!="number"||Number.isNaN(t)||t<1||t>2147483647)throw new it("ERR_ERL_WINDOW_MS",`Invalid windowMs value: ${t}${typeof t!="number"?` (${typeof t})`:""}, must be a number between 1 and 2147483647 when using the default MemoryStore`)}};function qie(t){if(typeof t!="object"||typeof t.error!="function"||typeof t.warn!="function")throw new TypeError("Provided logger does not implement the Logger interface")}var Gie=(t,e)=>{qie(e);let r;typeof t=="boolean"?r={default:t}:r={default:!0,...t};let n={enabled:r};for(let[i,s]of Object.entries(Uie))typeof s=="function"&&(n[i]=(...a)=>{if(r[i]??r.default)try{s.apply(n,a)}catch(o){o instanceof $d?e.warn(o):e.error(o)}});return n},Hie=t=>typeof t.incr=="function"&&typeof t.increment!="function",$ie=t=>{if(!Hie(t))return t;let e=t;class r{async increment(i){return new Promise((s,a)=>{e.incr(i,(o,u,c)=>{o&&a(o),s({totalHits:u,resetTime:c})})})}async decrement(i){return e.decrement(i)}async resetKey(i){return e.resetKey(i)}async resetAll(){if(typeof e.resetAll=="function")return e.resetAll()}}return new r},zie=t=>{let{validations:e,...r}=t;return{...r,validate:e.enabled}},Wie=t=>{let e=Bie(t),r=t.logger??Iie,n=Gie(e?.validate??!0,r);n.validationsConfig(),n.knownOptions(t),n.draftPolliHeaders(e.draft_polli_ratelimit_headers),n.onLimitReached(e.onLimitReached),e.ipv6Subnet!==void 0&&typeof e.ipv6Subnet!="function"&&n.ipv6Subnet(e.ipv6Subnet),n.keyGeneratorIpFallback(e.keyGenerator),n.ipv6SubnetOrKeyGenerator(e);let i=e.standardHeaders??!1;i===!0&&(i="draft-6");let s={windowMs:60*1e3,limit:t.max??5,message:"Too many requests, please try again later.",statusCode:429,legacyHeaders:t.headers??!0,identifier(a,o){let u="",c=s.requestPropertyName,{limit:l}=a[c],d=s.windowMs/1e3,p=s.windowMs/(1e3*60),f=s.windowMs/(1e3*60*60),m=s.windowMs/(1e3*60*60*24);return d<60?u=`${d}sec`:p<60?u=`${p}min`:f<24?u=`${f}hr${f>1?"s":""}`:u=`${m}day${m>1?"s":""}`,`${l}-in-${u}`},requestPropertyName:"rateLimit",skipFailedRequests:!1,skipSuccessfulRequests:!1,requestWasSuccessful:(a,o)=>o.statusCode<400,skip:(a,o)=>!1,async keyGenerator(a,o){n.ip(a.ip),n.trustProxy(a),n.xForwardedForHeader(a),n.forwardedHeader(a);let u=a.ip,c=56;return(0,pO.isIPv6)(u)&&(c=typeof s.ipv6Subnet=="function"?await s.ipv6Subnet(a,o):s.ipv6Subnet,typeof s.ipv6Subnet=="function"&&n.ipv6Subnet(c)),Cie(u,c)},ipv6Subnet:56,async handler(a,o,u,c){o.status(s.statusCode);let l=typeof s.message=="function"?await s.message(a,o):s.message;o.writableEnded||o.send(l)},passOnStoreError:!1,...e,standardHeaders:i,store:$ie(e.store??new Oie(n)),validations:n,logger:r};if(typeof s.store.increment!="function"||typeof s.store.decrement!="function"||typeof s.store.resetKey!="function"||s.store.resetAll!==void 0&&typeof s.store.resetAll!="function"||s.store.init!==void 0&&typeof s.store.init!="function")throw new TypeError("An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface.");return s},Vie=t=>async(e,r,n)=>{try{await Promise.resolve(t(e,r,n)).catch(n)}catch(i){n(i)}},Jie=t=>{let e=Wie(t??{}),r=zie(e);if(e.validations.creationStack(e.store),e.validations.unsharedStore(e.store),typeof e.store.init=="function")try{let s=e.store.init(r);s instanceof Promise&&s.catch(a=>e.logger.error(a,"express-rate-limit: async error during store initialization."))}catch(s){e.logger.error(s,"express-rate-limit: error during store initialization.")}let n=Vie(async(s,a,o)=>{let u=e.skipFailedRequests&&new Promise(S=>a.once("close",S)),c=(e.skipFailedRequests||e.skipSuccessfulRequests)&&new Promise(S=>a.once("finish",S)),l=e.skipFailedRequests&&new Promise(S=>a.once("error",S));if(await e.skip(s,a)){o();return}let p=s,f=await e.keyGenerator(s,a),m=0,h;try{let S=await e.store.increment(f);m=S.totalHits,h=S.resetTime}catch(S){if(e.passOnStoreError){e.logger.error(S,"express-rate-limit: error from store, allowing request without rate-limiting."),o();return}throw S}e.validations.positiveHits(m),e.validations.singleCount(s,e.store,f);let E=await(typeof e.limit=="function"?e.limit(s,a):e.limit);e.validations.limit(E);let R={limit:E,used:m,remaining:Math.max(E-m,0),resetTime:h,key:f};if(Object.defineProperty(R,"current",{configurable:!1,enumerable:!1,value:m}),p[e.requestPropertyName]=R,e.legacyHeaders&&!a.headersSent&&Die(a,R),e.standardHeaders&&!a.headersSent)switch(e.standardHeaders){case"draft-6":{Nie(a,R,e.windowMs);break}case"draft-7":{e.validations.headersResetTime(R.resetTime),Fie(a,R,e.windowMs);break}case"draft-8":{let P=await(typeof e.identifier=="function"?e.identifier(s,a):e.identifier);e.validations.headersResetTime(R.resetTime),Mie(a,R,e.windowMs,P,f);break}default:{e.validations.headersDraftVersion(e.standardHeaders);break}}if(e.skipFailedRequests||e.skipSuccessfulRequests){let S=!1,P=async()=>{S||(await e.store.decrement(f),S=!0)};e.skipFailedRequests&&(c&&c.then(async()=>{await e.requestWasSuccessful(s,a)||await P()}),u&&u.then(async()=>{a.writableEnded||await P()}),l&&l.then(async()=>{await P()})),e.skipSuccessfulRequests&&c&&c.then(async()=>{await e.requestWasSuccessful(s,a)&&await P()})}if(e.validations.disable(),m>E){(e.legacyHeaders||e.standardHeaders)&&jie(a,R,e.windowMs),e.handler(s,a,o,r);return}o()}),i=()=>{throw new Error("The current store does not support the get/getKey method")};return n.resetKey=e.store.resetKey.bind(e.store),n.getKey=typeof e.store.get=="function"?e.store.get.bind(e.store):i,n},Wd=Jie;var hO=Me(require("fs"),1),Zy=Me(require("path"),1),bO=require("url"),Yie={},Kie=Wd({windowMs:60*1e3,limit:600,standardHeaders:!0,legacyHeaders:!1});function Xie(){return typeof __dirname=="string"?__dirname:Zy.default.dirname((0,bO.fileURLToPath)(Yie.url))}function yO(t,e=Xie()){let r=Zy.default.resolve(e,"public");if(!hO.default.existsSync(r))throw new Error(`Could not find the build directory: ${r}, make sure to build the client first`);t.use(gO.default.static(r)),t.use("/{*path}",Kie,(n,i)=>{i.sendFile("index.html",{root:r})})}var dc=Me(require("node:crypto"),1),wv=Me(iI(),1),AI=Me(xI(),1);var dae=new Set(["127.0.0.1","::1","::ffff:127.0.0.1","localhost"]);function _I(t){if(!t)return!1;let e=t.replace(/^\[|\]$/g,"");return dae.has(e)?!0:e.startsWith("::ffff:")?e.slice(7).startsWith("127."):!!e.startsWith("127.")}var yv="OH_MY_PR_WEB_USERNAME",vv="OH_MY_PR_WEB_PASSWORD",fae="OH_MY_PR_SESSION_SECRET",EI="oh-my-pr.sid",SI=720*60*1e3,mae=(0,AI.default)(wv.default);function TI(t){let e=t?.trim();return e||void 0}function gae(t=process.env){return{username:TI(t[yv]),password:t[vv],sessionSecret:TI(t[fae])}}function hae(t){return!t.username||!t.password?null:{username:t.username,password:t.password}}function bae(t){return t.ip??t.socket?.remoteAddress}function PI(t){return _I(bae(t))}function kI(t,e){let r=dc.default.createHash("sha256").update(t).digest(),n=dc.default.createHash("sha256").update(e).digest();return dc.default.timingSafeEqual(r,n)}function bv(t,e){let r=!PI(t),n=t.session?.authenticatedWebUser??null,i=!r||!!(e&&n===e.username);return{requiresLogin:r,loginConfigured:e!==null,authenticated:i,username:i&&e?e.username:null}}function yae(t){return new Promise((e,r)=>{t.session.regenerate(n=>{if(n){r(n);return}e()})})}function vae(t){return new Promise((e,r)=>{t.session.save(n=>{if(n){r(n);return}e()})})}function wae(t){return new Promise((e,r)=>{t.session.destroy(n=>{if(n){r(n);return}e()})})}function RI(t,e){let r=e instanceof Error?e.message:String(e);return new Error(`Web auth ${t} failed: ${r}`,{cause:e})}function CI(t,e=gae()){let r=hae(e),n=e.sessionSecret??dc.default.randomBytes(32).toString("hex");t.use((0,wv.default)({name:EI,secret:n,store:new mae({checkPeriod:SI}),resave:!1,saveUninitialized:!1,cookie:{httpOnly:!0,sameSite:"lax",secure:!0,maxAge:SI}}));let i=Wd({windowMs:300*1e3,limit:20,standardHeaders:!0,legacyHeaders:!1});return t.get("/api/auth/status",(s,a)=>{a.json(bv(s,r))}),t.post("/api/auth/login",i,async(s,a,o)=>{if(!r){a.status(403).json({error:"Remote login is not configured",message:`Set ${yv} and ${vv} before using remote web access.`});return}let u=typeof s.body?.username=="string"?s.body.username:"",c=typeof s.body?.password=="string"?s.body.password:"";if(!kI(u,r.username)||!kI(c,r.password)){a.status(401).json({error:"Invalid credentials",message:"Username or password is incorrect."});return}let l="session regeneration";try{await yae(s),s.session.authenticatedWebUser=r.username,l="session save",await vae(s)}catch(d){o(RI(l,d));return}a.json(bv(s,r))}),t.post("/api/auth/logout",async(s,a,o)=>{try{s.session.authenticatedWebUser&&(await wae(s),a.clearCookie(EI))}catch(u){o(RI("session destroy",u));return}a.json(bv(s,r))}),{apiAccessMiddleware(s,a,o){if(PI(s)){o();return}if(!r){a.status(403).json({error:"Remote access is not configured",message:`Set ${yv} and ${vv} to allow remote dashboard login.`});return}if(s.session?.authenticatedWebUser===r.username){o();return}a.status(401).json({error:"Login required",message:"Sign in to use oh-my-pr from this network address."})}}}var II=require("http");var xae=lr("server"),Nn=(0,Qd.default)(),OI=(0,II.createServer)(Nn);function _ae(t){let e=t?.trim();if(!e||e==="false")return!1;if(e==="true")return!0;let r=Number(e);return Number.isInteger(r)&&r>=0?r:e}Nn.use(Qd.default.json({verify:(t,e,r)=>{t.rawBody=r}}));Nn.use(Qd.default.urlencoded({extended:!1}));Nn.set("trust proxy",_ae(process.env.OH_MY_PR_TRUST_PROXY));var Eae=CI(Nn);Nn.use("/api",Eae.apiAccessMiddleware);function LI(t,e="express"){X0.info({source:e},t)}async function Sae(t){let{default:e}=await import("open");await e(t)}(async()=>{await J8(OI,Nn),Nn.use((e,r,n,i)=>{let s=e,a=s.status||s.statusCode||500,o=s.message||"Internal Server Error";return xae.error({err:e instanceof Error?e.message:String(e),status:a},"Internal Server Error"),n.headersSent?i(e):n.status(a).json({message:o})}),yO(Nn);let t=parseInt(process.env.PORT||"5001",10);OI.listen({port:t,host:"0.0.0.0"},()=>{let e=`http://localhost:${t}`;console.log(`
|
|
1130
|
+
oh-my-pr v4.6.0
|
|
1131
1131
|
Dashboard: ${e}
|
|
1132
1132
|
`),!process.env.TAURI_DEV&&!process.env.OH_MY_PR_DESKTOP&&Sae(e).catch(n=>{LI(`Could not open browser automatically: ${n.message}`)})})})();0&&(module.exports={log});
|
|
1133
1133
|
/*! Bundled license information:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oh-my-pr",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"description": "Autonomous GitHub PR babysitter that watches repos, triages review feedback, and dispatches AI agents to fix code locally",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"github",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"@radix-ui/react-toggle-group": "^1.1.3",
|
|
86
86
|
"@radix-ui/react-tooltip": "^1.2.0",
|
|
87
87
|
"@tanstack/react-query": "^5.60.5",
|
|
88
|
-
"@tauri-apps/api": "^2.
|
|
88
|
+
"@tauri-apps/api": "^2.11.0",
|
|
89
89
|
"class-variance-authority": "^0.7.1",
|
|
90
90
|
"clsx": "^2.1.1",
|
|
91
91
|
"cmdk": "^1.1.1",
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
"@eslint/js": "^10.0.1",
|
|
131
131
|
"@tailwindcss/typography": "^0.5.15",
|
|
132
132
|
"@tailwindcss/vite": "^4.1.18",
|
|
133
|
-
"@tauri-apps/cli": "^2.
|
|
133
|
+
"@tauri-apps/cli": "^2.11.0",
|
|
134
134
|
"@types/connect-pg-simple": "^7.0.3",
|
|
135
135
|
"@types/express": "^5.0.0",
|
|
136
136
|
"@types/express-session": "^1.18.0",
|