mcp-garry-codes 1.0.0

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.
Files changed (177) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -0
  3. package/dist/config/env.d.ts +4 -0
  4. package/dist/config/env.d.ts.map +1 -0
  5. package/dist/config/env.js +27 -0
  6. package/dist/config/env.js.map +1 -0
  7. package/dist/config/schema.d.ts +28 -0
  8. package/dist/config/schema.d.ts.map +1 -0
  9. package/dist/config/schema.js +12 -0
  10. package/dist/config/schema.js.map +1 -0
  11. package/dist/contracts/common.d.ts +53 -0
  12. package/dist/contracts/common.d.ts.map +1 -0
  13. package/dist/contracts/common.js +30 -0
  14. package/dist/contracts/common.js.map +1 -0
  15. package/dist/contracts/linter_contracts.d.ts +79 -0
  16. package/dist/contracts/linter_contracts.d.ts.map +1 -0
  17. package/dist/contracts/linter_contracts.js +16 -0
  18. package/dist/contracts/linter_contracts.js.map +1 -0
  19. package/dist/contracts/scaffold_contracts.d.ts +84 -0
  20. package/dist/contracts/scaffold_contracts.d.ts.map +1 -0
  21. package/dist/contracts/scaffold_contracts.js +27 -0
  22. package/dist/contracts/scaffold_contracts.js.map +1 -0
  23. package/dist/contracts/security_contracts.d.ts +37 -0
  24. package/dist/contracts/security_contracts.d.ts.map +1 -0
  25. package/dist/contracts/security_contracts.js +6 -0
  26. package/dist/contracts/security_contracts.js.map +1 -0
  27. package/dist/contracts/test_contracts.d.ts +42 -0
  28. package/dist/contracts/test_contracts.d.ts.map +1 -0
  29. package/dist/contracts/test_contracts.js +10 -0
  30. package/dist/contracts/test_contracts.js.map +1 -0
  31. package/dist/contracts/wiki_contracts.d.ts +96 -0
  32. package/dist/contracts/wiki_contracts.d.ts.map +1 -0
  33. package/dist/contracts/wiki_contracts.js +17 -0
  34. package/dist/contracts/wiki_contracts.js.map +1 -0
  35. package/dist/core/cache.d.ts +9 -0
  36. package/dist/core/cache.d.ts.map +1 -0
  37. package/dist/core/cache.js +56 -0
  38. package/dist/core/cache.js.map +1 -0
  39. package/dist/core/errors.d.ts +37 -0
  40. package/dist/core/errors.d.ts.map +1 -0
  41. package/dist/core/errors.js +52 -0
  42. package/dist/core/errors.js.map +1 -0
  43. package/dist/core/file_utils.d.ts +13 -0
  44. package/dist/core/file_utils.d.ts.map +1 -0
  45. package/dist/core/file_utils.js +90 -0
  46. package/dist/core/file_utils.js.map +1 -0
  47. package/dist/core/logger.d.ts +18 -0
  48. package/dist/core/logger.d.ts.map +1 -0
  49. package/dist/core/logger.js +46 -0
  50. package/dist/core/logger.js.map +1 -0
  51. package/dist/core/process_runner.d.ts +15 -0
  52. package/dist/core/process_runner.d.ts.map +1 -0
  53. package/dist/core/process_runner.js +64 -0
  54. package/dist/core/process_runner.js.map +1 -0
  55. package/dist/domain/glua/ast.d.ts +12 -0
  56. package/dist/domain/glua/ast.d.ts.map +1 -0
  57. package/dist/domain/glua/ast.js +176 -0
  58. package/dist/domain/glua/ast.js.map +1 -0
  59. package/dist/domain/glua/lexer.d.ts +42 -0
  60. package/dist/domain/glua/lexer.d.ts.map +1 -0
  61. package/dist/domain/glua/lexer.js +232 -0
  62. package/dist/domain/glua/lexer.js.map +1 -0
  63. package/dist/domain/networking/net_auditor.d.ts +5 -0
  64. package/dist/domain/networking/net_auditor.d.ts.map +1 -0
  65. package/dist/domain/networking/net_auditor.js +188 -0
  66. package/dist/domain/networking/net_auditor.js.map +1 -0
  67. package/dist/domain/packaging/validator.d.ts +5 -0
  68. package/dist/domain/packaging/validator.d.ts.map +1 -0
  69. package/dist/domain/packaging/validator.js +121 -0
  70. package/dist/domain/packaging/validator.js.map +1 -0
  71. package/dist/domain/performance/hot_path_analyzer.d.ts +5 -0
  72. package/dist/domain/performance/hot_path_analyzer.d.ts.map +1 -0
  73. package/dist/domain/performance/hot_path_analyzer.js +127 -0
  74. package/dist/domain/performance/hot_path_analyzer.js.map +1 -0
  75. package/dist/domain/scaffolding/templates.d.ts +5 -0
  76. package/dist/domain/scaffolding/templates.d.ts.map +1 -0
  77. package/dist/domain/scaffolding/templates.js +219 -0
  78. package/dist/domain/scaffolding/templates.js.map +1 -0
  79. package/dist/domain/wiki/offline_db.d.ts +3 -0
  80. package/dist/domain/wiki/offline_db.d.ts.map +1 -0
  81. package/dist/domain/wiki/offline_db.js +534 -0
  82. package/dist/domain/wiki/offline_db.js.map +1 -0
  83. package/dist/domain/wiki/types.d.ts +29 -0
  84. package/dist/domain/wiki/types.d.ts.map +1 -0
  85. package/dist/domain/wiki/types.js +2 -0
  86. package/dist/domain/wiki/types.js.map +1 -0
  87. package/dist/index.d.ts +3 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +28 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/integrations/glualint_adapter.d.ts +6 -0
  92. package/dist/integrations/glualint_adapter.d.ts.map +1 -0
  93. package/dist/integrations/glualint_adapter.js +62 -0
  94. package/dist/integrations/glualint_adapter.js.map +1 -0
  95. package/dist/integrations/python_test_harness.d.ts +5 -0
  96. package/dist/integrations/python_test_harness.d.ts.map +1 -0
  97. package/dist/integrations/python_test_harness.js +67 -0
  98. package/dist/integrations/python_test_harness.js.map +1 -0
  99. package/dist/integrations/web_search_adapter.d.ts +6 -0
  100. package/dist/integrations/web_search_adapter.d.ts.map +1 -0
  101. package/dist/integrations/web_search_adapter.js +80 -0
  102. package/dist/integrations/web_search_adapter.js.map +1 -0
  103. package/dist/integrations/wiki_client.d.ts +13 -0
  104. package/dist/integrations/wiki_client.d.ts.map +1 -0
  105. package/dist/integrations/wiki_client.js +110 -0
  106. package/dist/integrations/wiki_client.js.map +1 -0
  107. package/dist/prompts/index.d.ts +73 -0
  108. package/dist/prompts/index.d.ts.map +1 -0
  109. package/dist/prompts/index.js +127 -0
  110. package/dist/prompts/index.js.map +1 -0
  111. package/dist/resources/realms_resources.d.ts +2 -0
  112. package/dist/resources/realms_resources.d.ts.map +1 -0
  113. package/dist/resources/realms_resources.js +30 -0
  114. package/dist/resources/realms_resources.js.map +1 -0
  115. package/dist/resources/schema_resources.d.ts +2 -0
  116. package/dist/resources/schema_resources.d.ts.map +1 -0
  117. package/dist/resources/schema_resources.js +43 -0
  118. package/dist/resources/schema_resources.js.map +1 -0
  119. package/dist/resources/wiki_resources.d.ts +3 -0
  120. package/dist/resources/wiki_resources.d.ts.map +1 -0
  121. package/dist/resources/wiki_resources.js +31 -0
  122. package/dist/resources/wiki_resources.js.map +1 -0
  123. package/dist/security/path_guard.d.ts +8 -0
  124. package/dist/security/path_guard.d.ts.map +1 -0
  125. package/dist/security/path_guard.js +37 -0
  126. package/dist/security/path_guard.js.map +1 -0
  127. package/dist/security/process_guard.d.ts +8 -0
  128. package/dist/security/process_guard.d.ts.map +1 -0
  129. package/dist/security/process_guard.js +26 -0
  130. package/dist/security/process_guard.js.map +1 -0
  131. package/dist/security/redaction.d.ts +6 -0
  132. package/dist/security/redaction.d.ts.map +1 -0
  133. package/dist/security/redaction.js +49 -0
  134. package/dist/security/redaction.js.map +1 -0
  135. package/dist/server.d.ts +3 -0
  136. package/dist/server.d.ts.map +1 -0
  137. package/dist/server.js +364 -0
  138. package/dist/server.js.map +1 -0
  139. package/dist/tools/analyze_performance.d.ts +4 -0
  140. package/dist/tools/analyze_performance.d.ts.map +1 -0
  141. package/dist/tools/analyze_performance.js +20 -0
  142. package/dist/tools/analyze_performance.js.map +1 -0
  143. package/dist/tools/audit_net_security.d.ts +4 -0
  144. package/dist/tools/audit_net_security.d.ts.map +1 -0
  145. package/dist/tools/audit_net_security.js +19 -0
  146. package/dist/tools/audit_net_security.js.map +1 -0
  147. package/dist/tools/get_gmod_symbol.d.ts +4 -0
  148. package/dist/tools/get_gmod_symbol.d.ts.map +1 -0
  149. package/dist/tools/get_gmod_symbol.js +30 -0
  150. package/dist/tools/get_gmod_symbol.js.map +1 -0
  151. package/dist/tools/lint_glua.d.ts +4 -0
  152. package/dist/tools/lint_glua.d.ts.map +1 -0
  153. package/dist/tools/lint_glua.js +24 -0
  154. package/dist/tools/lint_glua.js.map +1 -0
  155. package/dist/tools/package_and_validate.d.ts +4 -0
  156. package/dist/tools/package_and_validate.d.ts.map +1 -0
  157. package/dist/tools/package_and_validate.js +23 -0
  158. package/dist/tools/package_and_validate.js.map +1 -0
  159. package/dist/tools/run_glua_test.d.ts +4 -0
  160. package/dist/tools/run_glua_test.d.ts.map +1 -0
  161. package/dist/tools/run_glua_test.js +15 -0
  162. package/dist/tools/run_glua_test.js.map +1 -0
  163. package/dist/tools/scaffold_gmod_component.d.ts +4 -0
  164. package/dist/tools/scaffold_gmod_component.d.ts.map +1 -0
  165. package/dist/tools/scaffold_gmod_component.js +16 -0
  166. package/dist/tools/scaffold_gmod_component.js.map +1 -0
  167. package/dist/tools/search_gmod_wiki.d.ts +4 -0
  168. package/dist/tools/search_gmod_wiki.d.ts.map +1 -0
  169. package/dist/tools/search_gmod_wiki.js +30 -0
  170. package/dist/tools/search_gmod_wiki.js.map +1 -0
  171. package/dist/tools/web_search_glua.d.ts +4 -0
  172. package/dist/tools/web_search_glua.d.ts.map +1 -0
  173. package/dist/tools/web_search_glua.js +15 -0
  174. package/dist/tools/web_search_glua.js.map +1 -0
  175. package/package.json +58 -0
  176. package/src/test_harness/lua/glua_mock.lua +383 -0
  177. package/src/test_harness/python/gmod_tester.py +156 -0
@@ -0,0 +1,188 @@
1
+ import { FileUtils } from "../../core/file_utils.js";
2
+ import { GLuaAnalyzer } from "../glua/ast.js";
3
+ export class NetAuditor {
4
+ static async audit(addonDir, specificFiles) {
5
+ const filesToScan = specificFiles
6
+ ? specificFiles.map(f => ({ path: f, relativePath: f, sizeBytes: 0 }))
7
+ : await FileUtils.listLuaFiles(addonDir);
8
+ const registeredStrings = new Set();
9
+ const netStarts = [];
10
+ const netReceives = [];
11
+ const vulnerabilities = [];
12
+ // Step 1: Scan all files to collect registered strings and messages
13
+ for (const file of filesToScan) {
14
+ let content = "";
15
+ try {
16
+ content = await FileUtils.readFile(file.path);
17
+ }
18
+ catch {
19
+ continue;
20
+ }
21
+ const realm = GLuaAnalyzer.detectRealmFromPath(file.relativePath);
22
+ const lines = content.split("\n");
23
+ for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
24
+ const lineNum = lineIdx + 1;
25
+ const line = lines[lineIdx];
26
+ // util.AddNetworkString("...")
27
+ const addNetMatch = line.match(/util\.AddNetworkString\s*\(\s*["']([^"']+)["']\s*\)/);
28
+ if (addNetMatch) {
29
+ registeredStrings.add(addNetMatch[1]);
30
+ if (realm === "client") {
31
+ vulnerabilities.push({
32
+ severity: "critical",
33
+ type: "missing_network_string",
34
+ message_name: addNetMatch[1],
35
+ file: file.relativePath,
36
+ line: lineNum,
37
+ description: `util.AddNetworkString('${addNetMatch[1]}') is called on the CLIENT. This causes a fatal Lua error.`,
38
+ recommended_fix: "Move util.AddNetworkString calls to server-only scripts (e.g. init.lua or inside if SERVER then).",
39
+ vulnerable_code: line.trim()
40
+ });
41
+ }
42
+ }
43
+ // net.Start("...")
44
+ const startMatch = line.match(/net\.Start\s*\(\s*["']([^"']+)["']/);
45
+ if (startMatch) {
46
+ netStarts.push({
47
+ name: startMatch[1],
48
+ file: file.relativePath,
49
+ line: lineNum,
50
+ realm
51
+ });
52
+ }
53
+ // net.Receive("...", function(len, ply) ... )
54
+ const receiveMatch = line.match(/net\.Receive\s*\(\s*["']([^"']+)["']\s*,\s*function\s*\(([^)]*)\)/);
55
+ if (receiveMatch) {
56
+ const msgName = receiveMatch[1];
57
+ const funcParams = receiveMatch[2].split(",").map(p => p.trim());
58
+ const plyParam = funcParams[1] ?? "ply";
59
+ // Grab the body of the function (approx next 30 lines)
60
+ const bodyLines = lines.slice(lineIdx, Math.min(lines.length, lineIdx + 35));
61
+ const bodySnippet = bodyLines.join("\n");
62
+ // Check for auth verification
63
+ const hasAuthCheck = bodySnippet.includes(`${plyParam}:IsAdmin()`) ||
64
+ bodySnippet.includes(`${plyParam}:IsSuperAdmin()`) ||
65
+ bodySnippet.includes("CAMI.PlayerHasAccess") ||
66
+ bodySnippet.includes("ULib.ucl.query") ||
67
+ bodySnippet.includes("sam.ranks");
68
+ // Check data reads
69
+ const dataTypes = [];
70
+ if (bodySnippet.includes("net.ReadString"))
71
+ dataTypes.push("string");
72
+ if (bodySnippet.includes("net.ReadUInt") || bodySnippet.includes("net.ReadInt"))
73
+ dataTypes.push("number");
74
+ if (bodySnippet.includes("net.ReadEntity"))
75
+ dataTypes.push("entity");
76
+ if (bodySnippet.includes("net.ReadTable"))
77
+ dataTypes.push("table");
78
+ if (bodySnippet.includes("net.ReadBool") || bodySnippet.includes("net.ReadBit"))
79
+ dataTypes.push("boolean");
80
+ netReceives.push({
81
+ name: msgName,
82
+ file: file.relativePath,
83
+ line: lineNum,
84
+ realm,
85
+ codeSnippet: bodySnippet,
86
+ hasAuthCheck,
87
+ dataTypes
88
+ });
89
+ // Check for dangerous actions on server without auth
90
+ const isServerReceiver = realm === "server" || realm === "shared";
91
+ const hasDangerousAction = bodySnippet.includes(":SetUserGroup") ||
92
+ bodySnippet.includes(":Ban(") ||
93
+ bodySnippet.includes(":Kick(") ||
94
+ bodySnippet.includes("RunConsoleCommand") ||
95
+ bodySnippet.includes("sql.Query") ||
96
+ bodySnippet.includes("RunString") ||
97
+ bodySnippet.includes("SetDarkRPVar") ||
98
+ bodySnippet.includes("addMoney");
99
+ if (isServerReceiver && hasDangerousAction && !hasAuthCheck) {
100
+ vulnerabilities.push({
101
+ severity: "critical",
102
+ type: "unauthenticated_net_receiver",
103
+ message_name: msgName,
104
+ file: file.relativePath,
105
+ line: lineNum,
106
+ description: `net.Receive('${msgName}') performs state/administrative actions without checking '${plyParam}:IsAdmin()' or caller permissions. Any client can trigger this to exploit the server.`,
107
+ recommended_fix: `Add 'if not IsValid(${plyParam}) or not ${plyParam}:IsAdmin() then return end' at the beginning of the handler.`,
108
+ vulnerable_code: lines.slice(lineIdx, Math.min(lines.length, lineIdx + 5)).join("\n")
109
+ });
110
+ }
111
+ // Check for SQL injection: net.ReadString concatenated with sql.Query
112
+ if (bodySnippet.includes("net.ReadString") &&
113
+ /sql\.Query\s*\([^)]*\.\./.test(bodySnippet) &&
114
+ !bodySnippet.includes("sql.SQLStr")) {
115
+ vulnerabilities.push({
116
+ severity: "critical",
117
+ type: "sql_injection",
118
+ message_name: msgName,
119
+ file: file.relativePath,
120
+ line: lineNum,
121
+ description: `net.Receive('${msgName}') reads a string from the network and directly concatenates it into a sql.Query call without sql.SQLStr escaping.`,
122
+ recommended_fix: "Wrap all user-supplied strings with sql.SQLStr(str) before database queries.",
123
+ vulnerable_code: lines.slice(lineIdx, Math.min(lines.length, lineIdx + 6)).join("\n")
124
+ });
125
+ }
126
+ // Check for RunString exposure
127
+ if (bodySnippet.includes("net.ReadString") && bodySnippet.includes("RunString")) {
128
+ vulnerabilities.push({
129
+ severity: "critical",
130
+ type: "runstring_exposure",
131
+ message_name: msgName,
132
+ file: file.relativePath,
133
+ line: lineNum,
134
+ description: `net.Receive('${msgName}') passes an untrusted network string directly into RunString, allowing arbitrary Remote Code Execution (RCE).`,
135
+ recommended_fix: "Remove RunString. Use deterministic enumerations or structured commands instead.",
136
+ vulnerable_code: lines.slice(lineIdx, Math.min(lines.length, lineIdx + 5)).join("\n")
137
+ });
138
+ }
139
+ }
140
+ }
141
+ }
142
+ // Step 2: Cross-check missing network strings
143
+ for (const start of netStarts) {
144
+ if (!registeredStrings.has(start.name)) {
145
+ vulnerabilities.push({
146
+ severity: "high",
147
+ type: "missing_network_string",
148
+ message_name: start.name,
149
+ file: start.file,
150
+ line: start.line,
151
+ description: `net.Start('${start.name}') was called, but '${start.name}' was never registered with util.AddNetworkString in any scanned server file.`,
152
+ recommended_fix: `Add util.AddNetworkString("${start.name}") in your server initialization code.`,
153
+ vulnerable_code: `net.Start("${start.name}")`
154
+ });
155
+ }
156
+ }
157
+ // Step 3: Build Network Graph
158
+ const networkGraph = [];
159
+ const allMessageNames = new Set([
160
+ ...registeredStrings,
161
+ ...netStarts.map(s => s.name),
162
+ ...netReceives.map(r => r.name)
163
+ ]);
164
+ for (const msg of allMessageNames) {
165
+ const starts = netStarts.filter(s => s.name === msg);
166
+ const receives = netReceives.filter(r => r.name === msg);
167
+ const sentFrom = starts.length > 0 ? (starts[0].realm === "client" ? "client" : "server") : "unknown";
168
+ const receivedIn = receives.length > 0 ? (receives[0].realm === "client" ? "client" : "server") : "unknown";
169
+ const hasAuth = receives.every(r => r.hasAuthCheck);
170
+ const dataTypes = Array.from(new Set(receives.flatMap(r => r.dataTypes)));
171
+ networkGraph.push({
172
+ message: msg,
173
+ sent_from: sentFrom,
174
+ received_in: receivedIn,
175
+ has_auth_check: hasAuth,
176
+ registered_on_server: registeredStrings.has(msg),
177
+ data_types: dataTypes
178
+ });
179
+ }
180
+ return {
181
+ messages_found: allMessageNames.size,
182
+ registered_strings: Array.from(registeredStrings),
183
+ vulnerabilities,
184
+ network_graph: networkGraph
185
+ };
186
+ }
187
+ }
188
+ //# sourceMappingURL=net_auditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"net_auditor.js","sourceRoot":"","sources":["../../../src/domain/networking/net_auditor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { PackageAndValidateOutput } from "../../contracts/scaffold_contracts.js";
2
+ export declare class PackagingValidator {
3
+ static validateAndPackage(addonDir: string, buildGma?: boolean, outputGmaPath?: string): Promise<PackageAndValidateOutput>;
4
+ }
5
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/domain/packaging/validator.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,wBAAwB,EACzB,MAAM,uCAAuC,CAAC;AAsC/C,qBAAa,kBAAkB;WACT,kBAAkB,CACpC,QAAQ,EAAE,MAAM,EAChB,QAAQ,UAAQ,EAChB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,wBAAwB,CAAC;CAyFrC"}
@@ -0,0 +1,121 @@
1
+ import path from "node:path";
2
+ import { FileUtils } from "../../core/file_utils.js";
3
+ import { ProcessRunner } from "../../core/process_runner.js";
4
+ import { getConfig } from "../../config/env.js";
5
+ const GMA_ALLOWED_EXTENSIONS = new Set([
6
+ ".lua",
7
+ ".vmt",
8
+ ".vtf",
9
+ ".mdl",
10
+ ".vtx",
11
+ ".vvd",
12
+ ".phy",
13
+ ".wav",
14
+ ".mp3",
15
+ ".ogg",
16
+ ".png",
17
+ ".jpg",
18
+ ".jpeg",
19
+ ".json",
20
+ ".txt",
21
+ ".bsp",
22
+ ".nav",
23
+ ".ain",
24
+ ".pcf"
25
+ ]);
26
+ const VALID_ADDON_TYPES = new Set([
27
+ "ServerContent",
28
+ "gamemode",
29
+ "map",
30
+ "weapon",
31
+ "vehicle",
32
+ "npc",
33
+ "tool",
34
+ "effects",
35
+ "model"
36
+ ]);
37
+ export class PackagingValidator {
38
+ static async validateAndPackage(addonDir, buildGma = false, outputGmaPath) {
39
+ const warnings = [];
40
+ const illegalFiles = [];
41
+ // 1. Verify addon.json
42
+ let addonJsonValid = false;
43
+ const addonJsonPath = path.join(addonDir, "addon.json");
44
+ if (await FileUtils.fileExists(addonJsonPath)) {
45
+ try {
46
+ const rawJson = await FileUtils.readFile(addonJsonPath);
47
+ const parsed = JSON.parse(rawJson);
48
+ if (!parsed.title || typeof parsed.title !== "string") {
49
+ warnings.push("addon.json is missing a valid 'title' string.");
50
+ }
51
+ if (!parsed.type || !VALID_ADDON_TYPES.has(parsed.type)) {
52
+ warnings.push(`addon.json 'type' must be one of: ${Array.from(VALID_ADDON_TYPES).join(", ")}.`);
53
+ }
54
+ if (parsed.tags && (!Array.isArray(parsed.tags) || parsed.tags.length > 2)) {
55
+ warnings.push("addon.json 'tags' must be an array of maximum 2 tags.");
56
+ }
57
+ if (warnings.length === 0) {
58
+ addonJsonValid = true;
59
+ }
60
+ }
61
+ catch (err) {
62
+ warnings.push(`Failed to parse addon.json: ${String(err)}`);
63
+ }
64
+ }
65
+ else {
66
+ warnings.push("addon.json does not exist in the root of the addon directory.");
67
+ }
68
+ // 2. Scan all files for GMA compatibility
69
+ const allFiles = await FileUtils.listAllFiles(addonDir);
70
+ let totalSizeBytes = 0;
71
+ for (const file of allFiles) {
72
+ totalSizeBytes += file.sizeBytes;
73
+ const ext = path.extname(file.path).toLowerCase();
74
+ // Check if file is inside .git or .vscode or addon.json
75
+ if (file.relativePath.toLowerCase() === "addon.json") {
76
+ continue;
77
+ }
78
+ if (!GMA_ALLOWED_EXTENSIONS.has(ext)) {
79
+ illegalFiles.push({
80
+ path: file.relativePath,
81
+ reason: `Extension '${ext}' is rejected by Steam Workshop gmad compiler.`
82
+ });
83
+ }
84
+ // Check for uppercase characters in paths (causes Linux dedicated server 404s)
85
+ if (/[A-Z]/.test(file.relativePath)) {
86
+ warnings.push(`File path '${file.relativePath}' contains uppercase letters, which causes file-not-found issues on Linux servers.`);
87
+ }
88
+ }
89
+ // 3. Compile GMA if requested and gmad exists
90
+ let compiledGmaPath = undefined;
91
+ if (buildGma && illegalFiles.length === 0 && addonJsonValid) {
92
+ const gmadPath = getConfig().GMAD_PATH;
93
+ if (gmadPath) {
94
+ const destGma = outputGmaPath ?? path.join(addonDir, "out.gma");
95
+ try {
96
+ await ProcessRunner.execute(gmadPath, ["create", "-folder", addonDir, "-out", destGma], {
97
+ timeoutMs: 30000
98
+ });
99
+ compiledGmaPath = destGma;
100
+ }
101
+ catch (err) {
102
+ warnings.push(`Failed to compile GMA package: ${String(err)}`);
103
+ }
104
+ }
105
+ else {
106
+ warnings.push("GMAD_PATH environment variable is not configured; cannot compile .gma binary.");
107
+ }
108
+ }
109
+ const isValid = addonJsonValid && illegalFiles.length === 0;
110
+ return {
111
+ valid: isValid,
112
+ addon_json_valid: addonJsonValid,
113
+ total_files: allFiles.length,
114
+ total_size_bytes: totalSizeBytes,
115
+ illegal_files: illegalFiles,
116
+ warnings,
117
+ gma_path: compiledGmaPath
118
+ };
119
+ }
120
+ }
121
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.js","sourceRoot":"","sources":["../../../src/domain/packaging/validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { AnalyzePerformanceOutput } from "../../contracts/linter_contracts.js";
2
+ export declare class HotPathAnalyzer {
3
+ static analyze(code: string): AnalyzePerformanceOutput;
4
+ }
5
+ //# sourceMappingURL=hot_path_analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hot_path_analyzer.d.ts","sourceRoot":"","sources":["../../../src/domain/performance/hot_path_analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EAEzB,MAAM,qCAAqC,CAAC;AAgB7C,qBAAa,eAAe;WACZ,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,wBAAwB;CA+H9D"}
@@ -0,0 +1,127 @@
1
+ const HOT_PATH_HOOKS = [
2
+ "HUDPaint",
3
+ "HUDPaintBackground",
4
+ "RenderScreenspaceEffects",
5
+ "PostDrawTranslucentRenderables",
6
+ "PostDrawOpaqueRenderables",
7
+ "PostDrawViewModel",
8
+ "PreDrawViewModel",
9
+ "Think",
10
+ "Tick",
11
+ "Move",
12
+ "SetupMove"
13
+ ];
14
+ export class HotPathAnalyzer {
15
+ static analyze(code) {
16
+ const issues = [];
17
+ let allocationsInHotPath = 0;
18
+ let globalCallsInHotPath = 0;
19
+ let dynamicDrawCalls = 0;
20
+ const lines = code.split("\n");
21
+ let currentHook = null;
22
+ let hookScopeDepth = 0;
23
+ for (let i = 0; i < lines.length; i++) {
24
+ const lineNum = i + 1;
25
+ const line = lines[i];
26
+ // Detect entering a hot-path hook: hook.Add("HUDPaint", ... or function GM:HUDPaint() or ENT:Think()
27
+ for (const hook of HOT_PATH_HOOKS) {
28
+ const hookAddPattern = new RegExp(`hook\\.Add\\s*\\(\\s*["']${hook}["']`, "i");
29
+ const gmPattern = new RegExp(`function\\s+(?:GM|GAMEMODE):${hook}\\s*\\(`, "i");
30
+ const entPattern = new RegExp(`function\\s+(?:ENT|SWEP):${hook}\\s*\\(`, "i");
31
+ if (hookAddPattern.test(line) || gmPattern.test(line) || entPattern.test(line)) {
32
+ currentHook = hook;
33
+ hookScopeDepth = 1;
34
+ break;
35
+ }
36
+ }
37
+ if (currentHook) {
38
+ // 1. Color(...) table allocation
39
+ if (/Color\s*\(\s*\d+/.test(line)) {
40
+ allocationsInHotPath++;
41
+ issues.push({
42
+ hook_name: currentHook,
43
+ line: lineNum,
44
+ severity: "critical",
45
+ category: "allocation_in_render_hook",
46
+ description: `Calling Color(r,g,b,a) inside '${currentHook}' allocates a new Lua table every frame, triggering GC stutters.`,
47
+ recommended_code: `-- Define outside the hook once:\nlocal MY_COLOR = Color(255, 255, 255)\n\n-- Inside hook:\ndraw.SimpleText(..., MY_COLOR)`
48
+ });
49
+ }
50
+ // 2. Vector(...) or Angle(...) table allocation
51
+ if (/Vector\s*\(\s*[-0-9]/.test(line) || /Angle\s*\(\s*[-0-9]/.test(line)) {
52
+ allocationsInHotPath++;
53
+ issues.push({
54
+ hook_name: currentHook,
55
+ line: lineNum,
56
+ severity: "high",
57
+ category: "allocation_in_render_hook",
58
+ description: `Calling Vector() or Angle() inside '${currentHook}' allocates garbage every frame.`,
59
+ recommended_code: `-- Pre-allocate static vectors outside the hook or reuse an upvalue vector with vec:Set(x, y, z).`
60
+ });
61
+ }
62
+ // 3. Dynamic table creation {}
63
+ if (/=\s*\{/.test(line) && !line.includes("local") && !line.trim().startsWith("--")) {
64
+ allocationsInHotPath++;
65
+ issues.push({
66
+ hook_name: currentHook,
67
+ line: lineNum,
68
+ severity: "medium",
69
+ category: "allocation_in_render_hook",
70
+ description: `Instantiating dynamic table '{}' inside '${currentHook}'.`,
71
+ recommended_code: "Reuse a pre-allocated table and wipe it using table.Empty(tbl) if necessary."
72
+ });
73
+ }
74
+ // 4. Uncached surface.CreateFont
75
+ if (/surface\.CreateFont/.test(line)) {
76
+ dynamicDrawCalls++;
77
+ issues.push({
78
+ hook_name: currentHook,
79
+ line: lineNum,
80
+ severity: "critical",
81
+ category: "uncached_font",
82
+ description: `Calling surface.CreateFont inside '${currentHook}' creates a new Source Engine font handle every frame!`,
83
+ recommended_code: "Move surface.CreateFont to the top of your file outside any hooks."
84
+ });
85
+ }
86
+ // 5. Uncached Material(...) call
87
+ if (/Material\s*\(\s*["']/.test(line)) {
88
+ allocationsInHotPath++;
89
+ issues.push({
90
+ hook_name: currentHook,
91
+ line: lineNum,
92
+ severity: "high",
93
+ category: "uncached_material",
94
+ description: `Calling Material() inside '${currentHook}' queries the engine material cache repeatedly every frame.`,
95
+ recommended_code: `local MAT_ICON = Material("path/to/icon.png", "noclamp smooth")\n-- Inside hook:\nsurface.SetMaterial(MAT_ICON)`
96
+ });
97
+ }
98
+ // 6. Unlocalized standard globals (math.Clamp, math.floor)
99
+ if (/math\.(Clamp|floor|ceil|min|max|sin|cos|rad)/.test(line)) {
100
+ globalCallsInHotPath++;
101
+ }
102
+ // Track blocks to know when the hook ends
103
+ const openers = (line.match(/\b(function|then|do|repeat)\b/g) || []).length;
104
+ const closers = (line.match(/\bend\b/g) || []).length;
105
+ hookScopeDepth += (openers - closers);
106
+ if (hookScopeDepth <= 0) {
107
+ currentHook = null;
108
+ }
109
+ }
110
+ }
111
+ // Calculate score
112
+ const penalties = issues.filter(i => i.severity === "critical").length * 25 +
113
+ issues.filter(i => i.severity === "high").length * 15 +
114
+ issues.filter(i => i.severity === "medium").length * 5;
115
+ const score = Math.max(0, 100 - penalties);
116
+ return {
117
+ performance_score: score,
118
+ issues,
119
+ metrics: {
120
+ allocations_in_hot_path: allocationsInHotPath,
121
+ global_calls_in_hot_path: globalCallsInHotPath,
122
+ dynamic_draw_calls: dynamicDrawCalls
123
+ }
124
+ };
125
+ }
126
+ }
127
+ //# sourceMappingURL=hot_path_analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hot_path_analyzer.js","sourceRoot":"","sources":["../../../src/domain/performance/hot_path_analyzer.ts"],"names":[],"mappings":"AAKA,MAAM,cAAc,GAAG;IACrB,UAAU;IACV,oBAAoB;IACpB,0BAA0B;IAC1B,gCAAgC;IAChC,2BAA2B;IAC3B,mBAAmB;IACnB,kBAAkB;IAClB,OAAO;IACP,MAAM;IACN,MAAM;IACN,WAAW;CACZ,CAAC;AAEF,MAAM,OAAO,eAAe;IACnB,MAAM,CAAC,OAAO,CAAC,IAAY;QAChC,MAAM,MAAM,GAAuB,EAAE,CAAC;QACtC,IAAI,oBAAoB,GAAG,CAAC,CAAC;QAC7B,IAAI,oBAAoB,GAAG,CAAC,CAAC;QAC7B,IAAI,gBAAgB,GAAG,CAAC,CAAC;QAEzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAE/B,IAAI,WAAW,GAAkB,IAAI,CAAC;QACtC,IAAI,cAAc,GAAG,CAAC,CAAC;QAEvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAEtB,qGAAqG;YACrG,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;gBAClC,MAAM,cAAc,GAAG,IAAI,MAAM,CAAC,4BAA4B,IAAI,MAAM,EAAE,GAAG,CAAC,CAAC;gBAC/E,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,+BAA+B,IAAI,SAAS,EAAE,GAAG,CAAC,CAAC;gBAChF,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,4BAA4B,IAAI,SAAS,EAAE,GAAG,CAAC,CAAC;gBAE9E,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC/E,WAAW,GAAG,IAAI,CAAC;oBACnB,cAAc,GAAG,CAAC,CAAC;oBACnB,MAAM;gBACR,CAAC;YACH,CAAC;YAED,IAAI,WAAW,EAAE,CAAC;gBAChB,iCAAiC;gBACjC,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBAClC,oBAAoB,EAAE,CAAC;oBACvB,MAAM,CAAC,IAAI,CAAC;wBACV,SAAS,EAAE,WAAW;wBACtB,IAAI,EAAE,OAAO;wBACb,QAAQ,EAAE,UAAU;wBACpB,QAAQ,EAAE,2BAA2B;wBACrC,WAAW,EAAE,kCAAkC,WAAW,kEAAkE;wBAC5H,gBAAgB,EAAE,4HAA4H;qBAC/I,CAAC,CAAC;gBACL,CAAC;gBAED,gDAAgD;gBAChD,IAAI,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC1E,oBAAoB,EAAE,CAAC;oBACvB,MAAM,CAAC,IAAI,CAAC;wBACV,SAAS,EAAE,WAAW;wBACtB,IAAI,EAAE,OAAO;wBACb,QAAQ,EAAE,MAAM;wBAChB,QAAQ,EAAE,2BAA2B;wBACrC,WAAW,EAAE,uCAAuC,WAAW,kCAAkC;wBACjG,gBAAgB,EAAE,mGAAmG;qBACtH,CAAC,CAAC;gBACL,CAAC;gBAED,+BAA+B;gBAC/B,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;oBACpF,oBAAoB,EAAE,CAAC;oBACvB,MAAM,CAAC,IAAI,CAAC;wBACV,SAAS,EAAE,WAAW;wBACtB,IAAI,EAAE,OAAO;wBACb,QAAQ,EAAE,QAAQ;wBAClB,QAAQ,EAAE,2BAA2B;wBACrC,WAAW,EAAE,4CAA4C,WAAW,IAAI;wBACxE,gBAAgB,EAAE,8EAA8E;qBACjG,CAAC,CAAC;gBACL,CAAC;gBAED,iCAAiC;gBACjC,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBACrC,gBAAgB,EAAE,CAAC;oBACnB,MAAM,CAAC,IAAI,CAAC;wBACV,SAAS,EAAE,WAAW;wBACtB,IAAI,EAAE,OAAO;wBACb,QAAQ,EAAE,UAAU;wBACpB,QAAQ,EAAE,eAAe;wBACzB,WAAW,EAAE,sCAAsC,WAAW,wDAAwD;wBACtH,gBAAgB,EAAE,oEAAoE;qBACvF,CAAC,CAAC;gBACL,CAAC;gBAED,iCAAiC;gBACjC,IAAI,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBACtC,oBAAoB,EAAE,CAAC;oBACvB,MAAM,CAAC,IAAI,CAAC;wBACV,SAAS,EAAE,WAAW;wBACtB,IAAI,EAAE,OAAO;wBACb,QAAQ,EAAE,MAAM;wBAChB,QAAQ,EAAE,mBAAmB;wBAC7B,WAAW,EAAE,8BAA8B,WAAW,6DAA6D;wBACnH,gBAAgB,EAAE,iHAAiH;qBACpI,CAAC,CAAC;gBACL,CAAC;gBAED,2DAA2D;gBAC3D,IAAI,8CAA8C,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC9D,oBAAoB,EAAE,CAAC;gBACzB,CAAC;gBAED,0CAA0C;gBAC1C,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,gCAAgC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;gBAC5E,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;gBACtD,cAAc,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,CAAC;gBACtC,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;oBACxB,WAAW,GAAG,IAAI,CAAC;gBACrB,CAAC;YACH,CAAC;QACH,CAAC;QAED,kBAAkB;QAClB,MAAM,SAAS,GACb,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM,GAAG,EAAE;YACzD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM,GAAG,EAAE;YACrD,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;QAEzD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC,CAAC;QAE3C,OAAO;YACL,iBAAiB,EAAE,KAAK;YACxB,MAAM;YACN,OAAO,EAAE;gBACP,uBAAuB,EAAE,oBAAoB;gBAC7C,wBAAwB,EAAE,oBAAoB;gBAC9C,kBAAkB,EAAE,gBAAgB;aACrC;SACF,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,5 @@
1
+ import { ScaffoldGModComponentInput, ScaffoldGModComponentOutput } from "../../contracts/scaffold_contracts.js";
2
+ export declare class ScaffoldingService {
3
+ static scaffold(input: ScaffoldGModComponentInput, workspaceRoot?: string): Promise<ScaffoldGModComponentOutput>;
4
+ }
5
+ //# sourceMappingURL=templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/domain/scaffolding/templates.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,EAC5B,MAAM,uCAAuC,CAAC;AAI/C,qBAAa,kBAAkB;WACT,QAAQ,CAC1B,KAAK,EAAE,0BAA0B,EACjC,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,2BAA2B,CAAC;CA+NxC"}