|
| 1 | +import fs from "node:fs/promises"; |
| 2 | +import path from "node:path"; |
| 3 | +import os from "node:os"; |
| 4 | + |
| 5 | +async function fileExists(p) { |
| 6 | + try { |
| 7 | + await fs.access(p); |
| 8 | + return true; |
| 9 | + } catch { |
| 10 | + return false; |
| 11 | + } |
| 12 | +} |
| 13 | + |
| 14 | +async function findInstalledExtensionDirs() { |
| 15 | + const base = path.join(os.homedir(), ".vscode", "extensions"); |
| 16 | + if (!(await fileExists(base))) return []; |
| 17 | + const entries = await fs.readdir(base, { withFileTypes: true }); |
| 18 | + return entries |
| 19 | + .filter((e) => e.isDirectory() && e.name.startsWith("openai.chatgpt-")) |
| 20 | + .map((e) => ({ name: e.name, dir: path.join(base, e.name) })); |
| 21 | +} |
| 22 | + |
| 23 | +async function readActiveWebviewBundle(extDir) { |
| 24 | + const htmlPath = path.join(extDir, "webview", "index.html"); |
| 25 | + const html = await fs.readFile(htmlPath, "utf8"); |
| 26 | + const m = html.match(/src="\.\/assets\/(index-[^"]+\.js)"/); |
| 27 | + if (!m) return null; |
| 28 | + return m[1]; |
| 29 | +} |
| 30 | + |
| 31 | +async function readExtensionPackageJson(extDir) { |
| 32 | + const p = path.join(extDir, "package.json"); |
| 33 | + try { |
| 34 | + const txt = await fs.readFile(p, "utf8"); |
| 35 | + return JSON.parse(txt); |
| 36 | + } catch { |
| 37 | + return null; |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +async function findProfileForFolderName(folderName) { |
| 42 | + const base = path.join(process.cwd(), "docs", "patch-profiles", "openai.chatgpt"); |
| 43 | + |
| 44 | + // Preferred: match the "version part" (folder name without publisher/id prefix), |
| 45 | + // e.g. "openai.chatgpt-0.4.66-win32-x64" -> "0.4.66-win32-x64". |
| 46 | + const versionPart = folderName.startsWith("openai.chatgpt-") |
| 47 | + ? folderName.slice("openai.chatgpt-".length) |
| 48 | + : folderName; |
| 49 | + |
| 50 | + const candidates = [ |
| 51 | + path.join(base, versionPart), |
| 52 | + path.join(base, folderName), |
| 53 | + ]; |
| 54 | + |
| 55 | + for (const p of candidates) { |
| 56 | + if (await fileExists(p)) return p; |
| 57 | + } |
| 58 | + return null; |
| 59 | +} |
| 60 | + |
| 61 | +function out(lines) { |
| 62 | + process.stdout.write(lines.filter(Boolean).join("\n") + "\n"); |
| 63 | +} |
| 64 | + |
| 65 | +async function main() { |
| 66 | + const dirs = await findInstalledExtensionDirs(); |
| 67 | + if (dirs.length === 0) { |
| 68 | + out([ |
| 69 | + "No installed VS Code extension found matching: openai.chatgpt-*", |
| 70 | + `Searched: ${path.join(os.homedir(), ".vscode", "extensions")}`, |
| 71 | + ]); |
| 72 | + process.exit(2); |
| 73 | + } |
| 74 | + |
| 75 | + // Prefer newest mtime. |
| 76 | + const stats = await Promise.all( |
| 77 | + dirs.map(async (d) => ({ ...d, stat: await fs.stat(d.dir) })) |
| 78 | + ); |
| 79 | + stats.sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs); |
| 80 | + |
| 81 | + const chosen = stats[0]; |
| 82 | + const folderName = chosen.name; |
| 83 | + const extDir = chosen.dir; |
| 84 | + const pkg = await readExtensionPackageJson(extDir); |
| 85 | + const activeWebview = await readActiveWebviewBundle(extDir); |
| 86 | + const profile = await findProfileForFolderName(folderName); |
| 87 | + |
| 88 | + out([ |
| 89 | + `Installed extension folder: ${extDir}`, |
| 90 | + pkg?.version ? `Extension version (package.json): ${pkg.version}` : null, |
| 91 | + activeWebview ? `Active webview bundle: webview/assets/${activeWebview}` : null, |
| 92 | + profile ? `Matching patch profile: ${profile}` : "Matching patch profile: (none)", |
| 93 | + "", |
| 94 | + "Recommended next steps:", |
| 95 | + "1) Run tests: `npm test`", |
| 96 | + "2) Apply patch: `npm run apply`", |
| 97 | + "3) Verify (idempotent): `npm run verify`", |
| 98 | + "", |
| 99 | + "If no matching profile exists:", |
| 100 | + "- You can still try applying the patch (it uses robust anchors + markers).", |
| 101 | + "- If patching fails, collect the folder name + active bundle name and add a new profile folder under:", |
| 102 | + " `docs/patch-profiles/openai.chatgpt/<folderName>/`", |
| 103 | + ]); |
| 104 | +} |
| 105 | + |
| 106 | +await main(); |
0 commit comments