0xray 4.0.9 → 4.0.10
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 +1 -0
- package/AGENTS.md +10 -6
- package/README.md +15 -8
- package/SKILLS.md +3 -3
- package/dist/AGENTS.md +10 -6
- package/dist/CHANGELOG.md +16 -0
- package/dist/README.md +15 -8
- package/dist/SKILLS.md +3 -3
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/memory-routing/provider-loader.js +4 -0
- package/llms.txt +59 -0
- package/package.json +3 -2
- package/scripts/foundry/CHANGELOG.md +4 -0
- package/scripts/foundry/README.md +2 -0
- package/scripts/foundry/inspect.mjs +3 -0
- package/scripts/foundry/mint-suit.cjs +60 -2
- package/scripts/foundry/plant/skills/inspect/SKILL.md +1 -1
- package/scripts/foundry/validate-release-docs.mjs +69 -2
- package/scripts/foundry/version-manager.mjs +4 -0
- package/scripts/node/bridge-mcp-wiring.cjs +23 -0
- package/scripts/node/consumer-install-smoke.mjs +82 -14
- package/scripts/node/install-bridges.cjs +4 -0
- package/scripts/node/pack-tmp-suit-proof.mjs +242 -0
- package/scripts/node/wear-vendored-repertoire.cjs +120 -0
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/xray/features.json +1 -1
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* validate-release-docs.mjs — Blocking guard for release doc freshness.
|
|
4
4
|
*
|
|
5
|
-
* Ensures README, AGENTS, SKILLS, CHANGELOG, Docusaurus headers, and
|
|
6
|
-
* guides match package.json version and
|
|
5
|
+
* Ensures README, AGENTS, SKILLS, llms.txt, CHANGELOG, Docusaurus headers, and
|
|
6
|
+
* required guides match package.json version and mill law before tag/publish.
|
|
7
7
|
*
|
|
8
8
|
* Usage:
|
|
9
9
|
* npx @0xray/foundry docs-check
|
|
@@ -42,6 +42,24 @@ const REQUIRED_GUIDES = [
|
|
|
42
42
|
'docs-site/docs/agents/README.md',
|
|
43
43
|
];
|
|
44
44
|
|
|
45
|
+
function millLawLagErrors(content) {
|
|
46
|
+
const errors = [];
|
|
47
|
+
if (/Syncs \*\*45 framework skills\*\*/.test(content) || /syncs all 45 skills/i.test(content)) {
|
|
48
|
+
errors.push('lags mill law: default plant is mill+inspect, not 45-skill sync');
|
|
49
|
+
}
|
|
50
|
+
if (/SKILLS\.md \+ 45 skills → platform skill directories/.test(content)) {
|
|
51
|
+
errors.push('lags mill law: postinstall must not sync 45 skills as default plant');
|
|
52
|
+
}
|
|
53
|
+
return errors;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function millPlantMentionErrors(content) {
|
|
57
|
+
if (!content.includes('mill') || !content.includes('inspect')) {
|
|
58
|
+
return ['missing mill+inspect plant'];
|
|
59
|
+
}
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
|
|
45
63
|
const ROOT_DOC_CHECKS = [
|
|
46
64
|
{
|
|
47
65
|
rel: 'README.md',
|
|
@@ -62,6 +80,14 @@ const ROOT_DOC_CHECKS = [
|
|
|
62
80
|
if (!content.includes('install-bridges') && !content.includes('installAllBridges')) {
|
|
63
81
|
errors.push('missing install-bridges / installAllBridges reference');
|
|
64
82
|
}
|
|
83
|
+
if (!content.includes('llms.txt')) {
|
|
84
|
+
errors.push('missing llms.txt agent-map reference');
|
|
85
|
+
}
|
|
86
|
+
if (!content.includes('shopPlant') && !content.includes('shop-extract')) {
|
|
87
|
+
errors.push('missing shop plant / shopPlant');
|
|
88
|
+
}
|
|
89
|
+
errors.push(...millPlantMentionErrors(content));
|
|
90
|
+
errors.push(...millLawLagErrors(content));
|
|
65
91
|
return errors;
|
|
66
92
|
},
|
|
67
93
|
},
|
|
@@ -81,6 +107,11 @@ const ROOT_DOC_CHECKS = [
|
|
|
81
107
|
if (!content.includes('SKILLS.md')) {
|
|
82
108
|
errors.push('missing SKILLS.md link');
|
|
83
109
|
}
|
|
110
|
+
if (!content.includes('llms.txt')) {
|
|
111
|
+
errors.push('missing llms.txt agent-map reference');
|
|
112
|
+
}
|
|
113
|
+
errors.push(...millPlantMentionErrors(content));
|
|
114
|
+
errors.push(...millLawLagErrors(content));
|
|
84
115
|
return errors;
|
|
85
116
|
},
|
|
86
117
|
},
|
|
@@ -100,6 +131,11 @@ const ROOT_DOC_CHECKS = [
|
|
|
100
131
|
if (!content.includes('AsideContext')) {
|
|
101
132
|
errors.push('missing AsideContext section');
|
|
102
133
|
}
|
|
134
|
+
if (!content.includes('shopPlant') && !content.includes('shop-extract')) {
|
|
135
|
+
errors.push('missing shop plant / shopPlant');
|
|
136
|
+
}
|
|
137
|
+
errors.push(...millPlantMentionErrors(content));
|
|
138
|
+
errors.push(...millLawLagErrors(content));
|
|
103
139
|
return errors;
|
|
104
140
|
},
|
|
105
141
|
},
|
|
@@ -113,6 +149,37 @@ const ROOT_DOC_CHECKS = [
|
|
|
113
149
|
if (!content.includes(`**${counts.skills} skills**`)) {
|
|
114
150
|
errors.push(`skill count stale (expected **${counts.skills} skills**)`);
|
|
115
151
|
}
|
|
152
|
+
errors.push(...millPlantMentionErrors(content));
|
|
153
|
+
errors.push(...millLawLagErrors(content));
|
|
154
|
+
return errors;
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
rel: 'llms.txt',
|
|
159
|
+
validate: (content) => {
|
|
160
|
+
const errors = [];
|
|
161
|
+
if (kernelLineHasPatchStamp(content)) {
|
|
162
|
+
errors.push('kernel header must be era (major.minor), not a patch stamp');
|
|
163
|
+
}
|
|
164
|
+
if (!content.includes('mill') || !content.includes('inspect')) {
|
|
165
|
+
errors.push('missing mill+inspect plant');
|
|
166
|
+
}
|
|
167
|
+
if (!content.includes('shopPlant') && !content.includes('shop-extract')) {
|
|
168
|
+
errors.push('missing hangar / shopPlant coexistence');
|
|
169
|
+
}
|
|
170
|
+
if (!content.includes('69')) {
|
|
171
|
+
errors.push('missing Codex 69');
|
|
172
|
+
}
|
|
173
|
+
if (!content.includes('xray-governance') || !content.includes('xray-orchestrator')) {
|
|
174
|
+
errors.push('missing 7 MCP agent map');
|
|
175
|
+
}
|
|
176
|
+
if (!content.toLowerCase().includes('costume')) {
|
|
177
|
+
errors.push('missing no-costume-dump law');
|
|
178
|
+
}
|
|
179
|
+
if (/^## Complete catalog/m.test(content) || content.includes('`api-design` · `architect-tools`')) {
|
|
180
|
+
errors.push('llms.txt must not costume-dump the 45-skill catalog');
|
|
181
|
+
}
|
|
182
|
+
errors.push(...millLawLagErrors(content));
|
|
116
183
|
return errors;
|
|
117
184
|
},
|
|
118
185
|
},
|
|
@@ -348,6 +348,7 @@ export function getReleaseArtifactPaths(baseDir = resolveMillRoot()) {
|
|
|
348
348
|
'AGENTS.md',
|
|
349
349
|
'AGENTS-consumer.md',
|
|
350
350
|
'SKILLS.md',
|
|
351
|
+
'llms.txt',
|
|
351
352
|
'.grok-plugin/plugin.json',
|
|
352
353
|
'docs/README.md',
|
|
353
354
|
'docs-site/docs/index.md',
|
|
@@ -366,6 +367,9 @@ export function getReleaseArtifactPaths(baseDir = resolveMillRoot()) {
|
|
|
366
367
|
'docs-site/docs/architecture/GROK_GUIDE.md',
|
|
367
368
|
'docs-site/sidebars.ts',
|
|
368
369
|
'xray/features.json',
|
|
370
|
+
'.xray/features.json',
|
|
371
|
+
'docs/PIPELINE-FACET-SNAPSHOT.json',
|
|
372
|
+
'src/integrations/openclaw/plugin/xray-pre-tool/package.json',
|
|
369
373
|
];
|
|
370
374
|
return candidates.filter((rel) => fs.existsSync(path.join(baseDir, rel)));
|
|
371
375
|
}
|
|
@@ -39,6 +39,17 @@ function resolveRepertoireMcp(targetDir) {
|
|
|
39
39
|
const candidates = [
|
|
40
40
|
path.join(targetDir, "node_modules", "@0xray", "repertoire", "dist", "mcp", "server.js"),
|
|
41
41
|
path.join(targetDir, "vendor", "@0xray", "repertoire", "dist", "mcp", "server.js"),
|
|
42
|
+
path.join(
|
|
43
|
+
targetDir,
|
|
44
|
+
"node_modules",
|
|
45
|
+
"0xray",
|
|
46
|
+
"vendor",
|
|
47
|
+
"@0xray",
|
|
48
|
+
"repertoire",
|
|
49
|
+
"dist",
|
|
50
|
+
"mcp",
|
|
51
|
+
"server.js",
|
|
52
|
+
),
|
|
42
53
|
isRepertoirePackageRoot(selfRoot) ? path.join(selfRoot, "dist", "mcp", "server.js") : null,
|
|
43
54
|
isRepertoirePackageRoot(siblingRoot) ? path.join(siblingRoot, "dist", "mcp", "server.js") : null,
|
|
44
55
|
];
|
|
@@ -66,8 +77,20 @@ function resolveRepertoireProvider(targetDir) {
|
|
|
66
77
|
"provider",
|
|
67
78
|
"memory-routing-provider.js",
|
|
68
79
|
);
|
|
80
|
+
const packedVendorProvider = path.join(
|
|
81
|
+
targetDir,
|
|
82
|
+
"node_modules",
|
|
83
|
+
"0xray",
|
|
84
|
+
"vendor",
|
|
85
|
+
"@0xray",
|
|
86
|
+
"repertoire",
|
|
87
|
+
"dist",
|
|
88
|
+
"provider",
|
|
89
|
+
"memory-routing-provider.js",
|
|
90
|
+
);
|
|
69
91
|
if (fs.existsSync(nmProvider)) return nmProvider;
|
|
70
92
|
if (fs.existsSync(vendorProvider)) return vendorProvider;
|
|
93
|
+
if (fs.existsSync(packedVendorProvider)) return packedVendorProvider;
|
|
71
94
|
if (isRepertoirePackageRoot(siblingRoot) && fs.existsSync(siblingProvider)) return siblingProvider;
|
|
72
95
|
return null;
|
|
73
96
|
}
|
|
@@ -15,6 +15,7 @@ import os from "os";
|
|
|
15
15
|
import path from "path";
|
|
16
16
|
import { fileURLToPath } from "url";
|
|
17
17
|
import { createRequire } from "module";
|
|
18
|
+
import { proveSuitAfterInstall } from "./pack-tmp-suit-proof.mjs";
|
|
18
19
|
|
|
19
20
|
const require = createRequire(import.meta.url);
|
|
20
21
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -39,6 +40,64 @@ function run(cmd, cwd, { inherit = false } = {}) {
|
|
|
39
40
|
return inherit ? "" : String(result).trim();
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
function assertPackedPackageHasNoFileDeps(tarballPath) {
|
|
44
|
+
const packedJson = run(`tar -xOf ${JSON.stringify(tarballPath)} package/package.json`);
|
|
45
|
+
const packed = JSON.parse(packedJson);
|
|
46
|
+
const buckets = ["dependencies", "optionalDependencies", "peerDependencies"];
|
|
47
|
+
for (const bucket of buckets) {
|
|
48
|
+
const entries = packed[bucket] || {};
|
|
49
|
+
for (const [name, spec] of Object.entries(entries)) {
|
|
50
|
+
if (String(spec).startsWith("file:")) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`packed package.json must not ship file: ${bucket} ${name}=${spec} (consumers resolve file: from their root)`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (!String(packedJson).includes("vendor/@0xray/repertoire/")) {
|
|
58
|
+
const listing = run(`tar -tzf ${JSON.stringify(tarballPath)}`);
|
|
59
|
+
if (!listing.includes("package/vendor/@0xray/repertoire/package.json")) {
|
|
60
|
+
throw new Error("packed tarball missing vendor/@0xray/repertoire/package.json");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
console.log(" ✅ packed package.json has no file: deps; vendor organ is in the tarball");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function assertOrganRequire(tmpRoot) {
|
|
67
|
+
const consumerRequire = createRequire(path.join(tmpRoot, "package.json"));
|
|
68
|
+
let organPkg;
|
|
69
|
+
try {
|
|
70
|
+
organPkg = consumerRequire("@0xray/repertoire/package.json");
|
|
71
|
+
} catch (err) {
|
|
72
|
+
throw new Error(`require('@0xray/repertoire') failed: ${err.message}`);
|
|
73
|
+
}
|
|
74
|
+
if (organPkg.name !== "@0xray/repertoire") {
|
|
75
|
+
throw new Error(`required organ name ${organPkg.name}`);
|
|
76
|
+
}
|
|
77
|
+
if (organPkg.version !== "0.2.0") {
|
|
78
|
+
throw new Error(`required organ version ${organPkg.version}`);
|
|
79
|
+
}
|
|
80
|
+
if (fs.existsSync(path.join(tmpRoot, "vendor"))) {
|
|
81
|
+
throw new Error("consumer install must not create or require a consumer-root vendor/ tree");
|
|
82
|
+
}
|
|
83
|
+
console.log(" ✅ require('@0xray/repertoire') from consumer cwd (no consumer vendor/)");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function writeBareConsumerManifest(tmpRoot) {
|
|
87
|
+
fs.writeFileSync(
|
|
88
|
+
path.join(tmpRoot, "package.json"),
|
|
89
|
+
`${JSON.stringify(
|
|
90
|
+
{
|
|
91
|
+
name: "xray-consumer-test",
|
|
92
|
+
version: "0.0.1",
|
|
93
|
+
private: true,
|
|
94
|
+
},
|
|
95
|
+
null,
|
|
96
|
+
2,
|
|
97
|
+
)}\n`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
42
101
|
function assertFreshInstallDefaults(tmpRoot, version) {
|
|
43
102
|
const consumerFeaturesPath = path.join(tmpRoot, ".xray", "features.json");
|
|
44
103
|
if (!fs.existsSync(consumerFeaturesPath)) {
|
|
@@ -188,25 +247,30 @@ function main() {
|
|
|
188
247
|
throw new Error(`Tarball not found: ${tarballPath}`);
|
|
189
248
|
}
|
|
190
249
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
version: "1.0.0",
|
|
195
|
-
dependencies: {
|
|
196
|
-
"0xray": `file:${tarballPath}`,
|
|
197
|
-
},
|
|
198
|
-
};
|
|
199
|
-
fs.writeFileSync(
|
|
200
|
-
path.join(tmpRoot, "package.json"),
|
|
201
|
-
JSON.stringify(consumerPkg, null, 2) + "\n",
|
|
202
|
-
);
|
|
250
|
+
assertPackedPackageHasNoFileDeps(tarballPath);
|
|
251
|
+
|
|
252
|
+
writeBareConsumerManifest(tmpRoot);
|
|
203
253
|
|
|
204
|
-
console.log("📥 npm install (
|
|
205
|
-
run(
|
|
254
|
+
console.log("📥 npm install tarball into bare consumer (exact ENOENT repro)...");
|
|
255
|
+
run(`npm install ${JSON.stringify(tarballPath)}`, tmpRoot, { inherit: true });
|
|
256
|
+
|
|
257
|
+
const installLinksRoot = fs.mkdtempSync(path.join(os.tmpdir(), "0xray-smoke-links-"));
|
|
258
|
+
writeBareConsumerManifest(installLinksRoot);
|
|
259
|
+
console.log("📥 npm install tarball --install-links (file: ENOENT path)...");
|
|
260
|
+
run(`npm install ${JSON.stringify(tarballPath)} --install-links`, installLinksRoot, {
|
|
261
|
+
inherit: true,
|
|
262
|
+
});
|
|
263
|
+
assertOrganRequire(installLinksRoot);
|
|
264
|
+
try {
|
|
265
|
+
fs.rmSync(installLinksRoot, { recursive: true, force: true });
|
|
266
|
+
} catch {
|
|
267
|
+
/* best-effort */
|
|
268
|
+
}
|
|
206
269
|
|
|
207
270
|
const nmRoot = path.join(tmpRoot, "node_modules", "0xray");
|
|
208
271
|
const requiredFiles = [
|
|
209
272
|
"scripts/node/install-bridges.cjs",
|
|
273
|
+
"scripts/node/wear-vendored-repertoire.cjs",
|
|
210
274
|
"scripts/node/postinstall.cjs",
|
|
211
275
|
"scripts/foundry/mint-suit.cjs",
|
|
212
276
|
"scripts/foundry/hooks.mjs",
|
|
@@ -274,6 +338,7 @@ function main() {
|
|
|
274
338
|
console.log(` ✅ .mcp.json has ${XRAY_MCP_NAMES.length} npx MCP servers`);
|
|
275
339
|
|
|
276
340
|
assertFreshInstallDefaults(tmpRoot, version);
|
|
341
|
+
assertOrganRequire(tmpRoot);
|
|
277
342
|
|
|
278
343
|
const schemaInPackage = path.join(nmRoot, "xray", "features.schema.json");
|
|
279
344
|
if (!fs.existsSync(schemaInPackage)) {
|
|
@@ -283,6 +348,9 @@ function main() {
|
|
|
283
348
|
|
|
284
349
|
runUpgradeMergeSmoke(tmpRoot, nmRoot, version);
|
|
285
350
|
|
|
351
|
+
console.log("\n📋 Pack → tmp mill fasten + hangar shop plant (not costume)...");
|
|
352
|
+
proveSuitAfterInstall(tmpRoot, path.join(nmRoot, "scripts", "foundry"));
|
|
353
|
+
|
|
286
354
|
console.log("\n✅ Consumer install smoke passed\n");
|
|
287
355
|
} catch (err) {
|
|
288
356
|
console.error(`\n❌ Consumer smoke failed: ${err.message}\n`);
|
|
@@ -9,6 +9,7 @@ const path = require("path");
|
|
|
9
9
|
const os = require("os");
|
|
10
10
|
const { execSync } = require("child_process");
|
|
11
11
|
const { wantsCostume, isIsolatedHome, machineHome } = require("../foundry/mint-suit.cjs");
|
|
12
|
+
const { wearVendoredRepertoire } = require("./wear-vendored-repertoire.cjs");
|
|
12
13
|
const {
|
|
13
14
|
wireHermesBridge,
|
|
14
15
|
wireOpencodeBridge,
|
|
@@ -742,6 +743,8 @@ function installAllBridges(opts) {
|
|
|
742
743
|
/* noop */
|
|
743
744
|
});
|
|
744
745
|
|
|
746
|
+
wearVendoredRepertoire(packageRoot, targetDir, log);
|
|
747
|
+
|
|
745
748
|
if (!isConsumerInstall(packageRoot, targetDir)) {
|
|
746
749
|
log("install-bridges", "framework dogfood wear", "info");
|
|
747
750
|
installFrameworkDogfoodWear(packageRoot, log);
|
|
@@ -779,4 +782,5 @@ module.exports = {
|
|
|
779
782
|
isEphemeralInstallRoot,
|
|
780
783
|
isIsolatedHome,
|
|
781
784
|
installFrameworkDogfoodWear,
|
|
785
|
+
wearVendoredRepertoire,
|
|
782
786
|
};
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* pack-tmp-suit-proof.mjs — Clean-room pack → tmp install → mill fasten → hangar plant → inspect.
|
|
4
|
+
*
|
|
5
|
+
* Asserts (Task B):
|
|
6
|
+
* 1. `npm install` of the packed 0xray tgz works
|
|
7
|
+
* 2. `foundry mint --skip-live` fastens mill+inspect (not 45/42 costume)
|
|
8
|
+
* 3. groover-hangar factory shops + inspect do not costume-dump
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node scripts/node/pack-tmp-suit-proof.mjs
|
|
12
|
+
* npm run pack:tmp-proof
|
|
13
|
+
*
|
|
14
|
+
* Playwright / UI: n/a (mill CLI).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { execSync, spawnSync } from "node:child_process";
|
|
18
|
+
import fs from "node:fs";
|
|
19
|
+
import os from "node:os";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
|
|
23
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
const repoRoot = path.resolve(__dirname, "../..");
|
|
25
|
+
|
|
26
|
+
export const FACTORY_SHOP_SKILLS = ["shop-extract", "shop-witness", "shop-pin"];
|
|
27
|
+
export const MILL_PLANT_SKILLS = ["mill", "inspect"];
|
|
28
|
+
|
|
29
|
+
function run(cmd, cwd, { inherit = false } = {}) {
|
|
30
|
+
const result = execSync(cmd, {
|
|
31
|
+
cwd,
|
|
32
|
+
encoding: inherit ? undefined : "utf-8",
|
|
33
|
+
stdio: inherit ? "inherit" : "pipe",
|
|
34
|
+
});
|
|
35
|
+
return inherit ? "" : String(result).trim();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function foundryCliPath(millRoot) {
|
|
39
|
+
return path.join(millRoot, "cli.js");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function runFoundryCli(millRoot, consumerRoot, args) {
|
|
43
|
+
const isolatedHome = path.join(consumerRoot, ".proof-home");
|
|
44
|
+
fs.mkdirSync(isolatedHome, { recursive: true });
|
|
45
|
+
return spawnSync(process.execPath, [foundryCliPath(millRoot), ...args], {
|
|
46
|
+
cwd: consumerRoot,
|
|
47
|
+
encoding: "utf8",
|
|
48
|
+
env: {
|
|
49
|
+
...process.env,
|
|
50
|
+
FOUNDRY_ROOT: consumerRoot,
|
|
51
|
+
HOME: isolatedHome,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function lastJsonObject(text) {
|
|
57
|
+
const src = String(text || "");
|
|
58
|
+
for (let end = src.length - 1; end >= 0; end -= 1) {
|
|
59
|
+
if (src[end] !== "}") continue;
|
|
60
|
+
let depth = 0;
|
|
61
|
+
for (let start = end; start >= 0; start -= 1) {
|
|
62
|
+
const ch = src[start];
|
|
63
|
+
if (ch === "}") depth += 1;
|
|
64
|
+
else if (ch === "{") {
|
|
65
|
+
depth -= 1;
|
|
66
|
+
if (depth === 0) {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(src.slice(start, end + 1));
|
|
69
|
+
} catch {
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function plantHangarFactoryShops(consumerRoot) {
|
|
80
|
+
for (const name of FACTORY_SHOP_SKILLS) {
|
|
81
|
+
const dir = path.join(consumerRoot, ".opencode", "skills", name);
|
|
82
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
83
|
+
fs.writeFileSync(path.join(dir, "SKILL.md"), `groover-hangar plant ${name}\n`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function wornSkillNames(consumerRoot) {
|
|
88
|
+
const skillsDir = path.join(consumerRoot, ".opencode", "skills");
|
|
89
|
+
if (!fs.existsSync(skillsDir)) return [];
|
|
90
|
+
return fs.readdirSync(skillsDir).filter((name) =>
|
|
91
|
+
fs.existsSync(path.join(skillsDir, name, "SKILL.md")),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function assertFastenedMillPlant(consumerRoot) {
|
|
96
|
+
const inventoryPath = path.join(consumerRoot, ".xray", "foundry-inventory.json");
|
|
97
|
+
if (!fs.existsSync(inventoryPath)) {
|
|
98
|
+
throw new Error("foundry-inventory.json missing — mill did not fasten");
|
|
99
|
+
}
|
|
100
|
+
const inventory = JSON.parse(fs.readFileSync(inventoryPath, "utf8"));
|
|
101
|
+
if (inventory.costume === true) {
|
|
102
|
+
throw new Error("inventory.costume is true — costume dump is not default mill plant");
|
|
103
|
+
}
|
|
104
|
+
const millSkills = inventory.millPlant?.skills || [];
|
|
105
|
+
for (const name of MILL_PLANT_SKILLS) {
|
|
106
|
+
if (!millSkills.includes(name)) {
|
|
107
|
+
throw new Error(`mill plant missing ${name} (got ${millSkills.join(",")})`);
|
|
108
|
+
}
|
|
109
|
+
if (!fs.existsSync(path.join(consumerRoot, ".opencode", "skills", name, "SKILL.md"))) {
|
|
110
|
+
throw new Error(`worn mill plant missing ${name}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const foundry = path.join(consumerRoot, "foundry.json");
|
|
114
|
+
if (fs.existsSync(foundry)) {
|
|
115
|
+
const extra = JSON.parse(fs.readFileSync(foundry, "utf8"));
|
|
116
|
+
if (extra.costume === true) {
|
|
117
|
+
throw new Error("foundry.json costume:true is not the mill+inspect path");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const worn = wornSkillNames(consumerRoot);
|
|
121
|
+
const extras = worn.filter(
|
|
122
|
+
(name) => !MILL_PLANT_SKILLS.includes(name) && !FACTORY_SHOP_SKILLS.includes(name),
|
|
123
|
+
);
|
|
124
|
+
if (extras.length > 0) {
|
|
125
|
+
throw new Error(`costume dump extras before hangar plant: ${extras.join(",")}`);
|
|
126
|
+
}
|
|
127
|
+
return inventory;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function proveSuitAfterInstall(consumerRoot, millRoot) {
|
|
131
|
+
if (!fs.existsSync(foundryCliPath(millRoot))) {
|
|
132
|
+
throw new Error(`foundry CLI missing at ${foundryCliPath(millRoot)}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(" ⚒️ foundry mint --skip-live (fasten mill+inspect)...");
|
|
136
|
+
const mint = runFoundryCli(millRoot, consumerRoot, ["mint", "--skip-live"]);
|
|
137
|
+
if (mint.status !== 0) {
|
|
138
|
+
throw new Error(`foundry mint --skip-live failed:\n${mint.stdout}\n${mint.stderr}`);
|
|
139
|
+
}
|
|
140
|
+
const inventory = assertFastenedMillPlant(consumerRoot);
|
|
141
|
+
if (inventory.suit !== "fastened" && inventory.suit !== "overlay") {
|
|
142
|
+
throw new Error(`expected fastened/overlay suit, got ${inventory.suit}`);
|
|
143
|
+
}
|
|
144
|
+
console.log(` ✅ mint fastened mill+inspect (suit=${inventory.suit}, costume=${inventory.costume === true})`);
|
|
145
|
+
|
|
146
|
+
console.log(" 🏭 groover-hangar plant factory shops (not costume)...");
|
|
147
|
+
plantHangarFactoryShops(consumerRoot);
|
|
148
|
+
for (const name of FACTORY_SHOP_SKILLS) {
|
|
149
|
+
if (!fs.existsSync(path.join(consumerRoot, ".opencode", "skills", name, "SKILL.md"))) {
|
|
150
|
+
throw new Error(`hangar plant missing ${name}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
console.log(" 🔍 foundry inspect --skip-live (shop plant must not costume-dump)...");
|
|
155
|
+
const inspect = runFoundryCli(millRoot, consumerRoot, ["inspect", "--skip-live"]);
|
|
156
|
+
const report = lastJsonObject(inspect.stdout);
|
|
157
|
+
if (inspect.status !== 0 || !report?.ok) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`inspect failed after hangar plant (costume dump?):\n${inspect.stdout}\n${inspect.stderr}`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
const plantVsWorn = (report.checks || []).find((c) => c.id === "plant-vs-worn");
|
|
163
|
+
if (!plantVsWorn?.ok) {
|
|
164
|
+
throw new Error(`plant-vs-worn failed: ${JSON.stringify(plantVsWorn)}`);
|
|
165
|
+
}
|
|
166
|
+
const shopPlant = plantVsWorn.shopPlant || [];
|
|
167
|
+
for (const name of FACTORY_SHOP_SKILLS) {
|
|
168
|
+
if (!shopPlant.includes(name)) {
|
|
169
|
+
throw new Error(`inspect shopPlant missing ${name} (got ${shopPlant.join(",")})`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (Array.isArray(plantVsWorn.extraSkills) && plantVsWorn.extraSkills.length > 0) {
|
|
173
|
+
throw new Error(`inspect extraSkills after hangar plant: ${plantVsWorn.extraSkills.join(",")}`);
|
|
174
|
+
}
|
|
175
|
+
const after = assertFastenedMillPlant(consumerRoot);
|
|
176
|
+
if (after.costume === true) {
|
|
177
|
+
throw new Error("costume flipped true after hangar plant");
|
|
178
|
+
}
|
|
179
|
+
console.log(" ✅ hangar factory shops + inspect OK (costume false, no dump)");
|
|
180
|
+
return { inventory: after, report };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function packXrayTgz(xrayRoot = repoRoot) {
|
|
184
|
+
const packOut = run("npm pack --silent", xrayRoot);
|
|
185
|
+
const tarball = packOut.split("\n").filter(Boolean).pop();
|
|
186
|
+
const tarballPath = path.join(xrayRoot, tarball);
|
|
187
|
+
if (!fs.existsSync(tarballPath)) {
|
|
188
|
+
throw new Error(`Tarball not found: ${tarballPath}`);
|
|
189
|
+
}
|
|
190
|
+
return tarballPath;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function writeBareConsumerManifest(tmpRoot) {
|
|
194
|
+
fs.writeFileSync(
|
|
195
|
+
path.join(tmpRoot, "package.json"),
|
|
196
|
+
`${JSON.stringify({ name: "xray-pack-tmp-proof", version: "0.0.1", private: true }, null, 2)}\n`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function runPackTmpProof(xrayRoot = repoRoot) {
|
|
201
|
+
console.log("\n=== Pack → tmp suit proof ===\n");
|
|
202
|
+
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "0xray-pack-tmp-proof-"));
|
|
203
|
+
let tarballPath = null;
|
|
204
|
+
try {
|
|
205
|
+
console.log("📦 npm pack...");
|
|
206
|
+
tarballPath = packXrayTgz(xrayRoot);
|
|
207
|
+
writeBareConsumerManifest(tmpRoot);
|
|
208
|
+
console.log("📥 npm install tgz into clean tmp...");
|
|
209
|
+
run(`npm install ${JSON.stringify(tarballPath)}`, tmpRoot, { inherit: true });
|
|
210
|
+
const millRoot = path.join(tmpRoot, "node_modules", "0xray", "scripts", "foundry");
|
|
211
|
+
proveSuitAfterInstall(tmpRoot, millRoot);
|
|
212
|
+
console.log("\n✅ Pack → tmp suit proof passed (Playwright n/a — mill CLI)\n");
|
|
213
|
+
return { tmpRoot, tarballPath };
|
|
214
|
+
} catch (err) {
|
|
215
|
+
console.error(`\n❌ Pack → tmp suit proof failed: ${err.message}\n`);
|
|
216
|
+
throw err;
|
|
217
|
+
} finally {
|
|
218
|
+
if (tarballPath) {
|
|
219
|
+
try {
|
|
220
|
+
fs.unlinkSync(tarballPath);
|
|
221
|
+
} catch {
|
|
222
|
+
/* best-effort */
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
try {
|
|
226
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
227
|
+
} catch {
|
|
228
|
+
/* best-effort */
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const isMain =
|
|
234
|
+
process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
235
|
+
|
|
236
|
+
if (isMain) {
|
|
237
|
+
try {
|
|
238
|
+
runPackTmpProof();
|
|
239
|
+
} catch {
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* wear-vendored-repertoire.cjs — Materialize @0xray/repertoire from the
|
|
3
|
+
* packed vendor tree. Published 0xray must not ship a `file:` dependency:
|
|
4
|
+
* npm --install-links, pnpm, and yarn resolve that path from the consumer
|
|
5
|
+
* root (ENOENT on vendor/@0xray/repertoire), not from node_modules/0xray.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require("fs");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
|
|
11
|
+
function resolveVendoredRepertoire(packageRoot) {
|
|
12
|
+
const vendor = path.join(packageRoot, "vendor", "@0xray", "repertoire");
|
|
13
|
+
if (fs.existsSync(path.join(vendor, "package.json"))) return vendor;
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function repertoirePackageName(dir) {
|
|
18
|
+
try {
|
|
19
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8"));
|
|
20
|
+
return pkg.name || null;
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function destAlreadyWorn(dest, vendor) {
|
|
27
|
+
try {
|
|
28
|
+
if (!fs.existsSync(path.join(dest, "package.json"))) return false;
|
|
29
|
+
if (repertoirePackageName(dest) !== "@0xray/repertoire") return false;
|
|
30
|
+
return fs.realpathSync(dest) === fs.realpathSync(vendor);
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function destIsUsableRepertoire(dest) {
|
|
37
|
+
return repertoirePackageName(dest) === "@0xray/repertoire";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function removeBrokenDest(dest) {
|
|
41
|
+
try {
|
|
42
|
+
const st = fs.lstatSync(dest);
|
|
43
|
+
if (st.isSymbolicLink() && !fs.existsSync(dest)) {
|
|
44
|
+
fs.unlinkSync(dest);
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
/* dest absent */
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function wearOne(vendor, dest, log) {
|
|
52
|
+
removeBrokenDest(dest);
|
|
53
|
+
if (destAlreadyWorn(dest, vendor)) return true;
|
|
54
|
+
if (fs.existsSync(dest) && destIsUsableRepertoire(dest)) return true;
|
|
55
|
+
|
|
56
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
57
|
+
const rel = path.relative(path.dirname(dest), vendor);
|
|
58
|
+
const linkType = process.platform === "win32" ? "junction" : "dir";
|
|
59
|
+
try {
|
|
60
|
+
fs.symlinkSync(rel, dest, linkType);
|
|
61
|
+
return true;
|
|
62
|
+
} catch (linkErr) {
|
|
63
|
+
try {
|
|
64
|
+
fs.cpSync(vendor, dest, { recursive: true });
|
|
65
|
+
return true;
|
|
66
|
+
} catch (copyErr) {
|
|
67
|
+
if (typeof log === "function") {
|
|
68
|
+
log("wear-repertoire", "failed to wear vendored organ", "warning", {
|
|
69
|
+
dest,
|
|
70
|
+
linkError: linkErr.message,
|
|
71
|
+
copyError: copyErr.message,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function wearDestinations(packageRoot, targetDir) {
|
|
80
|
+
const dests = [path.join(packageRoot, "node_modules", "@0xray", "repertoire")];
|
|
81
|
+
if (targetDir && path.resolve(targetDir) !== path.resolve(packageRoot)) {
|
|
82
|
+
dests.push(path.join(targetDir, "node_modules", "@0xray", "repertoire"));
|
|
83
|
+
}
|
|
84
|
+
return dests;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function wearVendoredRepertoire(packageRoot, targetDir, log) {
|
|
88
|
+
const vendor = resolveVendoredRepertoire(packageRoot);
|
|
89
|
+
if (!vendor) return { worn: [], vendor: null };
|
|
90
|
+
|
|
91
|
+
const worn = [];
|
|
92
|
+
for (const dest of wearDestinations(packageRoot, targetDir)) {
|
|
93
|
+
if (wearOne(vendor, dest, log)) worn.push(dest);
|
|
94
|
+
}
|
|
95
|
+
if (worn.length && typeof log === "function") {
|
|
96
|
+
log("wear-repertoire", "vendored @0xray/repertoire worn", "info", {
|
|
97
|
+
vendor,
|
|
98
|
+
dests: worn,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return { worn, vendor };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function hasFileProtocolDependency(pkg) {
|
|
105
|
+
const buckets = [pkg.dependencies, pkg.optionalDependencies, pkg.peerDependencies];
|
|
106
|
+
for (const bucket of buckets) {
|
|
107
|
+
if (!bucket || typeof bucket !== "object") continue;
|
|
108
|
+
for (const spec of Object.values(bucket)) {
|
|
109
|
+
if (String(spec).startsWith("file:")) return true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = {
|
|
116
|
+
resolveVendoredRepertoire,
|
|
117
|
+
wearVendoredRepertoire,
|
|
118
|
+
wearDestinations,
|
|
119
|
+
hasFileProtocolDependency,
|
|
120
|
+
};
|