Merge codex/gstack-2 into gstack2-runtime-integration

Reconcile the four integrated v2 runtime implementations (unified execution
result contract, execution profiles, capability readiness, GitHub security)
with main's browser-provider hardening.

Conflict resolutions:
- runtimeContract() generator: keep new execution-result + doctor-capability
  paragraphs, adopt main's `[matching browser flags]` fallback wording;
  regenerate the six RUNTIME.md.
- package.json: keep the strict isolated test:gstack2 runner and marked 18.0.6
  security bump; adopt main's playwright-core alias.
- bun.lock: regenerated via bun install.
- release-hardening.test.ts: adopt main's browser-provider assertions
  (resolveServerLaunchTarget, --browser managed smoke loop).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sinabina
2026-07-21 13:15:03 -07:00
co-authored by Claude Opus 4.8
160 changed files with 4397 additions and 522 deletions
+271
View File
@@ -5,6 +5,7 @@ import path from "node:path";
import { spawn } from "node:child_process";
import { pathToFileURL } from "node:url";
import { main as runtimeMain } from "../runtime/cli.js";
import { configSetBrowserChoice } from "../runtime/config.js";
import { summarizeRuntimeBundle } from "../scripts/gstack2/audit-runtime-bundle";
import {
DEFAULT_CAPABILITY_LAUNCHERS,
@@ -14,6 +15,8 @@ import {
defaultBunBuilder,
installManagedRuntime,
normalizeManagedBrowserTree,
runInstallerCli,
runtimeReleaseComponentForPath,
runtimeNativePackagePaths,
uninstallManagedRuntime,
runCommand,
@@ -27,10 +30,26 @@ const ENTRIES = [
{ path: "cap/tool", build: "fixture", executable: true },
];
const CAPABILITIES = { "fixture-tool": "cap/tool" };
const BROWSER_ENTRIES = [
{ path: "runtime" },
{ path: "bin/gstack", executable: true },
{ path: "browse/dist/browse", build: "fixture", executable: true },
];
const BROWSER_CAPABILITIES = { browse: "browse/dist/browse" };
const REPO_ROOT = path.resolve(import.meta.dir, "..");
const FULL_RUNTIME_TEST_TIMEOUT_MS = process.platform === "win32" ? 120_000 : 30_000;
describe("GStack 2 managed runtime installer", () => {
test("release staging excludes Playwright bookkeeping and Windows dependency validators", () => {
expect(runtimeReleaseComponentForPath(".gstack-runtime-browsers/.links/example")).toBeNull();
expect(runtimeReleaseComponentForPath(".gstack-runtime-browsers/winldd-1007/DEPENDENCIES_VALIDATED")).toBeNull();
expect(runtimeReleaseComponentForPath(".gstack-runtime-browsers/winldd-1007/winldd.exe")).toBeNull();
expect(runtimeReleaseComponentForPath(".gstack-runtime-browsers/chromium_headless_shell-1208/chrome.exe"))
.toBe("browser-headless");
expect(() => runtimeReleaseComponentForPath(".gstack-runtime-browsers/unknown-1/payload"))
.toThrow("Unknown managed browser payload path");
});
test("browser link normalization accepts internal macOS-style links and rejects escape graphs", async () => {
if (process.platform === "win32") return;
const root = await fs.mkdtemp(path.join(os.tmpdir(), "gstack-browser-links-"));
@@ -102,6 +121,241 @@ describe("GStack 2 managed runtime installer", () => {
}, { createDefaultSource: false });
});
test("stable launchers inject the persisted installed-browser choice and honor clearing it", async () => {
await withFixture(async ({ source, home }) => {
await installFixture(source, home, "browser-config-launcher", {
entries: BROWSER_ENTRIES,
capabilities: BROWSER_CAPABILITIES,
});
const executable = await fs.realpath(process.execPath);
await configSetBrowserChoice(home, { provider: "installed", executablePath: executable });
const ambient = { ...process.env, GSTACK_CHROMIUM_PATH: path.join(home, "ambient-browser-must-not-win") };
const selected = await runInstalledLauncher(home, "browse", [], { capture: true, env: ambient });
expect(selected.stdout).toBe(executable);
await configSetBrowserChoice(home, { provider: "managed", executablePath: null });
await expect(runInstalledLauncher(home, "browse", [], { capture: true, env: ambient }))
.rejects.toThrow("Command failed");
await configSetBrowserChoice(home, null);
await expect(runInstalledLauncher(home, "browse", [], { capture: true, env: ambient }))
.rejects.toThrow("Command failed");
});
});
test("installed-browser launchers refuse visible commands before starting the browser binary", async () => {
await withFixture(async ({ source, home }) => {
const executable = await fs.realpath(process.execPath);
await installFixture(source, home, "installed-visible-refusal", {
entries: BROWSER_ENTRIES,
capabilities: BROWSER_CAPABILITIES,
browserChoice: { provider: "installed", executablePath: executable },
});
await configSetBrowserChoice(home, { provider: "installed", executablePath: executable });
await expect(runInstalledLauncher(home, "browse", ["connect"], { capture: true }))
.rejects.toMatchObject({ stderr: expect.stringContaining("Visible GStack Browser requires managed Chromium") });
await expect(runInstalledLauncher(home, "browse", ["pair-agent"], { capture: true }))
.rejects.toMatchObject({ stderr: expect.stringContaining("Visible GStack Browser requires managed Chromium") });
await expect(runInstalledLauncher(home, "browse", ["handoff"], { capture: true }))
.rejects.toMatchObject({ stderr: expect.stringContaining("Visible GStack Browser requires managed Chromium") });
});
});
test("managed headless launchers require the separately approved visible-browser slot", async () => {
await withFixture(async ({ source, home }) => {
await installFixture(source, home, "managed-visible-refusal", {
entries: BROWSER_ENTRIES,
capabilities: BROWSER_CAPABILITIES,
browserChoice: { provider: "managed", executablePath: null },
});
await configSetBrowserChoice(home, { provider: "managed", executablePath: null });
await expect(runInstalledLauncher(home, "browse", ["--headed"], { capture: true }))
.rejects.toMatchObject({ stderr: expect.stringContaining("does not include visible Chromium") });
await expect(runInstalledLauncher(home, "browse", ["connect"], { capture: true }))
.rejects.toMatchObject({ stderr: expect.stringContaining("does not include visible Chromium") });
await expect(runInstalledLauncher(home, "browse", ["handoff"], { capture: true }))
.rejects.toMatchObject({ stderr: expect.stringContaining("does not include visible Chromium") });
});
});
test("design-only launchers do not require an unrelated browser selection", async () => {
await withFixture(async ({ source, home }) => {
const design = path.join(source, "design", "dist", "design");
await fs.mkdir(path.dirname(design), { recursive: true });
await fs.writeFile(design, "#!/bin/sh\nprintf 'design ready\\n'\n", { mode: 0o755 });
await installManagedRuntime({
sourceDir: source,
home,
version: "design-without-browser",
entries: [...ENTRIES, { path: "design/dist/design", build: "fixture", executable: true }],
capabilities: { ...CAPABILITIES, "gstack-design": "design/dist/design" },
});
await configSetBrowserChoice(home, null);
expect((await runInstalledLauncher(home, "gstack-design", [], { capture: true })).stdout)
.toContain("design ready");
});
});
test("browser config refuses a provider that does not match the active runtime slot", async () => {
await withFixture(async ({ source, home }) => {
const result = await installFixture(source, home, "installed-slot");
const manifestPath = path.join(result.path, ".gstack-bundle.json");
const manifest = await readJson(manifestPath);
await fs.writeFile(manifestPath, JSON.stringify({
...manifest,
selectedCapabilities: ["browser"],
runtimeComponents: ["browser-code", "core"],
browserChoice: { provider: "installed", executablePath: process.execPath },
}));
const output = captureStream();
expect(await runtimeMain(["config", "browser", "managed"], {
cwd: source,
env: { ...process.env, GSTACK_HOME: home },
stdout: output.stream,
stderr: output.stream,
})).toBe(1);
expect(output.value()).toContain("active runtime was installed for installed Chromium");
const selected = captureStream();
expect(await runtimeMain(["config", "browser", "installed", process.execPath], {
cwd: source,
env: { ...process.env, GSTACK_HOME: home },
stdout: selected.stream,
stderr: selected.stream,
})).toBe(0);
expect((await readJson(path.join(home, "config.json"))).browser.provider).toBe("installed");
});
});
test("rollback validates a recorded installed browser before switching runtime slots", async () => {
await withFixture(async ({ root, source, home }) => {
const fallback = await installFixture(source, home, "installed-fallback");
const staleBrowser = path.join(root, "removed-chromium");
const fallbackManifestPath = path.join(fallback.path, ".gstack-bundle.json");
const fallbackManifest = await readJson(fallbackManifestPath);
await fs.writeFile(fallbackManifestPath, JSON.stringify({
...fallbackManifest,
selectedCapabilities: ["browser"],
runtimeComponents: ["browser-code", "core"],
browserChoice: { provider: "installed", executablePath: staleBrowser },
}));
const current = await installFixture(source, home, "managed-current");
const currentManifestPath = path.join(current.path, ".gstack-bundle.json");
const currentManifest = await readJson(currentManifestPath);
await fs.writeFile(currentManifestPath, JSON.stringify({
...currentManifest,
selectedCapabilities: ["browser"],
runtimeComponents: ["browser-headless", "core"],
browserChoice: { provider: "managed", executablePath: null },
}));
await configSetBrowserChoice(home, { provider: "managed", executablePath: null });
const output = captureStream();
expect(await runtimeMain(["upgrade", "--rollback"], {
cwd: source,
env: { ...process.env, GSTACK_HOME: home },
stdout: output.stream,
stderr: output.stream,
})).toBe(1);
expect(output.value()).toContain("unavailable or not executable");
expect((await readJson(path.join(home, "versions", "current.json"))).current).toBe("managed-current");
expect((await readJson(path.join(home, "config.json"))).browser)
.toEqual({ provider: "managed", executablePath: null });
});
});
test("rollback switches the runtime pointer and recorded browser choice in one recoverable transaction", async () => {
await withFixture(async ({ source, home }) => {
const executable = await fs.realpath(process.execPath);
const fallback = await installFixture(source, home, "installed-fallback-valid");
const fallbackManifestPath = path.join(fallback.path, ".gstack-bundle.json");
const fallbackManifest = await readJson(fallbackManifestPath);
await fs.writeFile(fallbackManifestPath, JSON.stringify({
...fallbackManifest,
selectedCapabilities: ["browser"],
runtimeComponents: ["browser-code", "core"],
browserChoice: { provider: "installed", executablePath: executable },
}));
const current = await installFixture(source, home, "managed-current-valid");
const currentManifestPath = path.join(current.path, ".gstack-bundle.json");
const currentManifest = await readJson(currentManifestPath);
await fs.writeFile(currentManifestPath, JSON.stringify({
...currentManifest,
selectedCapabilities: ["browser"],
runtimeComponents: ["browser-headless", "core"],
browserChoice: { provider: "managed", executablePath: null },
}));
await configSetBrowserChoice(home, { provider: "managed", executablePath: null });
const output = captureStream();
expect(await runtimeMain(["upgrade", "--rollback"], {
cwd: source,
env: { ...process.env, GSTACK_HOME: home },
stdout: output.stream,
stderr: output.stream,
})).toBe(0);
expect(await readJson(path.join(home, "versions", "current.json")))
.toMatchObject({ current: "installed-fallback-valid", lastKnownGood: "managed-current-valid" });
expect((await readJson(path.join(home, "config.json"))).browser)
.toEqual({ provider: "installed", executablePath: executable });
expect(await exists(path.join(home, ".gstack-runtime-transaction.json"))).toBe(false);
});
});
test("an installed-browser setup persists the choice only after activation and launches through it", async () => {
await withFixture(async ({ root, source, home }) => {
await fs.writeFile(path.join(source, "cap", "tool"), `#!/usr/bin/env node
process.stdout.write(process.env.GSTACK_CHROMIUM_PATH || "unset");
`, { mode: 0o755 });
const executable = await fs.realpath(process.execPath);
const output = captureStream();
expect(await runInstallerCli([
"--source", source,
"--home", home,
"--capabilities", "browser",
"--browser", "installed",
"--browser-path", executable,
"--install-now",
"--yes",
"--json",
], {
stdout: output.stream,
stderr: output.stream,
prepareDependencies: async () => {},
installOptions: { entries: BROWSER_ENTRIES, capabilities: BROWSER_CAPABILITIES },
})).toBe(0);
expect((await readJson(path.join(home, "config.json"))).browser)
.toEqual({ provider: "installed", executablePath: executable });
expect((await runInstalledLauncher(home, "browse", [], { capture: true })).stdout)
.toBe(executable);
const failedHome = path.join(root, "failed-home", ".gstack");
const failed = captureStream();
expect(await runInstallerCli([
"--source", source,
"--home", failedHome,
"--capabilities", "browser",
"--browser", "installed",
"--browser-path", executable,
"--install-now",
"--yes",
"--json",
], {
stdout: failed.stream,
stderr: failed.stream,
prepareDependencies: async () => {},
installOptions: {
entries: BROWSER_ENTRIES,
capabilities: BROWSER_CAPABILITIES,
smokeTest: async () => { throw new Error("fixture smoke failure"); },
},
})).toBe(1);
expect(await exists(path.join(failedHome, "config.json"))).toBe(false);
});
});
test("accepts a symlink to the source root but rejects links inside the allowlist", async () => {
if (process.platform === "win32") return;
await withFixture(async ({ root, source, home }) => {
@@ -409,6 +663,13 @@ describe("GStack 2 managed runtime installer", () => {
await fs.writeFile(path.join(browserRoot, "chromium-fixture", "chrome"), "fixture\n", { mode: 0o755 });
return { code: 0, stdout: "", stderr: "" };
}
const outfileIndex = args.indexOf("--outfile");
if (outfileIndex >= 0 && typeof args[outfileIndex + 1] === "string") {
const outfile = path.join(REPO_ROOT, args[outfileIndex + 1]);
await fs.mkdir(path.dirname(outfile), { recursive: true });
await fs.writeFile(outfile, "fixture runtime helper\n", { mode: 0o755 });
return { code: 0, stdout: "", stderr: "" };
}
if (args[0] === "--version" && (command === process.execPath || command.includes(".gstack-runtime-tools"))) {
return { code: 0, stdout: "1.3.14\n", stderr: "" };
}
@@ -756,14 +1017,17 @@ describe("GStack 2 managed runtime installer", () => {
test("a launcher repairs a crash journal before resolving any runtime", async () => {
await withFixture(async ({ source, home }) => {
await installFixture(source, home, "1.0.0");
await configSetBrowserChoice(home, { provider: "managed", executablePath: null });
const pointer = await readJson(path.join(home, "versions", "current.json"));
const manifest = await fs.readFile(path.join(home, "runtime-install.json"));
const config = await fs.readFile(path.join(home, "config.json"));
// Keep the launcher for this host executable so it can enter the shared
// recovery path. The transaction restores the inactive host variant.
const recoverableLauncher = process.platform === "win32" ? "gstack" : "gstack.cmd";
const launcherPath = path.join(home, "bin", recoverableLauncher);
const launcherBefore = await fs.readFile(launcherPath);
await fs.writeFile(path.join(home, "runtime-install.json"), '{"activeVersion":"crashed"}\n');
await configSetBrowserChoice(home, { provider: "installed", executablePath: process.execPath });
await fs.writeFile(launcherPath, "candidate launcher\n");
await fs.writeFile(path.join(home, "versions", "current.json"), `${JSON.stringify({
schemaVersion: 2,
@@ -780,6 +1044,7 @@ describe("GStack 2 managed runtime installer", () => {
previousPointerExists: true,
previousPointer: pointer,
files: [
{ path: "config.json", existed: true, mode: 0o644, dataBase64: config.toString("base64") },
{ path: "runtime-install.json", existed: true, mode: 0o600, dataBase64: manifest.toString("base64") },
{ path: `bin/${recoverableLauncher}`, existed: true, mode: 0o644, dataBase64: launcherBefore.toString("base64") },
],
@@ -795,6 +1060,7 @@ describe("GStack 2 managed runtime installer", () => {
const launched = await runInstalledLauncher(home, "gstack", ["doctor"], { capture: true });
expect(launched.stdout).toContain("gstack fixture doctor");
expect(await readJson(path.join(home, "versions", "current.json"))).toEqual(pointer);
expect(await fs.readFile(path.join(home, "config.json"))).toEqual(config);
expect(await fs.readFile(path.join(home, "runtime-install.json"))).toEqual(manifest);
expect(await fs.readFile(launcherPath)).toEqual(launcherBefore);
expect(await exists(path.join(home, ".gstack-runtime-transaction.json"))).toBe(false);
@@ -1087,15 +1353,20 @@ async function createSource(source: string) {
await fs.mkdir(path.join(source, "runtime"), { recursive: true });
await fs.mkdir(path.join(source, "bin"), { recursive: true });
await fs.mkdir(path.join(source, "cap"), { recursive: true });
await fs.mkdir(path.join(source, "browse", "dist"), { recursive: true });
await fs.writeFile(path.join(source, "package.json"), '{"name":"gstack","version":"2.0.0","type":"module"}\n');
await fs.writeFile(path.join(source, "runtime", "cli.js"), fixtureCli(""));
await fs.writeFile(path.join(source, "runtime", "tooling.js"),
'export async function resolveBashCommand(env = process.env) { return env.GSTACK_BASH || "bash"; }\n');
await fs.copyFile(path.join(REPO_ROOT, "runtime", "browser-choice.mjs"), path.join(source, "runtime", "browser-choice.mjs"));
await fs.writeFile(path.join(source, "bin", "gstack"), `#!/usr/bin/env node
import { main } from "../runtime/cli.js";
process.exitCode = await main(process.argv.slice(2));
`, { mode: 0o755 });
await fs.writeFile(path.join(source, "cap", "tool"), "#!/bin/sh\nprintf 'fixture capability %s\\n' \"$*\"\n", { mode: 0o755 });
await fs.writeFile(path.join(source, "browse", "dist", "browse"), `#!/usr/bin/env node
process.stdout.write(process.env.GSTACK_CHROMIUM_PATH || "unset");
`, { mode: 0o755 });
}
function fixtureCli(label: string) {