0xray 4.0.35 → 4.0.37
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/features.json +1 -1
- package/dist/CHANGELOG.md +25 -0
- package/dist/analytics/emerging-pattern-detector.d.ts +12 -1
- package/dist/analytics/emerging-pattern-detector.js +37 -12
- package/dist/analytics/pattern-learning-engine.js +1 -9
- package/dist/analytics/predictive-analytics.js +51 -33
- package/dist/analytics/prompt-pattern-analyzer.d.ts +1 -0
- package/dist/analytics/prompt-pattern-analyzer.js +39 -20
- package/dist/analytics/routing-performance-analyzer.d.ts +2 -0
- package/dist/analytics/routing-performance-analyzer.js +89 -37
- package/dist/analytics/simple-pattern-analyzer.d.ts +4 -0
- package/dist/analytics/simple-pattern-analyzer.js +66 -28
- package/dist/architect/architect-tools.d.ts +3 -0
- package/dist/architect/architect-tools.js +22 -0
- package/dist/cli/commands/grok-install.js +1 -1
- package/dist/cli/commands/hermes-install.js +11 -5
- package/dist/cli/commands/openclaw-install.js +13 -7
- package/dist/cli/commands/opencode-install.js +7 -4
- package/dist/cli/commands/suit-monitor.d.ts +30 -0
- package/dist/cli/commands/suit-monitor.js +128 -0
- package/dist/cli/index.js +52 -15
- package/dist/cli/suit-pulse.d.ts +29 -0
- package/dist/cli/suit-pulse.js +270 -0
- package/dist/delegation/agent-delegator.d.ts +4 -2
- package/dist/delegation/agent-delegator.js +46 -27
- package/dist/delegation/agent-expertise.js +2 -1
- package/dist/delegation/analytics/outcome-tracker.d.ts +6 -0
- package/dist/delegation/analytics/outcome-tracker.js +17 -3
- package/dist/delegation/ast-code-parser.d.ts +5 -0
- package/dist/delegation/ast-code-parser.js +53 -48
- package/dist/delegation/codebase-context-analyzer.d.ts +3 -7
- package/dist/delegation/codebase-context-analyzer.js +188 -159
- package/dist/delegation/session-coordinator.js +3 -2
- package/dist/enforcement/loaders/codex-loader.js +18 -14
- package/dist/enforcement/validators/architecture-validators.js +115 -9
- package/dist/governance/codex-policy.service.d.ts +12 -2
- package/dist/governance/codex-policy.service.js +91 -16
- package/dist/integrations/grok/hooks/session-start.js +6 -6
- package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
- package/dist/integrations/hooks/goggles-mcp.mjs +10 -3
- package/dist/integrations/hooks/goggles-pipeline.mjs +221 -44
- package/dist/integrations/hooks/goggles-planes.json +172 -8
- package/dist/integrations/hooks/plates.cjs +138 -0
- package/dist/integrations/hooks/station-hook-runtime.cjs +63 -5
- package/dist/integrations/hooks/write-home.cjs +59 -0
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/mcps/architect-tools.server.d.ts +6 -0
- package/dist/mcps/architect-tools.server.js +19 -18
- package/dist/mcps/orchestrator/server.d.ts +4 -3
- package/dist/mcps/orchestrator/server.js +29 -17
- package/dist/memory-routing/lookup-cache.d.ts +8 -0
- package/dist/memory-routing/lookup-cache.js +72 -0
- package/dist/memory-routing/plates.d.ts +1 -1
- package/dist/memory-routing/plates.js +26 -0
- package/dist/memory-routing/provider-loader.js +3 -0
- package/dist/metrics/agent-metrics.d.ts +14 -5
- package/dist/metrics/agent-metrics.js +362 -224
- package/dist/nucleus/synthesis-consult-receipt.js +38 -16
- package/dist/orchestrator/enhanced-multi-agent-orchestrator.d.ts +5 -0
- package/dist/orchestrator/enhanced-multi-agent-orchestrator.js +9 -0
- package/dist/orchestrator/multi-agent-orchestration-coordinator.js +1 -2
- package/dist/orchestrator/orchestrator.js +1 -2
- package/dist/processors/implementations/agents-md-validation-processor.d.ts +0 -1
- package/dist/processors/implementations/agents-md-validation-processor.js +0 -11
- package/dist/processors/implementations/console-log-guard-processor.js +39 -50
- package/dist/processors/implementations/performance-budget-processor.d.ts +0 -5
- package/dist/processors/implementations/performance-budget-processor.js +29 -38
- package/dist/processors/implementations/storytelling-trigger-processor.d.ts +5 -0
- package/dist/processors/implementations/storytelling-trigger-processor.js +69 -16
- package/dist/processors/implementations/test-auto-creation-processor.js +39 -36
- package/dist/reporting/framework-reporting-system.d.ts +3 -0
- package/dist/reporting/framework-reporting-system.js +61 -8
- package/dist/reporting/log-parser.js +188 -105
- package/dist/reporting/metrics.js +42 -18
- package/dist/security/prompt-security-validator.js +8 -7
- package/dist/security/security-auditor.d.ts +4 -0
- package/dist/security/security-auditor.js +132 -57
- package/dist/security/security-hardener.d.ts +2 -0
- package/dist/security/security-hardener.js +31 -6
- package/dist/security/security-headers.d.ts +2 -0
- package/dist/security/security-headers.js +23 -21
- package/dist/security/security-scanner.js +18 -4
- package/dist/session/session-cleanup-manager.d.ts +2 -0
- package/dist/session/session-cleanup-manager.js +29 -8
- package/dist/session/session-monitor.d.ts +14 -4
- package/dist/session/session-monitor.js +208 -106
- package/dist/session/session-state-manager.d.ts +11 -0
- package/dist/session/session-state-manager.js +90 -8
- package/docs-site/docs/plates/activity-log.md +38 -0
- package/docs-site/docs/plates/cursor-compact.md +35 -0
- package/docs-site/docs/plates/grok-compact.md +35 -0
- package/docs-site/docs/plates/index.md +25 -3
- package/docs-site/docs/plates/inference-files.md +35 -0
- package/docs-site/docs/plates/kept-line.md +35 -0
- package/docs-site/docs/plates/lens-gate.md +35 -0
- package/docs-site/docs/plates/lens-page.md +35 -0
- package/docs-site/docs/plates/notes-page.md +35 -0
- package/docs-site/docs/plates/package-face.md +35 -0
- package/docs-site/docs/plates/payload-heat.md +35 -0
- package/docs-site/docs/plates/pickup-stamp.md +35 -0
- package/docs-site/docs/plates/pre-tool.md +35 -0
- package/docs-site/docs/plates/record-map.md +41 -0
- package/docs-site/docs/plates/reflection-page.md +35 -0
- package/docs-site/docs/plates/review.md +57 -0
- package/docs-site/docs/plates/session-capture.md +38 -0
- package/docs-site/docs/plates/site-manual.md +35 -0
- package/docs-site/docs/plates/stamp-plate.md +35 -0
- package/docs-site/docs/plates/station-card.md +35 -0
- package/docs-site/docs/plates/station-heat.md +35 -0
- package/docs-site/docs/plates/suit-organs.md +38 -0
- package/docs-site/docs/plates/suit-settings.md +35 -0
- package/docs-site/docs/plates/suit-wear.md +37 -0
- package/docs-site/docs/plates/trail-state.md +35 -0
- package/docs-site/docs/plates/work-fresh.md +35 -0
- package/docs-site/docs/plates/write-home.md +38 -0
- package/package.json +4 -2
- package/scripts/mjs/run-grok-hook.mjs +50 -0
- package/scripts/node/install-bridges.cjs +305 -81
- package/scripts/node/postinstall.cjs +14 -3
- package/src/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/src/mcps/architect-tools.server.ts +25 -20
- package/src/mcps/orchestrator/server.ts +33 -19
- package/xray/features.json +1 -1
- package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/index.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/index.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/mcp/server.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/paths.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/paths.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/confidence-decay.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/confidence-decay.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +0 -1
- package/vendor/@0xray/repertoire/dist/types.d.ts.map +0 -1
- package/vendor/@0xray/repertoire/dist/types.js.map +0 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Runs a Grok hook from dist, or from src when a rebuild has removed dist.
|
|
4
|
+
* The hook command stays on this file, which prebuild does not delete.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, realpathSync } from 'node:fs';
|
|
7
|
+
import { dirname, join } from 'node:path';
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
const root = join(here, '..', '..');
|
|
12
|
+
|
|
13
|
+
export function resolveGrokHook(packageRoot, name) {
|
|
14
|
+
const base = String(name || '');
|
|
15
|
+
if (!base || base.includes('/') || base.includes('..') || !base.endsWith('.js')) return '';
|
|
16
|
+
const candidates = [
|
|
17
|
+
join(packageRoot, 'dist', 'integrations', 'grok', 'hooks', base),
|
|
18
|
+
join(packageRoot, 'src', 'integrations', 'grok', 'hooks', base),
|
|
19
|
+
];
|
|
20
|
+
return candidates.find((file) => existsSync(file)) || '';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function allow() {
|
|
24
|
+
process.stdout.write('{"decision":"allow"}\n');
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function main() {
|
|
29
|
+
const name = process.argv[2];
|
|
30
|
+
const hit = resolveGrokHook(root, name);
|
|
31
|
+
if (!hit) allow();
|
|
32
|
+
const extra = process.argv.slice(3);
|
|
33
|
+
process.argv = [process.argv[0], hit, ...extra];
|
|
34
|
+
try {
|
|
35
|
+
await import(pathToFileURL(hit).href);
|
|
36
|
+
} catch {
|
|
37
|
+
allow();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function samePath(left, right) {
|
|
42
|
+
try {
|
|
43
|
+
return realpathSync(left) === realpathSync(right);
|
|
44
|
+
} catch {
|
|
45
|
+
return left === right;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const invoked = Boolean(process.argv[1]) && samePath(process.argv[1], fileURLToPath(import.meta.url));
|
|
50
|
+
if (invoked) await main();
|
|
@@ -35,6 +35,110 @@ const {
|
|
|
35
35
|
XRAY_MCP_SERVERS,
|
|
36
36
|
} = require("./bridge-mcp-wiring.cjs");
|
|
37
37
|
|
|
38
|
+
const wearIo = { writes: 0, skips: 0 };
|
|
39
|
+
|
|
40
|
+
function resetWearIo() {
|
|
41
|
+
wearIo.writes = 0;
|
|
42
|
+
wearIo.skips = 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function readWearIo() {
|
|
46
|
+
return { writes: wearIo.writes, skips: wearIo.skips };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function sameFileBytes(filePath, body) {
|
|
50
|
+
let current;
|
|
51
|
+
try {
|
|
52
|
+
current = fs.readFileSync(filePath);
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
const next = Buffer.isBuffer(body) ? body : Buffer.from(body);
|
|
57
|
+
return current.length === next.length && current.equals(next);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function writeFileIfChanged(filePath, body) {
|
|
61
|
+
if (sameFileBytes(filePath, body)) {
|
|
62
|
+
wearIo.skips += 1;
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
fs.writeFileSync(filePath, body);
|
|
66
|
+
wearIo.writes += 1;
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function fileMode(filePath) {
|
|
71
|
+
try {
|
|
72
|
+
return fs.statSync(filePath).mode & 0o777;
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Node copyFileSync/cpSync still copy mode when the bytes already match. */
|
|
79
|
+
function adoptSourceMode(src, dest) {
|
|
80
|
+
const srcMode = fileMode(src);
|
|
81
|
+
const destMode = fileMode(dest);
|
|
82
|
+
if (srcMode === null || destMode === null || srcMode === destMode) return false;
|
|
83
|
+
fs.chmodSync(dest, srcMode);
|
|
84
|
+
wearIo.writes += 1;
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function copyFileIfChanged(src, dest) {
|
|
89
|
+
let srcBytes;
|
|
90
|
+
try {
|
|
91
|
+
srcBytes = fs.readFileSync(src);
|
|
92
|
+
} catch {
|
|
93
|
+
fs.copyFileSync(src, dest);
|
|
94
|
+
wearIo.writes += 1;
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
if (sameFileBytes(dest, srcBytes)) {
|
|
98
|
+
if (adoptSourceMode(src, dest)) return true;
|
|
99
|
+
wearIo.skips += 1;
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
fs.copyFileSync(src, dest);
|
|
103
|
+
wearIo.writes += 1;
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** cpSync without rewriting files whose bytes already match. Extra dest files stay. */
|
|
108
|
+
function copyEntryIfChanged(src, dest) {
|
|
109
|
+
const srcStat = fs.lstatSync(src);
|
|
110
|
+
if (srcStat.isDirectory()) {
|
|
111
|
+
if (fs.existsSync(dest) && !fs.lstatSync(dest).isDirectory()) {
|
|
112
|
+
fs.rmSync(dest, { recursive: true, force: true });
|
|
113
|
+
}
|
|
114
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
115
|
+
for (const name of fs.readdirSync(src)) {
|
|
116
|
+
copyEntryIfChanged(path.join(src, name), path.join(dest, name));
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!srcStat.isFile()) {
|
|
121
|
+
fs.cpSync(src, dest, { force: true });
|
|
122
|
+
wearIo.writes += 1;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
copyFileIfChanged(src, dest);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function chmodIfNeeded(filePath, mode) {
|
|
129
|
+
try {
|
|
130
|
+
if ((fs.statSync(filePath).mode & 0o777) === mode) {
|
|
131
|
+
wearIo.skips += 1;
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
fs.chmodSync(filePath, mode);
|
|
135
|
+
wearIo.writes += 1;
|
|
136
|
+
return true;
|
|
137
|
+
} catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
38
142
|
const SKIP_DIRS = new Set(["node_modules", "logs"]);
|
|
39
143
|
const MERGE_FILES = new Set(["enforcer-config.json"]);
|
|
40
144
|
const KEEP_IF_EXISTS = new Set([".yml", ".yaml", ".md"]);
|
|
@@ -116,9 +220,11 @@ function syncBuiltinSkills(targetSkillsDir, packageRoot) {
|
|
|
116
220
|
const destMd = path.join(targetSkillsDir, entry.name, "SKILL.md");
|
|
117
221
|
const destDir = path.dirname(destMd);
|
|
118
222
|
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
119
|
-
if (fs.existsSync(destMd) && fs.statSync(skillMd).mtime <= fs.statSync(destMd).mtime)
|
|
120
|
-
|
|
121
|
-
|
|
223
|
+
if (fs.existsSync(destMd) && fs.statSync(skillMd).mtime <= fs.statSync(destMd).mtime) {
|
|
224
|
+
wearIo.skips += 1;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (copyFileIfChanged(skillMd, destMd)) copied++;
|
|
122
228
|
}
|
|
123
229
|
} catch {
|
|
124
230
|
// best-effort
|
|
@@ -146,17 +252,17 @@ function copyTree(src, dest, relPath = "") {
|
|
|
146
252
|
const srcData = JSON.parse(fs.readFileSync(srcPath, "utf8"));
|
|
147
253
|
if (fs.existsSync(destPath)) {
|
|
148
254
|
const destData = JSON.parse(fs.readFileSync(destPath, "utf8"));
|
|
149
|
-
|
|
255
|
+
writeFileIfChanged(destPath, JSON.stringify(deepMerge(srcData, destData), null, 2));
|
|
150
256
|
} else {
|
|
151
|
-
|
|
257
|
+
copyFileIfChanged(srcPath, destPath);
|
|
152
258
|
}
|
|
153
259
|
} catch {
|
|
154
|
-
|
|
260
|
+
copyFileIfChanged(srcPath, destPath);
|
|
155
261
|
}
|
|
156
262
|
} else if (KEEP_IF_EXISTS.has(path.extname(srcPath)) && fs.existsSync(destPath)) {
|
|
157
263
|
continue;
|
|
158
264
|
} else {
|
|
159
|
-
|
|
265
|
+
copyFileIfChanged(srcPath, destPath);
|
|
160
266
|
}
|
|
161
267
|
}
|
|
162
268
|
}
|
|
@@ -168,11 +274,13 @@ function copyPluginDir(src, dest) {
|
|
|
168
274
|
fs.rmSync(dest, { recursive: true, force: true });
|
|
169
275
|
}
|
|
170
276
|
fs.cpSync(src, dest, { recursive: true, force: true });
|
|
277
|
+
wearIo.writes += 1;
|
|
171
278
|
return true;
|
|
172
279
|
} catch (e) {
|
|
173
280
|
try {
|
|
174
281
|
fs.rmSync(dest, { recursive: true, force: true });
|
|
175
282
|
fs.cpSync(src, dest, { recursive: true, force: true });
|
|
283
|
+
wearIo.writes += 1;
|
|
176
284
|
return true;
|
|
177
285
|
} catch {
|
|
178
286
|
throw e;
|
|
@@ -182,9 +290,8 @@ function copyPluginDir(src, dest) {
|
|
|
182
290
|
|
|
183
291
|
function writePluginMcpJson(pluginDir, targetDir, log, label) {
|
|
184
292
|
if (!fs.existsSync(pluginDir)) return;
|
|
185
|
-
const { buildPluginMcpJson } = require("./bridge-mcp-wiring.cjs");
|
|
186
293
|
const dest = path.join(pluginDir, ".mcp.json");
|
|
187
|
-
|
|
294
|
+
writeFileIfChanged(dest, pluginMcpBody(targetDir));
|
|
188
295
|
log(label, "plugin .mcp.json patched for consumer", "info");
|
|
189
296
|
}
|
|
190
297
|
|
|
@@ -228,10 +335,10 @@ function mergeOpencodeJson(targetDir, packageRoot, log) {
|
|
|
228
335
|
}
|
|
229
336
|
|
|
230
337
|
function grokHookShellCommand(packageRoot, scriptName, extraArgs) {
|
|
231
|
-
const script = path.join(packageRoot, "
|
|
338
|
+
const script = path.join(packageRoot, "scripts", "mjs", "run-grok-hook.mjs");
|
|
232
339
|
const extra = extraArgs ? ` ${extraArgs}` : "";
|
|
233
340
|
// JSON.stringify quotes so spaces/$ in install paths still exec (Grok fail-opens on crash).
|
|
234
|
-
return `XRAY_AI_PATH=${JSON.stringify(packageRoot)} node ${JSON.stringify(script)}${extra}`;
|
|
341
|
+
return `XRAY_AI_PATH=${JSON.stringify(packageRoot)} node ${JSON.stringify(script)} ${scriptName}${extra}`;
|
|
235
342
|
}
|
|
236
343
|
|
|
237
344
|
function patchGrokHookEntry(hook, packageRoot, targetDir, scriptName, extraArgs) {
|
|
@@ -260,7 +367,7 @@ function ensureGrokHookEvent(hooks, eventName) {
|
|
|
260
367
|
function patchGrokHooks(pluginDir, packageRoot, targetDir, log, label) {
|
|
261
368
|
const hooksDir = path.join(pluginDir, "hooks");
|
|
262
369
|
const hooksPath = path.join(hooksDir, "hooks.json");
|
|
263
|
-
const hookScript = path.join(packageRoot, "
|
|
370
|
+
const hookScript = path.join(packageRoot, "scripts", "mjs", "run-grok-hook.mjs");
|
|
264
371
|
if (!fs.existsSync(hooksPath)) {
|
|
265
372
|
if (!fs.existsSync(hooksDir)) fs.mkdirSync(hooksDir, { recursive: true });
|
|
266
373
|
if (!fs.existsSync(hooksPath)) return;
|
|
@@ -269,33 +376,7 @@ function patchGrokHooks(pluginDir, packageRoot, targetDir, log, label) {
|
|
|
269
376
|
try {
|
|
270
377
|
const hooks = JSON.parse(fs.readFileSync(hooksPath, "utf8"));
|
|
271
378
|
if (!fs.existsSync(hookScript)) return;
|
|
272
|
-
|
|
273
|
-
patchGrokHookEntry(hooks.hooks?.SessionStart?.[0]?.hooks?.[0], packageRoot, targetDir, "session-start.js");
|
|
274
|
-
patchGrokHookEntry(
|
|
275
|
-
hooks.hooks?.UserPromptSubmit?.[0]?.hooks?.[0],
|
|
276
|
-
packageRoot,
|
|
277
|
-
targetDir,
|
|
278
|
-
"session-start.js",
|
|
279
|
-
"--hook-event=user_prompt_submit",
|
|
280
|
-
);
|
|
281
|
-
patchGrokHookEntry(hooks.hooks?.PostToolUse?.[0]?.hooks?.[0], packageRoot, targetDir, "post-tool-use.js");
|
|
282
|
-
ensureGrokHookEvent(hooks, "PreCompact");
|
|
283
|
-
ensureGrokHookEvent(hooks, "PostCompact");
|
|
284
|
-
patchGrokHookEntry(
|
|
285
|
-
hooks.hooks?.PreCompact?.[0]?.hooks?.[0],
|
|
286
|
-
packageRoot,
|
|
287
|
-
targetDir,
|
|
288
|
-
"session-start.js",
|
|
289
|
-
"--hook-event=pre_compact",
|
|
290
|
-
);
|
|
291
|
-
patchGrokHookEntry(
|
|
292
|
-
hooks.hooks?.PostCompact?.[0]?.hooks?.[0],
|
|
293
|
-
packageRoot,
|
|
294
|
-
targetDir,
|
|
295
|
-
"session-start.js",
|
|
296
|
-
"--hook-event=post_compact",
|
|
297
|
-
);
|
|
298
|
-
fs.writeFileSync(hooksPath, JSON.stringify(hooks, null, 2) + "\n");
|
|
379
|
+
writeFileIfChanged(hooksPath, patchedGrokHooksBody(hooks, packageRoot, targetDir));
|
|
299
380
|
writeGrokDiscoveredHooks(targetDir, hooksPath, log, label);
|
|
300
381
|
log(label, "hooks.json patched → Grok command-string enforcement gate", "info");
|
|
301
382
|
} catch (e) {
|
|
@@ -310,7 +391,7 @@ function writeGrokDiscoveredHooks(targetDir, hooksPath, log, label) {
|
|
|
310
391
|
const destDir = path.join(targetDir, ".grok", "hooks");
|
|
311
392
|
fs.mkdirSync(destDir, { recursive: true });
|
|
312
393
|
const dest = path.join(destDir, "0xray.json");
|
|
313
|
-
|
|
394
|
+
copyFileIfChanged(hooksPath, dest);
|
|
314
395
|
log(label, "Grok discovered hooks → .grok/hooks/0xray.json", "info", { dest });
|
|
315
396
|
} catch (e) {
|
|
316
397
|
log(label, "Grok discovered hooks copy failed", "warn", { error: e.message });
|
|
@@ -325,10 +406,10 @@ function copyOpencodePlugin(packageRoot, opencodeDest, log) {
|
|
|
325
406
|
if (!fs.existsSync(pluginDestDir)) fs.mkdirSync(pluginDestDir, { recursive: true });
|
|
326
407
|
const scopePkg = path.join(opencodeDest, "package.json");
|
|
327
408
|
if (!fs.existsSync(scopePkg)) {
|
|
328
|
-
|
|
409
|
+
writeFileIfChanged(scopePkg, `${JSON.stringify({ type: "module" })}\n`);
|
|
329
410
|
}
|
|
330
411
|
const shim = `export { default } from ${JSON.stringify(pluginSource)};\n`;
|
|
331
|
-
|
|
412
|
+
writeFileIfChanged(pluginDest, shim);
|
|
332
413
|
log("opencode-bridge", "plugin shim written", "info");
|
|
333
414
|
}
|
|
334
415
|
|
|
@@ -411,6 +492,94 @@ function registerGrokMcpServers(targetDir, log, pluginDirs) {
|
|
|
411
492
|
}
|
|
412
493
|
}
|
|
413
494
|
|
|
495
|
+
|
|
496
|
+
function pluginMcpBody(targetDir) {
|
|
497
|
+
const { buildPluginMcpJson } = require("./bridge-mcp-wiring.cjs");
|
|
498
|
+
return JSON.stringify(buildPluginMcpJson(targetDir), null, 2) + "\n";
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function patchedGrokHooksBody(hooks, packageRoot, targetDir) {
|
|
502
|
+
patchGrokHookEntry(hooks.hooks?.PreToolUse?.[0]?.hooks?.[0], packageRoot, targetDir, "pre-tool-use.js");
|
|
503
|
+
patchGrokHookEntry(hooks.hooks?.SessionStart?.[0]?.hooks?.[0], packageRoot, targetDir, "session-start.js");
|
|
504
|
+
patchGrokHookEntry(
|
|
505
|
+
hooks.hooks?.UserPromptSubmit?.[0]?.hooks?.[0],
|
|
506
|
+
packageRoot,
|
|
507
|
+
targetDir,
|
|
508
|
+
"session-start.js",
|
|
509
|
+
"--hook-event=user_prompt_submit",
|
|
510
|
+
);
|
|
511
|
+
patchGrokHookEntry(hooks.hooks?.PostToolUse?.[0]?.hooks?.[0], packageRoot, targetDir, "post-tool-use.js");
|
|
512
|
+
ensureGrokHookEvent(hooks, "PreCompact");
|
|
513
|
+
ensureGrokHookEvent(hooks, "PostCompact");
|
|
514
|
+
patchGrokHookEntry(
|
|
515
|
+
hooks.hooks?.PreCompact?.[0]?.hooks?.[0],
|
|
516
|
+
packageRoot,
|
|
517
|
+
targetDir,
|
|
518
|
+
"session-start.js",
|
|
519
|
+
"--hook-event=pre_compact",
|
|
520
|
+
);
|
|
521
|
+
patchGrokHookEntry(
|
|
522
|
+
hooks.hooks?.PostCompact?.[0]?.hooks?.[0],
|
|
523
|
+
packageRoot,
|
|
524
|
+
targetDir,
|
|
525
|
+
"session-start.js",
|
|
526
|
+
"--hook-event=post_compact",
|
|
527
|
+
);
|
|
528
|
+
return JSON.stringify(hooks, null, 2) + "\n";
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function listTreeFiles(root) {
|
|
532
|
+
if (!fs.existsSync(root)) return null;
|
|
533
|
+
const files = new Map();
|
|
534
|
+
const stack = [""];
|
|
535
|
+
while (stack.length) {
|
|
536
|
+
const rel = stack.pop();
|
|
537
|
+
const abs = rel ? path.join(root, rel) : root;
|
|
538
|
+
let entries;
|
|
539
|
+
try {
|
|
540
|
+
entries = fs.readdirSync(abs, { withFileTypes: true });
|
|
541
|
+
} catch {
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
for (const entry of entries) {
|
|
545
|
+
const next = rel ? `${rel}/${entry.name}` : entry.name;
|
|
546
|
+
if (entry.isDirectory()) stack.push(next);
|
|
547
|
+
else if (entry.isFile()) files.set(next, path.join(abs, entry.name));
|
|
548
|
+
else return null;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return files;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/** Dest already holds the post-patch plugin, so a repeat wear must not rm+cp it. */
|
|
555
|
+
function grokPluginSettled(sourceDir, dest, packageRoot, targetDir) {
|
|
556
|
+
const sourceFiles = listTreeFiles(sourceDir);
|
|
557
|
+
const destFiles = listTreeFiles(dest);
|
|
558
|
+
if (!sourceFiles || !destFiles) return false;
|
|
559
|
+
const desired = new Map();
|
|
560
|
+
for (const [rel, abs] of sourceFiles) desired.set(rel, fs.readFileSync(abs));
|
|
561
|
+
desired.set(".mcp.json", Buffer.from(pluginMcpBody(targetDir)));
|
|
562
|
+
const hookScript = path.join(packageRoot, "scripts", "mjs", "run-grok-hook.mjs");
|
|
563
|
+
if (desired.has("hooks/hooks.json") && fs.existsSync(hookScript)) {
|
|
564
|
+
try {
|
|
565
|
+
const hooks = JSON.parse(desired.get("hooks/hooks.json").toString("utf8"));
|
|
566
|
+
desired.set("hooks/hooks.json", Buffer.from(patchedGrokHooksBody(hooks, packageRoot, targetDir)));
|
|
567
|
+
} catch {
|
|
568
|
+
return false;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (desired.size !== destFiles.size) return false;
|
|
572
|
+
for (const [rel, body] of desired) {
|
|
573
|
+
const abs = destFiles.get(rel);
|
|
574
|
+
if (!abs || !sameFileBytes(abs, body)) return false;
|
|
575
|
+
}
|
|
576
|
+
for (const [rel, abs] of sourceFiles) {
|
|
577
|
+
const destAbs = destFiles.get(rel);
|
|
578
|
+
if (destAbs) adoptSourceMode(abs, destAbs);
|
|
579
|
+
}
|
|
580
|
+
return true;
|
|
581
|
+
}
|
|
582
|
+
|
|
414
583
|
function installGrokBridge(targetDir, packageRoot, log, opts) {
|
|
415
584
|
const sourceDir = findGrokPluginSource(packageRoot);
|
|
416
585
|
if (!sourceDir) {
|
|
@@ -440,8 +609,16 @@ function installGrokBridge(targetDir, packageRoot, log, opts) {
|
|
|
440
609
|
});
|
|
441
610
|
continue;
|
|
442
611
|
}
|
|
443
|
-
|
|
444
|
-
|
|
612
|
+
const rel = dest.startsWith(machine) ? dest.replace(machine, "~") : path.relative(targetDir, dest);
|
|
613
|
+
const settled = grokPluginSettled(sourceDir, dest, packageRoot, targetDir);
|
|
614
|
+
let placed = false;
|
|
615
|
+
if (settled) {
|
|
616
|
+
wearIo.skips += 1;
|
|
617
|
+
placed = true;
|
|
618
|
+
} else {
|
|
619
|
+
placed = copyPluginDir(sourceDir, dest);
|
|
620
|
+
}
|
|
621
|
+
if (placed) {
|
|
445
622
|
log("grok-bridge", "plugin copied", "info", { path: rel || dest });
|
|
446
623
|
writePluginMcpJson(dest, targetDir, log, "grok-bridge");
|
|
447
624
|
patchGrokHooks(dest, packageRoot, targetDir, log, "grok-bridge");
|
|
@@ -483,16 +660,20 @@ function installHermesBridge(targetDir, packageRoot, log) {
|
|
|
483
660
|
log("hermes-bridge", "skip machine ~/.hermes plugin — isolated HOME", "info");
|
|
484
661
|
}
|
|
485
662
|
fs.mkdirSync(targetPluginDir, { recursive: true });
|
|
663
|
+
const hermesMcp = pluginMcpBody(targetDir);
|
|
486
664
|
for (const entry of fs.readdirSync(sourceDir)) {
|
|
487
665
|
const src = path.join(sourceDir, entry);
|
|
488
666
|
const dst = path.join(targetPluginDir, entry);
|
|
489
|
-
if (
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
fs.copyFileSync(src, dst);
|
|
667
|
+
if (entry === ".mcp.json" && sameFileBytes(dst, hermesMcp)) {
|
|
668
|
+
if (!adoptSourceMode(src, dst)) wearIo.skips += 1;
|
|
669
|
+
continue;
|
|
493
670
|
}
|
|
671
|
+
copyEntryIfChanged(src, dst);
|
|
494
672
|
}
|
|
495
|
-
|
|
673
|
+
const helperSrc = path.join(packageRoot, "scripts", "helpers", "find-project-root.mjs");
|
|
674
|
+
const helperDst = path.join(targetPluginDir, "scripts", "helpers", "find-project-root.mjs");
|
|
675
|
+
if (!fs.existsSync(helperDst)) copyHermesFindProjectRootHelper(packageRoot, targetPluginDir);
|
|
676
|
+
else if (fs.existsSync(helperSrc)) copyFileIfChanged(helperSrc, helperDst);
|
|
496
677
|
if (!ephemeral && !isolated && copyHermesHookRuntimes(packageRoot)) {
|
|
497
678
|
log("hermes-bridge", "hook runtimes copied", "info", { path: "~/.hermes/plugins/hooks" });
|
|
498
679
|
}
|
|
@@ -510,7 +691,7 @@ function installHermesBridge(targetDir, packageRoot, log) {
|
|
|
510
691
|
|
|
511
692
|
const rootMarker = path.join(targetPluginDir, "xray-consumer-root.txt");
|
|
512
693
|
if (!isEphemeralInstallRoot(targetDir)) {
|
|
513
|
-
|
|
694
|
+
writeFileIfChanged(rootMarker, targetDir + "\n");
|
|
514
695
|
log("hermes-bridge", "consumer root marker written", "info");
|
|
515
696
|
} else {
|
|
516
697
|
log("hermes-bridge", "skip machine consumer marker — ephemeral consumer", "info");
|
|
@@ -555,7 +736,7 @@ function installOpenclawBridge(targetDir, packageRoot, log) {
|
|
|
555
736
|
debug: false,
|
|
556
737
|
logLevel: "info",
|
|
557
738
|
};
|
|
558
|
-
|
|
739
|
+
writeFileIfChanged(configPath, JSON.stringify(sampleConfig, null, 2) + "\n");
|
|
559
740
|
log("openclaw-bridge", "config created", "info", { path: ".xray/config/openclaw.json" });
|
|
560
741
|
}
|
|
561
742
|
|
|
@@ -623,31 +804,40 @@ function readPackageVersion(packageRoot) {
|
|
|
623
804
|
}
|
|
624
805
|
|
|
625
806
|
function writeJsonFile(filePath, data) {
|
|
626
|
-
|
|
807
|
+
const body = `${JSON.stringify(data, null, 2)}\n`;
|
|
808
|
+
if (sameFileBytes(filePath, body)) {
|
|
809
|
+
wearIo.skips += 1;
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
fs.writeFileSync(filePath, body);
|
|
813
|
+
wearIo.writes += 1;
|
|
814
|
+
return true;
|
|
627
815
|
}
|
|
628
816
|
|
|
629
817
|
function applyResolvedMemoryRouting(dst, targetDir) {
|
|
630
818
|
try {
|
|
631
819
|
const features = JSON.parse(fs.readFileSync(dst, "utf8"));
|
|
632
820
|
const next = enableMemoryRoutingIfResolves(features, targetDir);
|
|
633
|
-
if (next.changed)
|
|
821
|
+
if (!next.changed) return false;
|
|
822
|
+
return writeJsonFile(dst, next.features);
|
|
634
823
|
} catch {
|
|
635
824
|
/* leftover default stays off when features.json is unreadable */
|
|
825
|
+
return false;
|
|
636
826
|
}
|
|
637
827
|
}
|
|
638
828
|
|
|
639
829
|
function deployXrayConfigFile(file, src, dst, packageRoot, targetDir) {
|
|
640
830
|
if (!fs.existsSync(dst)) {
|
|
641
|
-
|
|
831
|
+
copyFileIfChanged(src, dst);
|
|
642
832
|
if (file === "features.json") applyResolvedMemoryRouting(dst, targetDir);
|
|
643
833
|
return true;
|
|
644
834
|
}
|
|
645
835
|
|
|
646
836
|
if (file === "features.schema.json") {
|
|
647
837
|
if (fs.statSync(src).mtime > fs.statSync(dst).mtime) {
|
|
648
|
-
|
|
649
|
-
return true;
|
|
838
|
+
return copyFileIfChanged(src, dst);
|
|
650
839
|
}
|
|
840
|
+
wearIo.skips += 1;
|
|
651
841
|
return false;
|
|
652
842
|
}
|
|
653
843
|
|
|
@@ -672,26 +862,26 @@ function deployXrayConfigFile(file, src, dst, packageRoot, targetDir) {
|
|
|
672
862
|
profile: "guided",
|
|
673
863
|
};
|
|
674
864
|
}
|
|
675
|
-
writeJsonFile(dst, merged);
|
|
676
|
-
applyResolvedMemoryRouting(dst, targetDir);
|
|
677
|
-
return
|
|
865
|
+
const wroteMerged = writeJsonFile(dst, merged);
|
|
866
|
+
const wroteRouting = applyResolvedMemoryRouting(dst, targetDir);
|
|
867
|
+
return wroteMerged || wroteRouting;
|
|
678
868
|
}
|
|
679
|
-
writeJsonFile(dst, merged);
|
|
680
|
-
return true;
|
|
869
|
+
return writeJsonFile(dst, merged);
|
|
681
870
|
} catch {
|
|
682
871
|
if (fs.statSync(src).mtime > fs.statSync(dst).mtime) {
|
|
683
|
-
|
|
872
|
+
const wrote = copyFileIfChanged(src, dst);
|
|
684
873
|
if (file === "features.json") applyResolvedMemoryRouting(dst, targetDir);
|
|
685
|
-
return
|
|
874
|
+
return wrote;
|
|
686
875
|
}
|
|
876
|
+
wearIo.skips += 1;
|
|
687
877
|
return false;
|
|
688
878
|
}
|
|
689
879
|
}
|
|
690
880
|
|
|
691
881
|
if (fs.statSync(src).mtime > fs.statSync(dst).mtime) {
|
|
692
|
-
|
|
693
|
-
return true;
|
|
882
|
+
return copyFileIfChanged(src, dst);
|
|
694
883
|
}
|
|
884
|
+
wearIo.skips += 1;
|
|
695
885
|
return false;
|
|
696
886
|
}
|
|
697
887
|
|
|
@@ -816,12 +1006,8 @@ function fastenCursorHookScripts(targetDir, packageRoot, log) {
|
|
|
816
1006
|
const src = path.join(srcDir, name);
|
|
817
1007
|
if (!fs.existsSync(src)) continue;
|
|
818
1008
|
const dest = path.join(destDir, name);
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
fs.chmodSync(dest, 0o755);
|
|
822
|
-
} catch {
|
|
823
|
-
// chmod is best-effort on hosts that ignore mode
|
|
824
|
-
}
|
|
1009
|
+
copyFileIfChanged(src, dest);
|
|
1010
|
+
chmodIfNeeded(dest, 0o755);
|
|
825
1011
|
copied++;
|
|
826
1012
|
}
|
|
827
1013
|
return copied;
|
|
@@ -888,7 +1074,7 @@ function fastenCursorHooksAtUnsafe(targetDir, packageRoot, log) {
|
|
|
888
1074
|
if (previousText !== body) {
|
|
889
1075
|
saveCursorWearSnapshot(targetDir, previousText, body);
|
|
890
1076
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
891
|
-
|
|
1077
|
+
writeFileIfChanged(dest, body);
|
|
892
1078
|
}
|
|
893
1079
|
log("cursor-bridge", existing ? "hooks.json rewritten cloud-safe" : "hooks.json fastened", "info", {
|
|
894
1080
|
path: dest,
|
|
@@ -1784,7 +1970,7 @@ function removeWearExcludeLine(targetDir) {
|
|
|
1784
1970
|
const lines = fs.readFileSync(excludeFile, "utf8").split("\n");
|
|
1785
1971
|
const next = lines.filter((line) => line.trim() !== pattern);
|
|
1786
1972
|
if (next.length === lines.length) return;
|
|
1787
|
-
|
|
1973
|
+
writeFileIfChanged(excludeFile, next.join("\n"));
|
|
1788
1974
|
}
|
|
1789
1975
|
|
|
1790
1976
|
function saveCursorWearSnapshot(targetDir, previousText, writtenBody) {
|
|
@@ -1804,17 +1990,17 @@ function saveCursorWearSnapshot(targetDir, previousText, writtenBody) {
|
|
|
1804
1990
|
cursorDirExisted = fs.existsSync(path.join(targetDir, ".cursor"));
|
|
1805
1991
|
}
|
|
1806
1992
|
const backup = existed ? userBytesBeforeWear(previousText) : null;
|
|
1807
|
-
if (backup != null)
|
|
1993
|
+
if (backup != null) writeFileIfChanged(paths.backup, backup);
|
|
1808
1994
|
else if (fs.existsSync(paths.backup)) fs.unlinkSync(paths.backup);
|
|
1809
1995
|
const meta = { version: 1, existed, cursorDirExisted, also };
|
|
1810
|
-
|
|
1811
|
-
|
|
1996
|
+
writeFileIfChanged(paths.written, writtenBody);
|
|
1997
|
+
writeFileIfChanged(paths.meta, `${JSON.stringify(meta, null, 2)}\n`);
|
|
1812
1998
|
return { paths, meta };
|
|
1813
1999
|
}
|
|
1814
2000
|
|
|
1815
2001
|
function saveWearState(paths, meta) {
|
|
1816
2002
|
fs.mkdirSync(paths.dir, { recursive: true });
|
|
1817
|
-
|
|
2003
|
+
writeFileIfChanged(paths.meta, `${JSON.stringify(meta, null, 2)}\n`);
|
|
1818
2004
|
}
|
|
1819
2005
|
|
|
1820
2006
|
/** git prints the physical path. Node's tmpdir is the /var symlink. Compare the same file. */
|
|
@@ -1931,7 +2117,7 @@ function wearCursorHooks(targetDir, packageRoot, log, opts) {
|
|
|
1931
2117
|
}
|
|
1932
2118
|
if (changed) {
|
|
1933
2119
|
fs.mkdirSync(path.dirname(hooksPath), { recursive: true });
|
|
1934
|
-
|
|
2120
|
+
writeFileIfChanged(hooksPath, body);
|
|
1935
2121
|
} else if (!snap) {
|
|
1936
2122
|
write("cursor-bridge", "hooks.json already runs these hooks", "info", { path: hooksPath });
|
|
1937
2123
|
}
|
|
@@ -2097,11 +2283,46 @@ function setupProjectBridges(opts) {
|
|
|
2097
2283
|
* Framework dogfood: do not deploy consumer copies over the package,
|
|
2098
2284
|
* but keep Grok discovery hooks hot and enable Repertoire when it resolves.
|
|
2099
2285
|
*/
|
|
2100
|
-
function
|
|
2286
|
+
function materializeDogfoodFeatures(packageRoot, log) {
|
|
2101
2287
|
const featuresPath = path.join(packageRoot, ".xray", "features.json");
|
|
2102
|
-
if (fs.existsSync(featuresPath)) {
|
|
2103
|
-
|
|
2288
|
+
if (!fs.existsSync(featuresPath)) {
|
|
2289
|
+
const shipped = resolveXrayConfigSource(packageRoot, "features.json");
|
|
2290
|
+
fs.mkdirSync(path.dirname(featuresPath), { recursive: true });
|
|
2291
|
+
if (shipped && path.resolve(shipped) !== path.resolve(featuresPath)) {
|
|
2292
|
+
fs.copyFileSync(shipped, featuresPath);
|
|
2293
|
+
log("grok-dogfood", "features.json materialized from shipped template", "info");
|
|
2294
|
+
} else {
|
|
2295
|
+
writeJsonFile(featuresPath, { memory_routing: { enabled: false, provider: "null" } });
|
|
2296
|
+
log("grok-dogfood", "features.json materialized default-off", "info");
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
applyResolvedMemoryRouting(featuresPath, packageRoot);
|
|
2300
|
+
const stateDir = path.join(packageRoot, ".xray", "state", "repertoire");
|
|
2301
|
+
fs.mkdirSync(path.join(stateDir, "feedback"), { recursive: true });
|
|
2302
|
+
const signalsPath = path.join(stateDir, "curated_signals.json");
|
|
2303
|
+
if (!fs.existsSync(signalsPath)) {
|
|
2304
|
+
const candidates = [
|
|
2305
|
+
path.join(packageRoot, "vendor", "@0xray", "repertoire", "data", "curated_signals.json"),
|
|
2306
|
+
path.join(packageRoot, "..", "repertoire", "data", "curated_signals.json"),
|
|
2307
|
+
];
|
|
2308
|
+
const src = candidates.find((candidate) => fs.existsSync(candidate));
|
|
2309
|
+
if (src) fs.copyFileSync(src, signalsPath);
|
|
2310
|
+
else {
|
|
2311
|
+
// CuratedSignalsManager.load() reads document.signals, not a bare array.
|
|
2312
|
+
writeJsonFile(signalsPath, {
|
|
2313
|
+
description: "Curated high-signal primitives for Repertoire",
|
|
2314
|
+
schema_version: "1.1",
|
|
2315
|
+
signals: [],
|
|
2316
|
+
});
|
|
2317
|
+
}
|
|
2318
|
+
log("grok-dogfood", "repertoire signals materialized", "info");
|
|
2104
2319
|
}
|
|
2320
|
+
const inferenceState = path.join(stateDir, "inference-state.json");
|
|
2321
|
+
if (!fs.existsSync(inferenceState)) writeJsonFile(inferenceState, {});
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
function installFrameworkDogfoodWear(packageRoot, log) {
|
|
2325
|
+
materializeDogfoodFeatures(packageRoot, log);
|
|
2105
2326
|
|
|
2106
2327
|
installCursorBridge(packageRoot, packageRoot, log);
|
|
2107
2328
|
|
|
@@ -2157,6 +2378,9 @@ function installAllBridges(opts) {
|
|
|
2157
2378
|
module.exports = {
|
|
2158
2379
|
installAllBridges,
|
|
2159
2380
|
installGrokBridge,
|
|
2381
|
+
installHermesBridge,
|
|
2382
|
+
resetWearIo,
|
|
2383
|
+
readWearIo,
|
|
2160
2384
|
resolveGrokPluginDests,
|
|
2161
2385
|
resolveConsumerTargetDir,
|
|
2162
2386
|
isInstallPrefixTarget,
|