


















@@ -0,0 +1,161 @@
1+#!/usr/bin/env node
2+import { spawn } from "node:child_process";
3+4+export const BOUNDARY_CHECKS = [
5+["plugin-extension-boundary", "pnpm", ["run", "lint:plugins:no-extension-imports"]],
6+["lint:tmp:no-random-messaging", "pnpm", ["run", "lint:tmp:no-random-messaging"]],
7+["lint:tmp:channel-agnostic-boundaries", "pnpm", ["run", "lint:tmp:channel-agnostic-boundaries"]],
8+["lint:tmp:tsgo-core-boundary", "pnpm", ["run", "lint:tmp:tsgo-core-boundary"]],
9+["lint:tmp:no-raw-channel-fetch", "pnpm", ["run", "lint:tmp:no-raw-channel-fetch"]],
10+["lint:agent:ingress-owner", "pnpm", ["run", "lint:agent:ingress-owner"]],
11+[
12+"lint:plugins:no-register-http-handler",
13+"pnpm",
14+["run", "lint:plugins:no-register-http-handler"],
15+],
16+[
17+"lint:plugins:no-monolithic-plugin-sdk-entry-imports",
18+"pnpm",
19+["run", "lint:plugins:no-monolithic-plugin-sdk-entry-imports"],
20+],
21+[
22+"lint:plugins:no-extension-src-imports",
23+"pnpm",
24+["run", "lint:plugins:no-extension-src-imports"],
25+],
26+[
27+"lint:plugins:no-extension-test-core-imports",
28+"pnpm",
29+["run", "lint:plugins:no-extension-test-core-imports"],
30+],
31+[
32+"lint:plugins:plugin-sdk-subpaths-exported",
33+"pnpm",
34+["run", "lint:plugins:plugin-sdk-subpaths-exported"],
35+],
36+["deps:root-ownership:check", "pnpm", ["deps:root-ownership:check"]],
37+["web-search-provider-boundary", "pnpm", ["run", "lint:web-search-provider-boundaries"]],
38+["web-fetch-provider-boundary", "pnpm", ["run", "lint:web-fetch-provider-boundaries"]],
39+[
40+"extension-src-outside-plugin-sdk-boundary",
41+"pnpm",
42+["run", "lint:extensions:no-src-outside-plugin-sdk"],
43+],
44+[
45+"extension-plugin-sdk-internal-boundary",
46+"pnpm",
47+["run", "lint:extensions:no-plugin-sdk-internal"],
48+],
49+[
50+"extension-relative-outside-package-boundary",
51+"pnpm",
52+["run", "lint:extensions:no-relative-outside-package"],
53+],
54+["lint:ui:no-raw-window-open", "pnpm", ["lint:ui:no-raw-window-open"]],
55+].map(([label, command, args]) => ({ label, command, args }));
56+57+export function resolveConcurrency(value, fallback = 4) {
58+const parsed = Number.parseInt(String(value ?? ""), 10);
59+if (!Number.isFinite(parsed) || parsed < 1) {
60+return fallback;
61+}
62+return parsed;
63+}
64+65+export function formatCommand({ command, args }) {
66+return [command, ...args].join(" ");
67+}
68+69+function runSingleCheck(check, { cwd, env }) {
70+return new Promise((resolve) => {
71+const child = spawn(check.command, check.args, {
72+ cwd,
73+ env,
74+shell: false,
75+stdio: ["ignore", "pipe", "pipe"],
76+});
77+const chunks = [];
78+79+child.stdout.setEncoding("utf8");
80+child.stderr.setEncoding("utf8");
81+child.stdout.on("data", (chunk) => chunks.push(chunk));
82+child.stderr.on("data", (chunk) => chunks.push(chunk));
83+child.on("error", (error) => {
84+chunks.push(`${error.stack ?? error.message}\n`);
85+resolve({ check, code: 1, signal: null, output: chunks.join("") });
86+});
87+child.on("close", (code, signal) => {
88+resolve({ check, code: code ?? 1, signal, output: chunks.join("") });
89+});
90+});
91+}
92+93+function writeGroupedResult(result, output) {
94+const success = result.code === 0;
95+output.write(`::group::${result.check.label}\n`);
96+output.write(`$ ${formatCommand(result.check)}\n`);
97+if (result.output) {
98+output.write(result.output.endsWith("\n") ? result.output : `${result.output}\n`);
99+}
100+if (success) {
101+output.write(`[ok] ${result.check.label}\n`);
102+} else {
103+const suffix = result.signal ? ` (signal ${result.signal})` : ` (exit ${result.code})`;
104+output.write(
105+`::error title=${result.check.label} failed::${result.check.label} failed${suffix}\n`,
106+);
107+}
108+output.write("::endgroup::\n");
109+}
110+111+export async function runChecks(
112+checks = BOUNDARY_CHECKS,
113+{ concurrency = 4, cwd = process.cwd(), env = process.env, output = process.stdout } = {},
114+) {
115+const results = Array.from({ length: checks.length });
116+let nextIndex = 0;
117+let active = 0;
118+119+await new Promise((resolve) => {
120+const launch = () => {
121+if (nextIndex >= checks.length && active === 0) {
122+resolve();
123+return;
124+}
125+126+while (active < concurrency && nextIndex < checks.length) {
127+const index = nextIndex;
128+const check = checks[nextIndex++];
129+active += 1;
130+void runSingleCheck(check, { cwd, env })
131+.then((result) => {
132+results[index] = result;
133+})
134+.finally(() => {
135+active -= 1;
136+launch();
137+});
138+}
139+};
140+141+launch();
142+});
143+144+let failures = 0;
145+for (const result of results) {
146+writeGroupedResult(result, output);
147+if (result.code !== 0) {
148+failures += 1;
149+}
150+}
151+return failures;
152+}
153+154+if (import.meta.url === `file://${process.argv[1]}`) {
155+const concurrency = resolveConcurrency(
156+process.env.OPENCLAW_ADDITIONAL_BOUNDARY_CONCURRENCY ??
157+process.env.OPENCLAW_EXTENSION_BOUNDARY_CONCURRENCY,
158+);
159+const failures = await runChecks(BOUNDARY_CHECKS, { concurrency });
160+process.exitCode = failures === 0 ? 0 : 1;
161+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。