@methiu/raigal 0.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -0
- package/README.md +511 -0
- package/dist/AgentApp-DxfJZJXy.js +390 -0
- package/dist/adapters/astro.d.ts +22 -0
- package/dist/adapters/astro.js +20 -0
- package/dist/adapters/expo.d.ts +24 -0
- package/dist/adapters/expo.js +33 -0
- package/dist/adapters/nuxt.d.ts +33 -0
- package/dist/adapters/nuxt.js +41 -0
- package/dist/adapters/sveltekit.d.ts +15 -0
- package/dist/adapters/sveltekit.js +19 -0
- package/dist/adapters/vite.d.ts +26 -0
- package/dist/adapters/vite.js +33 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +29535 -0
- package/dist/core-BpOFk2i9.d.ts +34 -0
- package/dist/core-o3KNKhg2.js +100 -0
- package/dist/finding-assessment-M5fUTE-C.js +321 -0
- package/dist/index.d.ts +351 -0
- package/dist/index.js +20228 -0
- package/dist/json-DBN8fnd3.js +35 -0
- package/dist/json-DKuTrkDa.js +35 -0
- package/dist/mask-secrets-BXn-ZSEh.js +71 -0
- package/dist/mcp.js +15018 -0
- package/dist/mount-zBL9B_lE.js +18 -0
- package/dist/sarif-B-l1Fhzd.js +66 -0
- package/dist/sarif-B5s74n0Q.js +66 -0
- package/dist/version-CVfOxP_O.js +6 -0
- package/package.json +129 -0
- package/scripts/check-licenses.mjs +45 -0
- package/scripts/check-tarball-keys.mjs +106 -0
- package/scripts/gen-config-schema.mjs +35 -0
- package/scripts/install-tools.mjs +383 -0
- package/scripts/polyfill.cjs +10 -0
- package/scripts/release-guard.mjs +29 -0
- package/tools/jb/README.md +45 -0
- package/tools/jb/aislop.DotSettings +3 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
//#region src/ui/agent-tui/mount.ts
|
|
3
|
+
const mountAgentTui = async (store) => {
|
|
4
|
+
const [{ render }, React, { AgentApp }] = await Promise.all([
|
|
5
|
+
import("ink"),
|
|
6
|
+
import("react"),
|
|
7
|
+
import("./AgentApp-DxfJZJXy.js")
|
|
8
|
+
]);
|
|
9
|
+
process.stdout.write("\x1B[?1049h\x1B[?25l");
|
|
10
|
+
const instance = render(React.createElement(AgentApp, { store }), { exitOnCtrlC: false });
|
|
11
|
+
return { close: async () => {
|
|
12
|
+
instance.unmount();
|
|
13
|
+
await instance.waitUntilExit();
|
|
14
|
+
process.stdout.write("\x1B[?25h\x1B[?1049l");
|
|
15
|
+
} };
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { mountAgentTui };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { a as FIXED_SECRET_DIAGNOSTIC_MESSAGE, i as APP_VERSION, o as isSecretClassRule } from "./cli.js";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/output/sarif.ts
|
|
5
|
+
const SARIF_VERSION = "2.1.0";
|
|
6
|
+
const SARIF_SCHEMA = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json";
|
|
7
|
+
const levelFromSeverity = (severity) => {
|
|
8
|
+
if (severity === "error") return "error";
|
|
9
|
+
if (severity === "warning") return "warning";
|
|
10
|
+
return "note";
|
|
11
|
+
};
|
|
12
|
+
const oneBased = (value) => value >= 1 ? value : 1;
|
|
13
|
+
const toUri = (filePath) => filePath.split(path.sep).join("/");
|
|
14
|
+
const buildRules = (diagnostics) => {
|
|
15
|
+
const byId = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const d of diagnostics) {
|
|
17
|
+
if (byId.has(d.rule)) continue;
|
|
18
|
+
const isSecret = d.redactSource || isSecretClassRule(d.rule);
|
|
19
|
+
const desc = isSecret ? FIXED_SECRET_DIAGNOSTIC_MESSAGE : d.message;
|
|
20
|
+
const helpText = isSecret ? "Rotate it and load it from the environment." : d.help || d.message;
|
|
21
|
+
byId.set(d.rule, {
|
|
22
|
+
id: d.rule,
|
|
23
|
+
name: d.rule,
|
|
24
|
+
shortDescription: { text: desc },
|
|
25
|
+
help: { text: helpText }
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return [...byId.values()];
|
|
29
|
+
};
|
|
30
|
+
const buildSarifLog = (results) => {
|
|
31
|
+
const diagnostics = results.flatMap((r) => r.diagnostics);
|
|
32
|
+
const rules = buildRules(diagnostics);
|
|
33
|
+
const ruleIndex = new Map(rules.map((rule, index) => [rule.id, index]));
|
|
34
|
+
const sarifResults = diagnostics.map((d) => {
|
|
35
|
+
const isSecret = d.redactSource || isSecretClassRule(d.rule);
|
|
36
|
+
return {
|
|
37
|
+
ruleId: d.rule,
|
|
38
|
+
ruleIndex: ruleIndex.get(d.rule) ?? 0,
|
|
39
|
+
level: levelFromSeverity(d.severity),
|
|
40
|
+
message: { text: isSecret ? FIXED_SECRET_DIAGNOSTIC_MESSAGE : d.message },
|
|
41
|
+
locations: [{ physicalLocation: {
|
|
42
|
+
artifactLocation: { uri: toUri(d.filePath) },
|
|
43
|
+
region: {
|
|
44
|
+
startLine: oneBased(d.line),
|
|
45
|
+
startColumn: isSecret ? 1 : oneBased(d.column)
|
|
46
|
+
}
|
|
47
|
+
} }],
|
|
48
|
+
...d.changeContext ? { properties: { changeContext: d.changeContext } } : {}
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
$schema: SARIF_SCHEMA,
|
|
53
|
+
version: SARIF_VERSION,
|
|
54
|
+
runs: [{
|
|
55
|
+
tool: { driver: {
|
|
56
|
+
name: "aislop",
|
|
57
|
+
version: APP_VERSION,
|
|
58
|
+
informationUri: "https://github.com/meethiu/raigal",
|
|
59
|
+
rules
|
|
60
|
+
} },
|
|
61
|
+
results: sarifResults
|
|
62
|
+
}]
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
//#endregion
|
|
66
|
+
export { buildSarifLog };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { t as APP_VERSION } from "./version-CVfOxP_O.js";
|
|
2
|
+
import { n as isSecretClassRule, t as FIXED_SECRET_DIAGNOSTIC_MESSAGE } from "./mask-secrets-BXn-ZSEh.js";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
//#region src/output/sarif.ts
|
|
5
|
+
const SARIF_VERSION = "2.1.0";
|
|
6
|
+
const SARIF_SCHEMA = "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json";
|
|
7
|
+
const levelFromSeverity = (severity) => {
|
|
8
|
+
if (severity === "error") return "error";
|
|
9
|
+
if (severity === "warning") return "warning";
|
|
10
|
+
return "note";
|
|
11
|
+
};
|
|
12
|
+
const oneBased = (value) => value >= 1 ? value : 1;
|
|
13
|
+
const toUri = (filePath) => filePath.split(path.sep).join("/");
|
|
14
|
+
const buildRules = (diagnostics) => {
|
|
15
|
+
const byId = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const d of diagnostics) {
|
|
17
|
+
if (byId.has(d.rule)) continue;
|
|
18
|
+
const isSecret = d.redactSource || isSecretClassRule(d.rule);
|
|
19
|
+
const desc = isSecret ? FIXED_SECRET_DIAGNOSTIC_MESSAGE : d.message;
|
|
20
|
+
const helpText = isSecret ? "Rotate it and load it from the environment." : d.help || d.message;
|
|
21
|
+
byId.set(d.rule, {
|
|
22
|
+
id: d.rule,
|
|
23
|
+
name: d.rule,
|
|
24
|
+
shortDescription: { text: desc },
|
|
25
|
+
help: { text: helpText }
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return [...byId.values()];
|
|
29
|
+
};
|
|
30
|
+
const buildSarifLog = (results) => {
|
|
31
|
+
const diagnostics = results.flatMap((r) => r.diagnostics);
|
|
32
|
+
const rules = buildRules(diagnostics);
|
|
33
|
+
const ruleIndex = new Map(rules.map((rule, index) => [rule.id, index]));
|
|
34
|
+
const sarifResults = diagnostics.map((d) => {
|
|
35
|
+
const isSecret = d.redactSource || isSecretClassRule(d.rule);
|
|
36
|
+
return {
|
|
37
|
+
ruleId: d.rule,
|
|
38
|
+
ruleIndex: ruleIndex.get(d.rule) ?? 0,
|
|
39
|
+
level: levelFromSeverity(d.severity),
|
|
40
|
+
message: { text: isSecret ? FIXED_SECRET_DIAGNOSTIC_MESSAGE : d.message },
|
|
41
|
+
locations: [{ physicalLocation: {
|
|
42
|
+
artifactLocation: { uri: toUri(d.filePath) },
|
|
43
|
+
region: {
|
|
44
|
+
startLine: oneBased(d.line),
|
|
45
|
+
startColumn: isSecret ? 1 : oneBased(d.column)
|
|
46
|
+
}
|
|
47
|
+
} }],
|
|
48
|
+
...d.changeContext ? { properties: { changeContext: d.changeContext } } : {}
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
$schema: SARIF_SCHEMA,
|
|
53
|
+
version: SARIF_VERSION,
|
|
54
|
+
runs: [{
|
|
55
|
+
tool: { driver: {
|
|
56
|
+
name: "aislop",
|
|
57
|
+
version: APP_VERSION,
|
|
58
|
+
informationUri: "https://github.com/meethiu/raigal",
|
|
59
|
+
rules
|
|
60
|
+
} },
|
|
61
|
+
results: sarifResults
|
|
62
|
+
}]
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
//#endregion
|
|
66
|
+
export { buildSarifLog };
|
package/package.json
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@methiu/raigal",
|
|
3
|
+
"version": "0.16.2",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"description": "Catch the slop AI coding agents leave in your code: narrative comments, swallowed exceptions, as-any casts, dead code, oversized functions. 50+ rules across 10 language targets (TypeScript, JavaScript, Expo / React Native, Python, Go, Rust, Ruby, PHP, C#, C/C++). Sub-second, deterministic, no LLM at runtime.",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"bin": {
|
|
10
|
+
"raigal": "dist/cli.js",
|
|
11
|
+
"raigal-mcp": "dist/mcp.js",
|
|
12
|
+
"raigal-tools": "scripts/install-tools.mjs"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"scripts",
|
|
17
|
+
"tools/jb"
|
|
18
|
+
],
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./adapters/astro": {
|
|
25
|
+
"types": "./dist/adapters/astro.d.ts",
|
|
26
|
+
"default": "./dist/adapters/astro.js"
|
|
27
|
+
},
|
|
28
|
+
"./adapters/expo": {
|
|
29
|
+
"types": "./dist/adapters/expo.d.ts",
|
|
30
|
+
"default": "./dist/adapters/expo.js"
|
|
31
|
+
},
|
|
32
|
+
"./adapters/nuxt": {
|
|
33
|
+
"types": "./dist/adapters/nuxt.d.ts",
|
|
34
|
+
"default": "./dist/adapters/nuxt.js"
|
|
35
|
+
},
|
|
36
|
+
"./adapters/sveltekit": {
|
|
37
|
+
"types": "./dist/adapters/sveltekit.d.ts",
|
|
38
|
+
"default": "./dist/adapters/sveltekit.js"
|
|
39
|
+
},
|
|
40
|
+
"./adapters/vite": {
|
|
41
|
+
"types": "./dist/adapters/vite.d.ts",
|
|
42
|
+
"default": "./dist/adapters/vite.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"dev": "tsdown --watch",
|
|
47
|
+
"build": "node --require ./scripts/polyfill.cjs ./node_modules/tsdown/dist/run.mjs",
|
|
48
|
+
"typecheck": "tsc --noEmit",
|
|
49
|
+
"gen:schema": "pnpm build && node scripts/gen-config-schema.mjs",
|
|
50
|
+
"test": "pnpm build && vitest run",
|
|
51
|
+
"scan": "pnpm build && node dist/cli.js scan .",
|
|
52
|
+
"scan:exclude": "pnpm build && node dist/cli.js scan . --exclude .idea --exclude .gitnore --exclude node_modules",
|
|
53
|
+
"scan:include": "pnpm build && node dist/cli.js scan . --include src --include tests",
|
|
54
|
+
"scan:json": "pnpm build && node dist/cli.js scan . --json",
|
|
55
|
+
"check:licenses": "node scripts/check-licenses.mjs",
|
|
56
|
+
"check:tarball-keys": "node scripts/check-tarball-keys.mjs",
|
|
57
|
+
"release:guard": "node scripts/release-guard.mjs",
|
|
58
|
+
"quality": "pnpm typecheck && pnpm test && node scripts/check-tarball-keys.mjs && node dist/cli.js scan . --json"
|
|
59
|
+
},
|
|
60
|
+
"keywords": [
|
|
61
|
+
"raigal",
|
|
62
|
+
"ai-slop",
|
|
63
|
+
"code-quality",
|
|
64
|
+
"linter",
|
|
65
|
+
"formatter",
|
|
66
|
+
"cli",
|
|
67
|
+
"ai",
|
|
68
|
+
"copilot",
|
|
69
|
+
"code-review",
|
|
70
|
+
"static-analysis",
|
|
71
|
+
"typescript",
|
|
72
|
+
"javascript",
|
|
73
|
+
"python",
|
|
74
|
+
"go",
|
|
75
|
+
"rust",
|
|
76
|
+
"ruby",
|
|
77
|
+
"php",
|
|
78
|
+
"csharp",
|
|
79
|
+
"dotnet",
|
|
80
|
+
"cpp"
|
|
81
|
+
],
|
|
82
|
+
"author": {
|
|
83
|
+
"name": "Kenny Olawuwo",
|
|
84
|
+
"url": "https://raigal.dev"
|
|
85
|
+
},
|
|
86
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
87
|
+
"homepage": "https://raigal.dev",
|
|
88
|
+
"repository": {
|
|
89
|
+
"type": "git",
|
|
90
|
+
"url": "git+https://github.com/meethiu/raigal.git"
|
|
91
|
+
},
|
|
92
|
+
"bugs": {
|
|
93
|
+
"url": "https://github.com/meethiu/raigal/issues"
|
|
94
|
+
},
|
|
95
|
+
"engines": {
|
|
96
|
+
"node": ">=20"
|
|
97
|
+
},
|
|
98
|
+
"packageManager": "pnpm@10.28.0",
|
|
99
|
+
"dependencies": {
|
|
100
|
+
"@biomejs/biome": "^2.5.12",
|
|
101
|
+
"@clack/prompts": "^1.7.0",
|
|
102
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
103
|
+
"adm-zip": "^0.6.1",
|
|
104
|
+
"commander": "^14.0.3",
|
|
105
|
+
"expo-doctor": "^1.20.4",
|
|
106
|
+
"ink": "^7.1.1",
|
|
107
|
+
"ink-select-input": "^6.2.0",
|
|
108
|
+
"knip": "^6.34.0",
|
|
109
|
+
"micromatch": "^4.0.8",
|
|
110
|
+
"oxlint": "^1.81.0",
|
|
111
|
+
"picocolors": "^1.1.1",
|
|
112
|
+
"react": "^19.2.8",
|
|
113
|
+
"smol-toml": "^1.8.0",
|
|
114
|
+
"tar": "^7.5.22",
|
|
115
|
+
"typescript": "^6.0.3",
|
|
116
|
+
"wcwidth": "^1.0.1",
|
|
117
|
+
"yaml": "^2.9.0",
|
|
118
|
+
"zod": "^4.4.3"
|
|
119
|
+
},
|
|
120
|
+
"devDependencies": {
|
|
121
|
+
"@types/micromatch": "^4.0.10",
|
|
122
|
+
"@types/node": "^26.4.0",
|
|
123
|
+
"@types/react": "^19.2.18",
|
|
124
|
+
"ink-testing-library": "^4.0.0",
|
|
125
|
+
"tsdown": "^0.22.14",
|
|
126
|
+
"vite": "^8.2.2",
|
|
127
|
+
"vitest": "^4.1.11"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const ROOT = path.resolve(__dirname, "..");
|
|
8
|
+
const PKG_PATH = path.join(ROOT, "package.json");
|
|
9
|
+
const NODE_MODULES = path.join(ROOT, "node_modules");
|
|
10
|
+
|
|
11
|
+
const FORBIDDEN_LICENSES = [/GPL/i, /AGPL/i, /SSPL/i];
|
|
12
|
+
|
|
13
|
+
const pkg = JSON.parse(fs.readFileSync(PKG_PATH, "utf-8"));
|
|
14
|
+
const dependencies = Object.keys(pkg.dependencies || {});
|
|
15
|
+
|
|
16
|
+
const violations = [];
|
|
17
|
+
|
|
18
|
+
for (const dep of dependencies) {
|
|
19
|
+
const depPkgPath = path.join(NODE_MODULES, dep, "package.json");
|
|
20
|
+
if (!fs.existsSync(depPkgPath)) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const depPkg = JSON.parse(fs.readFileSync(depPkgPath, "utf-8"));
|
|
25
|
+
const license = depPkg.license || (Array.isArray(depPkg.licenses) ? depPkg.licenses.map((l) => l.type || l).join(", ") : "");
|
|
26
|
+
for (const regex of FORBIDDEN_LICENSES) {
|
|
27
|
+
if (typeof license === "string" && regex.test(license)) {
|
|
28
|
+
violations.push({ dep, license });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
console.warn(`Warning: Could not parse package.json for ${dep}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (violations.length > 0) {
|
|
37
|
+
console.error("Dependency license audit FAILED: GPL/AGPL packages detected in dependencies:");
|
|
38
|
+
for (const v of violations) {
|
|
39
|
+
console.error(` - ${v.dep}: ${v.license}`);
|
|
40
|
+
}
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log(`Dependency license audit PASSED (${dependencies.length} production dependencies verified).`);
|
|
45
|
+
process.exit(0);
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
// Allowed PostHog project API keys (empty by default; unauthorized keys fail the build).
|
|
8
|
+
const ALLOWED_KEYS = new Set([]);
|
|
9
|
+
|
|
10
|
+
// Break literal pattern to prevent detector self-matching
|
|
11
|
+
const PH_KEY_PREFIX = `${"p" + "h" + "c"}_`;
|
|
12
|
+
const PH_KEY_PATTERN = new RegExp(`${PH_KEY_PREFIX}[A-Za-z0-9_-]{20,}`, "g");
|
|
13
|
+
|
|
14
|
+
const ROOT_DIR = path.resolve(import.meta.dirname, "..");
|
|
15
|
+
|
|
16
|
+
const walkDirectory = (dir, callback) => {
|
|
17
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
18
|
+
for (const entry of entries) {
|
|
19
|
+
const fullPath = path.join(dir, entry.name);
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
walkDirectory(fullPath, callback);
|
|
22
|
+
} else if (entry.isFile()) {
|
|
23
|
+
callback(fullPath);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
let tarballPath = null;
|
|
29
|
+
let extractDir = null;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
// 1. Pack the package tarball
|
|
33
|
+
const packOutput = execSync("npm pack", {
|
|
34
|
+
cwd: ROOT_DIR,
|
|
35
|
+
encoding: "utf-8",
|
|
36
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
37
|
+
}).trim();
|
|
38
|
+
|
|
39
|
+
const lines = packOutput.split("\n");
|
|
40
|
+
const tarballName = lines[lines.length - 1]?.trim();
|
|
41
|
+
if (!tarballName || !tarballName.endsWith(".tgz")) {
|
|
42
|
+
throw new Error(`Unexpected npm pack output: "${packOutput}"`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
tarballPath = path.join(ROOT_DIR, tarballName);
|
|
46
|
+
if (!fs.existsSync(tarballPath)) {
|
|
47
|
+
throw new Error(`Packed tarball not found at ${tarballPath}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 2. Extract into a temporary directory
|
|
51
|
+
extractDir = fs.mkdtempSync(path.join(os.tmpdir(), "raigal-tarball-"));
|
|
52
|
+
execSync(`tar -xzf "${tarballPath}" -C "${extractDir}"`, {
|
|
53
|
+
stdio: "ignore",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const packageDir = path.join(extractDir, "package");
|
|
57
|
+
if (!fs.existsSync(packageDir)) {
|
|
58
|
+
throw new Error(`Extracted package directory not found at ${packageDir}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 3. Scan all extracted files for unapproved PostHog keys
|
|
62
|
+
const unauthorizedKeys = [];
|
|
63
|
+
|
|
64
|
+
walkDirectory(packageDir, (filePath) => {
|
|
65
|
+
// Only check text / script / metadata files
|
|
66
|
+
try {
|
|
67
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
68
|
+
const matches = content.match(PH_KEY_PATTERN);
|
|
69
|
+
if (matches) {
|
|
70
|
+
for (const match of matches) {
|
|
71
|
+
if (!ALLOWED_KEYS.has(match)) {
|
|
72
|
+
const relPath = path.relative(packageDir, filePath);
|
|
73
|
+
unauthorizedKeys.push({ key: match, file: relPath });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
// Skip binary files that cannot be decoded as utf-8
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
if (unauthorizedKeys.length > 0) {
|
|
83
|
+
console.error("Tarball PostHog key check FAILED: Unauthorized keys found in package:");
|
|
84
|
+
for (const item of unauthorizedKeys) {
|
|
85
|
+
console.error(` - Key "${item.key}" in file "${item.file}"`);
|
|
86
|
+
}
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log("Tarball key check passed: No unauthorized PostHog keys found in package.");
|
|
91
|
+
process.exit(0);
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.error(`Tarball key check error: ${err instanceof Error ? err.message : String(err)}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
} finally {
|
|
96
|
+
if (tarballPath && fs.existsSync(tarballPath)) {
|
|
97
|
+
try {
|
|
98
|
+
fs.rmSync(tarballPath, { force: true });
|
|
99
|
+
} catch {}
|
|
100
|
+
}
|
|
101
|
+
if (extractDir && fs.existsSync(extractDir)) {
|
|
102
|
+
try {
|
|
103
|
+
fs.rmSync(extractDir, { recursive: true, force: true });
|
|
104
|
+
} catch {}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { z } from "zod/v4";
|
|
6
|
+
import { AislopConfigSchema } from "../dist/index.js";
|
|
7
|
+
|
|
8
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const outPath = resolve(here, "..", "schema", "raigal.config.schema.json");
|
|
10
|
+
|
|
11
|
+
// Strip `required`/`additionalProperties:false` so partial user configs (every
|
|
12
|
+
// key has a default) validate, and allow the loader-handled `extends` key.
|
|
13
|
+
const relax = (node) => {
|
|
14
|
+
if (!node || typeof node !== "object") return;
|
|
15
|
+
if (Array.isArray(node)) {
|
|
16
|
+
for (const item of node) relax(item);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
delete node.required;
|
|
20
|
+
if (node.additionalProperties === false) delete node.additionalProperties;
|
|
21
|
+
for (const value of Object.values(node)) relax(value);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const jsonSchema = z.toJSONSchema(AislopConfigSchema, { target: "draft-2020-12" });
|
|
25
|
+
relax(jsonSchema);
|
|
26
|
+
jsonSchema.$id = "https://raigal.dev/schema/raigal.config.schema.json";
|
|
27
|
+
jsonSchema.title = "raigal configuration (.raigal/config.yml)";
|
|
28
|
+
jsonSchema.description = "Configuration schema for the raigal code-quality CLI.";
|
|
29
|
+
jsonSchema.properties.extends = {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "Path to a parent .raigal config to extend.",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
writeFileSync(outPath, `${JSON.stringify(jsonSchema, null, "\t")}\n`);
|
|
35
|
+
process.stdout.write(`Wrote ${outPath}\n`);
|