0xray 4.0.34 → 4.0.36

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 (160) hide show
  1. package/.grok-plugin/plugin.json +1 -1
  2. package/.xray/codex.json +12 -4
  3. package/.xray/features.json +1 -1
  4. package/AGENTS-consumer.md +6 -6
  5. package/AGENTS.md +5 -5
  6. package/README.md +2 -2
  7. package/dist/AGENTS.md +5 -5
  8. package/dist/CHANGELOG.md +26 -0
  9. package/dist/README.md +2 -2
  10. package/dist/cli/index.js +2 -5
  11. package/dist/governance/governance-service.js +1 -1
  12. package/dist/integrations/base/README.md +1 -9
  13. package/dist/integrations/base/index.d.ts +0 -1
  14. package/dist/integrations/base/index.js +0 -2
  15. package/dist/integrations/governance/index.js +31 -8
  16. package/dist/integrations/grok/hooks/grok-hook-utils.js +1 -1
  17. package/dist/integrations/grok/hooks/session-start.js +12 -0
  18. package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
  19. package/dist/integrations/hooks/goggles-mcp.mjs +10 -3
  20. package/dist/integrations/hooks/goggles-pipeline.mjs +110 -23
  21. package/dist/integrations/hooks/host-truth.mjs +74 -0
  22. package/dist/integrations/hooks/station-hook-runtime.cjs +3 -0
  23. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  24. package/dist/mcps/knowledge-skills/storyteller.server.js +0 -1
  25. package/dist/nucleus/delegation-gate.d.ts +2 -2
  26. package/dist/nucleus/delegation-gate.js +13 -2
  27. package/dist/nucleus/work-freshness.mjs +189 -0
  28. package/dist/processors/implementations/agents-md-validation-processor.js +1 -1
  29. package/docs-site/docs/plates/boot.md +17 -2
  30. package/docs-site/docs/plates/governance.md +3 -2
  31. package/docs-site/docs/plates/index.md +3 -1
  32. package/docs-site/docs/plates/memory-recall.md +26 -35
  33. package/docs-site/docs/plates/orchestration.md +39 -35
  34. package/docs-site/docs/plates/processor.md +34 -36
  35. package/docs-site/docs/plates/reporting.md +3 -8
  36. package/docs-site/docs/plates/routing.md +22 -11
  37. package/package.json +3 -2
  38. package/scripts/foundry/release.mjs +48 -5
  39. package/scripts/mjs/run-grok-hook.mjs +50 -0
  40. package/scripts/mjs/test-consumer-readiness.mjs +1 -0
  41. package/scripts/mjs/test-mcp-functionality.mjs +14 -9
  42. package/scripts/node/basic-security-audit.cjs +0 -1
  43. package/scripts/node/bridge-mcp-wiring.cjs +66 -3
  44. package/scripts/node/install-bridges.cjs +3 -3
  45. package/scripts/node/postinstall.cjs +1 -1
  46. package/scripts/node/prepare-consumer.cjs +11 -7
  47. package/scripts/node/setup-dev.cjs +18 -26
  48. package/scripts/node/validate-mcp-connectivity.js +2 -1
  49. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
  50. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  51. package/src/mcps/knowledge-skills/storyteller.server.ts +0 -1
  52. package/xray/agents_template.md +5 -5
  53. package/xray/codex.json +12 -4
  54. package/xray/features.json +1 -1
  55. package/dist/agents/known-names.d.ts +0 -26
  56. package/dist/agents/known-names.js +0 -72
  57. package/dist/analytics/consent-manager.d.ts +0 -92
  58. package/dist/analytics/consent-manager.js +0 -257
  59. package/dist/benchmark/performance-benchmark.d.ts +0 -103
  60. package/dist/benchmark/performance-benchmark.js +0 -373
  61. package/dist/cli/commands/analytics-disable.d.ts +0 -9
  62. package/dist/cli/commands/analytics-disable.js +0 -72
  63. package/dist/cli/commands/analytics-enable-action.d.ts +0 -8
  64. package/dist/cli/commands/analytics-enable-action.js +0 -82
  65. package/dist/cli/commands/analytics-preview.d.ts +0 -9
  66. package/dist/cli/commands/analytics-preview.js +0 -106
  67. package/dist/cli/commands/analytics-status.d.ts +0 -9
  68. package/dist/cli/commands/analytics-status.js +0 -68
  69. package/dist/cli/commands/enforce.d.ts +0 -12
  70. package/dist/cli/commands/enforce.js +0 -78
  71. package/dist/cli/commands/govern.d.ts +0 -29
  72. package/dist/cli/commands/govern.js +0 -149
  73. package/dist/cli/commands/security-audit.d.ts +0 -23
  74. package/dist/cli/commands/security-audit.js +0 -219
  75. package/dist/cli/server.d.ts +0 -1
  76. package/dist/cli/server.js +0 -147
  77. package/dist/config/routing-mappings.d.ts +0 -26
  78. package/dist/config/routing-mappings.js +0 -151
  79. package/dist/core/bridge.mjs +0 -922
  80. package/dist/core/trace-context.d.ts +0 -12
  81. package/dist/core/trace-context.js +0 -33
  82. package/dist/enforcement/index.d.ts +0 -22
  83. package/dist/enforcement/index.js +0 -40
  84. package/dist/enforcement/loaders/index.d.ts +0 -36
  85. package/dist/enforcement/loaders/index.js +0 -39
  86. package/dist/enforcement/test-auto-healing.d.ts +0 -83
  87. package/dist/enforcement/test-auto-healing.js +0 -325
  88. package/dist/inference/index.d.ts +0 -10
  89. package/dist/inference/index.js +0 -5
  90. package/dist/infrastructure/iac-validator.d.ts +0 -150
  91. package/dist/infrastructure/iac-validator.js +0 -465
  92. package/dist/infrastructure/schemas/cloud-schemas.d.ts +0 -482
  93. package/dist/infrastructure/schemas/cloud-schemas.js +0 -488
  94. package/dist/integrations/base/ExampleIntegration.d.ts +0 -46
  95. package/dist/integrations/base/ExampleIntegration.js +0 -180
  96. package/dist/integrations/enforcement-gate.d.ts +0 -44
  97. package/dist/integrations/enforcement-gate.js +0 -348
  98. package/dist/mcps/connection/connection-manager.d.ts +0 -39
  99. package/dist/mcps/connection/connection-manager.js +0 -79
  100. package/dist/mcps/index.d.ts +0 -17
  101. package/dist/mcps/index.js +0 -65
  102. package/dist/mcps/orchestrator/index.d.ts +0 -6
  103. package/dist/mcps/orchestrator/index.js +0 -5
  104. package/dist/metrics/index.d.ts +0 -6
  105. package/dist/metrics/index.js +0 -6
  106. package/dist/orchestrator/self-direction-activation.d.ts +0 -108
  107. package/dist/orchestrator/self-direction-activation.js +0 -244
  108. package/dist/orchestrator/universal-registry-bridge.d.ts +0 -84
  109. package/dist/orchestrator/universal-registry-bridge.js +0 -246
  110. package/dist/performance/performance-budget-enforcer.d.ts +0 -157
  111. package/dist/performance/performance-budget-enforcer.js +0 -433
  112. package/dist/performance/performance-regression-tester.d.ts +0 -107
  113. package/dist/performance/performance-regression-tester.js +0 -341
  114. package/dist/postprocessor/analysis/CodeChangeAnalyzer.d.ts +0 -11
  115. package/dist/postprocessor/analysis/CodeChangeAnalyzer.js +0 -50
  116. package/dist/postprocessor/config/ProcessorConfigLoader.d.ts +0 -44
  117. package/dist/postprocessor/config/ProcessorConfigLoader.js +0 -16
  118. package/dist/postprocessor/integration.d.ts +0 -156
  119. package/dist/postprocessor/integration.js +0 -194
  120. package/dist/postprocessor/metamorphosis/index.d.ts +0 -2
  121. package/dist/postprocessor/metamorphosis/index.js +0 -1
  122. package/dist/postprocessor/redeploy/RetryHandler.d.ts +0 -49
  123. package/dist/postprocessor/redeploy/RetryHandler.js +0 -118
  124. package/dist/processors/implementations/session-capture-processor.d.ts +0 -14
  125. package/dist/processors/implementations/session-capture-processor.js +0 -37
  126. package/dist/public/XrayService.d.ts +0 -17
  127. package/dist/public/XrayService.js +0 -40
  128. package/dist/public/index.d.ts +0 -4
  129. package/dist/public/index.js +0 -2
  130. package/dist/security/comprehensive-security-audit.d.ts +0 -157
  131. package/dist/security/comprehensive-security-audit.js +0 -1004
  132. package/dist/security/index.d.ts +0 -13
  133. package/dist/security/index.js +0 -13
  134. package/dist/security/security-agent-coordinator.d.ts +0 -71
  135. package/dist/security/security-agent-coordinator.js +0 -203
  136. package/dist/security/security-hardening-system.d.ts +0 -238
  137. package/dist/security/security-hardening-system.js +0 -726
  138. package/dist/security/security-orchestration-layer.d.ts +0 -118
  139. package/dist/security/security-orchestration-layer.js +0 -495
  140. package/dist/testing/memory-regression-suite.d.ts +0 -105
  141. package/dist/testing/memory-regression-suite.js +0 -258
  142. package/dist/tools/assess-complexity-tool.d.ts +0 -28
  143. package/dist/tools/assess-complexity-tool.js +0 -44
  144. package/dist/tools/query-routing-tool.d.ts +0 -17
  145. package/dist/tools/query-routing-tool.js +0 -59
  146. package/dist/utils/batch-operations.d.ts +0 -25
  147. package/dist/utils/batch-operations.js +0 -292
  148. package/dist/utils/task-graph.d.ts +0 -29
  149. package/dist/utils/task-graph.js +0 -106
  150. package/dist/utils/test-template-generator.d.ts +0 -25
  151. package/dist/utils/test-template-generator.js +0 -178
  152. package/dist/utils/token-manager.d.ts +0 -48
  153. package/dist/utils/token-manager.js +0 -163
  154. package/dist/validation/agent-config-validator.d.ts +0 -110
  155. package/dist/validation/agent-config-validator.js +0 -280
  156. package/dist/validation/orchestration-flow-validator.d.ts +0 -87
  157. package/dist/validation/orchestration-flow-validator.js +0 -606
  158. package/src/mcps/connection/connection-manager.ts +0 -91
  159. package/src/mcps/index.ts +0 -80
  160. package/src/mcps/orchestrator/index.ts +0 -29
@@ -1,922 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * 0xRay Universal Bridge
5
- *
6
- * Standalone bridge that provides 0xRay framework capabilities
7
- * to ANY consumer — no OpenCode dependency.
8
- *
9
- * Supports three transport modes:
10
- * 1. Stdin/Stdout (default): JSON pipe protocol
11
- * 2. Positional args: node bridge.mjs <command> [--json '{}'] [--cwd /path]
12
- * 3. HTTP server: node bridge.mjs --http --port 18431 [--cwd /path]
13
- *
14
- * Commands:
15
- * health Framework health check
16
- * codex-check Check code against codex rules
17
- * get-codex-prompt Get formatted codex terms for system prompt injection
18
- * get-config Get full framework configuration as JSON
19
- * validate Run validation on files
20
- * pre-process Run quality gate + pre-processors
21
- * post-process Run post-processors
22
- * stats Bridge/framework statistics
23
- * hooks Manage git hooks (install/uninstall/list/status)
24
- *
25
- * Usage (stdin):
26
- * echo '{"command":"health"}' | node bridge.mjs --cwd /path
27
- *
28
- * Usage (positional):
29
- * node bridge.mjs health --cwd /path
30
- * node bridge.mjs get-codex-prompt --cwd /path --json '{"severityFilter":["blocking"]}'
31
- *
32
- * Usage (HTTP):
33
- * node bridge.mjs --http --port 18431 --cwd /path
34
- * curl -X POST http://localhost:18431 -d '{"command":"get-codex-prompt"}'
35
- *
36
- * @since 2026-03-28
37
- */
38
-
39
- import {
40
- existsSync,
41
- readFileSync,
42
- writeFileSync,
43
- appendFileSync,
44
- mkdirSync,
45
- symlinkSync,
46
- unlinkSync,
47
- renameSync,
48
- lstatSync,
49
- } from "fs";
50
- import { join, dirname, resolve, relative } from "path";
51
- import { fileURLToPath } from "url";
52
- import { homedir } from "os";
53
- import { createServer } from "http";
54
- import { findProjectRoot } from "../../scripts/helpers/find-project-root.mjs";
55
-
56
- const __dirname = dirname(fileURLToPath(import.meta.url));
57
-
58
- // ============================================================================
59
- // Framework Components (lazy-loaded)
60
- // ============================================================================
61
-
62
- let QualityGateModule = null;
63
- let CodexFormatterModule = null;
64
- let frameworkReady = false;
65
- let frameworkLoadAttempted = false;
66
-
67
- function resolveBridgeProjectRoot() {
68
- return findProjectRoot([
69
- join(dirname(__dirname), "..", ".."),
70
- join(dirname(__dirname), "..", "..", ".."),
71
- ]);
72
- }
73
-
74
- // ============================================================================
75
- // Log Directory
76
- // ============================================================================
77
-
78
- function ensureLogDir(projectRoot) {
79
- const logDir = join(projectRoot, "logs", "framework");
80
- if (!existsSync(logDir)) {
81
- mkdirSync(logDir, { recursive: true });
82
- }
83
- return logDir;
84
- }
85
-
86
- function logToActivity(logDir, message) {
87
- try {
88
- const activityPath = join(logDir, "activity.log");
89
- const timestamp = new Date().toISOString();
90
- appendFileSync(activityPath, `${timestamp} [bridge] ${message}\n`, "utf-8");
91
- } catch {
92
- // Silent fail — never break the consumer over logging
93
- }
94
- }
95
-
96
- // ============================================================================
97
- // Framework Loading
98
- // ============================================================================
99
-
100
- async function loadFramework(projectRoot) {
101
- if (frameworkReady) return true;
102
- if (frameworkLoadAttempted) return false;
103
- frameworkLoadAttempted = true;
104
-
105
- const distDirs = [
106
- join(projectRoot, "dist"),
107
- join(projectRoot, "node_modules", "0xray", "dist"),
108
- ];
109
-
110
- for (const distDir of distDirs) {
111
- try {
112
- // Codex formatter (standalone, no deps)
113
- if (!CodexFormatterModule) {
114
- const cfPath = join(distDir, "core", "codex-formatter.js");
115
- if (!existsSync(cfPath)) {
116
- // dist/ not available for codex formatter — will use built-in fallback
117
- }
118
- if (existsSync(cfPath)) {
119
- const mod = await import(cfPath);
120
- CodexFormatterModule = mod;
121
- }
122
- }
123
-
124
- // Quality gate (standalone, no deps)
125
- if (!QualityGateModule) {
126
- const qgPath = join(distDir, "plugin", "quality-gate.js");
127
- if (existsSync(qgPath)) {
128
- const mod = await import(qgPath);
129
- QualityGateModule = mod;
130
- }
131
- }
132
-
133
- frameworkReady = true;
134
- return true;
135
- } catch {
136
- continue;
137
- }
138
- }
139
- return false;
140
- }
141
-
142
- // ============================================================================
143
- // Built-in Codex Fallback (when dist/ is not available)
144
- // NOTE: The canonical source is codex-formatter.ts BUILTIN_CODEX.
145
- // This is a minimal inline copy for bridge standalone mode only.
146
- // ============================================================================
147
-
148
- const BRIDGE_CODEX_FALLBACK = {
149
- version: "fallback-1.0.0",
150
- terms: [
151
- { id: "resolve-all-errors", title: "Resolve All Errors", description: "Never leave unhandled errors, rejected promises, or uncaught exceptions in production code.", severity: "blocking" },
152
- { id: "tests-required", title: "Tests Required", description: "Every new function, method, or module must have corresponding test coverage.", severity: "blocking" },
153
- { id: "no-console-in-production", title: "No Console in Production", description: "Use structured logging instead of console.log, console.warn, or console.error.", severity: "blocking" },
154
- { id: "type-safety", title: "Type Safety", description: "Prefer explicit types over 'any'. Use TypeScript strict mode features.", severity: "high" },
155
- { id: "input-validation", title: "Input Validation", description: "Validate all external inputs at the system boundary.", severity: "high" },
156
- ],
157
- };
158
-
159
- // ============================================================================
160
- // Command Handlers
161
- // ============================================================================
162
-
163
- async function handleHealth(input) {
164
- const projectRoot = resolveBridgeProjectRoot();
165
- const loaded = await loadFramework(projectRoot);
166
-
167
- const pkgPath = join(projectRoot, "package.json");
168
- let version = "unknown";
169
- if (existsSync(pkgPath)) {
170
- try {
171
- version = JSON.parse(readFileSync(pkgPath, "utf-8")).version;
172
- } catch {}
173
- }
174
-
175
- return {
176
- status: "ok",
177
- framework: loaded ? "loaded" : "not_loaded",
178
- version,
179
- projectRoot,
180
- components: {
181
- codexFormatter: !!CodexFormatterModule,
182
- qualityGate: !!QualityGateModule,
183
- },
184
- nodeVersion: process.version,
185
- };
186
- }
187
-
188
- // ============================================================================
189
- // Filesystem Codex Loader (bridge-native, no dist/ dependency)
190
- // ============================================================================
191
-
192
- /**
193
- * Load codex.json from the standard priority chain.
194
- * Same logic as codex-formatter.ts but inlined so bridge works standalone.
195
- */
196
- /** Codex candidate paths — single source of truth for both loadCodexFromFs and handleGetConfig */
197
- function getCodexCandidates(projectRoot) {
198
- const envDir = process.env.XRAY_CONFIG_DIR;
199
- const candidates = [];
200
- if (envDir) candidates.push(join(projectRoot, envDir, "codex.json"));
201
- candidates.push(join(projectRoot, ".xray", "codex.json"));
202
- candidates.push(join(projectRoot, ".opencode", "xray", "codex.json"));
203
- candidates.push(join(projectRoot, "codex.json"));
204
- return candidates;
205
- }
206
-
207
- function loadCodexFromFs(projectRoot) {
208
- const candidates = getCodexCandidates(projectRoot);
209
-
210
- for (const candidate of candidates) {
211
- try {
212
- if (existsSync(candidate)) {
213
- const raw = readFileSync(candidate, "utf-8");
214
- const parsed = JSON.parse(raw);
215
- if (parsed && parsed.version && Array.isArray(parsed.terms)) {
216
- return { codex: parsed, source: candidate };
217
- }
218
- }
219
- } catch {
220
- continue;
221
- }
222
- }
223
-
224
- return { codex: null, source: null };
225
- }
226
-
227
- async function handleGetCodexPrompt(input, projectRoot, logDir) {
228
- const { severityFilter, includeExamples, maxTerms, compressed } = input;
229
- logToActivity(logDir, `get-codex-prompt: severity=${severityFilter || "all"} compressed=${!!compressed}`);
230
-
231
- let prompt, termCount, totalTerms, version, source, charCount;
232
-
233
- if (CodexFormatterModule && CodexFormatterModule.formatCodexPrompt) {
234
- const result = CodexFormatterModule.formatCodexPrompt({
235
- projectRoot,
236
- severityFilter,
237
- includeExamples,
238
- maxTerms,
239
- compressed,
240
- });
241
- prompt = result.prompt;
242
- termCount = result.termCount;
243
- totalTerms = result.totalTerms;
244
- version = result.version;
245
- source = result.configPath;
246
- charCount = result.charCount;
247
- } else {
248
- // Try filesystem first, fall back to built-in
249
- const { codex, source: fsSource } = loadCodexFromFs(projectRoot);
250
- const termsSource = codex || BRIDGE_CODEX_FALLBACK;
251
-
252
- let terms = termsSource.terms;
253
- if (severityFilter) {
254
- terms = terms.filter((t) => severityFilter.includes(t.severity));
255
- }
256
- const severityOrder = { blocking: 0, high: 1, medium: 2 };
257
- terms.sort((a, b) => (severityOrder[a.severity] || 3) - (severityOrder[b.severity] || 3));
258
- if (maxTerms) terms = terms.slice(0, maxTerms);
259
-
260
- const lines = [`## 0xRay Universal Development Codex v${termsSource.version}`];
261
- const emojis = { blocking: "🔴", high: "🟡", medium: "🟢" };
262
- const labels = { blocking: "BLOCKING", high: "HIGH PRIORITY", medium: "MEDIUM" };
263
-
264
- for (const term of terms) {
265
- lines.push(`\n**${emojis[term.severity] || "⚪"} ${term.id}** [${labels[term.severity] || term.severity}]`);
266
- if (!compressed || term.severity !== "medium") {
267
- lines.push(` ${term.description}`);
268
- }
269
- }
270
-
271
- prompt = lines.join("\n");
272
- termCount = terms.length;
273
- totalTerms = termsSource.terms.length;
274
- version = termsSource.version;
275
- source = fsSource;
276
- charCount = prompt.length;
277
- }
278
-
279
- return {
280
- status: "ok",
281
- prompt,
282
- termCount,
283
- totalTerms,
284
- version,
285
- source,
286
- charCount,
287
- via: CodexFormatterModule ? "framework" : (source ? "filesystem" : "builtin"),
288
- };
289
- }
290
-
291
- async function handleGetConfig(input, projectRoot, logDir) {
292
- logToActivity(logDir, "get-config");
293
- const envDir = process.env.XRAY_CONFIG_DIR;
294
-
295
- const result = {
296
- status: "ok",
297
- projectRoot,
298
- nodeVersion: process.version,
299
- };
300
-
301
- // Load codex.json
302
- const codexPaths = getCodexCandidates(projectRoot);
303
-
304
- let codexPath = null;
305
- for (const p of codexPaths) {
306
- if (existsSync(p)) { codexPath = p; break; }
307
- }
308
-
309
- if (codexPath) {
310
- try {
311
- const codex = JSON.parse(readFileSync(codexPath, "utf-8"));
312
- result.codex = {
313
- path: codexPath,
314
- version: codex.version,
315
- termCount: codex.terms?.length || 0,
316
- categories: Object.keys(codex.categories || {}),
317
- };
318
- } catch {
319
- result.codex = { path: codexPath, error: "failed to parse" };
320
- }
321
- } else {
322
- result.codex = { path: null, note: "using built-in fallback codex" };
323
- }
324
-
325
- // Load features.json
326
- const featurePaths = [];
327
- if (envDir) featurePaths.push(join(projectRoot, envDir, "features.json"));
328
- featurePaths.push(join(projectRoot, ".xray", "features.json"));
329
- featurePaths.push(join(projectRoot, ".opencode", "xray", "features.json"));
330
-
331
- let featurePath = null;
332
- for (const p of featurePaths) {
333
- if (existsSync(p)) { featurePath = p; break; }
334
- }
335
-
336
- if (featurePath) {
337
- try {
338
- result.features = JSON.parse(readFileSync(featurePath, "utf-8"));
339
- } catch {
340
- result.features = { path: featurePath, error: "failed to parse" };
341
- }
342
- } else {
343
- result.features = { path: null, note: "no features.json found, using defaults" };
344
- }
345
-
346
- return result;
347
- }
348
-
349
- async function handleStats() {
350
- return {
351
- frameworkReady,
352
- codexFormatterAvailable: !!CodexFormatterModule,
353
- qualityGateAvailable: !!QualityGateModule,
354
- nodeVersion: process.version,
355
- projectRoot: resolveBridgeProjectRoot(),
356
- };
357
- }
358
-
359
- // ============================================================================
360
- // Quality Gate Helper (bridge-native)
361
- // ============================================================================
362
-
363
- /**
364
- * Run quality gate check using the loaded QualityGateModule.
365
- * Falls back to { passed: true } when module is not available.
366
- */
367
- async function runQualityGateCheck(context, projectRoot, logDir) {
368
- if (!QualityGateModule || !QualityGateModule.runQualityGate) {
369
- return { passed: true, violations: [], note: "quality-gate module not available" };
370
- }
371
-
372
- try {
373
- const result = await QualityGateModule.runQualityGate(context);
374
- return {
375
- passed: result.passed,
376
- violations: result.violations,
377
- checks: result.checks,
378
- };
379
- } catch (e) {
380
- logToActivity(logDir, `quality-gate error: ${e.message}`);
381
- return { passed: true, violations: [], error: e.message };
382
- }
383
- }
384
-
385
- // ============================================================================
386
- // Additional Command Handlers
387
- // ============================================================================
388
-
389
- /**
390
- * Validate one or more files using quality gate checks.
391
- */
392
- async function handleValidate(input, projectRoot, logDir) {
393
- const { files, operation } = input;
394
- logToActivity(logDir, `validate: files=${JSON.stringify(files || [])} operation=${operation}`);
395
-
396
- const fileArray = Array.isArray(files) ? files : (files ? [files] : []);
397
- if (fileArray.length === 0) {
398
- return { error: "validate requires a 'files' array" };
399
- }
400
-
401
- const results = [];
402
- for (const filePath of fileArray) {
403
- const qualityResult = await runQualityGateCheck(
404
- { tool: "write", args: { filePath } },
405
- projectRoot,
406
- logDir,
407
- );
408
- results.push({ file: filePath, ...qualityResult });
409
- }
410
-
411
- const allPassed = results.every((r) => r.passed);
412
- return {
413
- passed: allPassed,
414
- operation: operation || "validate",
415
- fileResults: results,
416
- };
417
- }
418
-
419
- /**
420
- * Check code against codex rules — quality gate + optional deep enforcement.
421
- */
422
- async function handleCodexCheck(input, projectRoot, logDir) {
423
- const { code, focusAreas, operation } = input;
424
- const codeLen = code?.length || 0;
425
- logToActivity(logDir, `codex-check: code_length=${codeLen} focus=${focusAreas} operation=${operation}`);
426
-
427
- const allViolations = [];
428
- const allChecks = [];
429
- let enforcerRan = false;
430
-
431
- // Phase 1: Quality gate (fast meta-checks + basic patterns)
432
- const qualityResult = await runQualityGateCheck(
433
- { tool: "write", args: { content: code } },
434
- projectRoot,
435
- logDir,
436
- );
437
-
438
- if (qualityResult.checks) {
439
- allChecks.push(...qualityResult.checks);
440
- }
441
- if (qualityResult.violations?.length) {
442
- allViolations.push(...qualityResult.violations);
443
- }
444
-
445
- // Phase 2: Enforcement validators (deep code analysis — security, quality, architecture)
446
- // Only run content-analysis validators — skip project-level validators that
447
- // require full context (docs, tests, CI, package.json) and always fail on snippets.
448
- const SNIPPET_SAFE_RULES = new Set([
449
- "security-by-design",
450
- "input-validation",
451
- "clean-debug-logs",
452
- "console-log-usage",
453
- "no-duplicate-code",
454
- "loop-safety",
455
- "no-over-engineering",
456
- "single-responsibility",
457
- "error-resolution",
458
- "module-system-consistency",
459
- ]);
460
-
461
- // Try to load ValidatorRegistry from enforcement/index.js
462
- let enforcerValidators = null;
463
- if (codeLen > 0) {
464
- const distDirs = [
465
- join(projectRoot, "dist"),
466
- join(projectRoot, "node_modules", "0xray", "dist"),
467
- ];
468
-
469
- for (const distDir of distDirs) {
470
- try {
471
- const rePath = join(distDir, "enforcement", "index.js");
472
- if (existsSync(rePath)) {
473
- const reModule = await import(rePath);
474
- const ValidatorRegistry = reModule.ValidatorRegistry;
475
- if (ValidatorRegistry) {
476
- enforcerValidators = new ValidatorRegistry();
477
- }
478
- }
479
- } catch (e) {
480
- // Skip — enforcer not available
481
- }
482
- if (enforcerValidators) break;
483
- }
484
- }
485
-
486
- if (enforcerValidators && codeLen > 0) {
487
- try {
488
- const ctx = { operation: "write", newCode: code, files: [] };
489
- const validators = enforcerValidators.getAllValidators();
490
- let enforcerViolations = 0;
491
-
492
- for (const v of validators) {
493
- if (!SNIPPET_SAFE_RULES.has(v.ruleId)) {
494
- if (focusAreas && focusAreas !== "all" && Array.isArray(focusAreas)) {
495
- if (focusAreas.includes(v.category)) {
496
- // Fall through to validate
497
- } else {
498
- continue;
499
- }
500
- } else {
501
- continue;
502
- }
503
- }
504
-
505
- try {
506
- const result = await v.validate(ctx);
507
- if (!result.passed) {
508
- enforcerViolations++;
509
- allViolations.push({
510
- id: v.ruleId,
511
- severity: v.severity || "error",
512
- message: result.message,
513
- suggestions: result.suggestions,
514
- });
515
- allChecks.push({ id: v.ruleId, passed: false, message: result.message });
516
- }
517
- } catch {
518
- // Skip broken validators gracefully
519
- }
520
- }
521
-
522
- enforcerRan = true;
523
- logToActivity(logDir, `codex-check: Validators ran against ${validators.length} rules (${SNIPPET_SAFE_RULES.size} snippet-safe), ${enforcerViolations} violations`);
524
- } catch (e) {
525
- logToActivity(logDir, `codex-check: Validator error: ${e.message}`);
526
- }
527
- } else if (!enforcerValidators) {
528
- logToActivity(logDir, `codex-check: Validators not available, quality gate only`);
529
- }
530
-
531
- const passed = allViolations.length === 0;
532
-
533
- return {
534
- passed,
535
- violations: allViolations,
536
- checks: allChecks,
537
- focusAreas: focusAreas || "all",
538
- enforcerRan,
539
- };
540
- }
541
-
542
- /**
543
- * Run quality gate check on a tool+args before execution.
544
- */
545
- async function handlePreProcess(input, projectRoot, logDir) {
546
- const { tool, args } = input;
547
- const startTime = Date.now();
548
-
549
- logToActivity(logDir, `pre-process: tool=${tool}`);
550
-
551
- // Quality gate check
552
- const qualityResult = await runQualityGateCheck(
553
- { tool, args: args || {} },
554
- projectRoot,
555
- logDir,
556
- );
557
-
558
- const duration = Date.now() - startTime;
559
- logToActivity(logDir, `pre-process: complete duration=${duration}ms quality=${qualityResult.passed}`);
560
-
561
- return {
562
- passed: qualityResult.passed,
563
- duration,
564
- qualityGate: qualityResult,
565
- };
566
- }
567
-
568
- /**
569
- * Post-process stub — ProcessorManager not available in standalone bridge.
570
- */
571
- async function handlePostProcess(input, projectRoot, logDir) {
572
- logToActivity(logDir, `post-process: stub (no ProcessorManager)`);
573
- return { ran: false, reason: "post-processors not available in standalone bridge" };
574
- }
575
-
576
- /**
577
- * Manage git hooks (install, uninstall, list, status).
578
- * Pure filesystem operations — no framework deps needed.
579
- */
580
- function handleHooks(input, projectRoot) {
581
- const { action, hooks } = input;
582
- const hookTypes = hooks || ["pre-commit", "post-commit", "pre-push", "post-push"];
583
- const gitHooksDir = join(projectRoot, ".git", "hooks");
584
- const xrayHooksDir = join(projectRoot, "hooks");
585
-
586
- if (!existsSync(gitHooksDir)) {
587
- return { error: "Not a git repository — no .git/hooks directory" };
588
- }
589
-
590
- const result = { managed: [], missing: [], external: [], stale: [] };
591
-
592
- // ── list / status ───────────────────────────────────────
593
- if (action === "list" || action === "status") {
594
- for (const hookName of hookTypes) {
595
- const gitHook = join(gitHooksDir, hookName);
596
- const xrayHook = join(xrayHooksDir, hookName);
597
-
598
- if (!existsSync(gitHook)) {
599
- result.missing.push(hookName);
600
- } else {
601
- try {
602
- const content = readFileSync(gitHook, "utf-8");
603
- if (content.includes("0xRay") || content.includes("xray") || content.includes("run-hook.js")) {
604
- result.managed.push(hookName);
605
- } else {
606
- result.external.push(hookName);
607
- }
608
- } catch {
609
- result.external.push(hookName);
610
- }
611
- }
612
-
613
- // Check if xray source hook exists
614
- if (!existsSync(xrayHook)) {
615
- result.stale.push(hookName);
616
- }
617
- }
618
-
619
- return {
620
- status: "ok",
621
- action,
622
- hooks: result,
623
- gitHooksDir,
624
- xrayHooksDir,
625
- };
626
- }
627
-
628
- // ── install ─────────────────────────────────────────────
629
- if (action === "install") {
630
- const installed = [];
631
- const skipped = [];
632
- const errors = [];
633
-
634
- for (const hookName of hookTypes) {
635
- const src = join(xrayHooksDir, hookName);
636
- const dst = join(gitHooksDir, hookName);
637
-
638
- if (!existsSync(src)) {
639
- skipped.push(hookName);
640
- continue;
641
- }
642
-
643
- try {
644
- // Backup existing non-xray hooks
645
- if (existsSync(dst)) {
646
- const content = readFileSync(dst, "utf-8");
647
- if (!content.includes("0xRay") && !content.includes("xray") && !content.includes("run-hook.js")) {
648
- renameSync(dst, `${dst}.xray-backup`);
649
- } else {
650
- unlinkSync(dst);
651
- }
652
- }
653
-
654
- // Create symlink
655
- const rel = relative(join(gitHooksDir), src);
656
- try {
657
- symlinkSync(rel, dst);
658
- } catch {
659
- // Symlink may fail (permissions, cross-device) — copy instead
660
- const srcContent = readFileSync(src, "utf-8");
661
- writeFileSync(dst, srcContent, { mode: 0o755 });
662
- }
663
- installed.push(hookName);
664
- } catch (err) {
665
- errors.push({ hook: hookName, error: err.message });
666
- }
667
- }
668
-
669
- return { status: "ok", action: "install", installed, skipped, errors };
670
- }
671
-
672
- // ── uninstall ───────────────────────────────────────────
673
- if (action === "uninstall") {
674
- const removed = [];
675
- const restored = [];
676
-
677
- for (const hookName of hookTypes) {
678
- const dst = join(gitHooksDir, hookName);
679
- const backup = `${dst}.xray-backup`;
680
-
681
- if (!existsSync(dst)) continue;
682
-
683
- try {
684
- const content = readFileSync(dst, "utf-8");
685
- const isXray = content.includes("0xRay") || content.includes("xray") || content.includes("run-hook.js");
686
-
687
- if (isXray || lstatSync(dst).isSymbolicLink()) {
688
- unlinkSync(dst);
689
-
690
- // Restore backup if exists
691
- if (existsSync(backup)) {
692
- renameSync(backup, dst);
693
- restored.push(hookName);
694
- } else {
695
- removed.push(hookName);
696
- }
697
- }
698
- } catch {
699
- // Skip unremovable hooks
700
- }
701
- }
702
-
703
- return { status: "ok", action: "uninstall", removed, restored };
704
- }
705
-
706
- return { error: `Unknown hooks action: ${action}. Use: install, uninstall, list, status` };
707
- }
708
-
709
- // ============================================================================
710
- // Known Commands
711
- // ============================================================================
712
-
713
- const KNOWN_COMMANDS = new Set([
714
- "health", "stats", "get-codex-prompt", "get-config", "validate",
715
- "codex-check", "pre-process", "post-process", "hooks",
716
- ]);
717
-
718
- // ============================================================================
719
- // HTTP Server Mode
720
- // ============================================================================
721
-
722
- function startHttpServer(port, projectRoot) {
723
- const logDir = ensureLogDir(projectRoot);
724
-
725
- return new Promise((resolve, reject) => {
726
- const server = createServer(async (req, res) => {
727
- // CORS headers (wildcard — restrict via XRAY_HTTP_CORS_ORIGIN env var in production)
728
- const corsOrigin = process.env.XRAY_HTTP_CORS_ORIGIN || "*";
729
- res.setHeader("Content-Type", "application/json");
730
- res.setHeader("Access-Control-Allow-Origin", corsOrigin);
731
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
732
- res.setHeader("Access-Control-Allow-Headers", "Content-Type");
733
-
734
- if (req.method === "OPTIONS") {
735
- res.writeHead(200);
736
- res.end();
737
- return;
738
- }
739
-
740
- if (req.method !== "POST" && req.method !== "GET") {
741
- res.writeHead(405);
742
- res.end(JSON.stringify({ error: "Method not allowed. Use POST." }));
743
- return;
744
- }
745
-
746
- // GET /health and GET /stats convenience endpoints
747
- if (req.method === "GET") {
748
- const url = new URL(req.url, `http://localhost:${port}`);
749
- if (url.pathname === "/health") {
750
- const result = await handleHealth({});
751
- res.writeHead(200);
752
- res.end(JSON.stringify(result));
753
- return;
754
- }
755
- if (url.pathname === "/stats") {
756
- const result = await handleStats();
757
- res.writeHead(200);
758
- res.end(JSON.stringify(result));
759
- return;
760
- }
761
- res.writeHead(404);
762
- res.end(JSON.stringify({ error: "Not found. POST / with JSON body." }));
763
- return;
764
- }
765
-
766
- // POST handler (1MB body size limit)
767
- const MAX_BODY_SIZE = 1024 * 1024;
768
- let body = "";
769
- let bodySize = 0;
770
- req.on("data", (chunk) => {
771
- bodySize += chunk.length;
772
- if (bodySize > MAX_BODY_SIZE) {
773
- res.writeHead(413);
774
- res.end(JSON.stringify({ error: "Request body too large (max 1MB)" }));
775
- req.destroy();
776
- return;
777
- }
778
- body += chunk;
779
- });
780
- req.on("end", async () => {
781
- try {
782
- const command = JSON.parse(body);
783
- const response = await dispatchCommand(command, projectRoot, logDir);
784
- const status = response.error ? 500 : 200;
785
- res.writeHead(status);
786
- res.end(JSON.stringify(response));
787
- } catch (err) {
788
- res.writeHead(400);
789
- res.end(JSON.stringify({ error: `Invalid JSON: ${err.message}` }));
790
- }
791
- });
792
- });
793
-
794
- server.on("error", (err) => {
795
- if (err.code === "EADDRINUSE") {
796
- reject(new Error(`Port ${port} already in use`));
797
- } else {
798
- reject(err);
799
- }
800
- });
801
-
802
- server.listen(port, () => {
803
- const timestamp = new Date().toISOString();
804
- logToActivity(logDir, `HTTP server started on port ${port}`);
805
- console.error(`[bridge] HTTP server listening on port ${port}`);
806
- resolve(server);
807
- });
808
- });
809
- }
810
-
811
- // ============================================================================
812
- // Command Dispatcher
813
- // ============================================================================
814
-
815
- async function dispatchCommand(command, projectRoot, logDir) {
816
- const cmd = command.command || "health";
817
-
818
- switch (cmd) {
819
- case "health":
820
- return await handleHealth(command);
821
- case "get-codex-prompt":
822
- return await handleGetCodexPrompt(command, projectRoot, logDir);
823
- case "get-config":
824
- return await handleGetConfig(command, projectRoot, logDir);
825
- case "validate":
826
- return await handleValidate(command, projectRoot, logDir);
827
- case "codex-check":
828
- return await handleCodexCheck(command, projectRoot, logDir);
829
- case "pre-process":
830
- return await handlePreProcess(command, projectRoot, logDir);
831
- case "post-process":
832
- return await handlePostProcess(command, projectRoot, logDir);
833
- case "hooks":
834
- return handleHooks(command, projectRoot);
835
- case "stats":
836
- return handleStats();
837
- default:
838
- return { error: `Unknown command: ${cmd}. Known: ${[...KNOWN_COMMANDS].join(", ")}` };
839
- }
840
- }
841
-
842
- // ============================================================================
843
- // Main
844
- // ============================================================================
845
-
846
- async function main() {
847
- const argv = process.argv.slice(2);
848
-
849
- // Parse flags
850
- let cwdOverride = null;
851
- let httpMode = false;
852
- let httpPort = 18431;
853
- let positionalCommand = null;
854
- let positionalPayload = null;
855
-
856
- for (let i = 0; i < argv.length; i++) {
857
- if (argv[i] === "--cwd" && argv[i + 1]) {
858
- cwdOverride = argv[i + 1];
859
- process.chdir(cwdOverride);
860
- i++;
861
- } else if (argv[i] === "--http") {
862
- httpMode = true;
863
- } else if (argv[i] === "--port" && argv[i + 1]) {
864
- httpPort = parseInt(argv[i + 1], 10);
865
- i++;
866
- } else if (argv[i] === "--json" && argv[i + 1]) {
867
- positionalPayload = argv[i + 1];
868
- i++;
869
- } else if (!argv[i].startsWith("-") && !positionalCommand && KNOWN_COMMANDS.has(argv[i])) {
870
- positionalCommand = argv[i];
871
- }
872
- }
873
-
874
- const projectRoot = resolveBridgeProjectRoot();
875
- const logDir = ensureLogDir(projectRoot);
876
-
877
- // Lazy-load framework
878
- if (!frameworkReady && !frameworkLoadAttempted) {
879
- await loadFramework(projectRoot);
880
- }
881
-
882
- // HTTP mode
883
- if (httpMode) {
884
- await startHttpServer(httpPort, projectRoot);
885
- return; // Server keeps running
886
- }
887
-
888
- // Stdin mode or positional mode
889
- let command;
890
-
891
- if (positionalCommand) {
892
- command = { command: positionalCommand };
893
- if (positionalPayload) {
894
- try {
895
- command = { ...command, ...JSON.parse(positionalPayload) };
896
- } catch {
897
- process.stdout.write(JSON.stringify({ error: "Invalid --json payload" }));
898
- process.exit(1);
899
- }
900
- }
901
- } else {
902
- // Stdin mode
903
- let input = "";
904
- for await (const chunk of process.stdin) {
905
- input += chunk;
906
- }
907
- try {
908
- command = JSON.parse(input);
909
- } catch {
910
- process.stdout.write(JSON.stringify({ error: "Invalid JSON input" }));
911
- process.exit(1);
912
- }
913
- }
914
-
915
- const response = await dispatchCommand(command, projectRoot, logDir);
916
- process.stdout.write(JSON.stringify(response));
917
- }
918
-
919
- main().catch((e) => {
920
- process.stdout.write(JSON.stringify({ error: e.message }));
921
- process.exit(1);
922
- });