mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
fix(agent-manager): harden PR refresh state
This commit is contained in:
@@ -249,7 +249,7 @@ export class PRStatusPoller {
|
||||
const pr = await this.cachedFetchPR(wt.branch, wt.path)
|
||||
if (!pr || this.stale(generation)) {
|
||||
if (this.stale(generation)) return
|
||||
const hash = `${worktreeId}:none`
|
||||
const hash = `${worktreeId}:${wt.branch}:none`
|
||||
if (this.lastHash.get(worktreeId) === hash) return
|
||||
this.lastHash.set(worktreeId, hash)
|
||||
this.options.onStatus(worktreeId, null)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* the file, so the lines below the comment are read from disk and attached to
|
||||
* the thread.
|
||||
*/
|
||||
import { readFile, stat } from "node:fs/promises"
|
||||
import { readFile, realpath, stat } from "node:fs/promises"
|
||||
import path from "node:path"
|
||||
import type { PRComment } from "../types"
|
||||
|
||||
@@ -32,7 +32,11 @@ function anchor(hunk: string): string | undefined {
|
||||
}
|
||||
|
||||
async function lines(dir: string, file: string): Promise<string[] | undefined> {
|
||||
const full = path.join(dir, file)
|
||||
const root = await realpath(dir).catch(() => undefined)
|
||||
const full = await realpath(path.resolve(dir, file)).catch(() => undefined)
|
||||
if (!root || !full) return undefined
|
||||
const rel = path.relative(root, full)
|
||||
if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) return undefined
|
||||
const info = await stat(full).catch(() => undefined)
|
||||
if (!info?.isFile() || info.size > SIZE) return undefined
|
||||
const hit = cache.get(full)
|
||||
|
||||
@@ -99,6 +99,14 @@ describe("withContext", () => {
|
||||
expect(items.map((item) => item.after)).toEqual([undefined, undefined, undefined, undefined])
|
||||
})
|
||||
|
||||
it("rejects a comment path outside the worktree", async () => {
|
||||
const dir = await repo()
|
||||
await writeFile(path.join(path.dirname(dir), "outside.ts"), SOURCE.join("\n"))
|
||||
const [item] = await withContext(dir, [thread({ file: "../outside.ts" })])
|
||||
|
||||
expect(item!.after).toBeUndefined()
|
||||
})
|
||||
|
||||
it("keeps every thread, in order, whatever the files say", async () => {
|
||||
const dir = await repo()
|
||||
const items = await withContext(dir, [thread({ threadId: "a" }), thread({ threadId: "b", outdated: true })])
|
||||
|
||||
@@ -102,7 +102,7 @@ export const PRPanel: Component<PRPanelProps> = (props) => {
|
||||
// expanded card and the scroll position, so the last list for this PR stays.
|
||||
const comments = createMemo<{ number: number; value: NonNullable<PRStatus["comments"]> } | undefined>((prev) => {
|
||||
const next = props.pr.comments
|
||||
if (next?.total) return { number: props.pr.number, value: next }
|
||||
if (next) return { number: props.pr.number, value: next }
|
||||
if (prev && prev.number === props.pr.number) return prev
|
||||
return undefined
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user