0xray 4.0.33 → 4.0.35
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/.grok-plugin/plugin.json +1 -1
- package/.xray/codex.json +12 -4
- package/.xray/features.json +1 -1
- package/AGENTS-consumer.md +6 -6
- package/AGENTS.md +5 -5
- package/README.md +2 -2
- package/dist/AGENTS.md +5 -5
- package/dist/CHANGELOG.md +25 -0
- package/dist/README.md +2 -2
- package/dist/cli/index.js +2 -5
- package/dist/governance/governance-service.js +1 -1
- package/dist/integrations/base/README.md +1 -9
- package/dist/integrations/base/index.d.ts +0 -1
- package/dist/integrations/base/index.js +0 -2
- package/dist/integrations/governance/index.js +31 -8
- package/dist/integrations/grok/hooks/grok-hook-utils.js +1 -1
- package/dist/integrations/grok/hooks/session-start.js +12 -0
- package/dist/integrations/hooks/goggles-pipeline.mjs +108 -22
- package/dist/integrations/hooks/goggles-planes.json +23 -0
- package/dist/integrations/hooks/host-truth.mjs +74 -0
- package/dist/integrations/hooks/station-hook-runtime.cjs +3 -0
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/mcps/knowledge-skills/storyteller.server.js +0 -1
- package/dist/nucleus/delegation-gate.d.ts +2 -2
- package/dist/nucleus/delegation-gate.js +13 -2
- package/dist/nucleus/work-freshness.mjs +189 -0
- package/dist/processors/implementations/agents-md-validation-processor.js +1 -1
- package/docs-site/docs/plates/boot.md +17 -2
- package/docs-site/docs/plates/governance.md +3 -2
- package/docs-site/docs/plates/index.md +3 -1
- package/docs-site/docs/plates/memory-recall.md +26 -35
- package/docs-site/docs/plates/orchestration.md +39 -35
- package/docs-site/docs/plates/processor.md +34 -36
- package/docs-site/docs/plates/reporting.md +3 -8
- package/docs-site/docs/plates/routing.md +22 -11
- package/package.json +2 -2
- package/scripts/foundry/release.mjs +48 -5
- package/scripts/mjs/test-consumer-readiness.mjs +1 -0
- package/scripts/mjs/test-mcp-functionality.mjs +14 -9
- package/scripts/node/basic-security-audit.cjs +0 -1
- package/scripts/node/bridge-mcp-wiring.cjs +66 -3
- package/scripts/node/postinstall.cjs +1 -1
- package/scripts/node/prepare-consumer.cjs +11 -7
- package/scripts/node/setup-dev.cjs +18 -26
- package/scripts/node/validate-mcp-connectivity.js +2 -1
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/src/mcps/knowledge-skills/storyteller.server.ts +0 -1
- package/xray/agents_template.md +5 -5
- package/xray/codex.json +12 -4
- package/xray/features.json +1 -1
- package/dist/agents/known-names.d.ts +0 -26
- package/dist/agents/known-names.js +0 -72
- package/dist/analytics/consent-manager.d.ts +0 -92
- package/dist/analytics/consent-manager.js +0 -257
- package/dist/benchmark/performance-benchmark.d.ts +0 -103
- package/dist/benchmark/performance-benchmark.js +0 -373
- package/dist/cli/commands/analytics-disable.d.ts +0 -9
- package/dist/cli/commands/analytics-disable.js +0 -72
- package/dist/cli/commands/analytics-enable-action.d.ts +0 -8
- package/dist/cli/commands/analytics-enable-action.js +0 -82
- package/dist/cli/commands/analytics-preview.d.ts +0 -9
- package/dist/cli/commands/analytics-preview.js +0 -106
- package/dist/cli/commands/analytics-status.d.ts +0 -9
- package/dist/cli/commands/analytics-status.js +0 -68
- package/dist/cli/commands/enforce.d.ts +0 -12
- package/dist/cli/commands/enforce.js +0 -78
- package/dist/cli/commands/govern.d.ts +0 -29
- package/dist/cli/commands/govern.js +0 -149
- package/dist/cli/commands/security-audit.d.ts +0 -23
- package/dist/cli/commands/security-audit.js +0 -219
- package/dist/cli/server.d.ts +0 -1
- package/dist/cli/server.js +0 -147
- package/dist/config/routing-mappings.d.ts +0 -26
- package/dist/config/routing-mappings.js +0 -151
- package/dist/core/bridge.mjs +0 -922
- package/dist/core/trace-context.d.ts +0 -12
- package/dist/core/trace-context.js +0 -33
- package/dist/enforcement/index.d.ts +0 -22
- package/dist/enforcement/index.js +0 -40
- package/dist/enforcement/loaders/index.d.ts +0 -36
- package/dist/enforcement/loaders/index.js +0 -39
- package/dist/enforcement/test-auto-healing.d.ts +0 -83
- package/dist/enforcement/test-auto-healing.js +0 -325
- package/dist/inference/index.d.ts +0 -10
- package/dist/inference/index.js +0 -5
- package/dist/infrastructure/iac-validator.d.ts +0 -150
- package/dist/infrastructure/iac-validator.js +0 -465
- package/dist/infrastructure/schemas/cloud-schemas.d.ts +0 -482
- package/dist/infrastructure/schemas/cloud-schemas.js +0 -488
- package/dist/integrations/base/ExampleIntegration.d.ts +0 -46
- package/dist/integrations/base/ExampleIntegration.js +0 -180
- package/dist/integrations/enforcement-gate.d.ts +0 -44
- package/dist/integrations/enforcement-gate.js +0 -348
- package/dist/mcps/connection/connection-manager.d.ts +0 -39
- package/dist/mcps/connection/connection-manager.js +0 -79
- package/dist/mcps/index.d.ts +0 -17
- package/dist/mcps/index.js +0 -65
- package/dist/mcps/orchestrator/index.d.ts +0 -6
- package/dist/mcps/orchestrator/index.js +0 -5
- package/dist/metrics/index.d.ts +0 -6
- package/dist/metrics/index.js +0 -6
- package/dist/orchestrator/self-direction-activation.d.ts +0 -108
- package/dist/orchestrator/self-direction-activation.js +0 -244
- package/dist/orchestrator/universal-registry-bridge.d.ts +0 -84
- package/dist/orchestrator/universal-registry-bridge.js +0 -246
- package/dist/performance/performance-budget-enforcer.d.ts +0 -157
- package/dist/performance/performance-budget-enforcer.js +0 -433
- package/dist/performance/performance-regression-tester.d.ts +0 -107
- package/dist/performance/performance-regression-tester.js +0 -341
- package/dist/postprocessor/analysis/CodeChangeAnalyzer.d.ts +0 -11
- package/dist/postprocessor/analysis/CodeChangeAnalyzer.js +0 -50
- package/dist/postprocessor/config/ProcessorConfigLoader.d.ts +0 -44
- package/dist/postprocessor/config/ProcessorConfigLoader.js +0 -16
- package/dist/postprocessor/integration.d.ts +0 -156
- package/dist/postprocessor/integration.js +0 -194
- package/dist/postprocessor/metamorphosis/index.d.ts +0 -2
- package/dist/postprocessor/metamorphosis/index.js +0 -1
- package/dist/postprocessor/redeploy/RetryHandler.d.ts +0 -49
- package/dist/postprocessor/redeploy/RetryHandler.js +0 -118
- package/dist/processors/implementations/session-capture-processor.d.ts +0 -14
- package/dist/processors/implementations/session-capture-processor.js +0 -37
- package/dist/public/XrayService.d.ts +0 -17
- package/dist/public/XrayService.js +0 -40
- package/dist/public/index.d.ts +0 -4
- package/dist/public/index.js +0 -2
- package/dist/security/comprehensive-security-audit.d.ts +0 -157
- package/dist/security/comprehensive-security-audit.js +0 -1004
- package/dist/security/index.d.ts +0 -13
- package/dist/security/index.js +0 -13
- package/dist/security/security-agent-coordinator.d.ts +0 -71
- package/dist/security/security-agent-coordinator.js +0 -203
- package/dist/security/security-hardening-system.d.ts +0 -238
- package/dist/security/security-hardening-system.js +0 -726
- package/dist/security/security-orchestration-layer.d.ts +0 -118
- package/dist/security/security-orchestration-layer.js +0 -495
- package/dist/testing/memory-regression-suite.d.ts +0 -105
- package/dist/testing/memory-regression-suite.js +0 -258
- package/dist/tools/assess-complexity-tool.d.ts +0 -28
- package/dist/tools/assess-complexity-tool.js +0 -44
- package/dist/tools/query-routing-tool.d.ts +0 -17
- package/dist/tools/query-routing-tool.js +0 -59
- package/dist/utils/batch-operations.d.ts +0 -25
- package/dist/utils/batch-operations.js +0 -292
- package/dist/utils/task-graph.d.ts +0 -29
- package/dist/utils/task-graph.js +0 -106
- package/dist/utils/test-template-generator.d.ts +0 -25
- package/dist/utils/test-template-generator.js +0 -178
- package/dist/utils/token-manager.d.ts +0 -48
- package/dist/utils/token-manager.js +0 -163
- package/dist/validation/agent-config-validator.d.ts +0 -110
- package/dist/validation/agent-config-validator.js +0 -280
- package/dist/validation/orchestration-flow-validator.d.ts +0 -87
- package/dist/validation/orchestration-flow-validator.js +0 -606
- package/src/mcps/connection/connection-manager.ts +0 -91
- package/src/mcps/index.ts +0 -80
- package/src/mcps/orchestrator/index.ts +0 -29
package/dist/core/bridge.mjs
DELETED
|
@@ -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
|
-
});
|