mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 17:53:39 +00:00
* feat(macos): host native browser tabs in the Control UI Browser panel * fix(browser): generate native Inspect script from UI source * fix(ui): satisfy native browser panel lint contracts * fix(macos): drain Mach-O classification output during packaging Avoid SIGPIPE from grep exiting early under pipefail, which could skip a universal framework and drop its secondary architecture. The existing packaging suite passes all 85 tests. * chore(ui): verify native browser localization baseline Run the keyless baseline and deterministic verification. The 79-entry raw-copy baseline is unchanged; locale metadata and translation memory remain untouched. * refactor(macos): consolidate reading tabs in the Browser panel Remove the split sidebar and its width persistence. Route legacy inline links to the default browser, reuse current URLs and retained redirect aliases through returned native tab IDs, and preserve popup, focus, cookie-store, and window lifecycle ownership. * docs(macos): describe native Browser panel tabs * Revert "fix(macos): drain Mach-O classification output during packaging" This reverts commit 259c9a508b280ca1c05ee02def96ae92df7e9100. * docs: keep the 2026.9.2 release notes unchanged * fix(macos): resolve native Browser panel review findings Require pointer-activated main-frame navigation before handing a native non-displayable HTTP(S) response to the default browser. Record activation per tab and consume it at response time through a pure response policy. Discard native captures and stale inspection replies when the captured URL changes. Let explicit tab selection supersede pending open activation, including replies that reuse an existing tab. Update the Browser docs. Regression proof: six new UI cases failed before the fixes, then passed; the extracted original Swift response policy failed three disallowed cases. Focused UI tests, pure Swift contracts, app/test builds, and Swift checks pass. The branch-wide changed gate is blocked by the existing hoisted dependency layout: UI @noble/hashes resolves into extensions/reef/node_modules, violating the core tsgo graph boundary. The workspace requires an isolated linker. * fix(ui): clear the Browser panel address after the last Mac tab closes * fix(ui): keep the Browser panel working on insecure origins without crypto.randomUUID * fix(ui): keep a superseded new-tab request from clearing the selected address * fix(macos): keep pending opens and blank new tabs distinct in the Browser panel * test(ui): stub getRandomValues deterministically for the insecure-origin panel case * test(ui): follow the Browser panel native link contract * refactor(ui): break the Browser panel controller import cycle * test(macos): wait for the dashboard document before posting the legacy inline link * test(macos): drop the page-driven legacy inline link smoke test The dashboard document never finishes loading on the hosted macOS runner, so the test could not post its message; the inline-to-external mapping it covered is a one-line switch in receiveLinkMessage. * fix(ui): keep native Browser tabs off the live screencast Carry the live stream into the extracted remote snapshot and viewport owners, behind the native-tab capture guard. Preserve frame ownership, screenshot fallback, resize restarts, and teardown on selection and suspension. Prove native-to-remote-to-native selection, stream closure, late-frame rejection, and native snapshots without Gateway browser availability. Document the stream and legacy inline link fallback. Preserve main's draining Mach-O checks after replaying the historical packaging fix and revert. Validation: 250 UI tests, six E2E scenarios, native builds and eight Swift contract tests, architecture and generated-script checks, i18n, and all changed checks. * ci: guard the browser inspect script check for frozen targets * perf(ui): keep the native browser bridge off the startup path * fix(ui): open links in the default browser while Settings hides the Browser panel * fix(ui): reclaim native browser tabs on explicit reselection * fix(ui): honor Settings fallback in native link menus * test(ci): include browser inspect in hosted guard fixtures * perf(ui): defer native link menus until host initialization Load the native menu before installing link handlers, with startup-owned cancellation and disposal. Regular browser startup no longer loads native-only menu dependencies; merged startup JS falls from 351292 to 343270 gzip bytes against the same main revision. * fix(ui): keep native link routing synchronous and load its menu on demand Restore bootstrap handler installation before startup so gateway admission starts synchronously. Defer the native menu until an external-link context event, share its pending import, and cancel superseded or disposed requests. Cover deferred loading, overlapping right-clicks, and abort/dispose; the admission E2E regressions now pass. * test(ui): use the native bridge boundary for palette occlusion Replace the file-wide native host module mock with a scoped WebKit bridge fixture. In the shared non-isolated lane, earlier imports could retain real host detection while the observer saw the mock. Preserve the open, close, and disconnect assertions; the original ordered twelve-file shard now passes all 1055 tests. * test(ui): seed collapsed navigation before recovery reloads Set the one-shot navigation intent at document initialization. Mutating the live URL immediately before Retry races router canonicalization during the document probe, causing the recovered fixture to show the expanded sidebar. Preserve floating-inbox recovery assertions; all three requested E2E files pass. * test(ui): keep deferred menu fixture compatible with the test target * test(ui): call native link routing synchronously in remaining fixtures
221 lines
7.1 KiB
TypeScript
221 lines
7.1 KiB
TypeScript
// Runs the repository check lanes selected by CLI arguments.
|
|
import { performance } from "node:perf_hooks";
|
|
import { booleanFlag, parseFlagArgs } from "./lib/arg-utils.mts";
|
|
import { printTimingSummary } from "./lib/check-timing-summary.mts";
|
|
import { runManagedCommand } from "./lib/managed-child-process.mts";
|
|
|
|
type CheckCommand = { name: string; args: string[] };
|
|
type RunManagedCheck = (options: { args: string[]; bin: string }) => Promise<number>;
|
|
|
|
export const PREFLIGHT_CHECKS: CheckCommand[] = [
|
|
{ name: "conflict markers", args: ["check:no-conflict-markers"] },
|
|
{ name: "script TypeScript erasability", args: ["check:script-erasability"] },
|
|
{ name: "max-lines suppression ratchet", args: ["check:max-lines-ratchet"] },
|
|
{ name: "assertion SAFETY comment ratchet", args: ["check:assertion-safety"] },
|
|
{ name: "changelog attributions", args: ["check:changelog-attributions"] },
|
|
{ name: "database-first legacy-store guard", args: ["check:database-first-legacy-stores"] },
|
|
{ name: "doctor deprecation registry", args: ["check:doctor-deprecation-registry"] },
|
|
{
|
|
name: "guarded extension wildcard re-exports",
|
|
args: ["lint:extensions:no-guarded-wildcard-reexports"],
|
|
},
|
|
{
|
|
name: "plugin-sdk wildcard re-exports",
|
|
args: ["lint:extensions:no-plugin-sdk-wildcard-reexports"],
|
|
},
|
|
{
|
|
name: "deprecated channel access seams",
|
|
args: ["lint:extensions:no-deprecated-channel-access"],
|
|
},
|
|
{ name: "media download helper guard", args: ["check:media-download-helpers"] },
|
|
{ name: "runtime sidecar loader guard", args: ["check:runtime-sidecar-loaders"] },
|
|
{ name: "tool display", args: ["tool-display:check"] },
|
|
{ name: "host env policy", args: ["check:host-env-policy:swift"] },
|
|
{ name: "browser inspect script", args: ["check:browser-inspect-script:swift"] },
|
|
{ name: "native conversation privacy defaults", args: ["native-catalogs:check"] },
|
|
{ name: "opengrep rule metadata", args: ["check:opengrep-rule-metadata"] },
|
|
{ name: "duplicate scan target coverage", args: ["dup:check:coverage"] },
|
|
{ name: "npm package-lock guard", args: ["deps:npm-lock:check"] },
|
|
{ name: "package patch guard", args: ["deps:patches:check"] },
|
|
];
|
|
|
|
/**
|
|
* Returns command usage text for the aggregate check runner.
|
|
*/
|
|
export function usage() {
|
|
return [
|
|
"Usage: node --import tsx scripts/check.mts [--timed] [--include-architecture] [--include-test-types]",
|
|
"",
|
|
"Runs the local check graph: guard preflights, typecheck, lint, and policy guards.",
|
|
"",
|
|
"Options:",
|
|
" --timed Print timing summary even when checks pass.",
|
|
" --include-architecture Run architecture import-cycle checks instead of runtime cycles.",
|
|
" --include-test-types Typecheck production and test sources.",
|
|
" -h, --help Show this help.",
|
|
].join("\n");
|
|
}
|
|
|
|
/**
|
|
* Parses aggregate check runner arguments.
|
|
*/
|
|
function parseCheckArgs(argv: string[]) {
|
|
return parseFlagArgs(
|
|
argv,
|
|
{ help: false, includeArchitecture: false, includeTestTypes: false, timed: false },
|
|
[
|
|
booleanFlag("--timed", "timed", true, { repeatable: true }),
|
|
booleanFlag("--include-architecture", "includeArchitecture", true, { repeatable: true }),
|
|
booleanFlag("--include-test-types", "includeTestTypes", true, { repeatable: true }),
|
|
booleanFlag("--help", "help", true, { repeatable: true }),
|
|
booleanFlag("-h", "help", true, { repeatable: true }),
|
|
],
|
|
{
|
|
ignoreDoubleDash: false,
|
|
onUnhandledArg(arg: string) {
|
|
throw new Error(`unknown argument: ${arg}\n\n${usage()}`);
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Runs selected repository check lanes.
|
|
*/
|
|
export async function main(argv = process.argv.slice(2)) {
|
|
let args;
|
|
try {
|
|
args = parseCheckArgs(argv);
|
|
} catch (error) {
|
|
console.error(error instanceof Error ? error.message : String(error));
|
|
process.exitCode = 2;
|
|
return;
|
|
}
|
|
if (args.help) {
|
|
console.log(usage());
|
|
process.exitCode = 0;
|
|
return;
|
|
}
|
|
|
|
const tailChecks = [
|
|
{ name: "webhook body guard", args: ["lint:webhook:no-low-level-body-read"] },
|
|
{ name: "runtime action config guard", args: ["check:no-runtime-action-load-config"] },
|
|
!args.includeArchitecture
|
|
? {
|
|
name: "deprecated API usage guard",
|
|
args: ["check:deprecated-api-usage"],
|
|
}
|
|
: null,
|
|
!args.includeArchitecture
|
|
? {
|
|
name: "wrapper shadowing guard",
|
|
args: ["check:wrapper-shadowing"],
|
|
}
|
|
: null,
|
|
{ name: "temp path guard", args: ["check:temp-path-guardrails"] },
|
|
{ name: "pairing store guard", args: ["lint:auth:no-pairing-store-group"] },
|
|
{ name: "pairing account guard", args: ["lint:auth:pairing-account-scope"] },
|
|
args.includeArchitecture
|
|
? { name: "architecture import cycles", args: ["check:architecture"] }
|
|
: { name: "runtime import cycles", args: ["check:import-cycles"] },
|
|
].filter((command) => command !== null);
|
|
|
|
const stages = [
|
|
{
|
|
name: "preflight guards",
|
|
parallel: true,
|
|
commands: PREFLIGHT_CHECKS,
|
|
},
|
|
{
|
|
name: "typecheck",
|
|
parallel: false,
|
|
commands: args.includeTestTypes
|
|
? [{ name: "typecheck all", args: ["tsgo:all"] }]
|
|
: [
|
|
{ name: "typecheck prod", args: ["tsgo:prod"] },
|
|
{ name: "typecheck scripts", args: ["tsgo:scripts"] },
|
|
{ name: "typecheck test root", args: ["tsgo:test:root"] },
|
|
],
|
|
},
|
|
{
|
|
name: "lint",
|
|
parallel: false,
|
|
commands: [
|
|
{ name: "lint", args: ["lint"] },
|
|
{ name: "format", args: ["format:check"] },
|
|
],
|
|
},
|
|
{
|
|
name: "policy guards",
|
|
parallel: true,
|
|
commands: tailChecks,
|
|
},
|
|
];
|
|
|
|
const timings = [];
|
|
let exitCode = 0;
|
|
|
|
for (const stage of stages) {
|
|
console.error(`\n[check] ${stage.name}`);
|
|
const results = stage.parallel
|
|
? await Promise.all(stage.commands.map((command) => runCommand(command)))
|
|
: await runSerial(stage.commands);
|
|
|
|
timings.push(...results);
|
|
const failed = results.find((result) => result.status !== 0);
|
|
if (failed) {
|
|
exitCode = failed.status;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (args.timed || exitCode !== 0) {
|
|
printSummary(timings);
|
|
}
|
|
|
|
process.exitCode = exitCode;
|
|
}
|
|
|
|
async function runSerial(commands: CheckCommand[]) {
|
|
const results: Array<Awaited<ReturnType<typeof runCommand>>> = [];
|
|
for (const command of commands) {
|
|
const result = await runCommand(command);
|
|
results.push(result);
|
|
if (result.status !== 0) {
|
|
break;
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* Runs one managed check command and returns timing/status details.
|
|
*/
|
|
export async function runCommand(
|
|
command: CheckCommand,
|
|
runManagedCommandImpl: RunManagedCheck = runManagedCommand,
|
|
) {
|
|
const startedAt = performance.now();
|
|
let status = 1;
|
|
try {
|
|
status = await runManagedCommandImpl({
|
|
args: command.args,
|
|
bin: "pnpm",
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
return {
|
|
name: command.name,
|
|
durationMs: performance.now() - startedAt,
|
|
status,
|
|
};
|
|
}
|
|
|
|
function printSummary(timings: Array<Awaited<ReturnType<typeof runCommand>>>) {
|
|
printTimingSummary("check", timings);
|
|
}
|
|
|
|
if (import.meta.main) {
|
|
await main();
|
|
}
|