comfy-qa 2.1.1 → 2.2.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/package.json +1 -1
- package/src/agent/orchestrator.ts +50 -34
- package/src/utils/comfyui.ts +10 -0
- package/src/utils/github.ts +22 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as path from "path";
|
|
2
2
|
import * as fs from "fs";
|
|
3
|
-
import { fetchPR, fetchIssue, parseRef } from "../utils/github";
|
|
4
|
-
import { detectRunningInstance, bootstrapWorkspace, type ComfyUIInstance } from "../utils/comfyui";
|
|
3
|
+
import { fetchPR, fetchIssue, parseRef, fetchDeploymentPreviewUrl } from "../utils/github";
|
|
4
|
+
import { detectRunningInstance, bootstrapWorkspace, type ComfyUIInstance, COMFYUI_REPOS, REPO_PROD_URLS } from "../utils/comfyui";
|
|
5
5
|
import { researchPR, researchIssue } from "./research";
|
|
6
6
|
import { startRecorder, navigateWithHUD } from "../browser/recorder";
|
|
7
7
|
import { runScenarioWithAgent, runScenarioResearchOnly } from "./browser-agent";
|
|
@@ -83,41 +83,57 @@ export async function runQA(opts: QAOptions): Promise<void> {
|
|
|
83
83
|
if (record) {
|
|
84
84
|
console.log(`\n[3/5] Recording phase — Playwright + HUD…`);
|
|
85
85
|
|
|
86
|
-
// Resolve
|
|
87
|
-
|
|
86
|
+
// Resolve target URL:
|
|
87
|
+
// ComfyUI repos → detect local instance or bootstrap
|
|
88
|
+
// Other web-app repos → Vercel/preview URL from PR comments, then prod fallback
|
|
89
|
+
let comfyUrl = opts.comfyUrl ?? null;
|
|
88
90
|
let bootstrappedInstance: ComfyUIInstance | null = null;
|
|
89
91
|
|
|
90
92
|
if (!comfyUrl) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
93
|
+
if (COMFYUI_REPOS.has(parsed.repo)) {
|
|
94
|
+
comfyUrl = await detectRunningInstance();
|
|
95
|
+
if (!comfyUrl) {
|
|
96
|
+
console.log(` [bootstrap] No running ComfyUI — cloning & building target repo…`);
|
|
97
|
+
const prBranch = targetType === "pr"
|
|
98
|
+
? (target as Awaited<ReturnType<typeof fetchPR>>).headRefName
|
|
99
|
+
: undefined;
|
|
100
|
+
|
|
101
|
+
const wsPath = await cloneWorkspace({
|
|
102
|
+
owner: parsed.owner,
|
|
103
|
+
repo: parsed.repo,
|
|
104
|
+
outputBase,
|
|
105
|
+
branch: prBranch,
|
|
106
|
+
prNumber: targetType === "pr" ? parsed.number : undefined,
|
|
107
|
+
});
|
|
108
|
+
await ensureQASkill(wsPath);
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
bootstrappedInstance = await bootstrapWorkspace({
|
|
112
|
+
owner: parsed.owner,
|
|
113
|
+
repo: parsed.repo,
|
|
114
|
+
outputBase,
|
|
115
|
+
branch: prBranch,
|
|
116
|
+
prNumber: targetType === "pr" ? parsed.number : undefined,
|
|
117
|
+
});
|
|
118
|
+
comfyUrl = bootstrappedInstance.url;
|
|
119
|
+
} catch (err) {
|
|
120
|
+
console.log(` [bootstrap] Failed: ${err}`);
|
|
121
|
+
console.log(` [bootstrap] Falling back to research-only mode`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
// Non-ComfyUI repo: use Vercel/preview URL or production fallback
|
|
126
|
+
if (targetType === "pr") {
|
|
127
|
+
console.log(` [target] Fetching deployment preview URL from PR comments…`);
|
|
128
|
+
comfyUrl = await fetchDeploymentPreviewUrl(parsed.owner, parsed.repo, parsed.number);
|
|
129
|
+
if (comfyUrl) {
|
|
130
|
+
console.log(` [target] Preview URL: ${comfyUrl}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (!comfyUrl) {
|
|
134
|
+
comfyUrl = REPO_PROD_URLS[parsed.repo] ?? null;
|
|
135
|
+
if (comfyUrl) console.log(` [target] Using production URL: ${comfyUrl}`);
|
|
136
|
+
}
|
|
121
137
|
}
|
|
122
138
|
}
|
|
123
139
|
|
package/src/utils/comfyui.ts
CHANGED
|
@@ -11,6 +11,16 @@ export interface ComfyUIInstance {
|
|
|
11
11
|
stop: () => Promise<void>;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/** Repos that ARE ComfyUI itself — use detectRunningInstance() for these */
|
|
15
|
+
export const COMFYUI_REPOS = new Set(["ComfyUI", "ComfyUI_frontend"]);
|
|
16
|
+
|
|
17
|
+
/** Production URLs for known web-app repos (fallback when no preview URL found) */
|
|
18
|
+
export const REPO_PROD_URLS: Record<string, string> = {
|
|
19
|
+
"registry-web": "https://registry.comfy.org",
|
|
20
|
+
"website": "https://www.comfy.org",
|
|
21
|
+
"comfy-portal": "https://comfy.org",
|
|
22
|
+
};
|
|
23
|
+
|
|
14
24
|
/** Known repo dependency graph: repo → related repos to clone into tmp/ */
|
|
15
25
|
const RELATED_REPOS: Record<string, { owner: string; repo: string; setup?: string }[]> = {
|
|
16
26
|
// Web UIs testable with Playwright — real backend, no mocks
|
package/src/utils/github.ts
CHANGED
|
@@ -95,3 +95,25 @@ export async function fetchRecentIssues(owner: string, repo: string, limit = 20)
|
|
|
95
95
|
const json = await $`gh issue list --repo ${owner}/${repo} --limit ${limit} --state open --json number,title,body,state,author,labels,url`.text();
|
|
96
96
|
return JSON.parse(json);
|
|
97
97
|
}
|
|
98
|
+
|
|
99
|
+
/** Extract the first Vercel/Netlify/preview deployment URL from PR bot comments */
|
|
100
|
+
export async function fetchDeploymentPreviewUrl(owner: string, repo: string, prNumber: number): Promise<string | null> {
|
|
101
|
+
const commentsJson = await $`gh api repos/${owner}/${repo}/issues/${prNumber}/comments --paginate`.text();
|
|
102
|
+
const comments: { user: { login: string }; body: string }[] = JSON.parse(commentsJson);
|
|
103
|
+
|
|
104
|
+
for (const c of comments) {
|
|
105
|
+
const login = c.user?.login ?? "";
|
|
106
|
+
// Only trust bot comments
|
|
107
|
+
if (!login.includes("bot") && login !== "github-actions") continue;
|
|
108
|
+
|
|
109
|
+
// Extract first https URL from known deploy platforms
|
|
110
|
+
const match = c.body.match(/https:\/\/[^\s\)\"\'<]+(?:vercel\.app|netlify\.app|pages\.dev|fly\.dev|railway\.app)[^\s\)\"\'<]*/);
|
|
111
|
+
if (match) {
|
|
112
|
+
// Skip feedback/avatar/non-app URLs
|
|
113
|
+
const url = match[0].replace(/[.,;]+$/, "");
|
|
114
|
+
if (url.includes("feedback") || url.includes("avatar") || url.includes("badge") || url.includes("svg")) continue;
|
|
115
|
+
return url;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|