0xray 4.0.1 → 4.0.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.
- package/.xray/features.json +1 -1
- package/AGENTS-consumer.md +4 -4
- package/AGENTS.md +3 -3
- package/README.md +1 -1
- package/SKILLS.md +1 -1
- package/dist/AGENTS.md +3 -3
- package/dist/CHANGELOG.md +36 -0
- package/dist/README.md +1 -1
- package/dist/SKILLS.md +1 -1
- package/dist/cli/commands/foundry-mint-wear.d.ts +2 -0
- package/dist/cli/commands/foundry-mint-wear.js +14 -0
- package/dist/cli/commands/hermes-install.js +2 -0
- package/dist/cli/commands/openclaw-install.js +2 -0
- package/dist/cli/commands/opencode-install.js +2 -0
- package/dist/enforcement/enforcer-tools.d.ts +7 -0
- package/dist/enforcement/enforcer-tools.js +18 -109
- package/dist/integrations/grok/grok-cli.js +2 -0
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/processors/implementations/version-compliance-processor.d.ts +3 -34
- package/dist/processors/implementations/version-compliance-processor.js +8 -135
- package/package.json +15 -14
- package/scripts/foundry/CHANGELOG.md +34 -0
- package/scripts/foundry/LICENSE +21 -0
- package/scripts/foundry/README.md +67 -0
- package/scripts/foundry/ci-monitor.mjs +115 -0
- package/scripts/foundry/cli.js +2 -0
- package/scripts/foundry/cli.mjs +55 -0
- package/scripts/foundry/docs-build.mjs +38 -0
- package/scripts/foundry/hooks.mjs +36 -0
- package/scripts/foundry/mill-root.mjs +62 -0
- package/scripts/foundry/mint-suit.cjs +313 -0
- package/scripts/foundry/mint.mjs +33 -0
- package/scripts/foundry/package.json +53 -0
- package/scripts/foundry/reconcile-version.mjs +150 -0
- package/scripts/foundry/release-gate.mjs +100 -0
- package/scripts/foundry/release.mjs +266 -0
- package/scripts/foundry/validate-release-docs.mjs +316 -0
- package/scripts/foundry/version-manager.mjs +444 -0
- package/scripts/node/ci-cd-auto-fix.cjs +12 -257
- package/scripts/node/consumer-install-smoke.mjs +2 -0
- package/scripts/node/enforce-version-compliance.sh +5 -20
- package/scripts/node/enforce-version-compliance.ts +13 -113
- package/scripts/node/foundry-shim.mjs +19 -0
- package/scripts/node/github-actions-monitor.cjs +13 -18
- package/scripts/node/postinstall.cjs +36 -1
- package/scripts/node/reconcile-version.mjs +3 -142
- package/scripts/node/release-gate.mjs +3 -74
- package/scripts/node/release.mjs +3 -171
- package/scripts/node/validate-release-docs.mjs +3 -285
- package/scripts/node/version-manager.mjs +3 -738
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/xray/features.json +1 -1
- package/scripts/node/release.js +0 -17
- package/scripts/node/sync-versions.mjs +0 -18
- package/scripts/node/universal-version-manager.js +0 -1043
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
* commits or publishes. Integrates with 0xRay's processor pipeline.
|
|
6
6
|
*
|
|
7
7
|
* Rules Enforced:
|
|
8
|
-
* 1.
|
|
9
|
-
* 2. package.json
|
|
10
|
-
* 3.
|
|
11
|
-
* 4. README SHOULD reference current version
|
|
8
|
+
* 1. package.json version is the SSOT (semver)
|
|
9
|
+
* 2. Mill SSOT is package.json via scripts/foundry/reconcile-version.mjs
|
|
10
|
+
* 3. Do not bump or publish from this processor (foundry mill ships)
|
|
12
11
|
*
|
|
13
12
|
* @processor_type pre
|
|
14
13
|
* @priority 25 (high - runs early, after preValidate, before errorBoundary)
|
|
@@ -100,53 +99,13 @@ export class VersionComplianceProcessor {
|
|
|
100
99
|
this.errors = [];
|
|
101
100
|
this.warnings = [];
|
|
102
101
|
this.fixes = [];
|
|
103
|
-
// Get all versions
|
|
104
102
|
const npmVersion = await this.getNpmVersion();
|
|
105
|
-
const uvmVersion = this.getUvmVersion();
|
|
106
103
|
const pkgVersion = this.getPackageVersion();
|
|
107
|
-
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
const uvmParsed = this.parseVersion(uvmVersion);
|
|
111
|
-
const expectedUvm = this.incrementPatch(npmParsed);
|
|
112
|
-
if (uvmVersion !== this.formatVersion(expectedUvm)) {
|
|
113
|
-
this.errors.push(`Version manager not 1 ahead of npm (NPM: ${npmVersion}, UVM: ${uvmVersion}, Expected: ${this.formatVersion(expectedUvm)})`);
|
|
114
|
-
this.fixes.push({
|
|
115
|
-
type: "update-uvm",
|
|
116
|
-
description: `Update UVM to ${this.formatVersion(expectedUvm)}`,
|
|
117
|
-
command: "node scripts/node/universal-version-manager.js",
|
|
118
|
-
autoFixable: false, // Requires manual edit
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
// Rule 2: package.json SHOULD match UVM
|
|
123
|
-
if (pkgVersion !== uvmVersion) {
|
|
124
|
-
this.warnings.push(`package.json version (${pkgVersion}) doesn't match UVM (${uvmVersion}) - run "npm version [patch|minor|major]" to sync`);
|
|
125
|
-
}
|
|
126
|
-
// Rule 3: Source files MUST be synchronized
|
|
127
|
-
const sourceVersion = this.getSourceVersion();
|
|
128
|
-
if (sourceVersion && sourceVersion !== uvmVersion) {
|
|
129
|
-
this.errors.push(`Source files not synchronized to UVM version (${sourceVersion} vs ${uvmVersion})`);
|
|
130
|
-
this.fixes.push({
|
|
131
|
-
type: "sync-source",
|
|
132
|
-
description: "Synchronize source files to UVM version",
|
|
133
|
-
command: "node scripts/node/universal-version-manager.js",
|
|
134
|
-
autoFixable: true,
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
// Rule 4: README SHOULD reference current version
|
|
138
|
-
const readmeVersion = this.getReadmeVersion();
|
|
139
|
-
if (readmeVersion &&
|
|
140
|
-
readmeVersion !== uvmVersion &&
|
|
141
|
-
readmeVersion !== pkgVersion) {
|
|
142
|
-
this.warnings.push(`README version (${readmeVersion}) may be outdated (UVM: ${uvmVersion}, package: ${pkgVersion})`);
|
|
143
|
-
this.fixes.push({
|
|
144
|
-
type: "update-readme",
|
|
145
|
-
description: "Update README version references",
|
|
146
|
-
command: "node scripts/node/universal-version-manager.js",
|
|
147
|
-
autoFixable: true,
|
|
148
|
-
});
|
|
104
|
+
const uvmVersion = "FROZEN";
|
|
105
|
+
if (!/^\d+\.\d+\.\d+/.test(pkgVersion)) {
|
|
106
|
+
this.errors.push(`package.json version is not semver (${pkgVersion})`);
|
|
149
107
|
}
|
|
108
|
+
this.warnings.push("SSOT is package.json via scripts/foundry/reconcile-version.mjs");
|
|
150
109
|
return {
|
|
151
110
|
compliant: this.errors.length === 0,
|
|
152
111
|
npmVersion,
|
|
@@ -172,29 +131,6 @@ export class VersionComplianceProcessor {
|
|
|
172
131
|
return "NOT_PUBLISHED";
|
|
173
132
|
}
|
|
174
133
|
}
|
|
175
|
-
/**
|
|
176
|
-
* Get Universal Version Manager version
|
|
177
|
-
*/
|
|
178
|
-
getUvmVersion() {
|
|
179
|
-
try {
|
|
180
|
-
const uvmPath = path.join(this.projectRoot, "scripts", "node", "universal-version-manager.js");
|
|
181
|
-
if (!fs.existsSync(uvmPath)) {
|
|
182
|
-
return "NOT_FOUND";
|
|
183
|
-
}
|
|
184
|
-
const content = fs.readFileSync(uvmPath, "utf-8");
|
|
185
|
-
// Match version in OFFICIAL_VERSIONS.framework.version format
|
|
186
|
-
const match = content.match(/OFFICIAL_VERSIONS\s*=\s*\{[\s\S]*?framework:\s*\{[\s\S]*?version:\s*["']([^"']+)["']/);
|
|
187
|
-
if (match && match[1]) {
|
|
188
|
-
return match[1];
|
|
189
|
-
}
|
|
190
|
-
// Fallback: grep for version pattern
|
|
191
|
-
const fallbackMatch = content.match(/version:\s*["'](\d+\.\d+\.\d+)["']/);
|
|
192
|
-
return fallbackMatch && fallbackMatch[1] ? fallbackMatch[1] : "NOT_FOUND";
|
|
193
|
-
}
|
|
194
|
-
catch {
|
|
195
|
-
return "ERROR";
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
134
|
/**
|
|
199
135
|
* Get package.json version
|
|
200
136
|
*/
|
|
@@ -211,69 +147,6 @@ export class VersionComplianceProcessor {
|
|
|
211
147
|
return "ERROR";
|
|
212
148
|
}
|
|
213
149
|
}
|
|
214
|
-
/**
|
|
215
|
-
* Get version from source files
|
|
216
|
-
*/
|
|
217
|
-
getSourceVersion() {
|
|
218
|
-
try {
|
|
219
|
-
const cliPath = path.join(this.projectRoot, "src", "cli", "index.ts");
|
|
220
|
-
if (!fs.existsSync(cliPath)) {
|
|
221
|
-
return null;
|
|
222
|
-
}
|
|
223
|
-
const content = fs.readFileSync(cliPath, "utf-8");
|
|
224
|
-
const match = content.match(/\.version\(["']([^"']+)["']\)/);
|
|
225
|
-
return match && match[1] ? match[1] : null;
|
|
226
|
-
}
|
|
227
|
-
catch {
|
|
228
|
-
return null;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Get version from README
|
|
233
|
-
*/
|
|
234
|
-
getReadmeVersion() {
|
|
235
|
-
try {
|
|
236
|
-
const readmePath = path.join(this.projectRoot, "README.md");
|
|
237
|
-
if (!fs.existsSync(readmePath)) {
|
|
238
|
-
return null;
|
|
239
|
-
}
|
|
240
|
-
const content = fs.readFileSync(readmePath, "utf-8");
|
|
241
|
-
// Match vX.Y.Z or X.Y.Z patterns - but exclude IP addresses (127.x.x)
|
|
242
|
-
// Look for version in badges, headers, or specific patterns
|
|
243
|
-
const match = content.match(/v?(1\.\d+\.\d+)/);
|
|
244
|
-
return match && match[1] ? match[1] : null;
|
|
245
|
-
}
|
|
246
|
-
catch {
|
|
247
|
-
return null;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Parse version string into components
|
|
252
|
-
*/
|
|
253
|
-
parseVersion(version) {
|
|
254
|
-
const parts = version.split(".");
|
|
255
|
-
return {
|
|
256
|
-
major: parseInt(parts[0] || "0", 10),
|
|
257
|
-
minor: parseInt(parts[1] || "0", 10),
|
|
258
|
-
patch: parseInt(parts[2] || "0", 10),
|
|
259
|
-
raw: version,
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Format version components back to string
|
|
264
|
-
*/
|
|
265
|
-
formatVersion(version) {
|
|
266
|
-
return `${version.major}.${version.minor}.${version.patch}`;
|
|
267
|
-
}
|
|
268
|
-
/**
|
|
269
|
-
* Increment patch version
|
|
270
|
-
*/
|
|
271
|
-
incrementPatch(version) {
|
|
272
|
-
return {
|
|
273
|
-
...version,
|
|
274
|
-
patch: version.patch + 1,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
150
|
/**
|
|
278
151
|
* Auto-fix version compliance issues
|
|
279
152
|
*/
|
|
@@ -318,7 +191,7 @@ export class VersionComplianceProcessor {
|
|
|
318
191
|
"============================",
|
|
319
192
|
"",
|
|
320
193
|
`NPM Published: ${result.npmVersion}`,
|
|
321
|
-
`
|
|
194
|
+
`UVM: ${result.uvmVersion}`,
|
|
322
195
|
`package.json: ${result.pkgVersion}`,
|
|
323
196
|
"",
|
|
324
197
|
result.compliant ? "✅ COMPLIANT" : "❌ NON-COMPLIANT",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "0xray",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.2",
|
|
4
4
|
"description": "Multi-agent orchestration and Codex governance for OpenCode, Hermes, Grok Build, and OpenClaw",
|
|
5
5
|
"readme": "README.md",
|
|
6
6
|
"license": "MIT",
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
},
|
|
62
62
|
"scripts": {
|
|
63
63
|
"prepublishOnly": "node scripts/node/reconcile-version.mjs --check && npm run prepare-consumer && npm run build:all && find dist -name '*.js.map' -o -name '*.d.ts.map' | xargs rm -f 2>/dev/null || true",
|
|
64
|
-
"version:bump": "node scripts/
|
|
65
|
-
"version": "node scripts/
|
|
64
|
+
"version:bump": "node scripts/foundry/reconcile-version.mjs",
|
|
65
|
+
"version": "node scripts/foundry/version-manager.mjs",
|
|
66
66
|
"prebuild": "rm -rf dist tsconfig.tsbuildinfo tsconfig.*.tsbuildinfo",
|
|
67
67
|
"build": "tsc && chmod +x dist/cli/index.js && mkdir -p dist/public dist/scripts && cp -r public/* dist/public/ && cp scripts/hooks/pre-command dist/scripts/ && cp scripts/hooks/pre-command.mjs dist/scripts/ && cp README.md AGENTS.md SKILLS.md CHANGELOG.md LICENSE .gitignore.default dist/ && find src -name '*.mjs' ! -path '*/__tests__/*' | while read f; do tgt=\"dist/${f#src/}\"; mkdir -p \"$(dirname $tgt)\"; cp \"$f\" \"$tgt\"; done && for dir in skills integrations mcps; do find src/$dir -type f ! -name '*.ts' ! -path '*/.pytest_cache/*' | while read f; do tgt=\"dist/${f#src/}\"; mkdir -p \"$(dirname $tgt)\"; cp \"$f\" \"$tgt\"; done; done && mkdir -p dist/plugin && (find dist/plugin -name '*codex-injection*.js' -exec cp {} dist/plugin/xray-codex-injection.js \\; 2>/dev/null || true) && cp -r src/opencode/ .opencode/ && mkdir -p .opencode/skills && cp -R src/skills/. .opencode/skills/ && mkdir -p .xray && for f in codex.json features.json features.schema.json config.json; do cp xray/$f .xray/$f 2>/dev/null || true; done",
|
|
68
68
|
"build:all": "npm run build",
|
|
@@ -112,20 +112,20 @@
|
|
|
112
112
|
"build:run": "node scripts/build/utils.js build",
|
|
113
113
|
"build:clean": "node scripts/build/utils.js clean",
|
|
114
114
|
"build:verify": "node scripts/build/utils.js verify",
|
|
115
|
-
"enforce:versions": "
|
|
116
|
-
"version:sync": "node scripts/
|
|
117
|
-
"release": "node scripts/
|
|
118
|
-
"release:minor": "node scripts/
|
|
119
|
-
"release:major": "node scripts/
|
|
120
|
-
"release:dry": "node scripts/
|
|
121
|
-
"version:reconcile": "node scripts/
|
|
115
|
+
"enforce:versions": "node scripts/foundry/validate-release-docs.mjs",
|
|
116
|
+
"version:sync": "node scripts/foundry/version-manager.mjs --artifacts-only",
|
|
117
|
+
"release": "node scripts/foundry/release.mjs patch",
|
|
118
|
+
"release:minor": "node scripts/foundry/release.mjs minor",
|
|
119
|
+
"release:major": "node scripts/foundry/release.mjs major",
|
|
120
|
+
"release:dry": "node scripts/foundry/release.mjs patch --dry-run",
|
|
121
|
+
"version:reconcile": "node scripts/foundry/reconcile-version.mjs",
|
|
122
122
|
"consumer:smoke": "node scripts/node/consumer-install-smoke.mjs",
|
|
123
|
-
"release:gate": "node scripts/
|
|
124
|
-
"release:docs-check": "node scripts/
|
|
123
|
+
"release:gate": "node scripts/foundry/release-gate.mjs",
|
|
124
|
+
"release:docs-check": "node scripts/foundry/validate-release-docs.mjs",
|
|
125
125
|
"pre-publish-guard": "node scripts/node/pre-publish-guard.js",
|
|
126
126
|
"safe-publish": "npm run prepare-consumer && npm run build",
|
|
127
|
-
"release:npm": "
|
|
128
|
-
"release:patch": "node scripts/
|
|
127
|
+
"release:npm": "node scripts/foundry/release.mjs --publish-only",
|
|
128
|
+
"release:patch": "node scripts/foundry/release.mjs patch",
|
|
129
129
|
"verify:orchestrator-behavior": "npm run build && node scripts/mjs/verify-orchestrator-behavior.mjs",
|
|
130
130
|
"verify:grok-delegation-gate": "npm run build && node scripts/mjs/verify-grok-delegation-gate.mjs && node scripts/mjs/verify-grok-synthesis-gate.mjs",
|
|
131
131
|
"verify:grok-synthesis-gate": "npm run build && node scripts/mjs/verify-grok-synthesis-gate.mjs",
|
|
@@ -146,6 +146,7 @@
|
|
|
146
146
|
"files": [
|
|
147
147
|
"dist/",
|
|
148
148
|
"scripts/node/",
|
|
149
|
+
"scripts/foundry/",
|
|
149
150
|
"scripts/integrations/",
|
|
150
151
|
"scripts/helpers/",
|
|
151
152
|
"scripts/hooks/",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.4] - 2026-09-04
|
|
4
|
+
|
|
5
|
+
`npx @0xray/foundry hooks` on a published mill uses the worn garment
|
|
6
|
+
`node_modules/0xray/scripts/hooks/install-hooks.cjs`. Dogfood still uses
|
|
7
|
+
`scripts/hooks` next to `scripts/foundry`. Missing installer fails honestly
|
|
8
|
+
(no spawn of `node_modules/@0xray/hooks/...`).
|
|
9
|
+
|
|
10
|
+
## [0.1.3] - 2026-09-04
|
|
11
|
+
|
|
12
|
+
Pages homepage is a baseUrl meta refresh to `/docs/`, not a client redirect to `/docs/introduction` (org-root 404).
|
|
13
|
+
|
|
14
|
+
Docusaurus is mill: `npx @0xray/foundry docs-build` (exo only). GitHub Pages no longer clobbers the generated homepage with a stale 3.x `static/index.html`.
|
|
15
|
+
|
|
16
|
+
## [0.1.2] - 2026-09-04
|
|
17
|
+
|
|
18
|
+
Mill owns GitHub CI, git hooks, and CI report. `npx @0xray/foundry ci` reports Actions (no auto-push). `npx @0xray/foundry hooks` installs pre/post git hooks. `0xRay CI/CD` is the mill gate on main.
|
|
19
|
+
|
|
20
|
+
## [0.1.1] - 2026-09-04
|
|
21
|
+
|
|
22
|
+
Live `release` (bump/commit/push/publish of the milled cwd) requires `--i-mean-it` or `FOUNDRY_RELEASE=1`. `--dry-run` stays free. Packed mill tarball mints overlay. Stamp keeps `[Unreleased]` and inserts the version under it.
|
|
23
|
+
|
|
24
|
+
## [0.1.0] - 2026-09-03
|
|
25
|
+
|
|
26
|
+
First mill package. Overlay their constitution, features, skills, and agents onto **project** hangers. Stamp/gate/docs-check mill `FOUNDRY_ROOT` or cwd. Not the worn exo.
|
|
27
|
+
|
|
28
|
+
Publish this package from this directory:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
cd scripts/foundry && npm publish --access public
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Do not `npm publish` from the 0xray repo root (that is `0xray@4.0.1`).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Xray Framework Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# @0xray/foundry
|
|
2
|
+
|
|
3
|
+
The **mill**, not the worn exo. Reconcile, stamp JSON/CHANGELOG, verify docs, gate, publish, overlay their suit params.
|
|
4
|
+
|
|
5
|
+
PPE (`evaluatePreToolGate`) stays worn. The mill plants **their** constitution, features, skills, and agents onto the hanger. It is not a fifth MCP. The suit is [`0xray`](https://www.npmjs.com/package/0xray).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i -D @0xray/foundry
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Commands
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx @0xray/foundry reconcile [patch|minor|major] [--apply] [--check]
|
|
17
|
+
npx @0xray/foundry stamp
|
|
18
|
+
npx @0xray/foundry docs-check
|
|
19
|
+
npx @0xray/foundry docs-build
|
|
20
|
+
npx @0xray/foundry gate [--verify-only]
|
|
21
|
+
npx @0xray/foundry release [patch|minor|major] --dry-run
|
|
22
|
+
npx @0xray/foundry release [patch|minor|major] --i-mean-it
|
|
23
|
+
npx @0xray/foundry release --publish-only --dry-run
|
|
24
|
+
npx @0xray/foundry release --publish-only --i-mean-it
|
|
25
|
+
npx @0xray/foundry mint
|
|
26
|
+
npx @0xray/foundry ci [--commit SHA] [--report]
|
|
27
|
+
npx @0xray/foundry hooks
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`release` without `--dry-run` requires `--i-mean-it` or `FOUNDRY_RELEASE=1`. It is a bump/commit/push/publish bot for the **milled cwd**, not a casual command.
|
|
31
|
+
|
|
32
|
+
`FOUNDRY_ROOT` overrides cwd (the repo being milled).
|
|
33
|
+
|
|
34
|
+
`gate` is build + test + docs-check. `docs-build` runs Docusaurus on the 0xray exo (`docs-site/`); stranger mills skip. `ci` reports GitHub Actions (no auto-push). `hooks` installs git pre/post hooks. GitHub `0xRay CI/CD` is the mill gate on `main`. `Deploy Docs` is the mill Pages put.
|
|
35
|
+
|
|
36
|
+
`docs-check` and `gate` run **full** 0xRay corpus only when the milled `package.json` name is `0xray` and `docs-site/` exists. Otherwise **light**: `package.json` + CHANGELOG.
|
|
37
|
+
|
|
38
|
+
`hooks` installs git hooks via `scripts/hooks/install-hooks.cjs` (dogfood) or `node_modules/0xray/scripts/hooks/install-hooks.cjs` (published mill + worn garment). It does not look for `@0xray/hooks`.
|
|
39
|
+
|
|
40
|
+
## Suit params (`mint`)
|
|
41
|
+
|
|
42
|
+
After the garment is on the hanger, overlay their plant (defaults; remap in `foundry.json` or `.xray/foundry.json`):
|
|
43
|
+
|
|
44
|
+
| Facet | Default plant | Hanger |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| Constitution | `xray/codex.json` | `.xray/codex.json` |
|
|
47
|
+
| Features / temperament | `xray/features.json` | `.xray/features.json` |
|
|
48
|
+
| Config | `xray/config.json` | `.xray/config.json` |
|
|
49
|
+
| Skills | `src/skills/<name>/SKILL.md` | `.opencode/skills/` and project `.grok/plugins/0xray/skills` if that dir exists |
|
|
50
|
+
| Agents | `src/opencode/agents/*.yml` | `.opencode/agents/` (OpenCode hangar; other floors do not read mill YML agents) |
|
|
51
|
+
| Agents card | `xray/AGENTS.md` | `AGENTS.md` if mill-managed |
|
|
52
|
+
|
|
53
|
+
**Mill-fill is law:** their keys/files win; mill names they did not plant stay unless they overlay the same name. JSON facets always merge. Skill overlay is project hangers only. CLI re-wear calls mint **once** after mill copies.
|
|
54
|
+
|
|
55
|
+
## Publish this mill
|
|
56
|
+
|
|
57
|
+
From **this directory**, not the 0xray repo root:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
cd scripts/foundry && npm publish --access public
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Repo-root `npm publish` is `0xray`. `npx @0xray/foundry release --i-mean-it` publishes the **milled cwd** package (their `publishConfig`; `--access public` only for `0xray` and `@0xray/foundry`).
|
|
64
|
+
|
|
65
|
+
## 0xRay exo
|
|
66
|
+
|
|
67
|
+
The mill lives in `scripts/foundry/` inside [0xRayAI/xray](https://github.com/0xRayAI/xray). `scripts/node/*.mjs` wrappers are shims onto this package.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Mill CI monitor. Reports GitHub Actions for the milled cwd.
|
|
4
|
+
* Not PPE. Not an auto-push bot.
|
|
5
|
+
*
|
|
6
|
+
* npx @0xray/foundry ci [--commit SHA] [--report]
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { resolveMillRoot } from "./mill-root.mjs";
|
|
13
|
+
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const commitIdx = args.indexOf("--commit");
|
|
16
|
+
const shaArg = commitIdx >= 0 ? args[commitIdx + 1] : "";
|
|
17
|
+
const sha = (shaArg && !shaArg.startsWith("-") ? shaArg : process.env.GITHUB_SHA || "").trim();
|
|
18
|
+
const root = resolveMillRoot();
|
|
19
|
+
const reportPath = path.join(root, ".opencode", "logs", "ci-cd-monitor-report.json");
|
|
20
|
+
|
|
21
|
+
function writeReport(report) {
|
|
22
|
+
fs.mkdirSync(path.dirname(reportPath), { recursive: true });
|
|
23
|
+
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function emptyReport(status, reason) {
|
|
27
|
+
return {
|
|
28
|
+
ci_status: status,
|
|
29
|
+
health_score: status === "healthy" || status === "unknown" ? 100 : 0,
|
|
30
|
+
issues: [],
|
|
31
|
+
reason,
|
|
32
|
+
sha: sha || null,
|
|
33
|
+
generatedAt: new Date().toISOString(),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function main() {
|
|
38
|
+
const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN || "";
|
|
39
|
+
const repo = process.env.GITHUB_REPOSITORY || "";
|
|
40
|
+
if (!token || !repo.includes("/")) {
|
|
41
|
+
const report = emptyReport(
|
|
42
|
+
"unknown",
|
|
43
|
+
"no GITHUB_TOKEN or GITHUB_REPOSITORY — mill CI monitor skipped",
|
|
44
|
+
);
|
|
45
|
+
writeReport(report);
|
|
46
|
+
process.stdout.write("SUCCESS: mill CI monitor skipped (no GitHub token)\n");
|
|
47
|
+
process.stdout.write("No failed jobs\n");
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const url = new URL(`https://api.github.com/repos/${repo}/actions/runs`);
|
|
52
|
+
url.searchParams.set("per_page", "50");
|
|
53
|
+
if (sha) url.searchParams.set("head_sha", sha);
|
|
54
|
+
|
|
55
|
+
const res = await fetch(url, {
|
|
56
|
+
headers: {
|
|
57
|
+
Authorization: `Bearer ${token}`,
|
|
58
|
+
Accept: "application/vnd.github+json",
|
|
59
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
60
|
+
"User-Agent": "0xray-foundry-ci-monitor",
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
if (!res.ok) {
|
|
64
|
+
const report = emptyReport("warning", `GitHub API ${res.status}`);
|
|
65
|
+
writeReport(report);
|
|
66
|
+
process.stderr.write(`mill ci: GitHub API ${res.status}\n`);
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
const body = await res.json();
|
|
70
|
+
const runs = Array.isArray(body.workflow_runs) ? body.workflow_runs : [];
|
|
71
|
+
const failed = runs.filter((run) => {
|
|
72
|
+
const conclusion = String(run.conclusion || "");
|
|
73
|
+
return conclusion === "failure" || conclusion === "timed_out" || conclusion === "startup_failure";
|
|
74
|
+
});
|
|
75
|
+
const issues = failed.map((run) => ({
|
|
76
|
+
name: run.name || run.display_title || "workflow",
|
|
77
|
+
conclusion: run.conclusion,
|
|
78
|
+
html_url: run.html_url,
|
|
79
|
+
head_sha: run.head_sha,
|
|
80
|
+
}));
|
|
81
|
+
const uniqueFails = new Set(issues.map((i) => i.name)).size;
|
|
82
|
+
const health = Math.max(0, 100 - uniqueFails * 20);
|
|
83
|
+
const ci_status = uniqueFails === 0 ? "healthy" : uniqueFails >= 3 ? "critical" : "warning";
|
|
84
|
+
const report = {
|
|
85
|
+
ci_status,
|
|
86
|
+
health_score: health,
|
|
87
|
+
issues,
|
|
88
|
+
sha: sha || null,
|
|
89
|
+
run_count: runs.length,
|
|
90
|
+
generatedAt: new Date().toISOString(),
|
|
91
|
+
};
|
|
92
|
+
writeReport(report);
|
|
93
|
+
|
|
94
|
+
if (uniqueFails === 0) {
|
|
95
|
+
process.stdout.write("SUCCESS: All workflows passed\n");
|
|
96
|
+
process.stdout.write("No failed jobs\n");
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
99
|
+
process.stdout.write(`FAILURE: ${issues.map((i) => i.name).join(", ")}\n`);
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const isMain =
|
|
104
|
+
process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
105
|
+
|
|
106
|
+
if (isMain) {
|
|
107
|
+
main()
|
|
108
|
+
.then((code) => process.exit(code))
|
|
109
|
+
.catch((err) => {
|
|
110
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export { main };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @0xray/foundry CLI — mill, not exo. No fifth MCP.
|
|
4
|
+
*
|
|
5
|
+
* npx @0xray/foundry <reconcile|stamp|gate|release|docs-check|docs-build|mint|ci|hooks> [...args]
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { millScript, resolveMillRoot } from "./mill-root.mjs";
|
|
10
|
+
|
|
11
|
+
const COMMANDS = {
|
|
12
|
+
reconcile: { script: "reconcile-version.mjs", preset: [] },
|
|
13
|
+
stamp: { script: "version-manager.mjs", preset: ["--artifacts-only"] },
|
|
14
|
+
gate: { script: "release-gate.mjs", preset: [] },
|
|
15
|
+
release: { script: "release.mjs", preset: [] },
|
|
16
|
+
"docs-check": { script: "validate-release-docs.mjs", preset: [] },
|
|
17
|
+
"docs-build": { script: "docs-build.mjs", preset: [] },
|
|
18
|
+
mint: { script: "mint.mjs", preset: [] },
|
|
19
|
+
ci: { script: "ci-monitor.mjs", preset: [] },
|
|
20
|
+
hooks: { script: "hooks.mjs", preset: [] },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const HELP =
|
|
24
|
+
"Usage: npx @0xray/foundry <reconcile|stamp|gate|release|docs-check|docs-build|mint|ci|hooks> [...args]\n" +
|
|
25
|
+
"\n" +
|
|
26
|
+
"Mill, not exo. FOUNDRY_ROOT overrides cwd (the repo being milled).\n" +
|
|
27
|
+
"release bumps/commits/pushes/publishes the milled cwd — pass --dry-run or --i-mean-it (or FOUNDRY_RELEASE=1).\n" +
|
|
28
|
+
"Mint overlays their plant onto project hangers (mill-fill). PPE stays worn.\n" +
|
|
29
|
+
"gate is build+test. docs-build is Docusaurus on the 0xray exo. ci reports GitHub Actions (no auto-push). hooks installs git pre/post hooks.\n";
|
|
30
|
+
|
|
31
|
+
const cmd = process.argv[2];
|
|
32
|
+
const rest = process.argv.slice(3);
|
|
33
|
+
|
|
34
|
+
if (cmd === "--help" || cmd === "-h" || cmd === "help") {
|
|
35
|
+
process.stdout.write(HELP);
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!cmd || !COMMANDS[cmd]) {
|
|
40
|
+
process.stderr.write(HELP);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const spec = COMMANDS[cmd];
|
|
45
|
+
const root = resolveMillRoot();
|
|
46
|
+
const result = spawnSync(
|
|
47
|
+
process.execPath,
|
|
48
|
+
[millScript(spec.script), ...spec.preset, ...rest],
|
|
49
|
+
{
|
|
50
|
+
stdio: "inherit",
|
|
51
|
+
cwd: root,
|
|
52
|
+
env: { ...process.env, FOUNDRY_ROOT: root },
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
process.exit(result.status === null ? 1 : result.status);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Mill Docusaurus build. Full corpus only on the 0xray exo (docs-site/).
|
|
4
|
+
* Stranger mills skip. Not PPE. Not an 8th MCP.
|
|
5
|
+
*
|
|
6
|
+
* npx @0xray/foundry docs-build
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { spawnSync } from "node:child_process";
|
|
10
|
+
import fs from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { isXrayExoRepo, resolveMillRoot } from "./mill-root.mjs";
|
|
14
|
+
|
|
15
|
+
function run(cmd, args, cwd) {
|
|
16
|
+
const result = spawnSync(cmd, args, { cwd, stdio: "inherit", env: process.env });
|
|
17
|
+
return result.status === null ? 1 : result.status;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function main() {
|
|
21
|
+
const root = resolveMillRoot();
|
|
22
|
+
if (!isXrayExoRepo(root)) {
|
|
23
|
+
process.stdout.write("docs-build: skipped (not 0xray exo with docs-site)\n");
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
const docs = path.join(root, "docs-site");
|
|
27
|
+
if (!fs.existsSync(path.join(docs, "node_modules"))) {
|
|
28
|
+
const install = run("npm", ["ci"], docs);
|
|
29
|
+
if (install !== 0) process.exit(install);
|
|
30
|
+
}
|
|
31
|
+
process.exit(run("npm", ["run", "build"], docs));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const isMain =
|
|
35
|
+
process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
36
|
+
if (isMain) {
|
|
37
|
+
main();
|
|
38
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Mill git hooks: install pre-commit / post-commit / pre-push / post-push.
|
|
4
|
+
* Wear-time installer lives at scripts/hooks/install-hooks.cjs (dogfood)
|
|
5
|
+
* or node_modules/0xray/scripts/hooks/install-hooks.cjs (published mill).
|
|
6
|
+
*
|
|
7
|
+
* npx @0xray/foundry hooks
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { spawnSync } from "node:child_process";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { resolveHookInstaller, resolveMillRoot } from "./mill-root.mjs";
|
|
14
|
+
|
|
15
|
+
function main() {
|
|
16
|
+
const root = resolveMillRoot();
|
|
17
|
+
const installer = resolveHookInstaller(root);
|
|
18
|
+
if (!installer) {
|
|
19
|
+
process.stderr.write(
|
|
20
|
+
"mill hooks: no install-hooks.cjs (dogfood scripts/hooks or node_modules/0xray/scripts/hooks)\n",
|
|
21
|
+
);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const result = spawnSync(process.execPath, [installer, ...process.argv.slice(2)], {
|
|
25
|
+
stdio: "inherit",
|
|
26
|
+
cwd: root,
|
|
27
|
+
env: { ...process.env, FOUNDRY_ROOT: root },
|
|
28
|
+
});
|
|
29
|
+
process.exit(result.status === null ? 1 : result.status);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const isMain =
|
|
33
|
+
process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
34
|
+
if (isMain) {
|
|
35
|
+
main();
|
|
36
|
+
}
|