0xray 3.5.5 → 4.0.1

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 (233) hide show
  1. package/.mcp.json +18 -14
  2. package/.opencode/skills/api-design/SKILL.md +1 -1
  3. package/.opencode/skills/architect-tools/SKILL.md +1 -1
  4. package/.opencode/skills/architecture-patterns/SKILL.md +1 -1
  5. package/.opencode/skills/auto-format/SKILL.md +1 -1
  6. package/.opencode/skills/boot-orchestrator/SKILL.md +2 -6
  7. package/.opencode/skills/bug-triage/SKILL.md +1 -1
  8. package/.opencode/skills/code-analyzer/SKILL.md +1 -1
  9. package/.opencode/skills/code-review/SKILL.md +1 -1
  10. package/.opencode/skills/enforcer/SKILL.md +1 -1
  11. package/.opencode/skills/framework-compliance-audit/SKILL.md +1 -1
  12. package/.opencode/skills/git-workflow/SKILL.md +1 -1
  13. package/.opencode/skills/hermes-agent/SKILL.md +36 -36
  14. package/.opencode/skills/inference-improve/SKILL.md +1 -1
  15. package/.opencode/skills/lint/SKILL.md +1 -1
  16. package/.opencode/skills/log-monitor/SKILL.md +1 -1
  17. package/.opencode/skills/model-health-check/SKILL.md +1 -1
  18. package/.opencode/skills/multimodal-looker/SKILL.md +1 -1
  19. package/.opencode/skills/orchestrator/SKILL.md +38 -14
  20. package/.opencode/skills/performance-analysis/SKILL.md +1 -1
  21. package/.opencode/skills/performance-optimization/SKILL.md +1 -1
  22. package/.opencode/skills/processor-pipeline/SKILL.md +1 -1
  23. package/.opencode/skills/project-analysis/SKILL.md +1 -1
  24. package/.opencode/skills/refactoring-strategies/SKILL.md +1 -1
  25. package/.opencode/skills/registry.json +65 -0
  26. package/.opencode/skills/researcher/SKILL.md +18 -5
  27. package/.opencode/skills/security-audit/SKILL.md +1 -1
  28. package/.opencode/skills/security-scan/SKILL.md +1 -1
  29. package/.opencode/skills/session-management/SKILL.md +1 -1
  30. package/.opencode/skills/state-manager/SKILL.md +1 -1
  31. package/.opencode/skills/storyteller/SKILL.md +1 -1
  32. package/.opencode/skills/strategist/SKILL.md +12 -0
  33. package/.opencode/skills/testing-best-practices/SKILL.md +37 -0
  34. package/.opencode/skills/testing-strategy/SKILL.md +14 -3
  35. package/.opencode/skills/ui-ux-design/SKILL.md +1 -1
  36. package/.opencode/skills/xray-orchestrator/index.ts +267 -0
  37. package/.xray/codex.json +12 -3
  38. package/.xray/features.json +28 -6
  39. package/.xray/features.schema.json +31 -0
  40. package/AGENTS-consumer.md +17 -7
  41. package/AGENTS.md +18 -10
  42. package/README.md +30 -27
  43. package/SKILLS.md +2 -2
  44. package/dist/AGENTS.md +18 -10
  45. package/dist/CHANGELOG.md +81 -11
  46. package/dist/README.md +30 -27
  47. package/dist/SKILLS.md +2 -2
  48. package/dist/cli/commands/hermes-install.js +3 -0
  49. package/dist/cli/commands/openclaw-install.js +14 -0
  50. package/dist/cli/index.js +10 -1
  51. package/dist/core/features-config.d.ts +15 -2
  52. package/dist/infrastructure/schemas/cloud-schemas.d.ts +1 -1
  53. package/dist/integrations/grok/grok-cli.d.ts +3 -0
  54. package/dist/integrations/grok/grok-cli.js +92 -4
  55. package/dist/integrations/grok/hooks/grok-hook-utils.js +53 -7
  56. package/dist/integrations/grok/hooks/post-tool-use.js +68 -46
  57. package/dist/integrations/grok/hooks/pre-tool-use.js +14 -31
  58. package/dist/integrations/grok/hooks/session-start.js +86 -4
  59. package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +31 -24
  60. package/dist/integrations/hermes-agent/bridge.mjs +26 -1
  61. package/dist/integrations/hooks/delegation-gate-runtime.mjs +32 -3
  62. package/dist/integrations/hooks/pipeline-hook-runtime.mjs +1 -1
  63. package/dist/integrations/hooks/station-hook-runtime.cjs +421 -0
  64. package/dist/integrations/hooks/station-hook-runtime.mjs +24 -0
  65. package/dist/integrations/hooks/station-memory-ingest.mjs +45 -0
  66. package/dist/integrations/hooks/station-memory-match.mjs +47 -0
  67. package/dist/integrations/openclaw/client.d.ts +5 -1
  68. package/dist/integrations/openclaw/client.js +17 -9
  69. package/dist/integrations/openclaw/config.js +0 -1
  70. package/dist/integrations/openclaw/hooks/pre-tool-gate-runtime.mjs +102 -0
  71. package/dist/integrations/openclaw/index.js +17 -0
  72. package/dist/integrations/openclaw/plugin/xray-pre-tool/index.js +75 -0
  73. package/dist/integrations/openclaw/plugin/xray-pre-tool/openclaw.plugin.json +10 -0
  74. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +12 -0
  75. package/dist/integrations/openclaw/pre-tool-gate.d.ts +19 -0
  76. package/dist/integrations/openclaw/pre-tool-gate.js +39 -0
  77. package/dist/mcps/orchestrator/handlers/complexity-handler.js +2 -2
  78. package/dist/memory-routing/index.d.ts +1 -1
  79. package/dist/memory-routing/index.js +1 -1
  80. package/dist/memory-routing/provider-loader.d.ts +4 -0
  81. package/dist/memory-routing/provider-loader.js +44 -2
  82. package/dist/memory-routing/provider-registry.js +3 -2
  83. package/dist/nucleus/autonomy-kernel.d.ts +2 -2
  84. package/dist/nucleus/autonomy-kernel.js +17 -6
  85. package/dist/nucleus/confer.js +17 -3
  86. package/dist/nucleus/delegation-gate.d.ts +14 -8
  87. package/dist/nucleus/delegation-gate.js +151 -16
  88. package/dist/nucleus/suit-temperament.d.ts +40 -0
  89. package/dist/nucleus/suit-temperament.js +157 -0
  90. package/dist/nucleus/user-aside.js +1 -1
  91. package/dist/plugin/xray-codex-injection.js +14 -3
  92. package/dist/postprocessor/triggers/GitHookTrigger.js +11 -5
  93. package/opencode.json +80 -0
  94. package/package.json +15 -5
  95. package/scripts/hooks/install-hooks.cjs +18 -3
  96. package/scripts/hooks/run-hook.js +67 -3
  97. package/scripts/mjs/run-repertoire-mcp.mjs +39 -0
  98. package/scripts/mjs/verify-confer-core.mjs +1 -1
  99. package/scripts/mjs/verify-delegation-gate-core.mjs +29 -2
  100. package/scripts/mjs/verify-four-floor-compact.mjs +524 -0
  101. package/scripts/mjs/verify-pipeline-facets.mjs +215 -0
  102. package/scripts/mjs/verify-temperament-live.mjs +261 -0
  103. package/scripts/node/bridge-mcp-wiring.cjs +277 -29
  104. package/scripts/node/consumer-install-smoke.mjs +55 -2
  105. package/scripts/node/install-bridges.cjs +195 -33
  106. package/scripts/node/postinstall.cjs +50 -27
  107. package/scripts/node/reconcile-version.mjs +0 -4
  108. package/scripts/node/release-gate.mjs +1 -1
  109. package/scripts/node/release.mjs +2 -2
  110. package/scripts/node/universal-version-manager.js +1 -1
  111. package/scripts/node/validate-release-docs.mjs +20 -33
  112. package/scripts/node/version-manager.mjs +63 -18
  113. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +31 -24
  114. package/src/integrations/openclaw/README.md +134 -0
  115. package/src/integrations/openclaw/plugin/xray-pre-tool/index.js +75 -0
  116. package/src/integrations/openclaw/plugin/xray-pre-tool/openclaw.plugin.json +10 -0
  117. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +12 -0
  118. package/src/mcps/orchestrator/handlers/complexity-handler.ts +2 -1
  119. package/vendor/@0xray/repertoire/AGENTS.md +163 -0
  120. package/vendor/@0xray/repertoire/LICENSE +21 -0
  121. package/vendor/@0xray/repertoire/README.md +120 -0
  122. package/vendor/@0xray/repertoire/data/curated_signals.json +208 -0
  123. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts +78 -0
  124. package/vendor/@0xray/repertoire/dist/RepertoireService.d.ts.map +1 -0
  125. package/vendor/@0xray/repertoire/dist/RepertoireService.js +153 -0
  126. package/vendor/@0xray/repertoire/dist/RepertoireService.js.map +1 -0
  127. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts +39 -0
  128. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.d.ts.map +1 -0
  129. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js +60 -0
  130. package/vendor/@0xray/repertoire/dist/governance/ontological-trap-enforcer.js.map +1 -0
  131. package/vendor/@0xray/repertoire/dist/index.d.ts +14 -0
  132. package/vendor/@0xray/repertoire/dist/index.d.ts.map +1 -0
  133. package/vendor/@0xray/repertoire/dist/index.js +14 -0
  134. package/vendor/@0xray/repertoire/dist/index.js.map +1 -0
  135. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts +26 -0
  136. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.d.ts.map +1 -0
  137. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js +109 -0
  138. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-ingester.js.map +1 -0
  139. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts +18 -0
  140. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.d.ts.map +1 -0
  141. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js +94 -0
  142. package/vendor/@0xray/repertoire/dist/ingestion/groover-log-parser.js.map +1 -0
  143. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts +7 -0
  144. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.d.ts.map +1 -0
  145. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js +18 -0
  146. package/vendor/@0xray/repertoire/dist/ingestion/orchestrator-feedback-ingester.js.map +1 -0
  147. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts +31 -0
  148. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.d.ts.map +1 -0
  149. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js +83 -0
  150. package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js.map +1 -0
  151. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts +6 -0
  152. package/vendor/@0xray/repertoire/dist/mcp/server.d.ts.map +1 -0
  153. package/vendor/@0xray/repertoire/dist/mcp/server.js +173 -0
  154. package/vendor/@0xray/repertoire/dist/mcp/server.js.map +1 -0
  155. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts +27 -0
  156. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.d.ts.map +1 -0
  157. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js +107 -0
  158. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/RepertoireOrchestratorBridge.js.map +1 -0
  159. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts +11 -0
  160. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.d.ts.map +1 -0
  161. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js +32 -0
  162. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/capability-enhancer.js.map +1 -0
  163. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts +9 -0
  164. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.d.ts.map +1 -0
  165. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js +72 -0
  166. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/confidence-gate.js.map +1 -0
  167. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts +5 -0
  168. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.d.ts.map +1 -0
  169. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js +18 -0
  170. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-agent-affinity.js.map +1 -0
  171. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts +31 -0
  172. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.d.ts.map +1 -0
  173. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js +218 -0
  174. package/vendor/@0xray/repertoire/dist/orchestrator-bridge/signal-injector.js.map +1 -0
  175. package/vendor/@0xray/repertoire/dist/paths.d.ts +24 -0
  176. package/vendor/@0xray/repertoire/dist/paths.d.ts.map +1 -0
  177. package/vendor/@0xray/repertoire/dist/paths.js +74 -0
  178. package/vendor/@0xray/repertoire/dist/paths.js.map +1 -0
  179. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts +141 -0
  180. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.d.ts.map +1 -0
  181. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js +210 -0
  182. package/vendor/@0xray/repertoire/dist/provider/memory-routing-provider.js.map +1 -0
  183. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts +48 -0
  184. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.d.ts.map +1 -0
  185. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js +221 -0
  186. package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js.map +1 -0
  187. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts +11 -0
  188. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.d.ts.map +1 -0
  189. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js +43 -0
  190. package/vendor/@0xray/repertoire/dist/registry/InferenceStateManager.js.map +1 -0
  191. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts +15 -0
  192. package/vendor/@0xray/repertoire/dist/registry/signal-prune.d.ts.map +1 -0
  193. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js +35 -0
  194. package/vendor/@0xray/repertoire/dist/registry/signal-prune.js.map +1 -0
  195. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts +3 -0
  196. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.d.ts.map +1 -0
  197. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js +24 -0
  198. package/vendor/@0xray/repertoire/dist/synthesis/concrete-sections.js.map +1 -0
  199. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts +24 -0
  200. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.d.ts.map +1 -0
  201. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js +148 -0
  202. package/vendor/@0xray/repertoire/dist/synthesis/meta-inference-engine.js.map +1 -0
  203. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts +4 -0
  204. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.d.ts.map +1 -0
  205. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js +44 -0
  206. package/vendor/@0xray/repertoire/dist/synthesis/primitive-confidence-aggregator.js.map +1 -0
  207. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts +22 -0
  208. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.d.ts.map +1 -0
  209. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js +66 -0
  210. package/vendor/@0xray/repertoire/dist/synthesis/synthesis-prompt-builder.js.map +1 -0
  211. package/vendor/@0xray/repertoire/dist/types.d.ts +210 -0
  212. package/vendor/@0xray/repertoire/dist/types.d.ts.map +1 -0
  213. package/vendor/@0xray/repertoire/dist/types.js +2 -0
  214. package/vendor/@0xray/repertoire/dist/types.js.map +1 -0
  215. package/vendor/@0xray/repertoire/docs/MEMORY-ROUTING-PROVIDER.md +54 -0
  216. package/vendor/@0xray/repertoire/hermes-mcp.example.json +20 -0
  217. package/vendor/@0xray/repertoire/mcp-config.example.json +11 -0
  218. package/vendor/@0xray/repertoire/package.json +124 -0
  219. package/vendor/@0xray/repertoire/schemas/curated-signal.schema.json +68 -0
  220. package/vendor/@0xray/repertoire/schemas/inference-log.schema.json +82 -0
  221. package/vendor/@0xray/repertoire/scripts/confirm-suit-all.mjs +253 -0
  222. package/vendor/@0xray/repertoire/scripts/confirm-suit-harness.mjs +220 -0
  223. package/vendor/@0xray/repertoire/scripts/run-live-synthesis-checkpoint.mjs +215 -0
  224. package/vendor/@0xray/repertoire/scripts/suit-bridge-shared.mjs +139 -0
  225. package/vendor/@0xray/repertoire/scripts/verify-grok-suit.mjs +304 -0
  226. package/vendor/@0xray/repertoire/scripts/verify-hermes-suit.mjs +163 -0
  227. package/vendor/@0xray/repertoire/scripts/verify-openclaw-suit.mjs +125 -0
  228. package/vendor/@0xray/repertoire/scripts/verify-opencode-suit.mjs +127 -0
  229. package/vendor/@0xray/repertoire/scripts/verify-synthesis-dogfood.mjs +207 -0
  230. package/vendor/@0xray/repertoire/skills/repertoire-trap-handling/SKILL.md +206 -0
  231. package/xray/codex.json +12 -3
  232. package/xray/features.json +28 -6
  233. package/xray/features.schema.json +31 -0
@@ -0,0 +1,304 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Verify 0xRay suit is fully wearable in Grok Build for this repo.
4
+ * Run: npm run build && node scripts/verify-grok-suit.mjs
5
+ */
6
+ import { execSync } from 'node:child_process';
7
+ import { existsSync, readFileSync, statSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { join, resolve } from 'node:path';
10
+ import { mcpStdioInitializeProbe, resolveConsumerRoot } from './suit-bridge-shared.mjs';
11
+
12
+ function resolveMemoryProviderPath(projectRoot) {
13
+ const local = join(projectRoot, 'dist/provider/memory-routing-provider.js');
14
+ if (existsSync(local)) return local;
15
+ const installed = join(
16
+ projectRoot,
17
+ 'node_modules/@0xray/repertoire/dist/provider/memory-routing-provider.js',
18
+ );
19
+ if (existsSync(installed)) return installed;
20
+ return local;
21
+ }
22
+
23
+ const root = resolveConsumerRoot();
24
+ let failed = 0;
25
+
26
+ function pass(name, detail = '') {
27
+ console.log(`✅ ${name}${detail ? ` — ${detail}` : ''}`);
28
+ }
29
+
30
+ function fail(name, detail = '') {
31
+ failed++;
32
+ console.error(`❌ ${name}${detail ? ` — ${detail}` : ''}`);
33
+ }
34
+
35
+ function resolveLiveHook(name) {
36
+ const candidates = [
37
+ join(root, 'node_modules/0xray/dist/integrations/grok/hooks', name),
38
+ join(homedir(), 'dev/xray/dist/integrations/grok/hooks', name),
39
+ ];
40
+ return candidates.find((p) => existsSync(p)) || null;
41
+ }
42
+
43
+ function tailActivityLog(workspace, tailBytes = 32768) {
44
+ const logFile = join(workspace, 'logs', 'framework', 'activity.log');
45
+ if (!existsSync(logFile)) return '';
46
+ const stat = statSync(logFile);
47
+ const start = Math.max(0, stat.size - tailBytes);
48
+ const buf = readFileSync(logFile).subarray(start);
49
+ return buf.toString('utf8');
50
+ }
51
+
52
+ function runHook(hookPath, fixture, env = {}) {
53
+ const payload = JSON.stringify(fixture);
54
+ return execSync(`printf '%s' '${payload.replace(/'/g, "'\\''")}' | node "${hookPath}"`, {
55
+ encoding: 'utf8',
56
+ env: { ...process.env, GROK_WORKSPACE_ROOT: root, ...env },
57
+ });
58
+ }
59
+
60
+ // 1. .xray layout
61
+ for (const f of ['codex.json', 'features.json', 'config.json']) {
62
+ const p = join(root, '.xray', f);
63
+ existsSync(p) ? pass(`.xray/${f}`) : fail(`.xray/${f}`, 'missing');
64
+ }
65
+
66
+ // 2. features.json contract
67
+ try {
68
+ const features = JSON.parse(readFileSync(join(root, '.xray/features.json'), 'utf8'));
69
+ let installedXrayVersion = 'unknown';
70
+ try {
71
+ const xrayPkg = JSON.parse(
72
+ readFileSync(join(root, 'node_modules/0xray/package.json'), 'utf8'),
73
+ );
74
+ installedXrayVersion = xrayPkg.version ?? 'unknown';
75
+ } catch {
76
+ /* optional */
77
+ }
78
+ if (features.version === installedXrayVersion) {
79
+ pass('features.json version', installedXrayVersion);
80
+ } else {
81
+ fail('features.json version', `got ${features.version}, expected ${installedXrayVersion}`);
82
+ }
83
+ if (features.inference_governance?.enabled) pass('inference_governance');
84
+ else fail('inference_governance', 'not enabled');
85
+ if (features.memory_routing?.enabled) pass('memory_routing');
86
+ else fail('memory_routing', 'not enabled');
87
+ if (features.synthesis?.enabled) pass('synthesis.enabled');
88
+ else fail('synthesis.enabled', 'not enabled');
89
+ } catch (e) {
90
+ fail('features.json parse', e.message);
91
+ }
92
+
93
+ // 3. Memory provider built (local dist or installed @0xray/repertoire)
94
+ const provider = resolveMemoryProviderPath(root);
95
+ existsSync(provider)
96
+ ? pass('memory-routing-provider.js')
97
+ : fail('memory-routing-provider.js', 'npm install @0xray/repertoire or npm run build');
98
+
99
+ // 4. .mcp.json — 7 xray + repertoire
100
+ try {
101
+ const mcp = JSON.parse(readFileSync(join(root, '.mcp.json'), 'utf8'));
102
+ const servers = Object.keys(mcp.mcpServers || {});
103
+ const xrayCount = servers.filter((s) => s.startsWith('xray-')).length;
104
+ if (xrayCount === 7) pass('.mcp.json xray servers', '7/7');
105
+ else fail('.mcp.json xray servers', `${xrayCount}/7`);
106
+ servers.includes('repertoire') ? pass('.mcp.json repertoire') : fail('.mcp.json repertoire', 'missing');
107
+ const mcpRaw = readFileSync(join(root, '.mcp.json'), 'utf8');
108
+ if (/\/Users\/[^"'\s]+/.test(mcpRaw)) {
109
+ fail('.mcp.json portable paths', 'absolute /Users/ path in committed .mcp.json');
110
+ } else {
111
+ pass('.mcp.json portable paths');
112
+ }
113
+ } catch (e) {
114
+ fail('.mcp.json', e.message);
115
+ }
116
+
117
+ // 5. Grok plugin + UserPromptSubmit boot hook
118
+ const grokPlugin = join(homedir(), '.grok/plugins/0xray');
119
+ const grokHooks = join(grokPlugin, 'hooks/hooks.json');
120
+ existsSync(grokPlugin) ? pass('Grok plugin', grokPlugin) : fail('Grok plugin', 'run: npx 0xray grok install --force');
121
+ if (existsSync(grokHooks)) {
122
+ const hooksRaw = readFileSync(grokHooks, 'utf8');
123
+ hooksRaw.includes('session-start.js')
124
+ ? pass('SessionStart hook')
125
+ : fail('SessionStart hook', 're-run: npx 0xray grok install --force');
126
+ hooksRaw.includes('pre-tool-use.js')
127
+ ? pass('PreToolUse hook (live Grok path)')
128
+ : fail('PreToolUse hook', 'missing from hooks.json');
129
+ hooksRaw.includes('UserPromptSubmit')
130
+ ? pass('UserPromptSubmit boot hook')
131
+ : fail('UserPromptSubmit boot hook', 'add session-start.js — npx 0xray grok install --force');
132
+ } else {
133
+ fail('Grok hooks.json', 'missing');
134
+ }
135
+
136
+ try {
137
+ const features = JSON.parse(readFileSync(join(root, '.xray/features.json'), 'utf8'));
138
+ if (features.multi_agent_orchestration?.lead_dev_mode) pass('lead_dev_mode in features.json');
139
+ else fail('lead_dev_mode', 'not enabled');
140
+ if (features.multi_agent_orchestration?.no_new_surface !== false) pass('no_new_surface in features.json');
141
+ else fail('no_new_surface', 'disabled');
142
+ } catch (e) {
143
+ fail('lead_dev_mode', e.message);
144
+ }
145
+
146
+ try {
147
+ const codex = JSON.parse(readFileSync(join(root, '.xray/codex.json'), 'utf8'));
148
+ if (codex.terms?.['69']) pass('codex term 69 (no new surface)');
149
+ else fail('codex term 69', 'missing');
150
+ } catch (e) {
151
+ fail('codex term 69', e.message);
152
+ }
153
+
154
+ // 6. 0xray package xray field (server-config-registry — no fallback)
155
+ try {
156
+ const xrayPkg = JSON.parse(
157
+ readFileSync(join(root, 'node_modules/0xray/package.json'), 'utf8'),
158
+ );
159
+ if (xrayPkg.xray?.mcpServersPath || xrayPkg.xray?.dist) pass('0xray package.json xray field');
160
+ else fail('0xray package.json xray field', 'missing — npm link ~/dev/xray && rebuild');
161
+ } catch (e) {
162
+ fail('0xray package.json xray field', e.message);
163
+ }
164
+
165
+ // 7. CLI health
166
+ try {
167
+ execSync('npx 0xray health', { cwd: root, stdio: 'pipe', encoding: 'utf8' });
168
+ pass('npx 0xray health');
169
+ } catch {
170
+ fail('npx 0xray health');
171
+ }
172
+
173
+ const preToolHook = resolveLiveHook('pre-tool-use.js');
174
+ const sessionHook = resolveLiveHook('session-start.js');
175
+ if (!preToolHook) fail('Live PreToolUse hook path', 'not found in node_modules/0xray');
176
+ else pass('Live PreToolUse hook path', preToolHook);
177
+
178
+ if (!sessionHook) fail('Live SessionStart hook path', 'not found');
179
+ else pass('Live SessionStart hook path', sessionHook);
180
+
181
+ // 8. SessionStart → session-boot.json + activity.log
182
+ const bootPath = join(root, '.xray', 'state', 'session-boot.json');
183
+ const activityBefore = tailActivityLog(root);
184
+ if (sessionHook) {
185
+ try {
186
+ runHook(
187
+ sessionHook,
188
+ {
189
+ hookEventName: 'session_start',
190
+ sessionId: 'verify-suit-probe',
191
+ workspaceRoot: root,
192
+ cwd: root,
193
+ },
194
+ { GROK_HOOK_EVENT: 'session_start', GROK_SESSION_ID: 'verify-suit-probe' },
195
+ );
196
+ if (existsSync(bootPath)) {
197
+ const boot = JSON.parse(readFileSync(bootPath, 'utf8'));
198
+ if (boot.lead_dev_mode === true) pass('session-boot.json written', 'lead_dev_mode=true');
199
+ else fail('session-boot.json', 'lead_dev_mode not true');
200
+ } else {
201
+ fail('session-boot.json', 'missing after SessionStart hook');
202
+ }
203
+ const activityAfter = tailActivityLog(root);
204
+ if (/grok-session-start.*session-boot-written/.test(activityAfter)) {
205
+ pass('SessionStart logged to activity.log');
206
+ } else {
207
+ fail('SessionStart activity.log', 'no grok-session-start entry');
208
+ }
209
+ } catch (e) {
210
+ fail('SessionStart hook probe', e.message?.slice(0, 160));
211
+ }
212
+ }
213
+
214
+ // 9. PreToolUse deny + activity.log (same path Grok uses live)
215
+ if (preToolHook) {
216
+ try {
217
+ const out = runHook(preToolHook, {
218
+ toolName: 'search_replace',
219
+ workspaceRoot: root,
220
+ toolInput: { path: 'src/mcps/new-thing.server.ts', new_string: 'export {}' },
221
+ });
222
+ if (/"decision"\s*:\s*"deny"/.test(out)) pass('PreToolUse deny (live hook path)');
223
+ else fail('PreToolUse deny', `got: ${out.slice(0, 120)}`);
224
+
225
+ const activity = tailActivityLog(root);
226
+ if (/grok-pre-tool-use.*deny/.test(activity)) {
227
+ pass('PreToolUse deny logged to activity.log');
228
+ } else {
229
+ fail('PreToolUse activity.log', 'no grok-pre-tool-use deny entry');
230
+ }
231
+ } catch (e) {
232
+ fail('PreToolUse hook probe', e.message?.slice(0, 160));
233
+ }
234
+ }
235
+
236
+ // 10. MCP enforcer stdio + no server-config-registry fallback (Node probe — no GNU timeout)
237
+ const activityBeforeEnforcer = tailActivityLog(root);
238
+ try {
239
+ await mcpStdioInitializeProbe({
240
+ cwd: root,
241
+ command: 'npx',
242
+ args: ['0xray', 'mcp', 'enforcer'],
243
+ timeoutMs: 6000,
244
+ });
245
+ pass('MCP enforcer stdio');
246
+
247
+ const activityNew = tailActivityLog(root).slice(activityBeforeEnforcer.length);
248
+ if (/using-fallback-path/.test(activityNew)) {
249
+ fail('server-config-registry', 'using-fallback-path in activity.log after enforcer boot');
250
+ } else {
251
+ pass('server-config-registry resolved (no fallback warning)');
252
+ }
253
+ } catch (e) {
254
+ fail('MCP enforcer stdio', e.message?.slice(0, 120));
255
+ }
256
+
257
+ // 11. Orchestrator behavior assertions (0xray@3.4.10+)
258
+ const behaviorScript = join(root, 'node_modules/0xray/scripts/mjs/verify-orchestrator-behavior.mjs');
259
+ if (!existsSync(behaviorScript)) {
260
+ fail('Orchestrator behavior assertions', 'missing script — npm install 0xray@^3.4.10');
261
+ } else {
262
+ try {
263
+ execSync(`node "${behaviorScript}"`, { cwd: root, stdio: 'pipe', encoding: 'utf8' });
264
+ pass('Orchestrator behavior assertions');
265
+ } catch (e) {
266
+ const detail = (e.stderr || e.stdout || e.message || '').toString().slice(0, 160);
267
+ fail('Orchestrator behavior assertions', detail);
268
+ }
269
+ }
270
+
271
+ // 12. Grok delegation + synthesis gate fixtures (0xray@3.5.0+)
272
+ for (const [label, scriptName] of [
273
+ ['Grok delegation gate fixture', 'verify-grok-delegation-gate.mjs'],
274
+ ['Grok synthesis gate fixture', 'verify-grok-synthesis-gate.mjs'],
275
+ ]) {
276
+ const gateScript = join(root, 'node_modules/0xray/scripts/mjs', scriptName);
277
+ if (!existsSync(gateScript)) {
278
+ fail(label, 'missing script — npm install 0xray@main (3.5.0+)');
279
+ } else {
280
+ try {
281
+ execSync(`node "${gateScript}"`, { cwd: root, stdio: 'pipe', encoding: 'utf8' });
282
+ pass(label);
283
+ } catch (e) {
284
+ const detail = (e.stderr || e.stdout || e.message || '').toString().slice(0, 160);
285
+ fail(label, detail);
286
+ }
287
+ }
288
+ }
289
+
290
+ // 13. Installed Grok plugin PostToolUse hook (3.5.0 auto-chain)
291
+ const installedGrokHooks = join(grokPlugin, 'hooks/hooks.json');
292
+ if (existsSync(installedGrokHooks)) {
293
+ const installedRaw = readFileSync(installedGrokHooks, 'utf8');
294
+ if (installedRaw.includes('PostToolUse') && installedRaw.includes('post-tool-use.js')) {
295
+ pass('Grok plugin PostToolUse hook installed');
296
+ } else {
297
+ fail('Grok plugin PostToolUse hook', 'run: npx 0xray grok install --force');
298
+ }
299
+ } else {
300
+ fail('Grok plugin PostToolUse hook', 'hooks.json missing — npx 0xray grok install --force');
301
+ }
302
+
303
+ console.log('\n' + (failed === 0 ? '🎉 Suit wearable — operate within 0xRay.' : `⚠️ ${failed} check(s) failed — fix before tuning.`));
304
+ process.exit(failed === 0 ? 0 : 1);
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Verify Hermes suit bridge for this consumer repo.
4
+ * Run: node scripts/verify-hermes-suit.mjs
5
+ */
6
+ import { execSync } from 'node:child_process';
7
+ import { existsSync, readFileSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { join, resolve } from 'node:path';
10
+ import {
11
+ HERMES_CONFIG_PATH,
12
+ HERMES_PLUGIN_DIR,
13
+ REPERTOIRE_MCP,
14
+ XRAY_MCP_SERVERS,
15
+ ensureConsumerRootMarker,
16
+ } from './suit-bridge-shared.mjs';
17
+
18
+ const root = resolve(import.meta.dirname, '..');
19
+ ensureConsumerRootMarker(
20
+ join(HERMES_PLUGIN_DIR, 'xray-consumer-root.txt'),
21
+ 'npx 0xray hermes install --force',
22
+ );
23
+ let failed = 0;
24
+
25
+ function pass(name, detail = '') {
26
+ console.log(`✅ ${name}${detail ? ` — ${detail}` : ''}`);
27
+ }
28
+
29
+ function fail(name, detail = '') {
30
+ failed++;
31
+ console.error(`❌ ${name}${detail ? ` — ${detail}` : ''}`);
32
+ }
33
+
34
+ // 1. Hermes CLI
35
+ try {
36
+ const hermesPath = execSync('which hermes', { encoding: 'utf8' }).trim();
37
+ pass('hermes cli', hermesPath);
38
+ } catch {
39
+ fail('hermes cli', 'not on PATH');
40
+ }
41
+
42
+ // 2. Plugin installed
43
+ const pluginYaml = join(HERMES_PLUGIN_DIR, 'plugin.yaml');
44
+ existsSync(pluginYaml) ? pass('xray-hermes plugin', HERMES_PLUGIN_DIR) : fail('xray-hermes plugin', 'run: npx 0xray hermes install --force');
45
+
46
+ // 3. Consumer root marker
47
+ const markerPath = join(HERMES_PLUGIN_DIR, 'xray-consumer-root.txt');
48
+ if (existsSync(markerPath)) {
49
+ const marked = readFileSync(markerPath, 'utf8').trim();
50
+ marked === root ? pass('consumer root marker', root) : fail('consumer root marker', `got ${marked}`);
51
+ } else {
52
+ fail('consumer root marker', 'missing');
53
+ }
54
+
55
+ // 4. Plugin enabled
56
+ try {
57
+ const plugins = execSync('hermes plugins list 2>&1', { encoding: 'utf8' })
58
+ .replace(/\x1b\[[0-9;]*m/g, '');
59
+ const enabledLine = plugins.split('\n').find((line) => /│\s*0xray-hermes\b/.test(line));
60
+ if (enabledLine?.includes('enabled') && !enabledLine.includes('not enabled')) {
61
+ pass('0xray-hermes plugin enabled');
62
+ } else {
63
+ fail('0xray-hermes plugin enabled', 'run: hermes plugins enable 0xray-hermes');
64
+ }
65
+ } catch (e) {
66
+ fail('0xray-hermes plugin enabled', e.message?.slice(0, 120));
67
+ }
68
+
69
+ // 5. Plugin .mcp.json
70
+ try {
71
+ const pluginMcp = JSON.parse(readFileSync(join(HERMES_PLUGIN_DIR, '.mcp.json'), 'utf8'));
72
+ const names = Object.keys(pluginMcp.mcpServers ?? {});
73
+ const xrayCount = names.filter((n) => n.startsWith('xray-')).length;
74
+ if (xrayCount === 7) pass('plugin .mcp.json xray servers', '7/7');
75
+ else fail('plugin .mcp.json xray servers', `${xrayCount}/7`);
76
+ } catch (e) {
77
+ fail('plugin .mcp.json', e.message);
78
+ }
79
+
80
+ // 6. Hermes config mcp_servers
81
+ try {
82
+ const listScript = join(root, 'scripts', 'list-hermes-mcp-servers.py');
83
+ const out = execSync(`python3 "${listScript}" "${HERMES_CONFIG_PATH}"`, { encoding: 'utf8' });
84
+ const { names } = JSON.parse(out.trim());
85
+ const xrayCount = names.filter((n) => n.startsWith('xray-')).length;
86
+ if (xrayCount === 7) pass('config.yaml mcp_servers xray', '7/7');
87
+ else fail('config.yaml mcp_servers xray', `${xrayCount}/7`);
88
+ names.includes(REPERTOIRE_MCP.name)
89
+ ? pass('config.yaml mcp_servers repertoire')
90
+ : fail('config.yaml mcp_servers repertoire', 'missing');
91
+ } catch (e) {
92
+ fail('config.yaml mcp_servers', e.message?.slice(0, 160));
93
+ }
94
+
95
+ // 7. Bridge health
96
+ try {
97
+ const out = execSync(
98
+ `printf '%s' '{"command":"health"}' | node "${join(HERMES_PLUGIN_DIR, 'bridge.mjs')}" --cwd "${root}"`,
99
+ { encoding: 'utf8', cwd: root, env: { ...process.env, XRAY_ROOT: root } },
100
+ );
101
+ const health = JSON.parse(out.trim());
102
+ if (health.framework === 'loaded') pass('bridge health', 'framework loaded');
103
+ else fail('bridge health', JSON.stringify(health).slice(0, 120));
104
+ if (health.projectRoot === root) pass('bridge projectRoot', root);
105
+ else fail('bridge projectRoot', `got ${health.projectRoot}`);
106
+ } catch (e) {
107
+ fail('bridge health', e.message?.slice(0, 160));
108
+ }
109
+
110
+ // 8. .xray contract (shared with Grok)
111
+ for (const f of ['codex.json', 'features.json', 'config.json']) {
112
+ existsSync(join(root, '.xray', f)) ? pass(`.xray/${f}`) : fail(`.xray/${f}`, 'missing');
113
+ }
114
+
115
+ // 9. MCP enforcer stdio (same surface Hermes mcp_servers uses)
116
+ try {
117
+ const init = JSON.stringify({
118
+ jsonrpc: '2.0',
119
+ id: 1,
120
+ method: 'initialize',
121
+ params: {
122
+ protocolVersion: '2024-11-05',
123
+ capabilities: {},
124
+ clientInfo: { name: 'verify-hermes-suit', version: '1.0' },
125
+ },
126
+ });
127
+ const out = execSync(
128
+ `printf '%s\\n' '${init.replace(/'/g, "'\\''")}' | timeout 12 npx 0xray mcp enforcer 2>/dev/null | head -1`,
129
+ { cwd: root, encoding: 'utf8', shell: '/bin/bash' },
130
+ );
131
+ if (out.includes('serverInfo')) pass('MCP enforcer stdio probe');
132
+ else fail('MCP enforcer stdio probe', out.slice(0, 120));
133
+ } catch (e) {
134
+ fail('MCP enforcer stdio probe', e.message?.slice(0, 160));
135
+ }
136
+
137
+ // 10. Plugin tool surface (plugin.yaml — live session loads on restart)
138
+ try {
139
+ const pluginYaml = readFileSync(join(HERMES_PLUGIN_DIR, 'plugin.yaml'), 'utf8');
140
+ const pluginTools = ['xray_validate', 'xray_codex_check', 'xray_health', 'xray_hooks'];
141
+ const found = pluginTools.filter((t) => pluginYaml.includes(t));
142
+ if (found.length === pluginTools.length) pass('plugin tools (plugin.yaml)', pluginTools.join(', '));
143
+ else fail('plugin tools (plugin.yaml)', `found ${found.length}/4`);
144
+ } catch (e) {
145
+ fail('plugin tools (plugin.yaml)', e.message?.slice(0, 120));
146
+ }
147
+
148
+ // 11. Delegation gate fixture (0xray@3.5.1+)
149
+ const hermesGateScript = join(root, 'node_modules/0xray/scripts/mjs/verify-hermes-delegation-gate.mjs');
150
+ if (existsSync(hermesGateScript)) {
151
+ try {
152
+ execSync(`node "${hermesGateScript}"`, { cwd: root, stdio: 'pipe', encoding: 'utf8' });
153
+ pass('Hermes delegation gate fixture', '4/4');
154
+ } catch (e) {
155
+ const detail = (e.stdout || e.stderr || e.message || '').split('\n').slice(-3).join(' ').slice(0, 160);
156
+ fail('Hermes delegation gate fixture', detail);
157
+ }
158
+ } else {
159
+ fail('Hermes delegation gate fixture', 'missing script — npm install 0xray@^3.5.1');
160
+ }
161
+
162
+ console.log('\n' + (failed === 0 ? '🎉 Hermes suit wearable.' : `⚠️ ${failed} Hermes check(s) failed.`));
163
+ process.exit(failed === 0 ? 0 : 1);
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Verify OpenClaw suit bridge for this consumer repo (native 0xray install).
4
+ */
5
+ import { execSync } from 'node:child_process';
6
+ import { existsSync, readFileSync } from 'node:fs';
7
+ import { homedir } from 'node:os';
8
+ import { join, resolve } from 'node:path';
9
+ import { REPERTOIRE_MCP, XRAY_MCP_SERVERS, ensureConsumerRootMarker } from './suit-bridge-shared.mjs';
10
+
11
+ const root = resolve(import.meta.dirname, '..');
12
+ const OPENCLAW_STATE = join(homedir(), '.openclaw');
13
+ ensureConsumerRootMarker(
14
+ join(OPENCLAW_STATE, 'xray-consumer-root.txt'),
15
+ 'npx 0xray openclaw install --force',
16
+ );
17
+ const OPENCLAW_CONFIG = join(OPENCLAW_STATE, 'openclaw.json');
18
+ let failed = 0;
19
+
20
+ function pass(name, detail = '') {
21
+ console.log(`✅ ${name}${detail ? ` — ${detail}` : ''}`);
22
+ }
23
+
24
+ function fail(name, detail = '') {
25
+ failed++;
26
+ console.error(`❌ ${name}${detail ? ` — ${detail}` : ''}`);
27
+ }
28
+
29
+ // 1. OpenClaw CLI
30
+ try {
31
+ const openclawPath = execSync('which openclaw', { encoding: 'utf8' }).trim();
32
+ pass('openclaw cli', openclawPath);
33
+ } catch {
34
+ fail('openclaw cli', 'not on PATH');
35
+ }
36
+
37
+ // 2. Consumer integration config
38
+ const consumerConfig = join(root, '.xray', 'config', 'openclaw.json');
39
+ existsSync(consumerConfig)
40
+ ? pass('consumer openclaw.json', consumerConfig)
41
+ : fail('consumer openclaw.json', 'run npx 0xray openclaw install');
42
+
43
+ // 3. Consumer root marker
44
+ const markerPath = join(OPENCLAW_STATE, 'xray-consumer-root.txt');
45
+ if (existsSync(markerPath)) {
46
+ const marked = readFileSync(markerPath, 'utf8').trim();
47
+ marked === root ? pass('consumer root marker', root) : fail('consumer root marker', `got ${marked}`);
48
+ } else {
49
+ fail('consumer root marker', 'missing');
50
+ }
51
+
52
+ // 4. xray-orchestrator skill synced
53
+ const orchestratorSkill = join(OPENCLAW_STATE, 'skills', 'xray-orchestrator', 'SKILL.md');
54
+ existsSync(orchestratorSkill)
55
+ ? pass('xray-orchestrator skill', orchestratorSkill)
56
+ : fail('xray-orchestrator skill', 'missing');
57
+
58
+ // 5. openclaw mcp list
59
+ try {
60
+ const listed = execSync('openclaw mcp list 2>&1', { encoding: 'utf8', timeout: 20000 });
61
+ const xrayListed = XRAY_MCP_SERVERS.filter((s) => listed.includes(s.name));
62
+ if (xrayListed.length === 7) pass('openclaw mcp list xray', '7/7');
63
+ else fail('openclaw mcp list xray', `${xrayListed.length}/7`);
64
+ listed.includes(REPERTOIRE_MCP.name)
65
+ ? pass('openclaw mcp list repertoire')
66
+ : fail('openclaw mcp list repertoire', 'not listed');
67
+ } catch (e) {
68
+ fail('openclaw mcp list', e.message?.slice(0, 160));
69
+ }
70
+
71
+ // 6. openclaw.json mcp.servers block
72
+ try {
73
+ const config = JSON.parse(readFileSync(OPENCLAW_CONFIG, 'utf8'));
74
+ const servers = config.mcp?.servers ?? {};
75
+ const xrayCount = Object.keys(servers).filter((n) => n.startsWith('xray-')).length;
76
+ if (xrayCount === 7) pass('openclaw.json mcp.servers xray', '7/7');
77
+ else fail('openclaw.json mcp.servers xray', `${xrayCount}/7`);
78
+ if (servers[REPERTOIRE_MCP.name]) pass('openclaw.json mcp.servers repertoire');
79
+ else fail('openclaw.json mcp.servers repertoire', 'missing');
80
+ if (servers['xray-enforcer']?.env?.XRAY_ROOT === root) {
81
+ pass('openclaw mcp XRAY_ROOT', root);
82
+ } else {
83
+ fail('openclaw mcp XRAY_ROOT', `got ${servers['xray-enforcer']?.env?.XRAY_ROOT}`);
84
+ }
85
+ } catch (e) {
86
+ fail('openclaw.json mcp.servers', e.message?.slice(0, 160));
87
+ }
88
+
89
+ // 7. .xray contract
90
+ for (const f of ['codex.json', 'features.json', 'config.json']) {
91
+ existsSync(join(root, '.xray', f)) ? pass(`.xray/${f}`) : fail(`.xray/${f}`, 'missing');
92
+ }
93
+
94
+ // 8. MCP stdio probe
95
+ try {
96
+ const init = JSON.stringify({
97
+ jsonrpc: '2.0',
98
+ id: 1,
99
+ method: 'initialize',
100
+ params: {
101
+ protocolVersion: '2024-11-05',
102
+ capabilities: {},
103
+ clientInfo: { name: 'verify-openclaw-suit', version: '1.0' },
104
+ },
105
+ });
106
+ const out = execSync(
107
+ `printf '%s\\n' '${init.replace(/'/g, "'\\''")}' | timeout 12 npx 0xray mcp enforcer 2>/dev/null | head -1`,
108
+ { cwd: root, encoding: 'utf8', shell: '/bin/bash' },
109
+ );
110
+ if (out.includes('serverInfo')) pass('MCP enforcer stdio probe');
111
+ else fail('MCP enforcer stdio probe', out.slice(0, 120));
112
+ } catch (e) {
113
+ fail('MCP enforcer stdio probe', e.message?.slice(0, 160));
114
+ }
115
+
116
+ // 9. npx 0xray health
117
+ try {
118
+ execSync('npx 0xray health', { cwd: root, stdio: 'pipe', encoding: 'utf8' });
119
+ pass('npx 0xray health');
120
+ } catch {
121
+ fail('npx 0xray health');
122
+ }
123
+
124
+ console.log('\n' + (failed === 0 ? '🎉 OpenClaw suit wearable.' : `⚠️ ${failed} OpenClaw check(s) failed.`));
125
+ process.exit(failed === 0 ? 0 : 1);