0xray 4.0.28 → 4.0.30
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 +3 -1
- package/dist/.gitignore.default +1 -0
- package/dist/CHANGELOG.md +16 -0
- package/dist/README.md +3 -1
- package/dist/cli/index.js +52 -44
- package/dist/integrations/cursor/README.md +21 -2
- package/dist/integrations/cursor/hooks/xray-cloud-hook.sh +61 -4
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/llms.txt +2 -0
- package/package.json +2 -2
- package/scripts/node/bridge-mcp-wiring.cjs +2 -1
- package/scripts/node/consumer-gitignore.cjs +1 -0
- package/scripts/node/consumer-install-smoke.mjs +9 -72
- package/scripts/node/install-bridges.cjs +1123 -14
- package/scripts/node/postinstall.cjs +6 -59
- package/scripts/node/setup.cjs +80 -71
- 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
|
@@ -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,12 +14,6 @@ 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 -->";
|
|
28
18
|
const XRAY_MANAGED_AGENTS_BEGIN = "<!-- 0xray-managed:begin -->";
|
|
29
19
|
const XRAY_MANAGED_AGENTS_END = "<!-- 0xray-managed:end -->";
|
|
@@ -85,53 +75,10 @@ function deployManagedAgents(packageRoot, targetDir, log) {
|
|
|
85
75
|
if (updated !== current) fs.writeFileSync(agentsDest, updated);
|
|
86
76
|
}
|
|
87
77
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
} else if (gitignoreResult === "merged") {
|
|
93
|
-
log("postinstall", "Merged 0xray suit entries into .gitignore", "info");
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Wear bridges for consumers (full 4-platform) and the framework repo (dogfood).
|
|
99
|
-
* installAllBridges already decides which path.
|
|
100
|
-
*/
|
|
101
|
-
function runPostinstall(packageRoot, targetDir, log) {
|
|
102
|
-
const logFn = log || structuredLog;
|
|
103
|
-
const resolvedPackage = path.resolve(packageRoot);
|
|
104
|
-
const resolvedTarget = path.resolve(targetDir);
|
|
105
|
-
const consumer = isConsumerInstall(resolvedPackage, resolvedTarget);
|
|
106
|
-
|
|
107
|
-
if (consumer) {
|
|
108
|
-
deployManagedAgents(resolvedPackage, resolvedTarget, logFn);
|
|
109
|
-
deployConsumerGitignore(resolvedPackage, resolvedTarget, logFn);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
try {
|
|
113
|
-
installAllBridges({
|
|
114
|
-
targetDir: resolvedTarget,
|
|
115
|
-
packageRoot: resolvedPackage,
|
|
116
|
-
log: logFn,
|
|
117
|
-
});
|
|
118
|
-
if (consumer) {
|
|
119
|
-
mintConsumerSuit(resolvedPackage, resolvedTarget, logFn);
|
|
120
|
-
}
|
|
121
|
-
} catch (e) {
|
|
122
|
-
logFn("postinstall", "Bridge install failed", "error", { error: e.message });
|
|
123
|
-
throw e;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (consumer) {
|
|
127
|
-
logFn(
|
|
128
|
-
"postinstall",
|
|
129
|
-
"0xRay framework installed (4 bridges). Run `npx 0xray setup` for symlinks/Hermes skill extras.",
|
|
130
|
-
"success",
|
|
131
|
-
);
|
|
132
|
-
} else {
|
|
133
|
-
logFn("postinstall", "framework dogfood wear complete", "success");
|
|
134
|
-
}
|
|
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`");
|
|
135
82
|
}
|
|
136
83
|
|
|
137
84
|
module.exports = {
|
package/scripts/node/setup.cjs
CHANGED
|
@@ -3,6 +3,84 @@
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
5
|
|
|
6
|
+
function syncSetupSkillsAndRootLinks(packageRoot, targetDir) {
|
|
7
|
+
/* 1. Sync core skills from src/skills/ (dev) or dist/skills/ (consumer) → .opencode/skills/ [P3-SKILLS-DE-DUP-SCOPE-AND-EXEC-01: orchestrator package SSOT canonical src/skills/orchestrator/SKILL.md; mirror .opencode/skills/orchestrator/ is built output of this logic; see paired guarded fw in status.ts getSkillsList + mapping append + deep ref 2026-05-26 authority for one-package dedup under pivoted surgical plan]
|
|
8
|
+
[P3-SKILLS-DE-DUP-RESEARCHER-FOLLOW-01: researcher package SSOT canonical src/skills/researcher/SKILL.md (high-value per deep ref + YML precedent + playbook P3-SKILLS-FOLLOW-01); mirror .opencode/skills/researcher/ is built output of this logic; see paired guarded fw in status.ts + rich append + YML @ harness-codex verification (FORCE exercised P3-YML + codex-enforcement + 100/60/0 + 3 subagents tracked + 0 hot + 3-hot re-establish + all prior green) + deep ref 2026-05-26 + Phase 3 Pivot + Term 61 + user's "create a todo list. spawn subagents..." + 6/6+7/7 + YML. Governance-visible hook for skills SSOT at install layer. Silent, additive, reversible, no behavior change, fw/echo only.]
|
|
9
|
+
[P3-SKILLS-FOLLOW-02: code-review package SSOT canonical src/skills/code-review/SKILL.md (next high-value per deep ref "parallel small batches" + @architect scoping rec 2 + YML @ "Ready for next" + this P3-SKILLS-FOLLOW-02 on user's spawn command); mirror .opencode/skills/code-review/ is built output of this logic; see paired guarded fw "P3-SKILLS-FOLLOW-02-code-review-ssot-preferred" in status.ts (after researcher) + rich append docs/reflections/p3-skills-follow-02-code-review-2026-05-27.md + YML @ harness-codex verification + 0 hot + 3-hot + all ties + ps 0 + this work (2-file surgical edit, harness post, mapping append after latest ~17099+, todo advance 1, spawn replacement, self-audit 100%, green + ready) + deep ref 2026-05-26 + Phase 3 Pivot + Term 61 + 6/6+7/7 + YML. Governance-visible hook for skills SSOT at install layer for code-review. Silent, additive, reversible, no behavior change, fw/echo only. "The box contains its builders. The relay is hot."] */
|
|
10
|
+
const skillsSource = fs.existsSync(path.join(packageRoot, "src", "skills"))
|
|
11
|
+
? path.join(packageRoot, "src", "skills")
|
|
12
|
+
: path.join(packageRoot, "dist", "skills");
|
|
13
|
+
const skillsDest = path.join(targetDir, ".opencode", "skills");
|
|
14
|
+
|
|
15
|
+
if (fs.existsSync(skillsSource)) {
|
|
16
|
+
try {
|
|
17
|
+
if (!fs.existsSync(skillsDest)) fs.mkdirSync(skillsDest, { recursive: true });
|
|
18
|
+
const skillDirs = fs.readdirSync(skillsSource, { withFileTypes: true });
|
|
19
|
+
let copied = 0, skipped = 0;
|
|
20
|
+
for (const entry of skillDirs) {
|
|
21
|
+
if (!entry.isDirectory()) continue;
|
|
22
|
+
const skillMd = path.join(skillsSource, entry.name, "SKILL.md");
|
|
23
|
+
if (!fs.existsSync(skillMd)) continue;
|
|
24
|
+
const destMd = path.join(skillsDest, entry.name, "SKILL.md");
|
|
25
|
+
const destDir = path.dirname(destMd);
|
|
26
|
+
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
27
|
+
if (fs.existsSync(destMd)) {
|
|
28
|
+
const destContent = fs.readFileSync(destMd, "utf8");
|
|
29
|
+
if (destContent.includes("source: community")) { skipped++; continue; }
|
|
30
|
+
if (fs.statSync(skillMd).mtime <= fs.statSync(destMd).mtime) continue;
|
|
31
|
+
}
|
|
32
|
+
fs.copyFileSync(skillMd, destMd);
|
|
33
|
+
copied++;
|
|
34
|
+
}
|
|
35
|
+
console.log(`✅ Skills: ${copied} updated, ${skipped} community skills preserved`);
|
|
36
|
+
} catch (e) { console.warn(`⚠️ Skills sync: ${e.message}`); }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 4. Create scripts symlink
|
|
40
|
+
const scriptsSource = path.join(packageRoot, "scripts");
|
|
41
|
+
const scriptsDest = path.join(targetDir, "scripts");
|
|
42
|
+
|
|
43
|
+
if (fs.existsSync(scriptsSource)) {
|
|
44
|
+
try {
|
|
45
|
+
if (fs.existsSync(scriptsDest)) {
|
|
46
|
+
const stats = fs.lstatSync(scriptsDest);
|
|
47
|
+
if (stats.isSymbolicLink()) {
|
|
48
|
+
console.log("ℹ️ Scripts symlink: exists");
|
|
49
|
+
} else {
|
|
50
|
+
console.log("⚠️ Scripts dir exists but is not a symlink");
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
fs.symlinkSync(scriptsSource, scriptsDest, "dir");
|
|
54
|
+
console.log("✅ Scripts symlink: created");
|
|
55
|
+
}
|
|
56
|
+
} catch (e) { console.warn(`⚠️ Scripts symlink: ${e.message}`); }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 5. Create dist symlink
|
|
60
|
+
const distSource = path.join(packageRoot, "dist");
|
|
61
|
+
const distDest = path.join(targetDir, "dist");
|
|
62
|
+
|
|
63
|
+
if (fs.existsSync(distSource)) {
|
|
64
|
+
try {
|
|
65
|
+
if (fs.existsSync(distDest)) {
|
|
66
|
+
const stats = fs.lstatSync(distDest);
|
|
67
|
+
if (stats.isSymbolicLink()) {
|
|
68
|
+
console.log("ℹ️ Dist symlink: exists");
|
|
69
|
+
} else {
|
|
70
|
+
console.log("⚠️ Dist exists but is not a symlink");
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
fs.symlinkSync(distSource, distDest, "dir");
|
|
74
|
+
console.log("✅ Dist symlink: created");
|
|
75
|
+
}
|
|
76
|
+
} catch (e) { console.warn(`⚠️ Dist symlink: ${e.message}`); }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { syncSetupSkillsAndRootLinks };
|
|
82
|
+
|
|
83
|
+
if (require.main === module) {
|
|
6
84
|
const packageRoot = path.join(__dirname, "..", "..");
|
|
7
85
|
const homeDir = require("os").homedir();
|
|
8
86
|
|
|
@@ -43,37 +121,7 @@ if (hasHermes) {
|
|
|
43
121
|
* Extended header for next high-value package (code-review). src/skills/code-review/SKILL.md canonical SSOT; .opencode/skills/code-review/ built mirror of this sync logic. See paired guarded fw log "P3-SKILLS-FOLLOW-02-code-review-ssot-preferred" in status.ts getSkillsList (inserted after researcher block) + rich append docs/reflections/p3-skills-follow-02-code-review-2026-05-27.md (per AGENTS) + CGT/Governance-visible for YML @ harness-codex verification + 0 hot + 3-hot re-establish + all ties to spawn command + deep ref + pivot + Term 61 + 6/6+7/7 + YML + ps 0 + this P3-SKILLS-FOLLOW-02 full work (2-file surgical, harness post green tsc 0 new on touched, mapping append after latest, todo advance exactly 1, spawn replacement to maintain 3-hot, self-audit 100%) + verification + "the box contains its builders. The relay is hot.". 2 files max total for this follow-02. Highly modular (100% prior P3-SKILLS/YML/7th pattern reuse exactly inside existing sync logic). Fully reversible (targeted delete of notes restores exact prior). fw/echo discipline. AGENTS org (scripts/ for sh/cjs). Term 61 surgical forward motion held (no bloat, pure on actual dedup). "the box contains its builders". Green + ready. Subagent ID for resume.
|
|
44
122
|
*/
|
|
45
123
|
|
|
46
|
-
|
|
47
|
-
[P3-SKILLS-DE-DUP-RESEARCHER-FOLLOW-01: researcher package SSOT canonical src/skills/researcher/SKILL.md (high-value per deep ref + YML precedent + playbook P3-SKILLS-FOLLOW-01); mirror .opencode/skills/researcher/ is built output of this logic; see paired guarded fw in status.ts + rich append + YML @ harness-codex verification (FORCE exercised P3-YML + codex-enforcement + 100/60/0 + 3 subagents tracked + 0 hot + 3-hot re-establish + all prior green) + deep ref 2026-05-26 + Phase 3 Pivot + Term 61 + user's "create a todo list. spawn subagents..." + 6/6+7/7 + YML. Governance-visible hook for skills SSOT at install layer. Silent, additive, reversible, no behavior change, fw/echo only.]
|
|
48
|
-
[P3-SKILLS-FOLLOW-02: code-review package SSOT canonical src/skills/code-review/SKILL.md (next high-value per deep ref "parallel small batches" + @architect scoping rec 2 + YML @ "Ready for next" + this P3-SKILLS-FOLLOW-02 on user's spawn command); mirror .opencode/skills/code-review/ is built output of this logic; see paired guarded fw "P3-SKILLS-FOLLOW-02-code-review-ssot-preferred" in status.ts (after researcher) + rich append docs/reflections/p3-skills-follow-02-code-review-2026-05-27.md + YML @ harness-codex verification + 0 hot + 3-hot + all ties + ps 0 + this work (2-file surgical edit, harness post, mapping append after latest ~17099+, todo advance 1, spawn replacement, self-audit 100%, green + ready) + deep ref 2026-05-26 + Phase 3 Pivot + Term 61 + 6/6+7/7 + YML. Governance-visible hook for skills SSOT at install layer for code-review. Silent, additive, reversible, no behavior change, fw/echo only. "The box contains its builders. The relay is hot."] */
|
|
49
|
-
const skillsSource = fs.existsSync(path.join(packageRoot, "src", "skills"))
|
|
50
|
-
? path.join(packageRoot, "src", "skills")
|
|
51
|
-
: path.join(packageRoot, "dist", "skills");
|
|
52
|
-
const skillsDest = path.join(targetDir, ".opencode", "skills");
|
|
53
|
-
|
|
54
|
-
if (fs.existsSync(skillsSource)) {
|
|
55
|
-
try {
|
|
56
|
-
if (!fs.existsSync(skillsDest)) fs.mkdirSync(skillsDest, { recursive: true });
|
|
57
|
-
const skillDirs = fs.readdirSync(skillsSource, { withFileTypes: true });
|
|
58
|
-
let copied = 0, skipped = 0;
|
|
59
|
-
for (const entry of skillDirs) {
|
|
60
|
-
if (!entry.isDirectory()) continue;
|
|
61
|
-
const skillMd = path.join(skillsSource, entry.name, "SKILL.md");
|
|
62
|
-
if (!fs.existsSync(skillMd)) continue;
|
|
63
|
-
const destMd = path.join(skillsDest, entry.name, "SKILL.md");
|
|
64
|
-
const destDir = path.dirname(destMd);
|
|
65
|
-
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
66
|
-
if (fs.existsSync(destMd)) {
|
|
67
|
-
const destContent = fs.readFileSync(destMd, "utf8");
|
|
68
|
-
if (destContent.includes("source: community")) { skipped++; continue; }
|
|
69
|
-
if (fs.statSync(skillMd).mtime <= fs.statSync(destMd).mtime) continue;
|
|
70
|
-
}
|
|
71
|
-
fs.copyFileSync(skillMd, destMd);
|
|
72
|
-
copied++;
|
|
73
|
-
}
|
|
74
|
-
console.log(`✅ Skills: ${copied} updated, ${skipped} community skills preserved`);
|
|
75
|
-
} catch (e) { console.warn(`⚠️ Skills sync: ${e.message}`); }
|
|
76
|
-
}
|
|
124
|
+
syncSetupSkillsAndRootLinks(packageRoot, targetDir);
|
|
77
125
|
|
|
78
126
|
// 2. Handle opencode.json merge
|
|
79
127
|
const rootOpencodeJson = path.join(packageRoot, "opencode.json");
|
|
@@ -121,46 +169,6 @@ if (fs.existsSync(pluginSource)) {
|
|
|
121
169
|
console.log("ℹ️ Plugin source not found (build may be needed)");
|
|
122
170
|
}
|
|
123
171
|
|
|
124
|
-
// 4. Create scripts symlink
|
|
125
|
-
const scriptsSource = path.join(packageRoot, "scripts");
|
|
126
|
-
const scriptsDest = path.join(targetDir, "scripts");
|
|
127
|
-
|
|
128
|
-
if (fs.existsSync(scriptsSource)) {
|
|
129
|
-
try {
|
|
130
|
-
if (fs.existsSync(scriptsDest)) {
|
|
131
|
-
const stats = fs.lstatSync(scriptsDest);
|
|
132
|
-
if (stats.isSymbolicLink()) {
|
|
133
|
-
console.log("ℹ️ Scripts symlink: exists");
|
|
134
|
-
} else {
|
|
135
|
-
console.log("⚠️ Scripts dir exists but is not a symlink");
|
|
136
|
-
}
|
|
137
|
-
} else {
|
|
138
|
-
fs.symlinkSync(scriptsSource, scriptsDest, "dir");
|
|
139
|
-
console.log("✅ Scripts symlink: created");
|
|
140
|
-
}
|
|
141
|
-
} catch (e) { console.warn(`⚠️ Scripts symlink: ${e.message}`); }
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// 5. Create dist symlink
|
|
145
|
-
const distSource = path.join(packageRoot, "dist");
|
|
146
|
-
const distDest = path.join(targetDir, "dist");
|
|
147
|
-
|
|
148
|
-
if (fs.existsSync(distSource)) {
|
|
149
|
-
try {
|
|
150
|
-
if (fs.existsSync(distDest)) {
|
|
151
|
-
const stats = fs.lstatSync(distDest);
|
|
152
|
-
if (stats.isSymbolicLink()) {
|
|
153
|
-
console.log("ℹ️ Dist symlink: exists");
|
|
154
|
-
} else {
|
|
155
|
-
console.log("⚠️ Dist exists but is not a symlink");
|
|
156
|
-
}
|
|
157
|
-
} else {
|
|
158
|
-
fs.symlinkSync(distSource, distDest, "dir");
|
|
159
|
-
console.log("✅ Dist symlink: created");
|
|
160
|
-
}
|
|
161
|
-
} catch (e) { console.warn(`⚠️ Dist symlink: ${e.message}`); }
|
|
162
|
-
}
|
|
163
|
-
|
|
164
172
|
// 6. Convert MCP paths in consumer opencode.json
|
|
165
173
|
const isConsumer = isConsumerInstall(packageRoot, targetDir);
|
|
166
174
|
const consumerPkgName = isConsumer
|
|
@@ -234,3 +242,4 @@ if (resolvedPackage !== resolvedTarget) {
|
|
|
234
242
|
}
|
|
235
243
|
|
|
236
244
|
console.log("\n✅ xray setup complete.\n");
|
|
245
|
+
}
|
|
@@ -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
|
};
|