0xray 4.0.5 → 4.0.7
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/dist/CHANGELOG.md +14 -0
- package/dist/cli/commands/hermes-install.js +7 -5
- package/dist/cli/commands/openclaw-install.js +10 -5
- package/package.json +1 -1
- package/scripts/foundry/CHANGELOG.md +8 -0
- package/scripts/foundry/cli.mjs +1 -1
- package/scripts/foundry/inspect.mjs +17 -1
- package/scripts/foundry/mill-dna.cjs +50 -0
- package/scripts/foundry/mill-keccak.cjs +306 -0
- package/scripts/foundry/mint-suit.cjs +8 -2
- package/scripts/foundry/mint.mjs +6 -1
- package/scripts/foundry/package.json +1 -1
- package/scripts/foundry/plant/agents/inspect.yml +1 -1
- package/scripts/foundry/plant/agents/mill.yml +1 -1
- package/scripts/foundry/plant/skills/inspect/SKILL.md +2 -0
- package/scripts/node/bridge-mcp-wiring.cjs +4 -2
- package/scripts/node/install-bridges.cjs +61 -19
package/dist/CHANGELOG.md
CHANGED
|
@@ -29,6 +29,20 @@ The format is based on [Conventional Commits](https://www.conventionalcommits.or
|
|
|
29
29
|
- Empty garment: mill plant (`mill` skill) is the hanger. Not 45/42 costume unless `foundry.json` `"costume": true`. Mill 0.1.5.
|
|
30
30
|
- Mill plant fastens **inspect** (inspect AI work) plus `mill`. Not empty. Mill 0.1.6.
|
|
31
31
|
- Nested mill **0.1.7**: inspect organ, four floors, passwd HOME, mill CI receipt. 4.0.5.
|
|
32
|
+
- Nested mill **0.1.8**: isolated Hermes/OpenClaw, consumer tarball GET, CLI mint inspects. 4.0.6.
|
|
33
|
+
- Nested mill **0.1.9**: inventory DNA for Groover 0xray-suit pack. 4.0.7.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## [4.0.7] - 2026-09-09
|
|
38
|
+
|
|
39
|
+
Nested mill **0.1.9**. Inventory writes `dna` (keccak256 of canonical JSON without `mintedAt`). Inspect echoes dna + pack `0xray-suit`. Mill does not mint on chain. Not 5.0.0.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## [4.0.6] - 2026-09-09
|
|
44
|
+
|
|
45
|
+
Nested mill **0.1.8**. Isolated HOME skips machine Hermes/OpenClaw. Inspect GETs public consumer `.tgz`. CLI `mint` runs inspect (chat is not done). Postinstall does not inspect. Not 5.0.0.
|
|
32
46
|
|
|
33
47
|
---
|
|
34
48
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { homedir } from 'os';
|
|
4
3
|
import { createRequire } from 'module';
|
|
5
4
|
import { frameworkLogger } from '../../core/framework-logger.js';
|
|
6
5
|
import { syncBuiltinSkills } from './skill-install.js';
|
|
@@ -8,6 +7,7 @@ import { mintAfterWear } from './foundry-mint-wear.js';
|
|
|
8
7
|
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
|
9
8
|
const require = createRequire(import.meta.url);
|
|
10
9
|
const packageRoot = path.join(__dirname, '..', '..', '..');
|
|
10
|
+
const millSuit = require(path.join(packageRoot, 'scripts', 'foundry', 'mint-suit.cjs'));
|
|
11
11
|
const wiring = require(path.join(packageRoot, 'scripts', 'node', 'bridge-mcp-wiring.cjs'));
|
|
12
12
|
export function registerHermesCommands(hermesCmd) {
|
|
13
13
|
hermesCmd
|
|
@@ -21,8 +21,10 @@ export function registerHermesCommands(hermesCmd) {
|
|
|
21
21
|
}
|
|
22
22
|
async function installForHermes(options = {}) {
|
|
23
23
|
frameworkLogger.log('hermes-integration', 'install-start', 'info', { options });
|
|
24
|
-
const
|
|
25
|
-
const targetPluginDir =
|
|
24
|
+
const isolated = millSuit.isIsolatedHome();
|
|
25
|
+
const targetPluginDir = isolated
|
|
26
|
+
? path.join(process.cwd(), '.hermes/plugins/xray-hermes')
|
|
27
|
+
: path.join(millSuit.machineHome(), '.hermes/plugins/xray-hermes');
|
|
26
28
|
const possibleSources = [
|
|
27
29
|
path.join(__dirname, '..', '..', '..', 'dist/integrations/hermes-agent'),
|
|
28
30
|
path.join(__dirname, '..', '..', '..', 'src/integrations/hermes-agent'),
|
|
@@ -65,10 +67,10 @@ async function installForHermes(options = {}) {
|
|
|
65
67
|
frameworkLogger.log('hermes-integration', 'skills-synced', 'info', { count: skillsCopied });
|
|
66
68
|
}
|
|
67
69
|
wiring.copyHermesFindProjectRootHelper(packageRoot, targetPluginDir);
|
|
68
|
-
if (wiring.copyHermesHookRuntimes(packageRoot)) {
|
|
70
|
+
if (!isolated && wiring.copyHermesHookRuntimes(packageRoot)) {
|
|
69
71
|
frameworkLogger.log('hermes-integration', 'hooks-copied', 'info', {});
|
|
70
72
|
}
|
|
71
|
-
const wired = wiring.wireHermesBridge(targetDir);
|
|
73
|
+
const wired = isolated ? { count: 0 } : wiring.wireHermesBridge(targetDir);
|
|
72
74
|
mintAfterWear(targetDir);
|
|
73
75
|
console.log(`\x1b[32m✓ Wired Hermes mcp_servers (${wired.count} servers)\x1b[0m`);
|
|
74
76
|
console.log(`\x1b[32m✓ Consumer root → ${targetDir}\x1b[0m`);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { homedir } from 'os';
|
|
4
3
|
import { createRequire } from 'module';
|
|
5
4
|
import { frameworkLogger } from '../../core/framework-logger.js';
|
|
6
5
|
import { syncBuiltinSkills } from './skill-install.js';
|
|
@@ -9,6 +8,7 @@ import { OpenClawConfigLoader } from '../../integrations/openclaw/config.js';
|
|
|
9
8
|
import { writeSuitSessionBoot } from '../../nucleus/suit-temperament.js';
|
|
10
9
|
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
|
11
10
|
const require = createRequire(import.meta.url);
|
|
11
|
+
const millSuit = require(path.join(__dirname, '..', '..', '..', 'scripts', 'foundry', 'mint-suit.cjs'));
|
|
12
12
|
const wiring = require(path.join(__dirname, '..', '..', '..', 'scripts', 'node', 'bridge-mcp-wiring.cjs'));
|
|
13
13
|
export function registerOpenClawCommands(openclawCmd) {
|
|
14
14
|
openclawCmd
|
|
@@ -40,14 +40,19 @@ async function installForOpenClaw(options = {}) {
|
|
|
40
40
|
const relative = path.relative(process.cwd(), configPath);
|
|
41
41
|
console.log(`\x1b[32m✓ Created OpenClaw config at ${relative}\x1b[0m`);
|
|
42
42
|
}
|
|
43
|
-
const
|
|
43
|
+
const isolated = millSuit.isIsolatedHome();
|
|
44
|
+
const openclawSkillsDir = isolated
|
|
45
|
+
? path.join(targetDir, '.openclaw', 'skills')
|
|
46
|
+
: path.join(millSuit.machineHome(), '.openclaw', 'skills');
|
|
44
47
|
const skillsCopied = syncBuiltinSkills(openclawSkillsDir);
|
|
45
48
|
if (skillsCopied > 0) {
|
|
46
49
|
console.log(`\x1b[32m✓ Synced ${skillsCopied} builtin skills to ~/.openclaw/skills/\x1b[0m`);
|
|
47
50
|
}
|
|
48
51
|
frameworkLogger.log('openclaw-integration', 'skills-synced', 'info', { count: skillsCopied });
|
|
49
52
|
mintAfterWear(targetDir);
|
|
50
|
-
const wired =
|
|
53
|
+
const wired = isolated
|
|
54
|
+
? { count: 0, path: configPath, method: 'skipped-isolated-home' }
|
|
55
|
+
: wiring.wireOpenClawBridge(targetDir);
|
|
51
56
|
console.log(`\x1b[32m✓ Wired OpenClaw MCP servers (${wired.count} via ${wired.method}) → ${wired.path}\x1b[0m`);
|
|
52
57
|
console.log(`\x1b[32m✓ Consumer root → ${targetDir}\x1b[0m`);
|
|
53
58
|
try {
|
|
@@ -57,8 +62,8 @@ async function installForOpenClaw(options = {}) {
|
|
|
57
62
|
/* session-boot is best-effort */
|
|
58
63
|
}
|
|
59
64
|
const packageRoot = path.resolve(__dirname, '..', '..', '..');
|
|
60
|
-
const hookInstalled = wiring.installOpenClawHostWear(packageRoot);
|
|
61
|
-
const cliBackend = wiring.maybeWriteOpenClawCliBackend();
|
|
65
|
+
const hookInstalled = isolated ? null : wiring.installOpenClawHostWear(packageRoot);
|
|
66
|
+
const cliBackend = isolated ? false : wiring.maybeWriteOpenClawCliBackend();
|
|
62
67
|
frameworkLogger.log('openclaw-integration', 'cli-backend', 'info', { written: cliBackend });
|
|
63
68
|
if (hookInstalled) {
|
|
64
69
|
console.log(`\x1b[32m✓ OpenClaw PreToolUse hook → ${hookInstalled}\x1b[0m`);
|
package/package.json
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.9] - 2026-09-09
|
|
4
|
+
|
|
5
|
+
Inventory writes `dna` (keccak256 of canonical JSON without `mintedAt`). Inspect receipt echoes `dna` + pack `0xray-suit` for Groover GRVR. Mill does not mint on chain. Not an 8th MCP. Not 5.0.
|
|
6
|
+
|
|
7
|
+
## [0.1.8] - 2026-09-09
|
|
8
|
+
|
|
9
|
+
Isolated HOME skips machine `~/.hermes` and `~/.openclaw` (passwd home, same gate as Grok). Inspect GETs a public consumer tarball (`@0xray/review-suit`). CLI `mint` runs inspect after overlay; postinstall does not. Not an 8th MCP. Not 5.0.
|
|
10
|
+
|
|
3
11
|
## [0.1.7] - 2026-09-04
|
|
4
12
|
|
|
5
13
|
Inspect is mill flesh: `npx @0xray/foundry inspect` runs diff, plant vs worn (mint fails costume dump), mill receipt, CI, GET the `.tgz` (HTTP 200), isolated HOME vs passwd home. Four project skill floors created on fasten (OpenCode, Grok, Hermes, OpenClaw). CI report is `.xray/foundry-ci-report.json`. Not an 8th MCP. Not 5.0.
|
package/scripts/foundry/cli.mjs
CHANGED
|
@@ -26,7 +26,7 @@ const HELP =
|
|
|
26
26
|
"\n" +
|
|
27
27
|
"Mill, not exo. FOUNDRY_ROOT overrides cwd (the repo being milled).\n" +
|
|
28
28
|
"release bumps/commits/pushes/publishes the milled cwd — pass --dry-run or --i-mean-it (or FOUNDRY_RELEASE=1).\n" +
|
|
29
|
-
"Mint fastens mill plant then overlays their plant (mill-fill JSON). PPE stays worn.\n" +
|
|
29
|
+
"Mint fastens mill plant then overlays their plant (mill-fill JSON), then runs inspect. PPE stays worn.\n" +
|
|
30
30
|
"inspect runs the six mill checks (diff, plant vs worn, receipt, CI, live tarball GET, isolated HOME). Not an 8th MCP.\n" +
|
|
31
31
|
"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";
|
|
32
32
|
|
|
@@ -119,6 +119,8 @@ function checkReceipt(root) {
|
|
|
119
119
|
suit: inventory.suit,
|
|
120
120
|
mill: inventory.mill,
|
|
121
121
|
millPlant: millPlantSkills,
|
|
122
|
+
dna: typeof inventory.dna === "string" ? inventory.dna : null,
|
|
123
|
+
pack: "0xray-suit",
|
|
122
124
|
detail: ok ? null : "millPlant.skills must include mill and inspect",
|
|
123
125
|
};
|
|
124
126
|
}
|
|
@@ -179,7 +181,7 @@ export async function checkLivePut(packages, fetchFn = fetch) {
|
|
|
179
181
|
return results;
|
|
180
182
|
}
|
|
181
183
|
|
|
182
|
-
function packagesToProbe(root) {
|
|
184
|
+
export function packagesToProbe(root) {
|
|
183
185
|
const pkgs = [];
|
|
184
186
|
const inventory = readJson(path.join(root, ".xray", "foundry-inventory.json"));
|
|
185
187
|
if (inventory?.mill?.name && inventory?.mill?.version) {
|
|
@@ -193,6 +195,17 @@ function packagesToProbe(root) {
|
|
|
193
195
|
if (self?.name === "0xray" && self?.version) {
|
|
194
196
|
pkgs.push({ name: "0xray", version: self.version });
|
|
195
197
|
}
|
|
198
|
+
if (
|
|
199
|
+
self &&
|
|
200
|
+
self.private !== true &&
|
|
201
|
+
inventory?.consumer?.name &&
|
|
202
|
+
inventory?.consumer?.version
|
|
203
|
+
) {
|
|
204
|
+
pkgs.push({
|
|
205
|
+
name: inventory.consumer.name,
|
|
206
|
+
version: inventory.consumer.version,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
196
209
|
const seen = new Set();
|
|
197
210
|
return pkgs.filter((p) => {
|
|
198
211
|
const key = `${p.name}@${p.version}`;
|
|
@@ -262,10 +275,13 @@ export async function inspectSuit(root, opts = {}) {
|
|
|
262
275
|
checks.push(checkIsolatedHome(env, machineHome));
|
|
263
276
|
|
|
264
277
|
const failed = checks.filter((c) => c.ok === false);
|
|
278
|
+
const receipt = checks.find((c) => c.id === "receipt") || {};
|
|
265
279
|
return {
|
|
266
280
|
ok: failed.length === 0,
|
|
267
281
|
failed: failed.map((c) => c.id),
|
|
268
282
|
checks,
|
|
283
|
+
dna: receipt.dna || null,
|
|
284
|
+
pack: "0xray-suit",
|
|
269
285
|
};
|
|
270
286
|
}
|
|
271
287
|
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical mill DNA for Groover GRVR. keccak256 of inventory without mintedAt/dna.
|
|
3
|
+
* Not a chain client. Not an 8th MCP.
|
|
4
|
+
*/
|
|
5
|
+
"use strict";
|
|
6
|
+
|
|
7
|
+
const { keccak_256 } = require("./mill-keccak.cjs");
|
|
8
|
+
|
|
9
|
+
function isPlainObject(value) {
|
|
10
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function canonicalJson(value) {
|
|
14
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
|
|
17
|
+
}
|
|
18
|
+
const keys = Object.keys(value).sort();
|
|
19
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function dnaPayload(inventory) {
|
|
23
|
+
if (!isPlainObject(inventory)) {
|
|
24
|
+
throw new Error("inventory object required for mill DNA");
|
|
25
|
+
}
|
|
26
|
+
const payload = { ...inventory };
|
|
27
|
+
delete payload.mintedAt;
|
|
28
|
+
delete payload.dna;
|
|
29
|
+
return payload;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function inventoryDna(inventory) {
|
|
33
|
+
const json = canonicalJson(dnaPayload(inventory));
|
|
34
|
+
const digest = keccak_256(Buffer.from(json, "utf8"));
|
|
35
|
+
return `0x${Buffer.from(digest).toString("hex")}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function attachInventoryDna(inventory) {
|
|
39
|
+
if (!isPlainObject(inventory)) return inventory;
|
|
40
|
+
const next = { ...inventory };
|
|
41
|
+
next.dna = inventoryDna(next);
|
|
42
|
+
return next;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = {
|
|
46
|
+
canonicalJson,
|
|
47
|
+
dnaPayload,
|
|
48
|
+
inventoryDna,
|
|
49
|
+
attachInventoryDna,
|
|
50
|
+
};
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/* vendored @noble/hashes keccak_256 — mill DNA. Not an 8th MCP. */
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// <stdin>
|
|
21
|
+
var stdin_exports = {};
|
|
22
|
+
__export(stdin_exports, {
|
|
23
|
+
keccak_256: () => keccak_256
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(stdin_exports);
|
|
26
|
+
|
|
27
|
+
// node_modules/@noble/hashes/esm/_u64.js
|
|
28
|
+
var U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
|
|
29
|
+
var _32n = /* @__PURE__ */ BigInt(32);
|
|
30
|
+
function fromBig(n, le = false) {
|
|
31
|
+
if (le)
|
|
32
|
+
return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
|
|
33
|
+
return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
|
|
34
|
+
}
|
|
35
|
+
function split(lst, le = false) {
|
|
36
|
+
const len = lst.length;
|
|
37
|
+
let Ah = new Uint32Array(len);
|
|
38
|
+
let Al = new Uint32Array(len);
|
|
39
|
+
for (let i = 0; i < len; i++) {
|
|
40
|
+
const { h, l } = fromBig(lst[i], le);
|
|
41
|
+
[Ah[i], Al[i]] = [h, l];
|
|
42
|
+
}
|
|
43
|
+
return [Ah, Al];
|
|
44
|
+
}
|
|
45
|
+
var rotlSH = (h, l, s) => h << s | l >>> 32 - s;
|
|
46
|
+
var rotlSL = (h, l, s) => l << s | h >>> 32 - s;
|
|
47
|
+
var rotlBH = (h, l, s) => l << s - 32 | h >>> 64 - s;
|
|
48
|
+
var rotlBL = (h, l, s) => h << s - 32 | l >>> 64 - s;
|
|
49
|
+
|
|
50
|
+
// node_modules/@noble/hashes/esm/utils.js
|
|
51
|
+
function isBytes(a) {
|
|
52
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
53
|
+
}
|
|
54
|
+
function anumber(n) {
|
|
55
|
+
if (!Number.isSafeInteger(n) || n < 0)
|
|
56
|
+
throw new Error("positive integer expected, got " + n);
|
|
57
|
+
}
|
|
58
|
+
function abytes(b, ...lengths) {
|
|
59
|
+
if (!isBytes(b))
|
|
60
|
+
throw new Error("Uint8Array expected");
|
|
61
|
+
if (lengths.length > 0 && !lengths.includes(b.length))
|
|
62
|
+
throw new Error("Uint8Array expected of length " + lengths + ", got length=" + b.length);
|
|
63
|
+
}
|
|
64
|
+
function aexists(instance, checkFinished = true) {
|
|
65
|
+
if (instance.destroyed)
|
|
66
|
+
throw new Error("Hash instance has been destroyed");
|
|
67
|
+
if (checkFinished && instance.finished)
|
|
68
|
+
throw new Error("Hash#digest() has already been called");
|
|
69
|
+
}
|
|
70
|
+
function aoutput(out, instance) {
|
|
71
|
+
abytes(out);
|
|
72
|
+
const min = instance.outputLen;
|
|
73
|
+
if (out.length < min) {
|
|
74
|
+
throw new Error("digestInto() expects output buffer of length at least " + min);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function u32(arr) {
|
|
78
|
+
return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
|
79
|
+
}
|
|
80
|
+
function clean(...arrays) {
|
|
81
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
82
|
+
arrays[i].fill(0);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
var isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68)();
|
|
86
|
+
function byteSwap(word) {
|
|
87
|
+
return word << 24 & 4278190080 | word << 8 & 16711680 | word >>> 8 & 65280 | word >>> 24 & 255;
|
|
88
|
+
}
|
|
89
|
+
function byteSwap32(arr) {
|
|
90
|
+
for (let i = 0; i < arr.length; i++) {
|
|
91
|
+
arr[i] = byteSwap(arr[i]);
|
|
92
|
+
}
|
|
93
|
+
return arr;
|
|
94
|
+
}
|
|
95
|
+
var swap32IfBE = isLE ? (u) => u : byteSwap32;
|
|
96
|
+
function utf8ToBytes(str) {
|
|
97
|
+
if (typeof str !== "string")
|
|
98
|
+
throw new Error("string expected");
|
|
99
|
+
return new Uint8Array(new TextEncoder().encode(str));
|
|
100
|
+
}
|
|
101
|
+
function toBytes(data) {
|
|
102
|
+
if (typeof data === "string")
|
|
103
|
+
data = utf8ToBytes(data);
|
|
104
|
+
abytes(data);
|
|
105
|
+
return data;
|
|
106
|
+
}
|
|
107
|
+
var Hash = class {
|
|
108
|
+
};
|
|
109
|
+
function createHasher(hashCons) {
|
|
110
|
+
const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
|
|
111
|
+
const tmp = hashCons();
|
|
112
|
+
hashC.outputLen = tmp.outputLen;
|
|
113
|
+
hashC.blockLen = tmp.blockLen;
|
|
114
|
+
hashC.create = () => hashCons();
|
|
115
|
+
return hashC;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// node_modules/@noble/hashes/esm/sha3.js
|
|
119
|
+
var _0n = BigInt(0);
|
|
120
|
+
var _1n = BigInt(1);
|
|
121
|
+
var _2n = BigInt(2);
|
|
122
|
+
var _7n = BigInt(7);
|
|
123
|
+
var _256n = BigInt(256);
|
|
124
|
+
var _0x71n = BigInt(113);
|
|
125
|
+
var SHA3_PI = [];
|
|
126
|
+
var SHA3_ROTL = [];
|
|
127
|
+
var _SHA3_IOTA = [];
|
|
128
|
+
for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
|
|
129
|
+
[x, y] = [y, (2 * x + 3 * y) % 5];
|
|
130
|
+
SHA3_PI.push(2 * (5 * y + x));
|
|
131
|
+
SHA3_ROTL.push((round + 1) * (round + 2) / 2 % 64);
|
|
132
|
+
let t = _0n;
|
|
133
|
+
for (let j = 0; j < 7; j++) {
|
|
134
|
+
R = (R << _1n ^ (R >> _7n) * _0x71n) % _256n;
|
|
135
|
+
if (R & _2n)
|
|
136
|
+
t ^= _1n << (_1n << /* @__PURE__ */ BigInt(j)) - _1n;
|
|
137
|
+
}
|
|
138
|
+
_SHA3_IOTA.push(t);
|
|
139
|
+
}
|
|
140
|
+
var IOTAS = split(_SHA3_IOTA, true);
|
|
141
|
+
var SHA3_IOTA_H = IOTAS[0];
|
|
142
|
+
var SHA3_IOTA_L = IOTAS[1];
|
|
143
|
+
var rotlH = (h, l, s) => s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s);
|
|
144
|
+
var rotlL = (h, l, s) => s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s);
|
|
145
|
+
function keccakP(s, rounds = 24) {
|
|
146
|
+
const B = new Uint32Array(5 * 2);
|
|
147
|
+
for (let round = 24 - rounds; round < 24; round++) {
|
|
148
|
+
for (let x = 0; x < 10; x++)
|
|
149
|
+
B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
|
|
150
|
+
for (let x = 0; x < 10; x += 2) {
|
|
151
|
+
const idx1 = (x + 8) % 10;
|
|
152
|
+
const idx0 = (x + 2) % 10;
|
|
153
|
+
const B0 = B[idx0];
|
|
154
|
+
const B1 = B[idx0 + 1];
|
|
155
|
+
const Th = rotlH(B0, B1, 1) ^ B[idx1];
|
|
156
|
+
const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
|
|
157
|
+
for (let y = 0; y < 50; y += 10) {
|
|
158
|
+
s[x + y] ^= Th;
|
|
159
|
+
s[x + y + 1] ^= Tl;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
let curH = s[2];
|
|
163
|
+
let curL = s[3];
|
|
164
|
+
for (let t = 0; t < 24; t++) {
|
|
165
|
+
const shift = SHA3_ROTL[t];
|
|
166
|
+
const Th = rotlH(curH, curL, shift);
|
|
167
|
+
const Tl = rotlL(curH, curL, shift);
|
|
168
|
+
const PI = SHA3_PI[t];
|
|
169
|
+
curH = s[PI];
|
|
170
|
+
curL = s[PI + 1];
|
|
171
|
+
s[PI] = Th;
|
|
172
|
+
s[PI + 1] = Tl;
|
|
173
|
+
}
|
|
174
|
+
for (let y = 0; y < 50; y += 10) {
|
|
175
|
+
for (let x = 0; x < 10; x++)
|
|
176
|
+
B[x] = s[y + x];
|
|
177
|
+
for (let x = 0; x < 10; x++)
|
|
178
|
+
s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
|
|
179
|
+
}
|
|
180
|
+
s[0] ^= SHA3_IOTA_H[round];
|
|
181
|
+
s[1] ^= SHA3_IOTA_L[round];
|
|
182
|
+
}
|
|
183
|
+
clean(B);
|
|
184
|
+
}
|
|
185
|
+
var Keccak = class _Keccak extends Hash {
|
|
186
|
+
// NOTE: we accept arguments in bytes instead of bits here.
|
|
187
|
+
constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) {
|
|
188
|
+
super();
|
|
189
|
+
this.pos = 0;
|
|
190
|
+
this.posOut = 0;
|
|
191
|
+
this.finished = false;
|
|
192
|
+
this.destroyed = false;
|
|
193
|
+
this.enableXOF = false;
|
|
194
|
+
this.blockLen = blockLen;
|
|
195
|
+
this.suffix = suffix;
|
|
196
|
+
this.outputLen = outputLen;
|
|
197
|
+
this.enableXOF = enableXOF;
|
|
198
|
+
this.rounds = rounds;
|
|
199
|
+
anumber(outputLen);
|
|
200
|
+
if (!(0 < blockLen && blockLen < 200))
|
|
201
|
+
throw new Error("only keccak-f1600 function is supported");
|
|
202
|
+
this.state = new Uint8Array(200);
|
|
203
|
+
this.state32 = u32(this.state);
|
|
204
|
+
}
|
|
205
|
+
clone() {
|
|
206
|
+
return this._cloneInto();
|
|
207
|
+
}
|
|
208
|
+
keccak() {
|
|
209
|
+
swap32IfBE(this.state32);
|
|
210
|
+
keccakP(this.state32, this.rounds);
|
|
211
|
+
swap32IfBE(this.state32);
|
|
212
|
+
this.posOut = 0;
|
|
213
|
+
this.pos = 0;
|
|
214
|
+
}
|
|
215
|
+
update(data) {
|
|
216
|
+
aexists(this);
|
|
217
|
+
data = toBytes(data);
|
|
218
|
+
abytes(data);
|
|
219
|
+
const { blockLen, state } = this;
|
|
220
|
+
const len = data.length;
|
|
221
|
+
for (let pos = 0; pos < len; ) {
|
|
222
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
223
|
+
for (let i = 0; i < take; i++)
|
|
224
|
+
state[this.pos++] ^= data[pos++];
|
|
225
|
+
if (this.pos === blockLen)
|
|
226
|
+
this.keccak();
|
|
227
|
+
}
|
|
228
|
+
return this;
|
|
229
|
+
}
|
|
230
|
+
finish() {
|
|
231
|
+
if (this.finished)
|
|
232
|
+
return;
|
|
233
|
+
this.finished = true;
|
|
234
|
+
const { state, suffix, pos, blockLen } = this;
|
|
235
|
+
state[pos] ^= suffix;
|
|
236
|
+
if ((suffix & 128) !== 0 && pos === blockLen - 1)
|
|
237
|
+
this.keccak();
|
|
238
|
+
state[blockLen - 1] ^= 128;
|
|
239
|
+
this.keccak();
|
|
240
|
+
}
|
|
241
|
+
writeInto(out) {
|
|
242
|
+
aexists(this, false);
|
|
243
|
+
abytes(out);
|
|
244
|
+
this.finish();
|
|
245
|
+
const bufferOut = this.state;
|
|
246
|
+
const { blockLen } = this;
|
|
247
|
+
for (let pos = 0, len = out.length; pos < len; ) {
|
|
248
|
+
if (this.posOut >= blockLen)
|
|
249
|
+
this.keccak();
|
|
250
|
+
const take = Math.min(blockLen - this.posOut, len - pos);
|
|
251
|
+
out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
|
|
252
|
+
this.posOut += take;
|
|
253
|
+
pos += take;
|
|
254
|
+
}
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
xofInto(out) {
|
|
258
|
+
if (!this.enableXOF)
|
|
259
|
+
throw new Error("XOF is not possible for this instance");
|
|
260
|
+
return this.writeInto(out);
|
|
261
|
+
}
|
|
262
|
+
xof(bytes) {
|
|
263
|
+
anumber(bytes);
|
|
264
|
+
return this.xofInto(new Uint8Array(bytes));
|
|
265
|
+
}
|
|
266
|
+
digestInto(out) {
|
|
267
|
+
aoutput(out, this);
|
|
268
|
+
if (this.finished)
|
|
269
|
+
throw new Error("digest() was already called");
|
|
270
|
+
this.writeInto(out);
|
|
271
|
+
this.destroy();
|
|
272
|
+
return out;
|
|
273
|
+
}
|
|
274
|
+
digest() {
|
|
275
|
+
return this.digestInto(new Uint8Array(this.outputLen));
|
|
276
|
+
}
|
|
277
|
+
destroy() {
|
|
278
|
+
this.destroyed = true;
|
|
279
|
+
clean(this.state);
|
|
280
|
+
}
|
|
281
|
+
_cloneInto(to) {
|
|
282
|
+
const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
|
|
283
|
+
to || (to = new _Keccak(blockLen, suffix, outputLen, enableXOF, rounds));
|
|
284
|
+
to.state32.set(this.state32);
|
|
285
|
+
to.pos = this.pos;
|
|
286
|
+
to.posOut = this.posOut;
|
|
287
|
+
to.finished = this.finished;
|
|
288
|
+
to.rounds = rounds;
|
|
289
|
+
to.suffix = suffix;
|
|
290
|
+
to.outputLen = outputLen;
|
|
291
|
+
to.enableXOF = enableXOF;
|
|
292
|
+
to.destroyed = this.destroyed;
|
|
293
|
+
return to;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
var gen = (suffix, blockLen, outputLen) => createHasher(() => new Keccak(blockLen, suffix, outputLen));
|
|
297
|
+
var keccak_256 = /* @__PURE__ */ (() => gen(1, 136, 256 / 8))();
|
|
298
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
299
|
+
0 && (module.exports = {
|
|
300
|
+
keccak_256
|
|
301
|
+
});
|
|
302
|
+
/*! Bundled license information:
|
|
303
|
+
|
|
304
|
+
@noble/hashes/esm/utils.js:
|
|
305
|
+
(*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
306
|
+
*/
|
|
@@ -10,6 +10,8 @@ const fs = require("fs");
|
|
|
10
10
|
const os = require("os");
|
|
11
11
|
const path = require("path");
|
|
12
12
|
|
|
13
|
+
const { attachInventoryDna, inventoryDna } = require("./mill-dna.cjs");
|
|
14
|
+
|
|
13
15
|
const DEFAULT_PARAMS = {
|
|
14
16
|
codex: "xray/codex.json",
|
|
15
17
|
features: "xray/features.json",
|
|
@@ -436,20 +438,22 @@ function mintConsumerFromSsot(packageRoot, targetDir, log, tree) {
|
|
|
436
438
|
},
|
|
437
439
|
mintedAt: new Date().toISOString(),
|
|
438
440
|
};
|
|
441
|
+
const withDna = attachInventoryDna(inventory);
|
|
439
442
|
const xrayDir = path.join(targetDir, ".xray");
|
|
440
443
|
if (!fs.existsSync(xrayDir)) fs.mkdirSync(xrayDir, { recursive: true });
|
|
441
444
|
fs.writeFileSync(
|
|
442
445
|
path.join(xrayDir, "foundry-inventory.json"),
|
|
443
|
-
`${JSON.stringify(
|
|
446
|
+
`${JSON.stringify(withDna, null, 2)}\n`,
|
|
444
447
|
);
|
|
445
448
|
if (log) {
|
|
446
449
|
log("foundry-mint", "Minted foundry-inventory from consumer mill SSOT", "info", {
|
|
447
450
|
consumer: consumer.name,
|
|
448
451
|
version: consumer.version,
|
|
449
452
|
suit,
|
|
453
|
+
dna: withDna.dna,
|
|
450
454
|
});
|
|
451
455
|
}
|
|
452
|
-
return
|
|
456
|
+
return withDna;
|
|
453
457
|
}
|
|
454
458
|
|
|
455
459
|
function mintConsumerSuit(millPackageRoot, targetDir, log) {
|
|
@@ -504,6 +508,8 @@ module.exports = {
|
|
|
504
508
|
listProjectSkillDirs,
|
|
505
509
|
mintConsumerFromSsot,
|
|
506
510
|
mintConsumerSuit,
|
|
511
|
+
attachInventoryDna,
|
|
512
|
+
inventoryDna,
|
|
507
513
|
isDogfood,
|
|
508
514
|
readPackageIdentity,
|
|
509
515
|
wornSkillNames,
|
package/scripts/foundry/mint.mjs
CHANGED
|
@@ -8,13 +8,15 @@ import { createRequire } from "node:module";
|
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { millPackageDir, resolveMillRoot } from "./mill-root.mjs";
|
|
11
|
+
import { inspectSuit } from "./inspect.mjs";
|
|
11
12
|
|
|
12
13
|
const require = createRequire(import.meta.url);
|
|
13
14
|
const { mintConsumerSuit } = require("./mint-suit.cjs");
|
|
14
15
|
|
|
15
|
-
function main() {
|
|
16
|
+
async function main() {
|
|
16
17
|
const target = resolveMillRoot();
|
|
17
18
|
const millRoot = millPackageDir();
|
|
19
|
+
const skipLive = process.argv.includes("--skip-live");
|
|
18
20
|
let inventory;
|
|
19
21
|
try {
|
|
20
22
|
inventory = mintConsumerSuit(millRoot, target, (component, action, status) => {
|
|
@@ -29,6 +31,9 @@ function main() {
|
|
|
29
31
|
process.exit(0);
|
|
30
32
|
}
|
|
31
33
|
process.stdout.write(`${JSON.stringify(inventory, null, 2)}\n`);
|
|
34
|
+
const report = await inspectSuit(target, { millRoot, skipLive });
|
|
35
|
+
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
36
|
+
if (!report.ok) process.exit(1);
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
const isMain =
|
|
@@ -230,12 +230,13 @@ function isIsolatedHome(env = process.env, machine = millSuit.machineHome()) {
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
function copyHermesHookRuntimes(packageRoot) {
|
|
233
|
+
if (millSuit.isIsolatedHome()) return false;
|
|
233
234
|
const hooksSrc = [
|
|
234
235
|
path.join(packageRoot, "dist", "integrations", "hooks"),
|
|
235
236
|
path.join(packageRoot, "src", "integrations", "hooks"),
|
|
236
237
|
].find((p) => fs.existsSync(p));
|
|
237
238
|
if (!hooksSrc) return false;
|
|
238
|
-
const hooksDst = path.join(
|
|
239
|
+
const hooksDst = path.join(millSuit.machineHome(), ".hermes", "plugins", "hooks");
|
|
239
240
|
fs.mkdirSync(hooksDst, { recursive: true });
|
|
240
241
|
fs.cpSync(hooksSrc, hooksDst, { recursive: true, force: true });
|
|
241
242
|
return true;
|
|
@@ -428,10 +429,11 @@ function resolveOpenClawPluginDir(packageRoot) {
|
|
|
428
429
|
}
|
|
429
430
|
|
|
430
431
|
function installOpenClawHostWear(packageRoot) {
|
|
432
|
+
if (millSuit.isIsolatedHome()) return null;
|
|
431
433
|
const source = resolveOpenClawPreToolHookSource(packageRoot);
|
|
432
434
|
if (!source) return null;
|
|
433
435
|
|
|
434
|
-
const hookDir = path.join(
|
|
436
|
+
const hookDir = path.join(millSuit.machineHome(), ".openclaw", "hooks");
|
|
435
437
|
fs.mkdirSync(hookDir, { recursive: true });
|
|
436
438
|
const dest = path.join(hookDir, "xray-pre-tool.mjs");
|
|
437
439
|
fs.copyFileSync(source, dest);
|
|
@@ -423,7 +423,16 @@ function installHermesBridge(targetDir, packageRoot, log) {
|
|
|
423
423
|
return;
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
-
const
|
|
426
|
+
const ephemeral = isEphemeralInstallRoot(targetDir);
|
|
427
|
+
const isolated = isIsolatedHome();
|
|
428
|
+
const machine = machineHome();
|
|
429
|
+
const targetPluginDir =
|
|
430
|
+
ephemeral || isolated
|
|
431
|
+
? path.join(targetDir, ".hermes", "plugins", "xray-hermes")
|
|
432
|
+
: path.join(machine, ".hermes", "plugins", "xray-hermes");
|
|
433
|
+
if (isolated) {
|
|
434
|
+
log("hermes-bridge", "skip machine ~/.hermes plugin — isolated HOME", "info");
|
|
435
|
+
}
|
|
427
436
|
fs.mkdirSync(targetPluginDir, { recursive: true });
|
|
428
437
|
for (const entry of fs.readdirSync(sourceDir)) {
|
|
429
438
|
const src = path.join(sourceDir, entry);
|
|
@@ -435,10 +444,15 @@ function installHermesBridge(targetDir, packageRoot, log) {
|
|
|
435
444
|
}
|
|
436
445
|
}
|
|
437
446
|
copyHermesFindProjectRootHelper(packageRoot, targetPluginDir);
|
|
438
|
-
if (copyHermesHookRuntimes(packageRoot)) {
|
|
447
|
+
if (!ephemeral && !isolated && copyHermesHookRuntimes(packageRoot)) {
|
|
439
448
|
log("hermes-bridge", "hook runtimes copied", "info", { path: "~/.hermes/plugins/hooks" });
|
|
440
449
|
}
|
|
441
|
-
log(
|
|
450
|
+
log(
|
|
451
|
+
"hermes-bridge",
|
|
452
|
+
"plugin copied",
|
|
453
|
+
"info",
|
|
454
|
+
{ path: ephemeral || isolated ? path.relative(targetDir, targetPluginDir) : "~/.hermes/plugins/xray-hermes" },
|
|
455
|
+
);
|
|
442
456
|
|
|
443
457
|
writePluginMcpJson(targetPluginDir, targetDir, log, "hermes-bridge");
|
|
444
458
|
|
|
@@ -453,11 +467,13 @@ function installHermesBridge(targetDir, packageRoot, log) {
|
|
|
453
467
|
log("hermes-bridge", "skip machine consumer marker — ephemeral consumer", "info");
|
|
454
468
|
}
|
|
455
469
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
470
|
+
if (!ephemeral && !isolated) {
|
|
471
|
+
try {
|
|
472
|
+
const result = wireHermesBridge(targetDir);
|
|
473
|
+
log("hermes-bridge", `mcp_servers wired (${result.count} servers)`, "info");
|
|
474
|
+
} catch (e) {
|
|
475
|
+
log("hermes-bridge", "mcp_servers wire failed", "warn", { error: e.message });
|
|
476
|
+
}
|
|
461
477
|
}
|
|
462
478
|
}
|
|
463
479
|
|
|
@@ -494,26 +510,52 @@ function installOpenclawBridge(targetDir, packageRoot, log) {
|
|
|
494
510
|
log("openclaw-bridge", "config created", "info", { path: ".xray/config/openclaw.json" });
|
|
495
511
|
}
|
|
496
512
|
|
|
497
|
-
const
|
|
498
|
-
|
|
513
|
+
const ephemeralOpenclaw = isEphemeralInstallRoot(targetDir);
|
|
514
|
+
const isolatedOpenclaw = isIsolatedHome();
|
|
515
|
+
if (!ephemeralOpenclaw && !isolatedOpenclaw) {
|
|
516
|
+
const copied = syncCostumeSkills(
|
|
517
|
+
path.join(machineHome(), ".openclaw", "skills"),
|
|
518
|
+
packageRoot,
|
|
519
|
+
targetDir,
|
|
520
|
+
);
|
|
521
|
+
if (copied > 0) {
|
|
522
|
+
log("openclaw-bridge", `skills synced (${copied})`, "info", { path: "~/.openclaw/skills/" });
|
|
523
|
+
}
|
|
524
|
+
} else {
|
|
525
|
+
log(
|
|
526
|
+
"openclaw-bridge",
|
|
527
|
+
isolatedOpenclaw
|
|
528
|
+
? "skip machine ~/.openclaw skills — isolated HOME"
|
|
529
|
+
: "skip machine ~/.openclaw skills — ephemeral consumer",
|
|
530
|
+
"info",
|
|
531
|
+
);
|
|
532
|
+
}
|
|
499
533
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
534
|
+
if (!ephemeralOpenclaw && !isolatedOpenclaw) {
|
|
535
|
+
try {
|
|
536
|
+
const result = wireOpenClawBridge(targetDir);
|
|
537
|
+
log("openclaw-bridge", `openclaw.json mcp wired (${result.count} servers)`, "info", {
|
|
538
|
+
method: result.method,
|
|
539
|
+
});
|
|
540
|
+
} catch (e) {
|
|
541
|
+
log("openclaw-bridge", "openclaw.json mcp wire failed", "warn", { error: e.message });
|
|
542
|
+
}
|
|
507
543
|
}
|
|
508
544
|
|
|
509
|
-
if (!
|
|
545
|
+
if (!ephemeralOpenclaw && !isolatedOpenclaw) {
|
|
510
546
|
const hook = installOpenClawHostWear(packageRoot);
|
|
511
547
|
if (hook) log("openclaw-bridge", "PreToolUse hook installed", "info", { path: hook });
|
|
512
548
|
if (maybeWriteOpenClawCliBackend()) {
|
|
513
549
|
log("openclaw-bridge", "opencode-cli backend written", "info");
|
|
514
550
|
}
|
|
515
551
|
} else {
|
|
516
|
-
log(
|
|
552
|
+
log(
|
|
553
|
+
"openclaw-bridge",
|
|
554
|
+
isolatedOpenclaw
|
|
555
|
+
? "skip machine PreToolUse wear — isolated HOME"
|
|
556
|
+
: "skip machine PreToolUse wear — ephemeral consumer",
|
|
557
|
+
"info",
|
|
558
|
+
);
|
|
517
559
|
}
|
|
518
560
|
}
|
|
519
561
|
|