v1.91.4.0 fix: Windows Opera cookie import wave (#2980, refs #2957) (#2987)

* fix: support Windows Opera and Opera GX cookie imports

Fixes #2957

* fix: repair Windows cookie decryption and make Opera import failures actionable

- strip the SHA-256(host_key) prefix Chromium adds to v10 values (DB meta v24+) on Windows
- keep receipts and name `$B handoff` recovery for App-Bound rows in browsers without native extraction
- explain missing browsers, missing profiles and ambiguous profile selection with next steps
- add windowsNative/resolveBrowserInfo, the operagx alias and sorted failure reasons in CLI output
- cover the Node server runtime with a real-DPAPI Windows test

* docs: document Windows Opera cookie import and guard browser lists against drift

* chore: file cookie-import follow-ups from the Opera fix wave review

* test: keep Opera receipt tests independent of the shared key cache

* test: declare generated gstack/llms.txt as a command-reference input for PR selection

* chore: release v1.91.4.0

* test: reconstruct the historical cookie-workflow approval after the Opera BROWSER.md additions

* test: run the real-DPAPI Windows check with the runner's environment

PowerShell launched with a stripped environment took ~18-21s on the Windows
runner (measured on a throwaway diagnostics run), past dpapiDecrypt's 10s
deadline; with the full environment it returns in ~0.3s. Only APPDATA is
redirected to the fixture's Opera root.

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
This commit is contained in:
Garry TanandMatt Van Horn authored and GitHub committed 2026-09-28 13:09:52 -07:00
1 parent 01593aa67c
commit d2a0bbcf4c
21 files changed
+1022 -69

No files matched your search

+2
View File
@@ -250,6 +250,8 @@ describe('dependency-free CI planner and report execution', () => {
const skillPath = path.join(fixture, 'setup-browser-cookies/SKILL.md');
const currentSkill = fs.readFileSync(skillPath, 'utf8');
fs.writeFileSync(skillPath, approvedCookieWorkflowSource(currentSkill));
const approvedBrowserPath = path.join(fixture, 'BROWSER.md');
fs.writeFileSync(approvedBrowserPath, approvedCookieWorkflowSource(fs.readFileSync(approvedBrowserPath, 'utf8')));
const reportDir = path.join(fixture, 'manual-report');
const manifestPath = path.join(reportDir, 'manifest.json');
const planned = run(['--emit-plan', manifestPath, '--slices', '1'], 'gate');
+1 -1
View File
@@ -16,7 +16,7 @@ function fixture() {
for (const file of [COOKIE_MANUAL_REVIEW_FILE, 'setup-browser-cookies/SKILL.md', 'BROWSER.md']) {
const target = join(root, file); mkdirSync(resolve(target, '..'), { recursive: true });
const source = readFileSync(join(ROOT, file), 'utf8');
writeFileSync(target, file === 'setup-browser-cookies/SKILL.md' ? approvedCookieWorkflowSource(source) : source);
writeFileSync(target, file === COOKIE_MANUAL_REVIEW_FILE ? source : approvedCookieWorkflowSource(source));
}
const entry = manualReviewFixture(root);
const approval = entry.manual_review!.approval;
+15 -2
View File
@@ -5,10 +5,23 @@ import type { EvalTestEntry } from './eval-store';
import { buildCookieWorkflowJudgeInput } from './cookie-workflow-judge-input';
import { COOKIE_MANUAL_REVIEW_FILE } from './cookie-workflow-manual-review';
// Later documentation added to the judged BROWSER.md section after the approval
// was recorded (v1.91.4.0 Windows Opera wave). Reversing it reconstructs the
// exact historical prompt bytes, including the section's line range.
const LATER_BROWSER_BLOCK = /\*\*Windows: Opera and Opera GX\.\*\*[\s\S]*?appear in CLI output\.\n\n/;
const LATER_BROWSER_EDITS: Array<[string, string]> = [
['The picker recognizes Chrome, Chromium, Brave, Edge, Windows-only Opera and Opera GX, and macOS-only Comet, Arc, and Dia.', 'The picker recognizes Chrome, Chromium, Brave, Edge, and macOS-only Comet, Arc, and Dia.'],
[' Opera and Opera GX are Windows-only and read from `%APPDATA%\\Opera Software\\Opera Stable` or `Opera GX Stable`, in `Default` or `Profile N` directories; legacy root-level layouts, Opera side profiles and portable or relocated installs are not detected. Opera has no native extraction, so its App-Bound cookies (if any) need manual sign-in.', ''],
];
export function approvedCookieWorkflowSource(source: string): string {
return source
let removedLines = 0;
let historical = source
.replace('sha256sum < "$tmpfile" | awk \'{print $(1)}\'', 'sha256sum "$tmpfile" | awk \'{print $1}\'')
.replace('shasum -a 256 < "$tmpfile" | awk \'{print $(1)}\'', 'shasum -a 256 "$tmpfile" | awk \'{print $1}\'');
.replace('shasum -a 256 < "$tmpfile" | awk \'{print $(1)}\'', 'shasum -a 256 "$tmpfile" | awk \'{print $1}\'')
.replace(LATER_BROWSER_BLOCK, block => { removedLines = block.split('\n').length - 1; return ''; });
for (const [later, earlier] of LATER_BROWSER_EDITS) historical = historical.replace(later, earlier);
return historical.replace(/(--- BEGIN FILE "BROWSER\.md" \(lines \d+-)(\d+)(; section\) ---)/, (_, head, end, tail) => `${head}${Number(end) - removedLines}${tail}`);
}
export function manualReviewFixture(root = resolve(import.meta.dir, '../..')): EvalTestEntry {
+1 -1
View File
@@ -1800,7 +1800,7 @@ export const E2E_TIERS: Record<string, 'gate' | 'periodic'> = {
*/
export const LLM_JUDGE_TOUCHFILES: Record<string, string[]> = {
'setup-browser-cookies/SKILL.md workflow': ['setup-browser-cookies/SKILL.md.tmpl', 'setup-browser-cookies/SKILL.md', 'BROWSER.md', 'test/helpers/cookie-workflow-judge-input.ts', 'test/cookie-workflow-judge-input.test.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/cookie-workflow-manual-review.test.ts', 'test/helpers/manual-judge-review-fixture.ts', '.github/cookie-workflow-manual-review.json', 'test/helpers/workflow-judge-input.ts', 'test/skill-llm-eval.test.ts'],
'command reference table': ['browse/sections/**', 'SKILL.md', 'SKILL.md.tmpl', 'browse/src/commands.ts', 'test/skill-llm-eval.test.ts'],
'command reference table': ['browse/sections/**', 'SKILL.md', 'SKILL.md.tmpl', 'browse/src/commands.ts', 'gstack/llms.txt', 'test/skill-llm-eval.test.ts'],
'snapshot flags reference': ['browse/sections/**', 'SKILL.md', 'SKILL.md.tmpl', 'browse/src/snapshot.ts', 'test/skill-llm-eval.test.ts'],
'browse/SKILL.md reference': ['browse/sections/**', 'browse/SKILL.md', 'browse/SKILL.md.tmpl', 'browse/src/**', 'test/skill-llm-eval.test.ts'],
'setup block': ['browse/SKILL.md', 'browse/SKILL.md.tmpl', 'scripts/resolvers/aside.ts', 'scripts/resolvers/browse.ts', 'test/skill-llm-eval.test.ts'],