@ijfw/install 1.6.1 → 1.6.3
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/ijfw.js +194 -181
- package/dist/install.js +331 -102
- package/dist/uninstall.js +112 -40
- package/package.json +3 -3
- package/src/install.ps1 +1 -1
package/dist/install.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __esm = (fn, res) => function __init() {
|
|
5
|
-
|
|
4
|
+
var __esm = (fn, res, err) => function __init() {
|
|
5
|
+
if (err) throw err[0];
|
|
6
|
+
try {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
} catch (e) {
|
|
9
|
+
throw err = [e], e;
|
|
10
|
+
}
|
|
6
11
|
};
|
|
7
12
|
var __export = (target, all) => {
|
|
8
13
|
for (var name in all)
|
|
@@ -292,21 +297,42 @@ function prettyName(targetId) {
|
|
|
292
297
|
};
|
|
293
298
|
return map[targetId] || String(targetId);
|
|
294
299
|
}
|
|
300
|
+
function backupTimestamp() {
|
|
301
|
+
const d = /* @__PURE__ */ new Date();
|
|
302
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
303
|
+
return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}`;
|
|
304
|
+
}
|
|
295
305
|
function readJsonOrEmpty(path3) {
|
|
296
306
|
if (!existsSync3(path3)) return {};
|
|
307
|
+
let raw;
|
|
308
|
+
try {
|
|
309
|
+
raw = readFileSync2(path3, "utf8");
|
|
310
|
+
} catch (err) {
|
|
311
|
+
const msg = err && err.message ? err.message : String(err);
|
|
312
|
+
throw new Error(`cannot read existing config at ${path3} (${msg}) -- fix permissions and re-run.`);
|
|
313
|
+
}
|
|
314
|
+
if (raw.charCodeAt(0) === 65279) raw = raw.slice(1);
|
|
315
|
+
if (!raw || raw.trim() === "") return {};
|
|
297
316
|
try {
|
|
298
|
-
const raw = readFileSync2(path3, "utf8");
|
|
299
|
-
if (!raw || raw.trim() === "") return {};
|
|
300
317
|
const parsed = JSON.parse(raw);
|
|
301
|
-
if (
|
|
302
|
-
return parsed;
|
|
318
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
303
319
|
} catch {
|
|
304
|
-
return {};
|
|
305
320
|
}
|
|
321
|
+
const bak = `${path3}.corrupt-${backupTimestamp()}.bak`;
|
|
322
|
+
try {
|
|
323
|
+
copyFileSync(path3, bak);
|
|
324
|
+
} catch (err) {
|
|
325
|
+
const msg = err && err.message ? err.message : String(err);
|
|
326
|
+
throw new Error(
|
|
327
|
+
`existing config at ${path3} is not valid JSON and a safety copy could not be written (${msg}) -- fix or move the file, then re-run.`
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
printWarn(`existing config at ${path3} is not valid JSON -- original saved to ${bak}; starting fresh with the IJFW entry only`);
|
|
331
|
+
return {};
|
|
306
332
|
}
|
|
307
333
|
function mergeJson(dst, serverJs, ts) {
|
|
308
334
|
mkdirSync2(dirname3(dst), { recursive: true });
|
|
309
|
-
requireBackup(dst, ts);
|
|
335
|
+
requireBackup(dst, ts || backupTimestamp());
|
|
310
336
|
const doc = readJsonOrEmpty(dst);
|
|
311
337
|
if (!doc.mcpServers || typeof doc.mcpServers !== "object") doc.mcpServers = {};
|
|
312
338
|
const isWin = IS_WIN;
|
|
@@ -334,7 +360,7 @@ function mergeJson(dst, serverJs, ts) {
|
|
|
334
360
|
}
|
|
335
361
|
function mergeToml(dst, serverJs, ts) {
|
|
336
362
|
mkdirSync2(dirname3(dst), { recursive: true });
|
|
337
|
-
requireBackup(dst, ts);
|
|
363
|
+
requireBackup(dst, ts || backupTimestamp());
|
|
338
364
|
let text = "";
|
|
339
365
|
try {
|
|
340
366
|
text = existsSync3(dst) ? readFileSync2(dst, "utf8") : "";
|
|
@@ -385,32 +411,6 @@ function stripTomlSection(text, sectionName) {
|
|
|
385
411
|
}
|
|
386
412
|
return out.join("\n");
|
|
387
413
|
}
|
|
388
|
-
function mergeYamlMcp(dst, serverJs, ts) {
|
|
389
|
-
mkdirSync2(dirname3(dst), { recursive: true });
|
|
390
|
-
if (ts) backup(dst, ts);
|
|
391
|
-
let text = "";
|
|
392
|
-
try {
|
|
393
|
-
text = existsSync3(dst) ? readFileSync2(dst, "utf8") : "";
|
|
394
|
-
} catch {
|
|
395
|
-
text = "";
|
|
396
|
-
}
|
|
397
|
-
text = stripSentinelBlock(text, "# IJFW-MCP-BEGIN ijfw-memory", "# IJFW-MCP-END ijfw-memory");
|
|
398
|
-
if (!/^mcp_servers:/m.test(text)) {
|
|
399
|
-
if (text && !text.endsWith("\n")) text += "\n";
|
|
400
|
-
text += "\nmcp_servers:\n";
|
|
401
|
-
}
|
|
402
|
-
const escaped = String(serverJs).replace(/"/g, '\\"');
|
|
403
|
-
let block = "";
|
|
404
|
-
if (!text.endsWith("\n")) block += "\n";
|
|
405
|
-
block += "# IJFW-MCP-BEGIN ijfw-memory\n";
|
|
406
|
-
block += " ijfw-memory:\n";
|
|
407
|
-
block += ' command: "node"\n';
|
|
408
|
-
block += ` args: ["${escaped}"]
|
|
409
|
-
`;
|
|
410
|
-
block += " enabled: true\n";
|
|
411
|
-
block += "# IJFW-MCP-END ijfw-memory\n";
|
|
412
|
-
writeAtomic(dst, text + block, { mode: 384 });
|
|
413
|
-
}
|
|
414
414
|
function stripSentinelBlock(text, beginMark, endMark) {
|
|
415
415
|
const lines = text.split("\n");
|
|
416
416
|
const out = [];
|
|
@@ -500,7 +500,7 @@ function isIndentedEnabledLine(line) {
|
|
|
500
500
|
}
|
|
501
501
|
function opencodeMerge(dst, serverJs, ts) {
|
|
502
502
|
mkdirSync2(dirname3(dst), { recursive: true });
|
|
503
|
-
|
|
503
|
+
backup(dst, ts || backupTimestamp());
|
|
504
504
|
const doc = readJsonOrEmpty(dst);
|
|
505
505
|
if (!doc.mcp || typeof doc.mcp !== "object") doc.mcp = {};
|
|
506
506
|
doc.mcp["ijfw-memory"] = { type: "local", command: ["node", serverJs] };
|
|
@@ -508,7 +508,7 @@ function opencodeMerge(dst, serverJs, ts) {
|
|
|
508
508
|
}
|
|
509
509
|
function openclawMerge(dst, serverJs, ts) {
|
|
510
510
|
mkdirSync2(dirname3(dst), { recursive: true });
|
|
511
|
-
|
|
511
|
+
backup(dst, ts || backupTimestamp());
|
|
512
512
|
const doc = readJsonOrEmpty(dst);
|
|
513
513
|
if (!doc.mcp || typeof doc.mcp !== "object") doc.mcp = {};
|
|
514
514
|
if (!doc.mcp.servers || typeof doc.mcp.servers !== "object") doc.mcp.servers = {};
|
|
@@ -554,7 +554,7 @@ function clineMerge(serverJs, home, ts) {
|
|
|
554
554
|
if (!userDir) userDir = osDefault;
|
|
555
555
|
const dst = join3(userDir, "globalStorage", ext, "settings", "cline_mcp_settings.json");
|
|
556
556
|
mkdirSync2(dirname3(dst), { recursive: true });
|
|
557
|
-
|
|
557
|
+
backup(dst, ts || backupTimestamp());
|
|
558
558
|
const doc = readJsonOrEmpty(dst);
|
|
559
559
|
if (!doc.mcpServers || typeof doc.mcpServers !== "object") doc.mcpServers = {};
|
|
560
560
|
doc.mcpServers["ijfw-memory"] = {
|
|
@@ -633,6 +633,9 @@ function ensureDir(p) {
|
|
|
633
633
|
} catch {
|
|
634
634
|
}
|
|
635
635
|
}
|
|
636
|
+
function stripBom(s) {
|
|
637
|
+
return s && s.charCodeAt(0) === 65279 ? s.slice(1) : s;
|
|
638
|
+
}
|
|
636
639
|
function copyIfAbsent(src, dst) {
|
|
637
640
|
if (!existsSync4(src)) return false;
|
|
638
641
|
if (existsSync4(dst)) return false;
|
|
@@ -716,13 +719,18 @@ async function installClaude(ctx) {
|
|
|
716
719
|
"known_marketplaces.json"
|
|
717
720
|
);
|
|
718
721
|
ensureDir(join4(ctx.home, ".claude", "plugins"));
|
|
719
|
-
|
|
722
|
+
const settingsBak = requireBackup(claudeSettings, ctx.ts);
|
|
720
723
|
let settings = {};
|
|
721
724
|
if (existsSync4(claudeSettings)) {
|
|
722
725
|
try {
|
|
723
|
-
settings = JSON.parse(readFileSync3(claudeSettings, "utf8") || "{}");
|
|
726
|
+
settings = JSON.parse(stripBom(readFileSync3(claudeSettings, "utf8")) || "{}");
|
|
724
727
|
} catch {
|
|
725
|
-
settings
|
|
728
|
+
ctx.log.warn("~/.claude/settings.json could not be parsed as JSON -- IJFW will not modify it.");
|
|
729
|
+
if (settingsBak) {
|
|
730
|
+
ctx.log.warn(`A copy of the current file was preserved at ${settingsBak}.`);
|
|
731
|
+
}
|
|
732
|
+
ctx.log.warn("Fix the JSON syntax error and re-run `ijfw install`.");
|
|
733
|
+
return { status: "noop" };
|
|
726
734
|
}
|
|
727
735
|
}
|
|
728
736
|
if (!settings || typeof settings !== "object") settings = {};
|
|
@@ -736,7 +744,7 @@ async function installClaude(ctx) {
|
|
|
736
744
|
let mp = {};
|
|
737
745
|
if (existsSync4(claudeMarketplaces)) {
|
|
738
746
|
try {
|
|
739
|
-
mp = JSON.parse(readFileSync3(claudeMarketplaces, "utf8") || "{}");
|
|
747
|
+
mp = JSON.parse(stripBom(readFileSync3(claudeMarketplaces, "utf8")) || "{}");
|
|
740
748
|
} catch {
|
|
741
749
|
mp = {};
|
|
742
750
|
}
|
|
@@ -785,7 +793,7 @@ async function installCodex(ctx) {
|
|
|
785
793
|
}
|
|
786
794
|
const configToml = join4(ctx.home, ".codex", "config.toml");
|
|
787
795
|
ensureDir(dirname4(configToml));
|
|
788
|
-
mergeToml(configToml, ctx.serverJsNative);
|
|
796
|
+
mergeToml(configToml, ctx.serverJsNative, ctx.ts);
|
|
789
797
|
const hooksDst = join4(ctx.home, ".codex", "hooks.json");
|
|
790
798
|
const hooksSrc = join4(ctx.repoRoot, "codex", ".codex", "hooks.json");
|
|
791
799
|
const hooksBase = join4(ctx.home, ".codex", "hooks");
|
|
@@ -914,18 +922,41 @@ async function installGemini(ctx) {
|
|
|
914
922
|
}
|
|
915
923
|
const dst = join4(ctx.home, ".gemini", "settings.json");
|
|
916
924
|
ensureDir(dirname4(dst));
|
|
917
|
-
mergeJson(dst, ctx.serverJsNative);
|
|
925
|
+
mergeJson(dst, ctx.serverJsNative, ctx.ts);
|
|
918
926
|
const extDst = join4(ctx.home, ".gemini", "extensions", "ijfw");
|
|
919
927
|
const extSrc = join4(ctx.repoRoot, "gemini", "extensions", "ijfw");
|
|
920
928
|
for (const sub of ["hooks", "skills", "commands", "agents", "policies"]) {
|
|
921
929
|
ensureDir(join4(extDst, sub));
|
|
922
930
|
}
|
|
923
|
-
for (const rel of [
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
"
|
|
927
|
-
|
|
928
|
-
|
|
931
|
+
for (const rel of ["gemini-extension.json", "hooks/hooks.json"]) {
|
|
932
|
+
const srcFile = join4(extSrc, rel);
|
|
933
|
+
if (!existsSync4(srcFile)) continue;
|
|
934
|
+
let desired = "";
|
|
935
|
+
try {
|
|
936
|
+
desired = readFileSync3(srcFile, "utf8");
|
|
937
|
+
} catch {
|
|
938
|
+
continue;
|
|
939
|
+
}
|
|
940
|
+
desired = desired.split("{{extensionPath}}").join(extDst);
|
|
941
|
+
const dstFile = join4(extDst, rel);
|
|
942
|
+
let current = null;
|
|
943
|
+
try {
|
|
944
|
+
current = existsSync4(dstFile) ? readFileSync3(dstFile, "utf8") : null;
|
|
945
|
+
} catch {
|
|
946
|
+
current = null;
|
|
947
|
+
}
|
|
948
|
+
if (current === desired) continue;
|
|
949
|
+
if (current !== null) {
|
|
950
|
+
try {
|
|
951
|
+
copyFileSync2(dstFile, `${dstFile}.bak.${ctx.ts}`);
|
|
952
|
+
} catch {
|
|
953
|
+
}
|
|
954
|
+
ctx.log.note(`Updated ${rel} (previous copy backed up to ${rel}.bak.${ctx.ts})`);
|
|
955
|
+
}
|
|
956
|
+
ensureDir(dirname4(dstFile));
|
|
957
|
+
writeAtomic(dstFile, desired);
|
|
958
|
+
}
|
|
959
|
+
for (const rel of ["IJFW.md", "policies/ijfw.toml"]) {
|
|
929
960
|
const dstFile = join4(extDst, rel);
|
|
930
961
|
if (!existsSync4(dstFile)) {
|
|
931
962
|
ensureDir(dirname4(dstFile));
|
|
@@ -1051,6 +1082,101 @@ function renderWaylandPluginToml(ctx) {
|
|
|
1051
1082
|
""
|
|
1052
1083
|
].join("\n");
|
|
1053
1084
|
}
|
|
1085
|
+
function stripSentinelLines(text, beginMark, endMark) {
|
|
1086
|
+
const lines = text.split("\n");
|
|
1087
|
+
const out = [];
|
|
1088
|
+
let skip = false;
|
|
1089
|
+
for (const line of lines) {
|
|
1090
|
+
if (line === beginMark) {
|
|
1091
|
+
skip = true;
|
|
1092
|
+
continue;
|
|
1093
|
+
}
|
|
1094
|
+
if (line === endMark) {
|
|
1095
|
+
skip = false;
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
if (skip) continue;
|
|
1099
|
+
out.push(line);
|
|
1100
|
+
}
|
|
1101
|
+
return out.join("\n");
|
|
1102
|
+
}
|
|
1103
|
+
function hermesMergeYamlMcp(ctx, dst, serverJs) {
|
|
1104
|
+
ensureDir(dirname4(dst));
|
|
1105
|
+
requireBackup(dst, ctx.ts);
|
|
1106
|
+
let text = "";
|
|
1107
|
+
try {
|
|
1108
|
+
text = existsSync4(dst) ? stripBom(readFileSync3(dst, "utf8")) : "";
|
|
1109
|
+
} catch {
|
|
1110
|
+
text = "";
|
|
1111
|
+
}
|
|
1112
|
+
text = stripSentinelLines(text, HERMES_MCP_BEGIN, HERMES_MCP_END);
|
|
1113
|
+
const escaped = String(serverJs).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
1114
|
+
const block = [
|
|
1115
|
+
HERMES_MCP_BEGIN,
|
|
1116
|
+
" ijfw-memory:",
|
|
1117
|
+
' command: "node"',
|
|
1118
|
+
` args: ["${escaped}"]`,
|
|
1119
|
+
" enabled: true",
|
|
1120
|
+
HERMES_MCP_END
|
|
1121
|
+
];
|
|
1122
|
+
const lines = text.split("\n");
|
|
1123
|
+
let anchorIdx = -1;
|
|
1124
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1125
|
+
const line = lines[i];
|
|
1126
|
+
if (line.startsWith("mcp_servers:")) {
|
|
1127
|
+
const rest = line.slice("mcp_servers:".length).trim();
|
|
1128
|
+
if (rest === "" || rest.startsWith("#")) {
|
|
1129
|
+
anchorIdx = i;
|
|
1130
|
+
break;
|
|
1131
|
+
}
|
|
1132
|
+
const beforeComment = rest.split("#")[0].replace(/\s/g, "");
|
|
1133
|
+
if (beforeComment === "{}") {
|
|
1134
|
+
lines[i] = "mcp_servers:";
|
|
1135
|
+
anchorIdx = i;
|
|
1136
|
+
break;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
let merged;
|
|
1141
|
+
if (anchorIdx >= 0) {
|
|
1142
|
+
lines.splice(anchorIdx + 1, 0, ...block);
|
|
1143
|
+
merged = lines.join("\n");
|
|
1144
|
+
} else if (/^mcp_servers:/m.test(text)) {
|
|
1145
|
+
ctx.log.warn("Hermes config.yaml uses an inline mcp_servers map -- cannot merge safely.");
|
|
1146
|
+
ctx.log.warn('Add an "ijfw-memory" entry to mcp_servers manually (command: node, args: [server.js path]).');
|
|
1147
|
+
return false;
|
|
1148
|
+
} else {
|
|
1149
|
+
const prefix = text.trim() === "" ? "" : (text.endsWith("\n") ? text : `${text}
|
|
1150
|
+
`) + "\n";
|
|
1151
|
+
merged = `${prefix}mcp_servers:
|
|
1152
|
+
${block.join("\n")}`;
|
|
1153
|
+
}
|
|
1154
|
+
if (!merged.endsWith("\n")) merged += "\n";
|
|
1155
|
+
writeAtomic(dst, merged, { mode: 384 });
|
|
1156
|
+
return true;
|
|
1157
|
+
}
|
|
1158
|
+
function hermesInlineEnabledHas(dst, pluginName) {
|
|
1159
|
+
let text = "";
|
|
1160
|
+
try {
|
|
1161
|
+
text = existsSync4(dst) ? readFileSync3(dst, "utf8") : "";
|
|
1162
|
+
} catch {
|
|
1163
|
+
return false;
|
|
1164
|
+
}
|
|
1165
|
+
const esc = pluginName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1166
|
+
const nameRe = new RegExp(`[\\[,]\\s*["']?${esc}["']?\\s*[,\\]]`);
|
|
1167
|
+
let inPlugins = false;
|
|
1168
|
+
for (const line of text.split("\n")) {
|
|
1169
|
+
if (/^plugins:\s*$/.test(line)) {
|
|
1170
|
+
inPlugins = true;
|
|
1171
|
+
continue;
|
|
1172
|
+
}
|
|
1173
|
+
if (inPlugins && /^\S/.test(line) && line.trim() !== "") inPlugins = false;
|
|
1174
|
+
if (inPlugins && /^\s+enabled:\s*\[.+\]\s*$/.test(line) && nameRe.test(line)) {
|
|
1175
|
+
return true;
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
return false;
|
|
1179
|
+
}
|
|
1054
1180
|
async function installHermes(ctx) {
|
|
1055
1181
|
if (ctx.ijfwCustomDir) {
|
|
1056
1182
|
return customDirNoop(
|
|
@@ -1062,7 +1188,7 @@ async function installHermes(ctx) {
|
|
|
1062
1188
|
}
|
|
1063
1189
|
const dst = join4(ctx.home, ".hermes", "config.yaml");
|
|
1064
1190
|
ensureDir(dirname4(dst));
|
|
1065
|
-
|
|
1191
|
+
hermesMergeYamlMcp(ctx, dst, ctx.serverJsNative);
|
|
1066
1192
|
ensureDir(join4(ctx.home, ".hermes"));
|
|
1067
1193
|
copyIfAbsent(
|
|
1068
1194
|
join4(ctx.repoRoot, "hermes", "HERMES.md"),
|
|
@@ -1087,40 +1213,44 @@ async function installHermes(ctx) {
|
|
|
1087
1213
|
}
|
|
1088
1214
|
if (readdirErr) {
|
|
1089
1215
|
ctx.log.warn(`Hermes plugin tree readdir failed: ${readdirErr.message || readdirErr}`);
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1216
|
+
ctx.log.warn("Leaving the installed Hermes plugin tree untouched.");
|
|
1217
|
+
} else {
|
|
1218
|
+
const srcNames = new Set(entries.filter((n) => n !== "__pycache__"));
|
|
1219
|
+
let dstEntries = [];
|
|
1220
|
+
try {
|
|
1221
|
+
dstEntries = readdirSync(pluginDst);
|
|
1222
|
+
} catch {
|
|
1223
|
+
}
|
|
1224
|
+
for (const name of dstEntries) {
|
|
1225
|
+
if (name === "__pycache__") continue;
|
|
1226
|
+
if (!srcNames.has(name)) {
|
|
1227
|
+
try {
|
|
1228
|
+
rmSync(join4(pluginDst, name), { recursive: true, force: true });
|
|
1229
|
+
} catch (err) {
|
|
1230
|
+
ctx.log.warn(`Hermes plugin: could not remove stale ${name}: ${err.message || err}`);
|
|
1231
|
+
}
|
|
1104
1232
|
}
|
|
1105
1233
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1234
|
+
for (const name of entries) {
|
|
1235
|
+
if (name === "__pycache__") continue;
|
|
1236
|
+
const src = join4(pluginSrc, name);
|
|
1237
|
+
const dstEntry = join4(pluginDst, name);
|
|
1238
|
+
try {
|
|
1239
|
+
const st = statSync2(src);
|
|
1240
|
+
if (st.isDirectory()) {
|
|
1241
|
+
cpSync(src, dstEntry, { recursive: true, force: true });
|
|
1242
|
+
} else if (st.isFile()) {
|
|
1243
|
+
copyFileSync2(src, dstEntry);
|
|
1244
|
+
}
|
|
1245
|
+
} catch {
|
|
1117
1246
|
}
|
|
1118
|
-
} catch {
|
|
1119
1247
|
}
|
|
1120
1248
|
}
|
|
1121
1249
|
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1250
|
+
if (!hermesInlineEnabledHas(dst, "ijfw")) {
|
|
1251
|
+
mergeYamlPluginsEnabled(dst, "ijfw");
|
|
1252
|
+
}
|
|
1253
|
+
mergeYamlHook(dst, "plugins/ijfw/hooks/pre_tool_use_extension_check.py");
|
|
1124
1254
|
ctx.log.ok("Installed Hermes bundle: MCP + HERMES.md + skills + plugin + tier-2 hook");
|
|
1125
1255
|
return { status: "ok" };
|
|
1126
1256
|
}
|
|
@@ -1148,13 +1278,13 @@ async function installCursor(ctx) {
|
|
|
1148
1278
|
}
|
|
1149
1279
|
const dst = join4(cwd, ".cursor", "mcp.json");
|
|
1150
1280
|
ensureDir(dirname4(dst));
|
|
1151
|
-
mergeJson(dst, ctx.serverJsNative);
|
|
1281
|
+
mergeJson(dst, ctx.serverJsNative, ctx.ts);
|
|
1152
1282
|
const rulesDir = join4(cwd, ".cursor", "rules");
|
|
1153
1283
|
ensureDir(rulesDir);
|
|
1154
1284
|
const ruleSrc = join4(ctx.repoRoot, "cursor", ".cursor", "rules", "ijfw.mdc");
|
|
1155
1285
|
if (existsSync4(ruleSrc)) {
|
|
1156
1286
|
try {
|
|
1157
|
-
|
|
1287
|
+
installHook(ruleSrc, join4(rulesDir, "ijfw.mdc"), ctx.ts);
|
|
1158
1288
|
} catch {
|
|
1159
1289
|
}
|
|
1160
1290
|
}
|
|
@@ -1172,7 +1302,7 @@ async function installWindsurf(ctx) {
|
|
|
1172
1302
|
}
|
|
1173
1303
|
const dst = join4(ctx.home, ".codeium", "windsurf", "mcp_config.json");
|
|
1174
1304
|
ensureDir(dirname4(dst));
|
|
1175
|
-
mergeJson(dst, ctx.serverJsNative);
|
|
1305
|
+
mergeJson(dst, ctx.serverJsNative, ctx.ts);
|
|
1176
1306
|
const cwd = ctx.cwd || process.cwd();
|
|
1177
1307
|
if (!guardProjectWrite(cwd, ctx.home, {
|
|
1178
1308
|
platformLabel: "Windsurf project rules (.windsurfrules)",
|
|
@@ -1198,9 +1328,12 @@ async function installWindsurf(ctx) {
|
|
|
1198
1328
|
}
|
|
1199
1329
|
return { status: "ok" };
|
|
1200
1330
|
}
|
|
1331
|
+
var HERMES_MCP_BEGIN, HERMES_MCP_END;
|
|
1201
1332
|
var init_install_targets_1_7 = __esm({
|
|
1202
1333
|
"src/install-targets-1-7.js"() {
|
|
1203
1334
|
init_install_helpers();
|
|
1335
|
+
HERMES_MCP_BEGIN = "# IJFW-MCP-BEGIN ijfw-memory";
|
|
1336
|
+
HERMES_MCP_END = "# IJFW-MCP-END ijfw-memory";
|
|
1204
1337
|
}
|
|
1205
1338
|
});
|
|
1206
1339
|
|
|
@@ -1230,6 +1363,30 @@ function commandExists(name) {
|
|
|
1230
1363
|
const r = spawnSync(probeCmd, [name], { stdio: "ignore" });
|
|
1231
1364
|
return r.status === 0;
|
|
1232
1365
|
}
|
|
1366
|
+
function vscodeMcpMerge(dst, serverJs, ts) {
|
|
1367
|
+
ensureDir2(path.dirname(dst));
|
|
1368
|
+
requireBackup(dst, ts);
|
|
1369
|
+
let doc = {};
|
|
1370
|
+
try {
|
|
1371
|
+
if (fs.existsSync(dst)) {
|
|
1372
|
+
let raw = fs.readFileSync(dst, "utf8");
|
|
1373
|
+
if (raw && raw.charCodeAt(0) === 65279) raw = raw.slice(1);
|
|
1374
|
+
if (raw.trim() !== "") {
|
|
1375
|
+
const parsed = JSON.parse(raw);
|
|
1376
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) doc = parsed;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
} catch {
|
|
1380
|
+
doc = {};
|
|
1381
|
+
}
|
|
1382
|
+
if (!doc.servers || typeof doc.servers !== "object") doc.servers = {};
|
|
1383
|
+
if (doc.mcpServers && typeof doc.mcpServers === "object") {
|
|
1384
|
+
delete doc.mcpServers["ijfw-memory"];
|
|
1385
|
+
if (Object.keys(doc.mcpServers).length === 0) delete doc.mcpServers;
|
|
1386
|
+
}
|
|
1387
|
+
doc.servers["ijfw-memory"] = { type: "stdio", command: "node", args: [serverJs] };
|
|
1388
|
+
writeAtomic(dst, JSON.stringify(doc, null, 2) + "\n", { mode: 384 });
|
|
1389
|
+
}
|
|
1233
1390
|
function installCopilot(ctx) {
|
|
1234
1391
|
if (ctx.ijfwCustomDir) {
|
|
1235
1392
|
printInfo("Custom-dir install -- skipping Copilot project writes.");
|
|
@@ -1251,7 +1408,7 @@ function installCopilot(ctx) {
|
|
|
1251
1408
|
}
|
|
1252
1409
|
const dst = path.join(cwd, ".vscode", "mcp.json");
|
|
1253
1410
|
ensureDir2(path.dirname(dst));
|
|
1254
|
-
|
|
1411
|
+
vscodeMcpMerge(dst, ctx.serverJsNative || ctx.serverJs, ctx.ts);
|
|
1255
1412
|
const rulesDst = path.join(cwd, ".github", "copilot-instructions.md");
|
|
1256
1413
|
const rulesSrc = path.join(ctx.repoRoot, "copilot", "copilot-instructions.md");
|
|
1257
1414
|
const wroteRules = copyIfMissing(rulesSrc, rulesDst);
|
|
@@ -1270,7 +1427,7 @@ function installOpencode(ctx) {
|
|
|
1270
1427
|
}
|
|
1271
1428
|
const dst = path.join(ctx.home, ".config", "opencode", "opencode.json");
|
|
1272
1429
|
ensureDir2(path.dirname(dst));
|
|
1273
|
-
opencodeMerge(dst, ctx.serverJsNative || ctx.serverJs);
|
|
1430
|
+
opencodeMerge(dst, ctx.serverJsNative || ctx.serverJs, ctx.ts);
|
|
1274
1431
|
printOk(`Merged MCP into ${dst} (opencode mcp.local schema)`);
|
|
1275
1432
|
return { status: "ok" };
|
|
1276
1433
|
}
|
|
@@ -1282,7 +1439,7 @@ function installQwen(ctx) {
|
|
|
1282
1439
|
}
|
|
1283
1440
|
const dst = path.join(ctx.home, ".qwen", "settings.json");
|
|
1284
1441
|
ensureDir2(path.dirname(dst));
|
|
1285
|
-
mergeJson(dst, ctx.serverJsNative || ctx.serverJs);
|
|
1442
|
+
mergeJson(dst, ctx.serverJsNative || ctx.serverJs, ctx.ts);
|
|
1286
1443
|
printOk(`Merged MCP into ${dst}`);
|
|
1287
1444
|
return { status: "ok" };
|
|
1288
1445
|
}
|
|
@@ -1292,7 +1449,7 @@ function installCline(ctx) {
|
|
|
1292
1449
|
printOk("Cline: real platform config left untouched.");
|
|
1293
1450
|
return { status: "noop" };
|
|
1294
1451
|
}
|
|
1295
|
-
const dst = clineMerge(ctx.serverJsNative || ctx.serverJs, ctx.home);
|
|
1452
|
+
const dst = clineMerge(ctx.serverJsNative || ctx.serverJs, ctx.home, ctx.ts);
|
|
1296
1453
|
printOk(`Merged MCP into ${dst} (cline globalStorage schema)`);
|
|
1297
1454
|
return { status: "ok" };
|
|
1298
1455
|
}
|
|
@@ -1304,7 +1461,7 @@ function installKimi(ctx) {
|
|
|
1304
1461
|
}
|
|
1305
1462
|
const dst = path.join(ctx.home, ".kimi", "mcp.json");
|
|
1306
1463
|
ensureDir2(path.dirname(dst));
|
|
1307
|
-
mergeJson(dst, ctx.serverJsNative || ctx.serverJs);
|
|
1464
|
+
mergeJson(dst, ctx.serverJsNative || ctx.serverJs, ctx.ts);
|
|
1308
1465
|
printOk(`Merged MCP into ${dst}`);
|
|
1309
1466
|
return { status: "ok" };
|
|
1310
1467
|
}
|
|
@@ -1331,7 +1488,7 @@ function installOpenclaw(ctx) {
|
|
|
1331
1488
|
}
|
|
1332
1489
|
}
|
|
1333
1490
|
ensureDir2(path.dirname(dst));
|
|
1334
|
-
openclawMerge(dst, serverJs);
|
|
1491
|
+
openclawMerge(dst, serverJs, ctx.ts);
|
|
1335
1492
|
if (cliRegistered) {
|
|
1336
1493
|
printOk(`Registered ijfw-memory via 'openclaw mcp set' AND file-write merge (${dst})`);
|
|
1337
1494
|
} else {
|
|
@@ -1363,10 +1520,10 @@ function installAntigravity(ctx) {
|
|
|
1363
1520
|
const serverJs = ctx.serverJsNative || ctx.serverJs;
|
|
1364
1521
|
const ideDst = path.join(ctx.home, ".gemini", "antigravity", "mcp_config.json");
|
|
1365
1522
|
ensureDir2(path.dirname(ideDst));
|
|
1366
|
-
mergeJson(ideDst, serverJs);
|
|
1523
|
+
mergeJson(ideDst, serverJs, ctx.ts);
|
|
1367
1524
|
const cliDst = path.join(ctx.home, ".gemini", "config", "mcp_config.json");
|
|
1368
1525
|
ensureDir2(path.dirname(cliDst));
|
|
1369
|
-
mergeJson(cliDst, serverJs);
|
|
1526
|
+
mergeJson(cliDst, serverJs, ctx.ts);
|
|
1370
1527
|
printOk(`Merged MCP into ${ideDst} + ${cliDst} (Antigravity IDE + CLI)`);
|
|
1371
1528
|
return { status: "ok" };
|
|
1372
1529
|
}
|
|
@@ -1422,11 +1579,15 @@ function snapshotPreExistingDirs(home) {
|
|
|
1422
1579
|
}
|
|
1423
1580
|
function writeLedger({ home, ijfwHome, preExisting }) {
|
|
1424
1581
|
const preSet = new Set(preExisting || []);
|
|
1582
|
+
const owned = allOwnedDirs();
|
|
1425
1583
|
const created = [];
|
|
1426
|
-
for (const rel of
|
|
1584
|
+
for (const rel of owned) {
|
|
1427
1585
|
if (!preSet.has(rel) && existsSync5(join5(home, rel))) created.push(rel);
|
|
1428
1586
|
}
|
|
1429
|
-
const
|
|
1587
|
+
const prev = readLedger(ijfwHome).createdDirs.filter(
|
|
1588
|
+
(rel) => owned.includes(rel) && existsSync5(join5(home, rel))
|
|
1589
|
+
);
|
|
1590
|
+
const ledger = { version: 1, createdDirs: [.../* @__PURE__ */ new Set([...prev, ...created])] };
|
|
1430
1591
|
try {
|
|
1431
1592
|
mkdirSync4(ijfwHome, { recursive: true, mode: 448 });
|
|
1432
1593
|
writeFileSync4(ledgerPath(ijfwHome), JSON.stringify(ledger, null, 2) + "\n", { mode: 384 });
|
|
@@ -1513,6 +1674,12 @@ var init_install_ledger = __esm({
|
|
|
1513
1674
|
qwen: [["~/.qwen/settings.json", "m", "mcpServers.ijfw-memory"]],
|
|
1514
1675
|
kimi: [["~/.kimi/mcp.json", "m", "mcpServers.ijfw-memory"]],
|
|
1515
1676
|
opencode: [["~/.config/opencode/opencode.json", "m", "mcp.ijfw-memory"]],
|
|
1677
|
+
cline: [["VS Code globalStorage <Code|VSCodium>/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json", "m", "mcpServers.ijfw-memory"]],
|
|
1678
|
+
antigravity: [
|
|
1679
|
+
["~/.gemini/antigravity/mcp_config.json", "m", "mcpServers.ijfw-memory (IDE)"],
|
|
1680
|
+
["~/.gemini/config/mcp_config.json", "m", "mcpServers.ijfw-memory (CLI agy)"]
|
|
1681
|
+
],
|
|
1682
|
+
pi: [["~/.pi/agent/AGENTS.md", "c", "context file (rules-only, no MCP)"]],
|
|
1516
1683
|
cursor: [["./.cursor/mcp.json + rules/ijfw.mdc", "mc", "project-scoped MCP + rule"]],
|
|
1517
1684
|
windsurf: [
|
|
1518
1685
|
["~/.codeium/windsurf/mcp_config.json", "m", "mcpServers.ijfw-memory"],
|
|
@@ -2178,7 +2345,7 @@ var init_install_flow = __esm({
|
|
|
2178
2345
|
// src/install.js
|
|
2179
2346
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
2180
2347
|
import { existsSync as existsSync6, rmSync as rmSync2, mkdirSync as mkdirSync5, realpathSync as realpathSync2, renameSync as renameSync3, readdirSync as readdirSync3, cpSync as cpSync2 } from "node:fs";
|
|
2181
|
-
import { resolve as resolve4, join as join6, dirname as dirname5 } from "node:path";
|
|
2348
|
+
import { resolve as resolve4, join as join6, dirname as dirname5, basename as basename2 } from "node:path";
|
|
2182
2349
|
import { homedir as homedir3, platform as platform2 } from "node:os";
|
|
2183
2350
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
2184
2351
|
|
|
@@ -2344,10 +2511,10 @@ function parseArgs(argv) {
|
|
|
2344
2511
|
for (let i = 2; i < argv.length; i++) {
|
|
2345
2512
|
const a = argv[i];
|
|
2346
2513
|
if (a === "--yes" || a === "-y") out.yes = true;
|
|
2347
|
-
else if (a === "--dir") out.dir = argv[++i];
|
|
2514
|
+
else if (a === "--dir") out.dir = requireFlagValue("--dir", "a path", argv[++i]);
|
|
2348
2515
|
else if (a === "--no-marketplace") out.noMarketplace = true;
|
|
2349
2516
|
else if (a === "--branch") {
|
|
2350
|
-
out.branch = argv[++i];
|
|
2517
|
+
out.branch = requireFlagValue("--branch", "a name", argv[++i]);
|
|
2351
2518
|
out.branchExplicit = true;
|
|
2352
2519
|
} else if (a === "--purge") out.purge = true;
|
|
2353
2520
|
else if (a === "--dry-run" || a === "--print-plan") out.dryRun = true;
|
|
@@ -2358,6 +2525,13 @@ function parseArgs(argv) {
|
|
|
2358
2525
|
}
|
|
2359
2526
|
return out;
|
|
2360
2527
|
}
|
|
2528
|
+
function requireFlagValue(flag, what, value) {
|
|
2529
|
+
if (value == null || value.startsWith("-")) {
|
|
2530
|
+
console.error(`${flag} requires ${what} argument`);
|
|
2531
|
+
process.exit(1);
|
|
2532
|
+
}
|
|
2533
|
+
return value;
|
|
2534
|
+
}
|
|
2361
2535
|
function skipNetwork() {
|
|
2362
2536
|
return process.env.IJFW_SKIP_NETWORK === "1";
|
|
2363
2537
|
}
|
|
@@ -2461,6 +2635,16 @@ function runCheck(cmd, args, opts) {
|
|
|
2461
2635
|
const r = spawnSync2(cmd, args, { encoding: "utf8", ...opts });
|
|
2462
2636
|
return { status: r.status, stdout: r.stdout || "", stderr: r.stderr || "", spawnError: r.error?.code, signal: r.signal };
|
|
2463
2637
|
}
|
|
2638
|
+
function looksLikeIjfwInstall(dir) {
|
|
2639
|
+
try {
|
|
2640
|
+
if (basename2(resolve4(dir)) === ".ijfw") return true;
|
|
2641
|
+
if (existsSync6(join6(dir, "install-ledger.json"))) return true;
|
|
2642
|
+
if (existsSync6(join6(dir, "install-method"))) return true;
|
|
2643
|
+
if (existsSync6(join6(dir, "mcp-server", "src", "server.js")) && existsSync6(join6(dir, "claude"))) return true;
|
|
2644
|
+
} catch {
|
|
2645
|
+
}
|
|
2646
|
+
return false;
|
|
2647
|
+
}
|
|
2464
2648
|
function cloneOrPull(dir, branch) {
|
|
2465
2649
|
if (skipNetwork()) {
|
|
2466
2650
|
if (existsSync6(dir)) {
|
|
@@ -2500,6 +2684,13 @@ function cloneOrPull(dir, branch) {
|
|
|
2500
2684
|
console.log(` origin migration: ${currentOrigin} -> ${DEFAULT_REPO}`);
|
|
2501
2685
|
}
|
|
2502
2686
|
}
|
|
2687
|
+
const CANONICAL_PATTERN = /^https:\/\/github\.com\/ferroxlabs\/ijfw(\.git)?\/?$/i;
|
|
2688
|
+
const isIjfwOrigin = CANONICAL_PATTERN.test(currentOrigin) || STALE_PATTERNS.some((re) => re.test(currentOrigin));
|
|
2689
|
+
if (!isIjfwOrigin && !looksLikeIjfwInstall(dir)) {
|
|
2690
|
+
throw new Error(
|
|
2691
|
+
`Refusing to update ${dir}: it is a git checkout of "${currentOrigin}", not an IJFW install. Check your --dir / IJFW_HOME setting, or remove the directory and retry.`
|
|
2692
|
+
);
|
|
2693
|
+
}
|
|
2503
2694
|
const fetch = spawnSync2("git", ["-C", dir, "fetch", "--depth", "1", "origin", branch], { stdio: "inherit" });
|
|
2504
2695
|
if (fetch.status !== 0) throw new Error(`IJFW fetch did not complete (exit ${fetch.status}) -- check network access and retry.`);
|
|
2505
2696
|
const co = spawnSync2("git", ["-C", dir, "checkout", "-f", "FETCH_HEAD"], { stdio: "inherit" });
|
|
@@ -2526,29 +2717,50 @@ function cloneOrPull(dir, branch) {
|
|
|
2526
2717
|
".ijfw"
|
|
2527
2718
|
// internal — recall counter, indexes, layout version
|
|
2528
2719
|
];
|
|
2720
|
+
if (!looksLikeIjfwInstall(dir)) {
|
|
2721
|
+
let entries = null;
|
|
2722
|
+
try {
|
|
2723
|
+
entries = readdirSync3(dir);
|
|
2724
|
+
} catch {
|
|
2725
|
+
}
|
|
2726
|
+
if (entries && entries.length === 0) {
|
|
2727
|
+
const r = spawnSync2("git", ["clone", "--depth", "1", "--branch", branch, DEFAULT_REPO, dir], { stdio: "inherit" });
|
|
2728
|
+
if (r.status !== 0) throw new Error(`IJFW repo fetch did not complete (exit ${r.status}) -- check network access and retry.`);
|
|
2729
|
+
return "cloned";
|
|
2730
|
+
}
|
|
2731
|
+
throw new Error(
|
|
2732
|
+
`Refusing to replace ${dir}: it exists but does not look like an IJFW install (no install ledger, install-method file, or IJFW checkout markers). Check your --dir / IJFW_HOME setting, or move the directory aside and retry.`
|
|
2733
|
+
);
|
|
2734
|
+
}
|
|
2529
2735
|
const backupDir = dir + ".bak." + Date.now();
|
|
2530
2736
|
renameSync3(dir, backupDir);
|
|
2531
2737
|
try {
|
|
2532
2738
|
const r = spawnSync2("git", ["clone", "--depth", "1", "--branch", branch, DEFAULT_REPO, dir], { stdio: "inherit" });
|
|
2533
2739
|
if (r.status !== 0) throw new Error(`IJFW repo fetch did not complete (exit ${r.status}) -- check network access and retry.`);
|
|
2534
|
-
|
|
2740
|
+
const restoredItems = [];
|
|
2535
2741
|
for (const item of RESTORE_ALLOWLIST) {
|
|
2536
2742
|
const src = join6(backupDir, item);
|
|
2537
2743
|
if (existsSync6(src)) {
|
|
2538
2744
|
const dst = join6(dir, item);
|
|
2539
|
-
if (existsSync6(dst)) rmSync2(dst, { recursive: true, force: true });
|
|
2540
2745
|
try {
|
|
2746
|
+
if (existsSync6(dst)) rmSync2(dst, { recursive: true, force: true });
|
|
2541
2747
|
cpSync2(src, dst, { recursive: true, dereference: false });
|
|
2542
|
-
|
|
2543
|
-
restoredCount++;
|
|
2748
|
+
restoredItems.push(item);
|
|
2544
2749
|
} catch (cpErr) {
|
|
2545
2750
|
const msg = cpErr && cpErr.message ? cpErr.message : String(cpErr);
|
|
2546
2751
|
throw new Error(
|
|
2547
|
-
`IJFW restore:
|
|
2752
|
+
`IJFW restore: copy failed for "${item}" (${msg}). Your data is still intact under: ${backupDir}. The previous state of ${dir} will be restored from it.`
|
|
2548
2753
|
);
|
|
2549
2754
|
}
|
|
2550
2755
|
}
|
|
2551
2756
|
}
|
|
2757
|
+
const restoredCount = restoredItems.length;
|
|
2758
|
+
for (const item of restoredItems) {
|
|
2759
|
+
try {
|
|
2760
|
+
rmSync2(join6(backupDir, item), { recursive: true, force: true });
|
|
2761
|
+
} catch {
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2552
2764
|
let backupResidual = [];
|
|
2553
2765
|
try {
|
|
2554
2766
|
backupResidual = readdirSync3(backupDir);
|
|
@@ -2563,8 +2775,15 @@ function cloneOrPull(dir, branch) {
|
|
|
2563
2775
|
}
|
|
2564
2776
|
return "updated";
|
|
2565
2777
|
} catch (err) {
|
|
2566
|
-
|
|
2567
|
-
|
|
2778
|
+
try {
|
|
2779
|
+
if (existsSync6(dir)) rmSync2(dir, { recursive: true, force: true });
|
|
2780
|
+
renameSync3(backupDir, dir);
|
|
2781
|
+
} catch (rollbackErr) {
|
|
2782
|
+
const msg = rollbackErr && rollbackErr.message ? rollbackErr.message : String(rollbackErr);
|
|
2783
|
+
console.error(
|
|
2784
|
+
` [!] rollback failed (${msg}). Your original data is preserved at: ${backupDir}. Move it back to ${dir} manually.`
|
|
2785
|
+
);
|
|
2786
|
+
}
|
|
2568
2787
|
throw err;
|
|
2569
2788
|
}
|
|
2570
2789
|
}
|
|
@@ -2599,7 +2818,15 @@ async function main() {
|
|
|
2599
2818
|
process.exit(0);
|
|
2600
2819
|
}
|
|
2601
2820
|
const createdThisRun = !existsSync6(target);
|
|
2821
|
+
let platformConfigPhase = false;
|
|
2602
2822
|
const sigint = () => {
|
|
2823
|
+
if (platformConfigPhase) {
|
|
2824
|
+
console.warn(
|
|
2825
|
+
`
|
|
2826
|
+
[!] install interrupted while platform configs were being written. Some platform configs may already reference ${target} -- the partial install was kept so they keep working. Rerun \`npx -p @ijfw/install ijfw-install\` to complete it, or \`ijfw-uninstall\` to remove IJFW from all platform configs.`
|
|
2827
|
+
);
|
|
2828
|
+
process.exit(130);
|
|
2829
|
+
}
|
|
2603
2830
|
if (createdThisRun && existsSync6(target)) {
|
|
2604
2831
|
try {
|
|
2605
2832
|
rmSync2(target, { recursive: true, force: true });
|
|
@@ -2619,6 +2846,7 @@ async function main() {
|
|
|
2619
2846
|
console.log(` version: ${ref}`);
|
|
2620
2847
|
const action = cloneOrPull(target, ref);
|
|
2621
2848
|
console.log(` repo ${action}`);
|
|
2849
|
+
platformConfigPhase = true;
|
|
2622
2850
|
await runInstallScript(target);
|
|
2623
2851
|
console.log(" platform configs applied");
|
|
2624
2852
|
const canonicalDir = join6(homedir3(), ".ijfw");
|
|
@@ -2659,5 +2887,6 @@ if (isDirectRun()) {
|
|
|
2659
2887
|
}
|
|
2660
2888
|
export {
|
|
2661
2889
|
findBash,
|
|
2890
|
+
looksLikeIjfwInstall,
|
|
2662
2891
|
resolveBranchOrTag
|
|
2663
2892
|
};
|