0xray 3.3.1 → 3.4.2

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 (70) hide show
  1. package/.opencode/enforcer-config.json +1 -4
  2. package/.xray/codex.json +598 -0
  3. package/.xray/config.json +25 -0
  4. package/.xray/features.json +146 -0
  5. package/.xray/features.schema.json +33 -0
  6. package/AGENTS-consumer.md +127 -10
  7. package/AGENTS.md +172 -56
  8. package/README.md +159 -82
  9. package/SKILLS.md +79 -53
  10. package/dist/AGENTS.md +172 -56
  11. package/dist/CHANGELOG.md +90 -51
  12. package/dist/README.md +159 -82
  13. package/dist/SKILLS.md +79 -53
  14. package/dist/analytics/routing-refiner.js +0 -2
  15. package/dist/core/boot-orchestrator.js +9 -1
  16. package/dist/core/features-config.js +5 -1
  17. package/dist/integrations/grok/grok-cli.js +47 -25
  18. package/dist/integrations/grok/hooks/grok-hook-activity.js +53 -0
  19. package/dist/integrations/grok/hooks/grok-hook-utils.js +261 -0
  20. package/dist/integrations/grok/hooks/pre-tool-use.js +85 -115
  21. package/dist/integrations/grok/hooks/session-start.js +52 -0
  22. package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +20 -3
  23. package/dist/integrations/hermes-agent/bridge.mjs +11 -1
  24. package/dist/mcps/config/server-config-registry.js +4 -3
  25. package/dist/mcps/orchestrator/handlers/complexity-handler.js +33 -3
  26. package/dist/mcps/orchestrator/server.js +1 -1
  27. package/dist/mcps/registry.json +0 -1
  28. package/dist/mcps/researcher.server.js +3 -0
  29. package/dist/nucleus/autonomy-kernel.js +176 -0
  30. package/dist/nucleus/index.js +1 -0
  31. package/dist/orchestrator/universal-registry-bridge.js +1 -1
  32. package/dist/skills/boot-orchestrator/SKILL.md +1 -5
  33. package/dist/skills/orchestrator/SKILL.md +35 -12
  34. package/dist/skills/registry.json +0 -1
  35. package/dist/skills/researcher/SKILL.md +17 -4
  36. package/dist/skills/strategist/SKILL.md +12 -0
  37. package/dist/skills/testing-strategy/SKILL.md +13 -2
  38. package/package.json +23 -8
  39. package/scripts/node/consumer-install-smoke.mjs +163 -0
  40. package/scripts/node/enforce-agents-md.mjs +0 -26
  41. package/scripts/node/enforce-version-compliance.sh +0 -1
  42. package/scripts/node/enforce-version-compliance.ts +0 -10
  43. package/scripts/node/install-bridges.cjs +154 -39
  44. package/scripts/node/pre-publish-guard.js +108 -165
  45. package/scripts/node/reconcile-version.mjs +147 -0
  46. package/scripts/node/release-gate.mjs +66 -0
  47. package/scripts/node/release.js +5 -147
  48. package/scripts/node/release.mjs +106 -181
  49. package/scripts/node/setup.cjs +21 -26
  50. package/scripts/node/sync-versions.mjs +12 -134
  51. package/scripts/node/universal-version-manager.js +22 -4
  52. package/scripts/node/validate-release-docs.mjs +299 -0
  53. package/scripts/node/version-manager.mjs +225 -49
  54. package/src/integrations/grok/plugin/0xray/hooks/hooks.json +20 -3
  55. package/src/mcps/config/__tests__/server-config-registry.test.ts +9 -0
  56. package/src/mcps/config/server-config-registry.ts +5 -3
  57. package/src/mcps/orchestrator/handlers/complexity-handler.ts +54 -3
  58. package/src/mcps/orchestrator/server.ts +1 -1
  59. package/src/mcps/registry.json +0 -1
  60. package/src/mcps/researcher.server.ts +4 -0
  61. package/src/opencode/enforcer-config.json +1 -4
  62. package/src/skills/boot-orchestrator/SKILL.md +1 -5
  63. package/src/skills/orchestrator/SKILL.md +35 -12
  64. package/src/skills/registry.json +0 -1
  65. package/src/skills/researcher/SKILL.md +17 -4
  66. package/src/skills/strategist/SKILL.md +12 -0
  67. package/src/skills/testing-strategy/SKILL.md +13 -2
  68. package/xray/config.json +25 -0
  69. package/xray/features.json +146 -0
  70. package/xray/features.schema.json +33 -0
@@ -2,30 +2,29 @@
2
2
 
3
3
  /**
4
4
  * Pre-Publish Validation Script
5
- *
6
- * Enforces all requirements before publishing:
7
- * 1. Git status clean (committed & pushed)
8
- * 2. package.json version matches version manager
9
- * 3. Version been manager has run
10
- * 4. All tests pass
11
- * 5. Build succeeds
12
- *
13
- * Usage:
14
- * node scripts/node/pre-publish-guard.js
15
- * npm run pre-publish-guard
5
+ *
6
+ * Full mode: git + reconcile + build + test + consumer smoke
7
+ * --verify-only: git + reconcile + (optional smoke via release-gate)
8
+ * --skip-smoke: skip consumer install smoke
9
+ * --skip-build / --skip-tests: skip redundant steps when gate already ran
16
10
  */
17
11
 
18
12
  import { execSync } from 'child_process';
19
13
  import fs from 'fs';
20
14
  import path from 'path';
21
15
  import { fileURLToPath } from 'url';
22
- import { createRequire } from 'module';
23
16
 
24
- const require = createRequire(import.meta.url);
25
17
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
26
18
  const rootDir = path.resolve(__dirname, '../..');
27
19
 
28
- // Colors
20
+ const verifyOnly = process.argv.includes('--verify-only');
21
+ const skipSmoke =
22
+ process.argv.includes('--skip-smoke') ||
23
+ process.env.SKIP_CONSUMER_SMOKE === '1' ||
24
+ verifyOnly;
25
+ const skipBuild = process.argv.includes('--skip-build') || verifyOnly;
26
+ const skipTests = process.argv.includes('--skip-tests') || verifyOnly;
27
+
29
28
  const RED = '\x1b[31m';
30
29
  const GREEN = '\x1b[32m';
31
30
  const YELLOW = '\x1b[33m';
@@ -48,224 +47,168 @@ function log(msg, type = 'info') {
48
47
 
49
48
  function run(cmd, options = {}) {
50
49
  try {
51
- return execSync(cmd, {
52
- cwd: rootDir,
53
- stdio: 'pipe',
50
+ return execSync(cmd, {
51
+ cwd: rootDir,
52
+ stdio: 'pipe',
54
53
  encoding: 'utf-8',
55
- ...options
54
+ ...options,
56
55
  }).trim();
57
- } catch (e) {
56
+ } catch {
58
57
  return null;
59
58
  }
60
59
  }
61
60
 
61
+ /** Runtime/generated paths only — never ignore scripts/, src/, or CHANGELOG */
62
+ const ARTIFACT_PREFIXES = [
63
+ '.opencode/codex.codex',
64
+ '.opencode/enforcer-config.json',
65
+ '.opencode/package.json',
66
+ '.opencode/command/',
67
+ '.opencode/activity-report.json',
68
+ '.opencode/init.sh',
69
+ '.xray/inference/',
70
+ 'logs/',
71
+ 'backups/',
72
+ '.ci-reports/',
73
+ ];
74
+
62
75
  function checkGitStatus() {
63
76
  log('Checking git status...', 'step');
64
-
65
- // Check for uncommitted changes
77
+
66
78
  const status = run('git status --porcelain');
67
79
  if (status && status.length > 0) {
68
- // Allow untracked files but not uncommitted changes
69
- const lines = status.split('\n').filter(l => !l.startsWith('??'));
70
-
71
- // Filter out files modified by prepare-consumer and version:sync (build artifacts)
72
- // These are generated from source and don't need separate commits
73
- const artifactPrefixes = [
74
- '.opencode/codex.codex',
75
- '.opencode/enforcer-config.json',
76
- '.opencode/package.json',
77
- '.opencode/command/',
78
- '.opencode/activity-report.json',
79
- 'command/',
80
- 'enforcer-config.json',
81
- 'docs/',
82
- 'tests/',
83
- 'tweets/',
84
- 'scripts/',
85
- 'src/__tests__/',
86
- 'src/skills/registry.json',
87
- 'CHANGELOG.md',
88
- 'AGENTS.md',
89
- 'AGENTS-full.md',
90
- 'AGENTS-consumer.md',
91
- 'README.md',
92
- 'logs/',
93
- 'backups/',
94
- 'Users/',
95
- ];
96
-
97
- const realChanges = lines.filter(l => {
98
- const filePath = l.slice(3); // skip status prefix (e.g., " M " or "M ")
99
- return !artifactPrefixes.some(prefix => filePath.startsWith(prefix));
80
+ const lines = status.split('\n').filter((l) => !l.startsWith('??'));
81
+
82
+ const realChanges = lines.filter((l) => {
83
+ const filePath = l.slice(2).trim();
84
+ return !ARTIFACT_PREFIXES.some((prefix) => filePath.startsWith(prefix));
100
85
  });
101
-
86
+
102
87
  if (realChanges.length > 0) {
103
- errors.push(`Git has uncommitted source changes:\n${realChanges.join('\n')}`);
104
- log('Git has uncommitted source changes', 'error');
88
+ errors.push(`Git has uncommitted changes:\n${realChanges.join('\n')}`);
89
+ log('Uncommitted changes block release', 'error');
105
90
  return false;
106
91
  }
107
-
92
+
108
93
  if (lines.length > 0) {
109
- log('Uncommitted build artifacts present (OK — generated by version sync)', 'info');
94
+ log('Generated artifacts present (OK)', 'info');
110
95
  }
111
- log('Untracked files present (OK)', 'warn');
112
96
  }
113
-
114
- // Check if current branch is up to date with remote
97
+
115
98
  const currentBranch = run('git rev-parse --abbrev-ref HEAD');
116
99
  const remoteCommit = run(`git rev-parse origin/${currentBranch} 2>/dev/null || echo ""`);
117
100
  const localCommit = run('git rev-parse HEAD');
118
-
101
+
119
102
  if (remoteCommit && remoteCommit !== localCommit) {
120
- errors.push(`Local commit ${localCommit.slice(0,7)} not pushed to origin/${currentBranch}`);
103
+ errors.push(`Local commit ${localCommit.slice(0, 7)} not pushed to origin/${currentBranch}`);
121
104
  log(`Not pushed to origin/${currentBranch}`, 'error');
122
105
  return false;
123
106
  }
124
-
125
- log(`Git status OK (committed & pushed)`, 'success');
107
+
108
+ log('Git status OK', 'success');
126
109
  return true;
127
110
  }
128
111
 
129
- function checkVersionSync() {
130
- log('Checking version synchronization...', 'step');
131
-
132
- // Load version manager
133
- const vmPath = path.join(rootDir, 'scripts/node/universal-version-manager.js');
134
- const vmContent = fs.readFileSync(vmPath, 'utf-8');
135
- const versionMatch = vmContent.match(/version:\s*"([^"]+)"/);
136
-
137
- if (!versionMatch) {
138
- errors.push('Cannot find version in version manager');
112
+ function checkVersionReconcile() {
113
+ log('Checking version reconcile...', 'step');
114
+ try {
115
+ execSync('node scripts/node/reconcile-version.mjs --check', {
116
+ cwd: rootDir,
117
+ stdio: 'inherit',
118
+ });
119
+ log('Version reconcile OK', 'success');
120
+ return true;
121
+ } catch {
122
+ errors.push('Version reconcile failed — run: npm run version:reconcile');
139
123
  return false;
140
124
  }
141
-
142
- const vmVersion = versionMatch[1];
143
-
144
- // Load package.json
145
- const pkgPath = path.join(rootDir, 'package.json');
146
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
147
- const pkgVersion = pkg.version;
148
-
149
- if (pkgVersion !== vmVersion) {
150
- // package.json is often 1 ahead of UVM after npm version patch — non-blocking
151
- warnings.push(`Version mismatch: package.json (${pkgVersion}) != version manager (${vmVersion})`);
152
- log(`Version mismatch: package.json=${pkgVersion}, version-manager=${vmVersion}`, 'warn');
153
- return true; // non-blocking — matches pre-commit hook behavior
154
- }
155
-
156
- log(`Versions aligned: ${pkgVersion}`, 'success');
157
- return true;
158
125
  }
159
126
 
160
- function checkVersionManagerRan() {
161
- log('Checking version manager was run...', 'step');
162
-
163
- // Check that version manager script has current date as lastUpdated
164
- const vmPath = path.join(rootDir, 'scripts/node/universal-version-manager.js');
165
- const vmContent = fs.readFileSync(vmPath, 'utf-8');
166
-
167
- // Check that files have been updated (look for recent timestamps or version)
168
- const { resolveConfigPath } = require('../helpers/resolve-config-path.cjs');
169
- let features;
127
+ function runBuild() {
128
+ if (skipBuild) {
129
+ log('Build skipped', 'info');
130
+ return true;
131
+ }
132
+ log('Running build...', 'step');
170
133
  try {
171
- const featuresPath = resolveConfigPath('features.json', rootDir);
172
- features = JSON.parse(fs.readFileSync(featuresPath, 'utf-8'));
173
- // Version manager should have updated version fields
174
- if (!features.version && !features.xray_version) {
175
- warnings.push('features.json may not have been synced by version manager');
176
- log('features.json may need version sync', 'warn');
177
- } else {
178
- log('Version manager appears to have run', 'success');
179
- }
134
+ execSync('npm run build', { cwd: rootDir, stdio: 'inherit' });
135
+ log('Build succeeded', 'success');
136
+ return true;
180
137
  } catch {
181
- log('features.json not found — skipping version sync check', 'warn');
138
+ errors.push('Build failed');
139
+ return false;
182
140
  }
183
-
184
- return true;
185
141
  }
186
142
 
187
143
  function runTests() {
144
+ if (skipTests) {
145
+ log('Tests skipped', 'info');
146
+ return true;
147
+ }
188
148
  log('Running tests...', 'step');
189
-
190
149
  try {
191
150
  execSync('npm test', { cwd: rootDir, stdio: 'inherit' });
192
151
  log('Tests passed', 'success');
193
152
  return true;
194
- } catch (e) {
153
+ } catch {
195
154
  errors.push('Tests failed');
196
- log('Tests failed', 'error');
197
155
  return false;
198
156
  }
199
157
  }
200
158
 
201
- function runBuild() {
202
- log('Running build...', 'step');
203
-
159
+ function runConsumerSmoke() {
160
+ if (skipSmoke) {
161
+ log('Consumer smoke skipped', 'warn');
162
+ return true;
163
+ }
164
+ log('Running consumer install smoke...', 'step');
204
165
  try {
205
- execSync('npm run build', { cwd: rootDir, stdio: 'inherit' });
206
- log('Build succeeded', 'success');
166
+ execSync('node scripts/node/consumer-install-smoke.mjs', { cwd: rootDir, stdio: 'inherit' });
167
+ log('Consumer smoke passed', 'success');
207
168
  return true;
208
- } catch (e) {
209
- errors.push('Build failed');
210
- log('Build failed', 'error');
169
+ } catch {
170
+ errors.push('Consumer install smoke failed');
211
171
  return false;
212
172
  }
213
173
  }
214
174
 
215
175
  function main() {
216
176
  console.log('\n' + '='.repeat(60));
217
- log('PRE-PUBLISH VALIDATION', 'step');
177
+ log(verifyOnly ? 'PRE-PUBLISH VERIFY' : 'PRE-PUBLISH VALIDATION', 'step');
218
178
  console.log('='.repeat(60) + '\n');
219
-
179
+
220
180
  const checks = [
221
- { name: 'Git Status', fn: checkGitStatus },
222
- { name: 'Version Sync', fn: checkVersionSync },
223
- { name: 'Version Manager', fn: checkVersionManagerRan },
224
- { name: 'Build', fn: runBuild },
225
- { name: 'Tests', fn: runTests },
181
+ checkGitStatus,
182
+ checkVersionReconcile,
183
+ runBuild,
184
+ runTests,
185
+ runConsumerSmoke,
226
186
  ];
227
-
187
+
228
188
  let allPassed = true;
229
- for (const check of checks) {
230
- if (!check.fn()) {
189
+ for (const fn of checks) {
190
+ if (!fn()) {
231
191
  allPassed = false;
232
192
  break;
233
193
  }
234
194
  }
235
-
195
+
236
196
  console.log('\n' + '='.repeat(60));
237
-
197
+
238
198
  if (allPassed) {
239
- log('ALL CHECKS PASSED - Ready to publish!', 'success');
199
+ log('ALL CHECKS PASSED', 'success');
240
200
  console.log('='.repeat(60) + '\n');
241
- console.log(`Run: ${GREEN}npm publish${RESET}\n`);
242
201
  process.exit(0);
243
- } else {
244
- log('VALIDATION FAILED', 'error');
245
- console.log('='.repeat(60) + '\n');
246
-
247
- if (errors.length > 0) {
248
- console.log(`${RED}Errors:${RESET}`);
249
- errors.forEach(e => console.log(` • ${e}`));
250
- console.log('');
251
- }
252
-
253
- if (warnings.length > 0) {
254
- console.log(`${YELLOW}Warnings:${RESET}`);
255
- warnings.forEach(w => console.log(` • ${w}`));
256
- console.log('');
257
- }
258
-
259
- console.log(`${YELLOW}To fix:${RESET}`);
260
- console.log(' 1. Commit & push all changes: git add -A && git commit -m "..." && git push');
261
- console.log(' 2. Bump version in package.json');
262
- console.log(' 3. Update version in scripts/node/universal-version-manager.js');
263
- console.log(' 4. Run: npm run version:sync');
264
- console.log(' 5. Commit & push version changes');
265
- console.log(' 6. Run: npm run pre-publish-guard\n');
266
-
267
- process.exit(1);
268
202
  }
203
+
204
+ log('VALIDATION FAILED', 'error');
205
+ console.log('='.repeat(60) + '\n');
206
+ if (errors.length) {
207
+ console.log(`${RED}Errors:${RESET}`);
208
+ errors.forEach((e) => console.log(` • ${e}`));
209
+ }
210
+ console.log(`\n${YELLOW}Fix:${RESET} npm run release:gate or npm run version:reconcile\n`);
211
+ process.exit(1);
269
212
  }
270
213
 
271
- main();
214
+ main();
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * reconcile-version.mjs — Single version truth before bump/tag/publish.
4
+ *
5
+ * Sources: npm registry, package.json, latest git tag.
6
+ *
7
+ * Usage:
8
+ * node scripts/node/reconcile-version.mjs # report
9
+ * node scripts/node/reconcile-version.mjs --check # fail if not publish-ready
10
+ * node scripts/node/reconcile-version.mjs patch # print next patch target
11
+ * node scripts/node/reconcile-version.mjs patch --apply # write package.json
12
+ */
13
+
14
+ import { execSync } from "child_process";
15
+ import fs from "fs";
16
+ import path from "path";
17
+ import { fileURLToPath } from "url";
18
+
19
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
20
+ const rootDir = path.resolve(__dirname, "../..");
21
+ const pkgPath = path.join(rootDir, "package.json");
22
+
23
+ const args = process.argv.slice(2);
24
+ const checkMode = args.includes("--check");
25
+ const applyMode = args.includes("--apply");
26
+ const printTarget = args.includes("--print-target");
27
+ const bumpType = args.find((a) => ["major", "minor", "patch"].includes(a)) || null;
28
+
29
+ function run(cmd) {
30
+ try {
31
+ return execSync(cmd, { cwd: rootDir, encoding: "utf-8" }).trim();
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ function readLocalVersion() {
38
+ return JSON.parse(fs.readFileSync(pkgPath, "utf-8")).version;
39
+ }
40
+
41
+ function readPublishedVersion() {
42
+ const v = run("npm view 0xray version");
43
+ return v || "0.0.0";
44
+ }
45
+
46
+ function readLatestTag() {
47
+ const tag = run('git describe --tags --abbrev=0 2>/dev/null');
48
+ if (!tag) return null;
49
+ return tag.replace(/^v/, "");
50
+ }
51
+
52
+ function parseVersion(version) {
53
+ const [major, minor, patch] = version.split(".").map((n) => parseInt(n, 10));
54
+ return { major, minor, patch };
55
+ }
56
+
57
+ function bumpVersion(version, type) {
58
+ const v = parseVersion(version);
59
+ switch (type) {
60
+ case "major":
61
+ return `${v.major + 1}.0.0`;
62
+ case "minor":
63
+ return `${v.major}.${v.minor + 1}.0`;
64
+ case "patch":
65
+ return `${v.major}.${v.minor}.${v.patch + 1}`;
66
+ default:
67
+ return version;
68
+ }
69
+ }
70
+
71
+ function compare(a, b) {
72
+ const va = parseVersion(a);
73
+ const vb = parseVersion(b);
74
+ if (va.major !== vb.major) return va.major - vb.major;
75
+ if (va.minor !== vb.minor) return va.minor - vb.minor;
76
+ return va.patch - vb.patch;
77
+ }
78
+
79
+ function maxVersion(...versions) {
80
+ return versions.reduce((best, cur) => (compare(cur, best) > 0 ? cur : best));
81
+ }
82
+
83
+ function fail(msg) {
84
+ console.error(`❌ ${msg}`);
85
+ process.exit(1);
86
+ }
87
+
88
+ function main() {
89
+ const local = readLocalVersion();
90
+ const npm = readPublishedVersion();
91
+ const tag = readLatestTag();
92
+
93
+ const baseline = maxVersion(npm, tag || "0.0.0");
94
+ const target = bumpType ? bumpVersion(baseline, bumpType) : local;
95
+
96
+ if (printTarget && bumpType) {
97
+ console.log(target);
98
+ return;
99
+ }
100
+
101
+ console.log("\n=== Version Reconcile ===\n");
102
+ console.log(` npm registry : ${npm}`);
103
+ console.log(` package.json : ${local}`);
104
+ console.log(` latest tag : ${tag ? `v${tag}` : "(none)"}`);
105
+ if (bumpType) {
106
+ console.log(` bump ${bumpType} : ${target} (from baseline ${baseline})`);
107
+ }
108
+
109
+ if (applyMode && bumpType) {
110
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
111
+ pkg.version = target;
112
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
113
+ console.log(`\n✅ Updated package.json → ${target}`);
114
+ }
115
+
116
+ if (!checkMode) {
117
+ if (compare(local, npm) <= 0 && !bumpType) {
118
+ console.log("\n⚠️ Local version is not ahead of npm — bump before publishing.");
119
+ } else {
120
+ console.log("\n✅ Reconcile report complete.");
121
+ }
122
+ return;
123
+ }
124
+
125
+ // Strict checks for release gate / pre-tag
126
+ if (compare(local, npm) <= 0) {
127
+ fail(`package.json (${local}) must be > npm (${npm}). Run: node scripts/node/reconcile-version.mjs patch --apply`);
128
+ }
129
+
130
+ if (tag && compare(local, tag) < 0) {
131
+ fail(`package.json (${local}) is behind latest tag v${tag}`);
132
+ }
133
+
134
+ if (tag && compare(local, tag) === 0 && compare(npm, local) < 0) {
135
+ fail(`tag v${tag} exists but npm is only ${npm} — publish or retag`);
136
+ }
137
+
138
+ if (tag && compare(local, tag) > 0 && compare(npm, tag) === 0) {
139
+ console.log(`✅ Ready to tag v${local} (npm at ${npm})`);
140
+ } else if (compare(local, npm) > 0) {
141
+ console.log(`✅ Ready to publish ${local} (npm at ${npm})`);
142
+ } else {
143
+ console.log("✅ Version reconcile OK");
144
+ }
145
+ }
146
+
147
+ main();
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * release-gate.mjs — Single release checkpoint.
4
+ *
5
+ * Modes:
6
+ * (default) build + test + consumer smoke — run once before commit
7
+ * --verify-only git + reconcile + release docs + consumer smoke — after push, before tag
8
+ * --skip-smoke skip consumer install smoke
9
+ * --skip-docs skip release docs validation (not recommended for publish)
10
+ *
11
+ * Usage:
12
+ * node scripts/node/release-gate.mjs
13
+ * node scripts/node/release-gate.mjs --verify-only
14
+ */
15
+
16
+ import { execSync } from "child_process";
17
+ import path from "path";
18
+ import { fileURLToPath } from "url";
19
+
20
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
+ const rootDir = path.resolve(__dirname, "../..");
22
+ const verifyOnly = process.argv.includes("--verify-only");
23
+ const skipSmoke = process.argv.includes("--skip-smoke");
24
+ const skipDocs = process.argv.includes("--skip-docs");
25
+
26
+ function step(label, cmd) {
27
+ console.log(`\n${"─".repeat(60)}\n🔄 ${label}\n${"─".repeat(60)}\n`);
28
+ execSync(cmd, { cwd: rootDir, stdio: "inherit" });
29
+ }
30
+
31
+ function main() {
32
+ const mode = verifyOnly ? "verify" : "full";
33
+ console.log("\n╔════════════════════════════════════════════════════════╗");
34
+ console.log(`║ 🛡️ 0xRay Release Gate (${mode})`.padEnd(57) + "║");
35
+ console.log("╚════════════════════════════════════════════════════════╝");
36
+
37
+ try {
38
+ if (verifyOnly) {
39
+ step("1/4 Git + reconcile", "node scripts/node/pre-publish-guard.js --verify-only");
40
+ if (!skipDocs) {
41
+ step("2/4 Release docs", "node scripts/node/validate-release-docs.mjs");
42
+ }
43
+ if (!skipSmoke) {
44
+ step("3/4 Consumer install smoke", "node scripts/node/consumer-install-smoke.mjs");
45
+ }
46
+ } else {
47
+ step("1/4 Build", "npm run build");
48
+ step("2/4 Tests", "npm test");
49
+ if (!skipSmoke) {
50
+ step("3/4 Consumer install smoke", "node scripts/node/consumer-install-smoke.mjs");
51
+ }
52
+ if (!skipDocs && process.argv.includes("--with-docs")) {
53
+ step("4/4 Release docs", "node scripts/node/validate-release-docs.mjs");
54
+ }
55
+ }
56
+
57
+ console.log("\n╔════════════════════════════════════════════════════════╗");
58
+ console.log("║ ✅ RELEASE GATE PASSED ║");
59
+ console.log("╚════════════════════════════════════════════════════════╝\n");
60
+ } catch {
61
+ console.error("\n❌ RELEASE GATE FAILED — do not tag or publish\n");
62
+ process.exit(1);
63
+ }
64
+ }
65
+
66
+ main();