0xray 4.0.27 → 4.0.29
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/.gitignore.default +1 -0
- package/.grok-plugin/plugin.json +1 -1
- package/.xray/features.json +1 -1
- package/README.md +4 -2
- package/dist/.gitignore.default +1 -0
- package/dist/CHANGELOG.md +16 -0
- package/dist/README.md +4 -2
- package/dist/cli/index.js +52 -44
- package/dist/integrations/cursor/README.md +21 -2
- package/dist/integrations/cursor/hooks/xray-cloud-hook.sh +62 -5
- package/dist/integrations/hooks/plates.cjs +7 -0
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/memory-routing/plates.d.ts +1 -1
- package/dist/memory-routing/plates.js +1 -0
- package/docs-site/docs/plates/house.md +50 -0
- package/docs-site/docs/plates/index.md +1 -1
- package/llms.txt +2 -0
- package/package.json +1 -1
- package/scripts/foundry/inspect.mjs +47 -2
- package/scripts/node/bridge-mcp-wiring.cjs +321 -29
- package/scripts/node/consumer-gitignore.cjs +1 -0
- package/scripts/node/consumer-install-smoke.mjs +9 -38
- package/scripts/node/install-bridges.cjs +1049 -33
- package/scripts/node/mcp-launch.cjs +70 -0
- package/scripts/node/postinstall.cjs +46 -70
- package/scripts/node/unwear-cursor-hooks.cjs +21 -0
- package/scripts/node/wear-cursor-hooks.cjs +17 -0
- package/scripts/node/wear-vendored-repertoire.cjs +35 -0
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/xray/features.json +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Start an MCP server with an allowlisted environment.
|
|
5
|
+
*
|
|
6
|
+
* node mcp-launch.cjs --keep PATH,HOME,XRAY_ROOT[,NAME...] -- <cmd> [args...]
|
|
7
|
+
*
|
|
8
|
+
* argv carries names only. The host merges the config `env` block onto this
|
|
9
|
+
* process, so values come from the live environment. The child receives
|
|
10
|
+
* process.env filtered to those names. stdio is inherited. Signals and the
|
|
11
|
+
* exit code are forwarded.
|
|
12
|
+
*/
|
|
13
|
+
const { spawn } = require("child_process");
|
|
14
|
+
|
|
15
|
+
function fail(message) {
|
|
16
|
+
process.stderr.write(`mcp-launch: ${message}\n`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseArgs(argv) {
|
|
21
|
+
const keepIdx = argv.indexOf("--keep");
|
|
22
|
+
const dash = argv.indexOf("--");
|
|
23
|
+
if (keepIdx < 0 || dash < 0 || dash !== keepIdx + 2 || dash >= argv.length - 1) {
|
|
24
|
+
fail("usage: node mcp-launch.cjs --keep NAME[,NAME...] -- <cmd> [args...]");
|
|
25
|
+
}
|
|
26
|
+
const keep = argv[keepIdx + 1]
|
|
27
|
+
.split(",")
|
|
28
|
+
.map((name) => name.trim())
|
|
29
|
+
.filter(Boolean);
|
|
30
|
+
if (keep.length === 0) fail("--keep needs at least one name");
|
|
31
|
+
for (const name of keep) {
|
|
32
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) fail(`refusing keep name ${name}`);
|
|
33
|
+
}
|
|
34
|
+
return { keep, command: argv[dash + 1], args: argv.slice(dash + 2) };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function childEnv(keep) {
|
|
38
|
+
const env = {};
|
|
39
|
+
for (const name of keep) {
|
|
40
|
+
if (process.env[name] != null) env[name] = process.env[name];
|
|
41
|
+
}
|
|
42
|
+
return env;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function main() {
|
|
46
|
+
const { keep, command, args } = parseArgs(process.argv.slice(2));
|
|
47
|
+
const child = spawn(command, args, { env: childEnv(keep), stdio: "inherit" });
|
|
48
|
+
const forward = (signal) => {
|
|
49
|
+
if (!child.pid) return;
|
|
50
|
+
try {
|
|
51
|
+
child.kill(signal);
|
|
52
|
+
} catch {
|
|
53
|
+
/* child already exited */
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
57
|
+
process.on(signal, () => forward(signal));
|
|
58
|
+
}
|
|
59
|
+
child.on("error", (err) => fail(err.message));
|
|
60
|
+
child.on("exit", (code, signal) => {
|
|
61
|
+
if (signal) {
|
|
62
|
+
process.removeAllListeners(signal);
|
|
63
|
+
process.kill(process.pid, signal);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
process.exit(code == null ? 1 : code);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
main();
|
|
@@ -2,12 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
|
-
const {
|
|
6
|
-
|
|
7
|
-
resolveConsumerTargetDir,
|
|
8
|
-
isConsumerInstall,
|
|
9
|
-
} = require("./install-bridges.cjs");
|
|
10
|
-
const { applyConsumerGitignore } = require("./consumer-gitignore.cjs");
|
|
5
|
+
const { resolveConsumerTargetDir } = require("./install-bridges.cjs");
|
|
6
|
+
const { linkVendoredRepertoire } = require("./wear-vendored-repertoire.cjs");
|
|
11
7
|
const {
|
|
12
8
|
overlayConsumerTree,
|
|
13
9
|
mintConsumerFromSsot,
|
|
@@ -18,13 +14,9 @@ const {
|
|
|
18
14
|
readPackageIdentity,
|
|
19
15
|
} = require("../foundry/mint-suit.cjs");
|
|
20
16
|
|
|
21
|
-
function structuredLog(component, action, status, details) {
|
|
22
|
-
const ts = new Date().toISOString();
|
|
23
|
-
const detailsPart = details ? ` | ${JSON.stringify(details)}` : "";
|
|
24
|
-
console.log(`${ts} [${component}] ${action} - ${String(status).toUpperCase()}${detailsPart}`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
17
|
const XRAY_MANAGED_AGENTS_MARKER = "<!-- 0xray-managed -->";
|
|
18
|
+
const XRAY_MANAGED_AGENTS_BEGIN = "<!-- 0xray-managed:begin -->";
|
|
19
|
+
const XRAY_MANAGED_AGENTS_END = "<!-- 0xray-managed:end -->";
|
|
28
20
|
|
|
29
21
|
function fillConsumerPlaceholders(content, consumer) {
|
|
30
22
|
const name = consumer.name || "this project";
|
|
@@ -36,73 +28,57 @@ function fillConsumerPlaceholders(content, consumer) {
|
|
|
36
28
|
.join(versionParen);
|
|
37
29
|
}
|
|
38
30
|
|
|
39
|
-
function
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
fs.readFileSync(agentsDest, "utf8").includes(XRAY_MANAGED_AGENTS_MARKER);
|
|
46
|
-
if (shouldDeployAgents) {
|
|
47
|
-
let content = fs.readFileSync(agentsConsumer, "utf8");
|
|
48
|
-
const consumer = readPackageIdentity(path.join(targetDir, "package.json"));
|
|
49
|
-
content = fillConsumerPlaceholders(content, consumer);
|
|
50
|
-
if (!content.includes(XRAY_MANAGED_AGENTS_MARKER)) {
|
|
51
|
-
content = `${content.trimEnd()}\n\n${XRAY_MANAGED_AGENTS_MARKER}\n`;
|
|
52
|
-
}
|
|
53
|
-
fs.writeFileSync(agentsDest, content);
|
|
54
|
-
} else {
|
|
55
|
-
log("postinstall", "Skipped AGENTS.md (consumer-customized)", "info");
|
|
31
|
+
function renderManagedAgents(packageRoot, targetDir) {
|
|
32
|
+
let content = fs.readFileSync(path.join(packageRoot, "AGENTS-consumer.md"), "utf8");
|
|
33
|
+
const consumer = readPackageIdentity(path.join(targetDir, "package.json"));
|
|
34
|
+
content = fillConsumerPlaceholders(content, consumer).trim();
|
|
35
|
+
if (!content.includes(XRAY_MANAGED_AGENTS_MARKER)) {
|
|
36
|
+
content = `${content}\n\n${XRAY_MANAGED_AGENTS_MARKER}`;
|
|
56
37
|
}
|
|
38
|
+
return `${XRAY_MANAGED_AGENTS_BEGIN}\n${content}\n${XRAY_MANAGED_AGENTS_END}\n`;
|
|
57
39
|
}
|
|
58
40
|
|
|
59
|
-
function
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
41
|
+
function managedRegion(text) {
|
|
42
|
+
const beginAt = text.indexOf(XRAY_MANAGED_AGENTS_BEGIN);
|
|
43
|
+
const endAt = text.indexOf(XRAY_MANAGED_AGENTS_END);
|
|
44
|
+
if (beginAt === -1 || endAt === -1 || endAt < beginAt) return null;
|
|
45
|
+
return {
|
|
46
|
+
before: text.slice(0, beginAt),
|
|
47
|
+
after: text.slice(endAt + XRAY_MANAGED_AGENTS_END.length),
|
|
48
|
+
};
|
|
66
49
|
}
|
|
67
50
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
*/
|
|
72
|
-
function runPostinstall(packageRoot, targetDir, log) {
|
|
73
|
-
const logFn = log || structuredLog;
|
|
74
|
-
const resolvedPackage = path.resolve(packageRoot);
|
|
75
|
-
const resolvedTarget = path.resolve(targetDir);
|
|
76
|
-
const consumer = isConsumerInstall(resolvedPackage, resolvedTarget);
|
|
51
|
+
function outsideRegionEdited(parts) {
|
|
52
|
+
return parts.before.trim() !== "" || parts.after.trim() !== "";
|
|
53
|
+
}
|
|
77
54
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
55
|
+
function deployManagedAgents(packageRoot, targetDir, log) {
|
|
56
|
+
const agentsConsumer = path.join(packageRoot, "AGENTS-consumer.md");
|
|
57
|
+
const agentsDest = path.join(targetDir, "AGENTS.md");
|
|
58
|
+
if (!fs.existsSync(agentsConsumer)) return;
|
|
59
|
+
const next = renderManagedAgents(packageRoot, targetDir);
|
|
60
|
+
if (!fs.existsSync(agentsDest)) {
|
|
61
|
+
fs.writeFileSync(agentsDest, next);
|
|
62
|
+
return;
|
|
81
63
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
});
|
|
89
|
-
if (consumer) {
|
|
90
|
-
mintConsumerSuit(resolvedPackage, resolvedTarget, logFn);
|
|
91
|
-
}
|
|
92
|
-
} catch (e) {
|
|
93
|
-
logFn("postinstall", "Bridge install failed", "error", { error: e.message });
|
|
94
|
-
throw e;
|
|
64
|
+
const current = fs.readFileSync(agentsDest, "utf8");
|
|
65
|
+
const parts = managedRegion(current);
|
|
66
|
+
if (!parts || outsideRegionEdited(parts)) {
|
|
67
|
+
fs.writeFileSync(path.join(targetDir, "AGENTS.md.0xray-new"), next);
|
|
68
|
+
log("postinstall", "AGENTS.md left in place; wrote AGENTS.md.0xray-new", "info");
|
|
69
|
+
return;
|
|
95
70
|
}
|
|
71
|
+
const beginAt = next.indexOf(XRAY_MANAGED_AGENTS_BEGIN);
|
|
72
|
+
const endAt = next.indexOf(XRAY_MANAGED_AGENTS_END);
|
|
73
|
+
const interior = next.slice(beginAt, endAt + XRAY_MANAGED_AGENTS_END.length);
|
|
74
|
+
const updated = `${parts.before}${interior}${parts.after}`;
|
|
75
|
+
if (updated !== current) fs.writeFileSync(agentsDest, updated);
|
|
76
|
+
}
|
|
96
77
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
"success",
|
|
102
|
-
);
|
|
103
|
-
} else {
|
|
104
|
-
logFn("postinstall", "framework dogfood wear complete", "success");
|
|
105
|
-
}
|
|
78
|
+
/** npm install links vendored @0xray/repertoire and nothing else. The suit is `npx 0xray wear`. */
|
|
79
|
+
function runPostinstall(packageRoot, targetDir, _log) {
|
|
80
|
+
linkVendoredRepertoire(packageRoot, targetDir);
|
|
81
|
+
console.log("Run `npx 0xray wear`");
|
|
106
82
|
}
|
|
107
83
|
|
|
108
84
|
module.exports = {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Remove the Cursor suit. Prints "restored" only when the file bytes match
|
|
4
|
+
* the snapshot exactly. A mismatch keeps the snapshot and exits non-zero.
|
|
5
|
+
*
|
|
6
|
+
* npx 0xray unwear
|
|
7
|
+
* node node_modules/0xray/scripts/node/unwear-cursor-hooks.cjs [project]
|
|
8
|
+
*/
|
|
9
|
+
const path = require("path");
|
|
10
|
+
const { unwearCursorHooks } = require("./install-bridges.cjs");
|
|
11
|
+
|
|
12
|
+
const target = path.resolve(process.argv[2] || process.cwd());
|
|
13
|
+
let restored = false;
|
|
14
|
+
try {
|
|
15
|
+
restored = unwearCursorHooks(target);
|
|
16
|
+
} catch (err) {
|
|
17
|
+
process.stderr.write(`${err && err.message ? err.message : err}\n`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
if (!restored) process.exit(1);
|
|
21
|
+
process.stdout.write(`unwear: restored cursor hooks at ${target}\n`);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Wear the Cursor suit in a git work tree.
|
|
4
|
+
* npx 0xray wear
|
|
5
|
+
* node node_modules/0xray/scripts/node/wear-cursor-hooks.cjs [project]
|
|
6
|
+
*/
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const { wearCursorHooks } = require("./install-bridges.cjs");
|
|
9
|
+
|
|
10
|
+
const target = path.resolve(process.argv[2] || process.cwd());
|
|
11
|
+
const packageRoot = path.resolve(__dirname, "..", "..");
|
|
12
|
+
try {
|
|
13
|
+
wearCursorHooks(target, packageRoot, () => {});
|
|
14
|
+
} catch (err) {
|
|
15
|
+
process.stderr.write(`${err && err.message ? err.message : err}\n`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
@@ -122,6 +122,38 @@ function wearDestinations(packageRoot, targetDir) {
|
|
|
122
122
|
return dests;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
function repertoireLinkDest(targetDir) {
|
|
126
|
+
return path.join(targetDir, "node_modules", "@0xray", "repertoire");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function relativeRepertoireLink(vendor, dest) {
|
|
130
|
+
return path.relative(path.dirname(dest), vendor);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Create one relative symlink at <target>/node_modules/@0xray/repertoire.
|
|
135
|
+
* A real directory or a symlink that is not this link is left in place.
|
|
136
|
+
*/
|
|
137
|
+
function linkVendoredRepertoire(packageRoot, targetDir) {
|
|
138
|
+
const vendor = resolveVendoredRepertoire(packageRoot);
|
|
139
|
+
if (!vendor) return { linked: false, vendor: null, dest: null };
|
|
140
|
+
const dest = repertoireLinkDest(targetDir);
|
|
141
|
+
const rel = relativeRepertoireLink(vendor, dest);
|
|
142
|
+
try {
|
|
143
|
+
const st = fs.lstatSync(dest);
|
|
144
|
+
if (st.isSymbolicLink() && fs.readlinkSync(dest) === rel) {
|
|
145
|
+
return { linked: true, vendor, dest, unchanged: true };
|
|
146
|
+
}
|
|
147
|
+
return { linked: false, vendor, dest, skipped: true };
|
|
148
|
+
} catch {
|
|
149
|
+
/* dest absent */
|
|
150
|
+
}
|
|
151
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
152
|
+
const linkType = process.platform === "win32" ? "junction" : "dir";
|
|
153
|
+
fs.symlinkSync(rel, dest, linkType);
|
|
154
|
+
return { linked: true, vendor, dest, unchanged: false };
|
|
155
|
+
}
|
|
156
|
+
|
|
125
157
|
function wearVendoredRepertoire(packageRoot, targetDir, log) {
|
|
126
158
|
const vendor = resolveVendoredRepertoire(packageRoot);
|
|
127
159
|
if (!vendor) return { worn: [], vendor: null };
|
|
@@ -153,6 +185,9 @@ function hasFileProtocolDependency(pkg) {
|
|
|
153
185
|
module.exports = {
|
|
154
186
|
resolveVendoredRepertoire,
|
|
155
187
|
wearVendoredRepertoire,
|
|
188
|
+
linkVendoredRepertoire,
|
|
189
|
+
relativeRepertoireLink,
|
|
190
|
+
repertoireLinkDest,
|
|
156
191
|
wearDestinations,
|
|
157
192
|
hasFileProtocolDependency,
|
|
158
193
|
};
|