@bigbrainforge/setup 3.19.0 → 3.21.0
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/setup.cjs +207 -81
- package/package.json +2 -2
package/dist/setup.cjs
CHANGED
|
@@ -8,6 +8,28 @@ var __commonJS = (cb, mod) => function __require() {
|
|
|
8
8
|
}
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
// ../forge-plugin/lib/paste-command.js
|
|
12
|
+
var require_paste_command = __commonJS({
|
|
13
|
+
"../forge-plugin/lib/paste-command.js"(exports2, module2) {
|
|
14
|
+
function pasteCommand(alias, subcommand = "set") {
|
|
15
|
+
if (process.env.FORGE_DIST_BIN === "1") {
|
|
16
|
+
return `npx forge secrets ${subcommand} ${alias}`;
|
|
17
|
+
}
|
|
18
|
+
const path2 = require("node:path");
|
|
19
|
+
const scriptAbs = path2.resolve(__dirname, "..", "scripts", "secrets.js");
|
|
20
|
+
return `node "${scriptAbs}" ${subcommand} ${alias}`;
|
|
21
|
+
}
|
|
22
|
+
function pasteCommandIfAvailable(alias, subcommand = "set") {
|
|
23
|
+
if (process.env.FORGE_DIST_BIN === "1") return pasteCommand(alias, subcommand);
|
|
24
|
+
const fs = require("node:fs");
|
|
25
|
+
const path2 = require("node:path");
|
|
26
|
+
const scriptAbs = path2.resolve(__dirname, "..", "scripts", "secrets.js");
|
|
27
|
+
return fs.existsSync(scriptAbs) ? pasteCommand(alias, subcommand) : null;
|
|
28
|
+
}
|
|
29
|
+
module2.exports = { pasteCommand, pasteCommandIfAvailable };
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
11
33
|
// ../forge-plugin/lib/rename-with-retry.js
|
|
12
34
|
var require_rename_with_retry = __commonJS({
|
|
13
35
|
"../forge-plugin/lib/rename-with-retry.js"(exports2, module2) {
|
|
@@ -86,6 +108,7 @@ var require_license = __commonJS({
|
|
|
86
108
|
var fs = require("node:fs");
|
|
87
109
|
var os2 = require("node:os");
|
|
88
110
|
var path2 = require("node:path");
|
|
111
|
+
var { pasteCommandIfAvailable } = require_paste_command();
|
|
89
112
|
var { renameWithRetry } = require_rename_with_retry();
|
|
90
113
|
var LICENSE_RE2 = /^forge_lic_[0-9A-Za-z]{43}$/;
|
|
91
114
|
function licensePath(io = {}) {
|
|
@@ -106,7 +129,8 @@ var require_license = __commonJS({
|
|
|
106
129
|
return raw !== null && LICENSE_RE2.test(raw);
|
|
107
130
|
}
|
|
108
131
|
function missingHint() {
|
|
109
|
-
|
|
132
|
+
const paste = pasteCommandIfAvailable("license");
|
|
133
|
+
return "no Forge license on this machine \u2014 run /forge:setup license" + (paste ? `, or paste it in your own terminal with: ${paste}` : "");
|
|
110
134
|
}
|
|
111
135
|
function resolveLicense(io = {}) {
|
|
112
136
|
const env = io.env ?? process.env;
|
|
@@ -189,6 +213,21 @@ var require_license = __commonJS({
|
|
|
189
213
|
}
|
|
190
214
|
});
|
|
191
215
|
|
|
216
|
+
// ../forge-plugin/lib/sanitize-terminal.js
|
|
217
|
+
var require_sanitize_terminal = __commonJS({
|
|
218
|
+
"../forge-plugin/lib/sanitize-terminal.js"(exports2, module2) {
|
|
219
|
+
var TERMINAL_CONTROL_RE = (
|
|
220
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: matching control characters is the entire point.
|
|
221
|
+
/[\u0000-\u001F\u007F-\u009F\u00AD\u061C\u200B-\u200F\u2028-\u202E\u2060-\u2064\u2066-\u2069\uFEFF]/g
|
|
222
|
+
);
|
|
223
|
+
function sanitizeForTerminal2(value) {
|
|
224
|
+
const text = value === null || value === void 0 ? "" : String(value);
|
|
225
|
+
return text.replace(TERMINAL_CONTROL_RE, "");
|
|
226
|
+
}
|
|
227
|
+
module2.exports = { sanitizeForTerminal: sanitizeForTerminal2 };
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
192
231
|
// ../forge-plugin/lib/discover-instance.js
|
|
193
232
|
var require_discover_instance = __commonJS({
|
|
194
233
|
"../forge-plugin/lib/discover-instance.js"(exports2, module2) {
|
|
@@ -196,6 +235,14 @@ var require_discover_instance = __commonJS({
|
|
|
196
235
|
var os2 = require("node:os");
|
|
197
236
|
var path2 = require("node:path");
|
|
198
237
|
var { resolveLicense } = require_license();
|
|
238
|
+
var { sanitizeForTerminal: sanitizeForTerminal2 } = require_sanitize_terminal();
|
|
239
|
+
function failure(reason, hint, extra = {}) {
|
|
240
|
+
const cleaned = {};
|
|
241
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
242
|
+
cleaned[key] = typeof value === "string" ? sanitizeForTerminal2(value) : value;
|
|
243
|
+
}
|
|
244
|
+
return { ok: false, reason, hint: sanitizeForTerminal2(hint), ...cleaned };
|
|
245
|
+
}
|
|
199
246
|
var DEFAULT_LICENSING_URL2 = "https://licensing.bigbrainforge.com";
|
|
200
247
|
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
|
|
201
248
|
function isSecureUrl2(value) {
|
|
@@ -215,17 +262,12 @@ var require_discover_instance = __commonJS({
|
|
|
215
262
|
const parsed = new URL(base);
|
|
216
263
|
scheme = `${parsed.protocol}//${parsed.hostname}`;
|
|
217
264
|
} catch {
|
|
218
|
-
return {
|
|
219
|
-
ok: false,
|
|
220
|
-
reason: "insecure_licensing_url",
|
|
221
|
-
hint: `FORGE_LICENSING_URL is not a valid URL: ${base}`
|
|
222
|
-
};
|
|
265
|
+
return failure("insecure_licensing_url", `FORGE_LICENSING_URL is not a valid URL: ${base}`);
|
|
223
266
|
}
|
|
224
|
-
return
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
};
|
|
267
|
+
return failure(
|
|
268
|
+
"insecure_licensing_url",
|
|
269
|
+
`FORGE_LICENSING_URL must use https:// (got ${scheme}). Plain http:// is allowed only for loopback (127.0.0.1, localhost, ::1) \u2014 refusing to send the license credential in cleartext.`
|
|
270
|
+
);
|
|
229
271
|
}
|
|
230
272
|
function pluginConfigPath(io = {}) {
|
|
231
273
|
const env = io.env ?? process.env;
|
|
@@ -235,7 +277,8 @@ var require_discover_instance = __commonJS({
|
|
|
235
277
|
function readPinnedUrl(io) {
|
|
236
278
|
try {
|
|
237
279
|
const config = JSON.parse(fs.readFileSync(pluginConfigPath(io), "utf8"));
|
|
238
|
-
|
|
280
|
+
const pinned = typeof config.mcp_url === "string" && config.mcp_url.length > 0 ? config.mcp_url : null;
|
|
281
|
+
return pinned === null ? null : sanitizeForTerminal2(pinned);
|
|
239
282
|
} catch {
|
|
240
283
|
return null;
|
|
241
284
|
}
|
|
@@ -276,11 +319,10 @@ var require_discover_instance = __commonJS({
|
|
|
276
319
|
signal: AbortSignal.timeout(15e3)
|
|
277
320
|
});
|
|
278
321
|
} catch {
|
|
279
|
-
return
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
};
|
|
322
|
+
return failure(
|
|
323
|
+
"discovery_unreachable",
|
|
324
|
+
`licensing service unreachable \u2014 check network egress to ${base}`
|
|
325
|
+
);
|
|
284
326
|
}
|
|
285
327
|
let body = {};
|
|
286
328
|
try {
|
|
@@ -289,43 +331,39 @@ var require_discover_instance = __commonJS({
|
|
|
289
331
|
body = {};
|
|
290
332
|
}
|
|
291
333
|
if (response.status === 401) {
|
|
292
|
-
return
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
};
|
|
334
|
+
return failure(
|
|
335
|
+
"license_invalid",
|
|
336
|
+
body.error || "unknown or revoked license \u2014 contact your org admin"
|
|
337
|
+
);
|
|
297
338
|
}
|
|
298
339
|
if (!response.ok || typeof body.instance_url !== "string" || body.instance_url.length === 0) {
|
|
299
|
-
return
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
};
|
|
340
|
+
return failure(
|
|
341
|
+
"discovery_unreachable",
|
|
342
|
+
`discovery failed (HTTP ${response.status}) at ${base}/license/discover`
|
|
343
|
+
);
|
|
304
344
|
}
|
|
305
345
|
return {
|
|
306
346
|
ok: true,
|
|
307
|
-
instance_url: body.instance_url.replace(/\/$/, ""),
|
|
308
|
-
deployment_model: body.deployment_model
|
|
347
|
+
instance_url: sanitizeForTerminal2(body.instance_url).replace(/\/$/, ""),
|
|
348
|
+
deployment_model: body.deployment_model === null || body.deployment_model === void 0 ? null : sanitizeForTerminal2(body.deployment_model)
|
|
309
349
|
};
|
|
310
350
|
}
|
|
311
351
|
function checkPinConflict(pinned, discovered, acceptNewInstance) {
|
|
312
352
|
if (!pinned || sameHost(pinned, discovered) || acceptNewInstance === true) {
|
|
313
353
|
return null;
|
|
314
354
|
}
|
|
315
|
-
return
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
pinned_url: pinned,
|
|
319
|
-
|
|
320
|
-
hint: `discovery returned ${discovered} but this machine is pinned to ${pinned}. Refusing to release the license to a new host. If the change is expected (your org migrated instances), re-run with --accept-new-instance.`
|
|
321
|
-
};
|
|
355
|
+
return failure(
|
|
356
|
+
"pin_conflict",
|
|
357
|
+
`discovery returned ${discovered} but this machine is pinned to ${pinned}. Refusing to release the license to a new host. If the change is expected (your org migrated instances), re-run with --accept-new-instance.`,
|
|
358
|
+
{ pinned_url: pinned, discovered_url: discovered }
|
|
359
|
+
);
|
|
322
360
|
}
|
|
323
361
|
async function discoverInstance2(io = {}) {
|
|
324
362
|
const env = io.env ?? process.env;
|
|
325
363
|
const doFetch = io.fetchImpl ?? fetch;
|
|
326
364
|
const license = resolveLicense(io);
|
|
327
365
|
if (license.value === null) {
|
|
328
|
-
return
|
|
366
|
+
return failure("no_license", license.hint);
|
|
329
367
|
}
|
|
330
368
|
const pinned = readPinnedUrl(io);
|
|
331
369
|
if (pinned && io.forceDiscovery !== true) {
|
|
@@ -342,11 +380,10 @@ var require_discover_instance = __commonJS({
|
|
|
342
380
|
}
|
|
343
381
|
const { instance_url: discovered, deployment_model: deploymentModel } = discovery;
|
|
344
382
|
if (!isSecureUrl2(discovered)) {
|
|
345
|
-
return
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
};
|
|
383
|
+
return failure(
|
|
384
|
+
"insecure_instance_url",
|
|
385
|
+
`discovery returned an insecure instance_url (${discovered}) \u2014 refusing to pin it. The instance must be served over https:// (plain http:// is allowed only for loopback), so the license is never exchanged over a cleartext connection.`
|
|
386
|
+
);
|
|
350
387
|
}
|
|
351
388
|
const conflict = checkPinConflict(pinned, discovered, io.acceptNewInstance);
|
|
352
389
|
if (conflict) {
|
|
@@ -355,7 +392,7 @@ var require_discover_instance = __commonJS({
|
|
|
355
392
|
if (!pinned || !sameHost(pinned, discovered)) {
|
|
356
393
|
const pinResult = pinInstanceUrl(io, discovered);
|
|
357
394
|
if (!pinResult.ok) {
|
|
358
|
-
return
|
|
395
|
+
return failure("discovery_unreachable", pinResult.error);
|
|
359
396
|
}
|
|
360
397
|
}
|
|
361
398
|
return {
|
|
@@ -436,6 +473,27 @@ var require_npmrc_license = __commonJS({
|
|
|
436
473
|
const status = foreignScope ? "declined" : changed ? "seeded" : "already";
|
|
437
474
|
return { content: body, changed, replacedLegacy, foreignScope, foreignScopeTarget, status };
|
|
438
475
|
}
|
|
476
|
+
function inspectNpmrcLicense(content) {
|
|
477
|
+
const src = String(content ?? "");
|
|
478
|
+
let scopeActive = false;
|
|
479
|
+
let authActive = false;
|
|
480
|
+
let foreignScopeTarget = null;
|
|
481
|
+
for (const raw of src.split("\n")) {
|
|
482
|
+
const trimmed = raw.trim();
|
|
483
|
+
if (trimmed.length === 0 || isCommentLine(trimmed)) continue;
|
|
484
|
+
if (trimmed === SCOPE_LINE) {
|
|
485
|
+
scopeActive = true;
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
if (trimmed.startsWith(`${DIST_AUTH_KEY}:_authToken=`)) {
|
|
489
|
+
if (trimmed.length > `${DIST_AUTH_KEY}:_authToken=`.length) authActive = true;
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
const foreignTarget = foreignScopeTargetOf(trimmed);
|
|
493
|
+
if (foreignTarget !== null && !ownsLine(trimmed)) foreignScopeTarget = foreignTarget;
|
|
494
|
+
}
|
|
495
|
+
return { seeded: scopeActive && authActive, foreignScopeTarget };
|
|
496
|
+
}
|
|
439
497
|
function writeNpmrcLicense2(license, io) {
|
|
440
498
|
const target = path2.join(io.homedir, ".npmrc");
|
|
441
499
|
let existing = "";
|
|
@@ -453,7 +511,14 @@ var require_npmrc_license = __commonJS({
|
|
|
453
511
|
}
|
|
454
512
|
return { ...result, path: target };
|
|
455
513
|
}
|
|
456
|
-
module2.exports = {
|
|
514
|
+
module2.exports = {
|
|
515
|
+
applyNpmrcLicense,
|
|
516
|
+
inspectNpmrcLicense,
|
|
517
|
+
writeNpmrcLicense: writeNpmrcLicense2,
|
|
518
|
+
SCOPE_LINE,
|
|
519
|
+
authLine,
|
|
520
|
+
DIST_HOST
|
|
521
|
+
};
|
|
457
522
|
}
|
|
458
523
|
});
|
|
459
524
|
|
|
@@ -911,6 +976,22 @@ var require_exec_cli = __commonJS({
|
|
|
911
976
|
}
|
|
912
977
|
});
|
|
913
978
|
|
|
979
|
+
// ../forge-plugin/lib/prompt-abort.js
|
|
980
|
+
var require_prompt_abort = __commonJS({
|
|
981
|
+
"../forge-plugin/lib/prompt-abort.js"(exports2, module2) {
|
|
982
|
+
var PROMPT_ABORTED = "PROMPT_ABORTED";
|
|
983
|
+
function promptAbortError2(what = "prompt") {
|
|
984
|
+
const err = new Error(`${what} aborted (Ctrl-C or end of input).`);
|
|
985
|
+
err.code = PROMPT_ABORTED;
|
|
986
|
+
return err;
|
|
987
|
+
}
|
|
988
|
+
function isPromptAbort2(err) {
|
|
989
|
+
return Boolean(err) && err.code === PROMPT_ABORTED;
|
|
990
|
+
}
|
|
991
|
+
module2.exports = { PROMPT_ABORTED, promptAbortError: promptAbortError2, isPromptAbort: isPromptAbort2 };
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
|
|
914
995
|
// src/setup.js
|
|
915
996
|
var os = require("node:os");
|
|
916
997
|
var path = require("node:path");
|
|
@@ -924,6 +1005,8 @@ var {
|
|
|
924
1005
|
assertSafeVersion
|
|
925
1006
|
} = require_plugin_bundle();
|
|
926
1007
|
var { resolveSpawn } = require_exec_cli();
|
|
1008
|
+
var { sanitizeForTerminal } = require_sanitize_terminal();
|
|
1009
|
+
var { isPromptAbort, promptAbortError } = require_prompt_abort();
|
|
927
1010
|
var LICENSE_RE = /^forge_lic_[0-9A-Za-z]{43}$/;
|
|
928
1011
|
var DEFAULT_LICENSING_URL = "https://licensing.bigbrainforge.com";
|
|
929
1012
|
var DEFAULT_DIST_URL = "https://licensing.bigbrainforge.com";
|
|
@@ -937,26 +1020,52 @@ function runShim(io, cmd, args) {
|
|
|
937
1020
|
function errMessage(err) {
|
|
938
1021
|
return err && err.message ? err.message : String(err);
|
|
939
1022
|
}
|
|
1023
|
+
async function promptHidden(q, { input = process.stdin, output = process.stdout } = {}) {
|
|
1024
|
+
const readline = require("node:readline");
|
|
1025
|
+
return new Promise((res, rej) => {
|
|
1026
|
+
const rl = readline.createInterface({ input, output, terminal: true });
|
|
1027
|
+
let settled = false;
|
|
1028
|
+
rl.on("close", () => {
|
|
1029
|
+
if (settled) return;
|
|
1030
|
+
settled = true;
|
|
1031
|
+
output.write("\n");
|
|
1032
|
+
rej(promptAbortError("license prompt"));
|
|
1033
|
+
});
|
|
1034
|
+
const orig = rl._writeToOutput.bind(rl);
|
|
1035
|
+
const columnsAt = (k) => {
|
|
1036
|
+
const saved = rl.cursor;
|
|
1037
|
+
rl.cursor = k;
|
|
1038
|
+
const pos = rl.getCursorPos();
|
|
1039
|
+
rl.cursor = saved;
|
|
1040
|
+
return pos.rows === 0 ? pos.cols : pos.rows * rl.columns + pos.cols;
|
|
1041
|
+
};
|
|
1042
|
+
rl._writeToOutput = (s) => {
|
|
1043
|
+
if (s.includes("\n") || s.includes("\r")) return orig("");
|
|
1044
|
+
if (s.startsWith(q)) {
|
|
1045
|
+
return orig(q + "*".repeat(Math.max(0, columnsAt(rl.line.length) - columnsAt(0))));
|
|
1046
|
+
}
|
|
1047
|
+
const end = rl.cursor;
|
|
1048
|
+
const start = Math.max(0, end - s.length);
|
|
1049
|
+
if (rl.line.slice(start, end) === s) {
|
|
1050
|
+
return orig("*".repeat(Math.max(0, columnsAt(end) - columnsAt(start))));
|
|
1051
|
+
}
|
|
1052
|
+
return orig(s.replace(/[^ ]/g, "*"));
|
|
1053
|
+
};
|
|
1054
|
+
rl.question(q, (a) => {
|
|
1055
|
+
settled = true;
|
|
1056
|
+
rl.close();
|
|
1057
|
+
output.write("\n");
|
|
1058
|
+
res(a.trim());
|
|
1059
|
+
});
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
940
1062
|
function defaultIo() {
|
|
941
1063
|
return {
|
|
942
1064
|
homedir: os.homedir(),
|
|
943
1065
|
env: process.env,
|
|
944
1066
|
print: (l) => process.stdout.write(`${l}
|
|
945
1067
|
`),
|
|
946
|
-
promptHidden:
|
|
947
|
-
const readline = require("node:readline");
|
|
948
|
-
return new Promise((res) => {
|
|
949
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
950
|
-
const orig = rl._writeToOutput.bind(rl);
|
|
951
|
-
rl._writeToOutput = (s) => s.includes("\n") || s === q ? orig(s) : orig("");
|
|
952
|
-
process.stdout.write(q);
|
|
953
|
-
rl.question("", (a) => {
|
|
954
|
-
rl.close();
|
|
955
|
-
process.stdout.write("\n");
|
|
956
|
-
res(a.trim());
|
|
957
|
-
});
|
|
958
|
-
});
|
|
959
|
-
},
|
|
1068
|
+
promptHidden: (q) => promptHidden(q),
|
|
960
1069
|
fetch: (url, opts) => globalThis.fetch(url, opts),
|
|
961
1070
|
run: (cmd, args = [], opts = {}) => {
|
|
962
1071
|
const { stdio, ...rest } = opts;
|
|
@@ -969,15 +1078,19 @@ function defaultIo() {
|
|
|
969
1078
|
}
|
|
970
1079
|
};
|
|
971
1080
|
}
|
|
1081
|
+
function say(io, line) {
|
|
1082
|
+
io.print(sanitizeForTerminal(line));
|
|
1083
|
+
}
|
|
972
1084
|
function fail(io, tier, message, fix) {
|
|
973
|
-
io
|
|
974
|
-
io
|
|
975
|
-
if (fix) io
|
|
1085
|
+
say(io, "");
|
|
1086
|
+
say(io, `\u2717 [${tier}] ${message}`);
|
|
1087
|
+
if (fix) say(io, ` fix: ${fix}`);
|
|
976
1088
|
return 1;
|
|
977
1089
|
}
|
|
978
1090
|
async function runSetup(argv, io = defaultIo()) {
|
|
979
1091
|
const args = argv.filter((a) => !a.startsWith("--"));
|
|
980
1092
|
const acceptNewInstance = argv.includes("--accept-new-instance");
|
|
1093
|
+
const print = (line) => say(io, line);
|
|
981
1094
|
const tarProbe = io.run("tar", ["--version"]);
|
|
982
1095
|
if (tarProbe.error || tarProbe.status !== 0) {
|
|
983
1096
|
return fail(
|
|
@@ -988,7 +1101,19 @@ async function runSetup(argv, io = defaultIo()) {
|
|
|
988
1101
|
);
|
|
989
1102
|
}
|
|
990
1103
|
let license = args[0] ?? "";
|
|
991
|
-
if (!license)
|
|
1104
|
+
if (!license) {
|
|
1105
|
+
try {
|
|
1106
|
+
license = await io.promptHidden("Paste your Forge license: ");
|
|
1107
|
+
} catch (err) {
|
|
1108
|
+
if (!isPromptAbort(err)) throw err;
|
|
1109
|
+
return fail(
|
|
1110
|
+
io,
|
|
1111
|
+
"credential",
|
|
1112
|
+
"setup was cancelled at the license prompt \u2014 nothing on this machine was changed",
|
|
1113
|
+
`rerun when you have the license to hand: ${RERUN_SETUP_CMD}`
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
992
1117
|
if (!LICENSE_RE.test(license)) {
|
|
993
1118
|
return fail(
|
|
994
1119
|
io,
|
|
@@ -1006,11 +1131,12 @@ async function runSetup(argv, io = defaultIo()) {
|
|
|
1006
1131
|
`fix the named problem (usually permissions on ~/.claude/forge), then rerun: ${RERUN_SETUP_CMD}`
|
|
1007
1132
|
);
|
|
1008
1133
|
}
|
|
1009
|
-
|
|
1134
|
+
print(`\u2713 license stored (${wrote.path})`);
|
|
1010
1135
|
const licensingUrl = io.env.FORGE_LICENSING_URL || DEFAULT_LICENSING_URL;
|
|
1011
1136
|
const discoveryFetch = async (url, opts) => {
|
|
1012
1137
|
const res = await io.fetch(url, opts);
|
|
1013
|
-
|
|
1138
|
+
if (res.ok !== void 0) return res;
|
|
1139
|
+
return { ...res, ok: res.status >= 200 && res.status < 300 };
|
|
1014
1140
|
};
|
|
1015
1141
|
const discovered = await discoverInstance({
|
|
1016
1142
|
homedir: io.homedir,
|
|
@@ -1034,7 +1160,7 @@ async function runSetup(argv, io = defaultIo()) {
|
|
|
1034
1160
|
) : `check network egress to ${licensingUrl} (it must be on your allowlist)`
|
|
1035
1161
|
);
|
|
1036
1162
|
}
|
|
1037
|
-
|
|
1163
|
+
print(`\u2713 instance pinned: ${discovered.instance_url} (${discovered.deployment_model})`);
|
|
1038
1164
|
let npmrc;
|
|
1039
1165
|
try {
|
|
1040
1166
|
npmrc = writeNpmrcLicense(license, { homedir: io.homedir, env: io.env });
|
|
@@ -1048,11 +1174,11 @@ async function runSetup(argv, io = defaultIo()) {
|
|
|
1048
1174
|
}
|
|
1049
1175
|
if (npmrc.status === "declined") {
|
|
1050
1176
|
const legacyNote = npmrc.replacedLegacy ? " (a legacy npm.pkg.github.com credential line found alongside it was removed)" : "";
|
|
1051
|
-
|
|
1177
|
+
print(
|
|
1052
1178
|
npmrc.foreignScopeTarget === "" ? `! npm NOT seeded \u2014 ~/.npmrc has a malformed @bigbrainforge:registry= line (no value). Fix or remove that line, then rerun setup; otherwise no action is needed.${legacyNote}` : `! npm NOT seeded \u2014 @bigbrainforge:registry already points at ${npmrc.foreignScopeTarget} (an enterprise proxy or other custom config). If that isn't your organization's proxy, remove that line from ~/.npmrc and rerun setup; otherwise no action is needed.${legacyNote}`
|
|
1053
1179
|
);
|
|
1054
1180
|
} else {
|
|
1055
|
-
|
|
1181
|
+
print(
|
|
1056
1182
|
npmrc.status === "seeded" ? `\u2713 npm seeded (${npmrc.path})${npmrc.replacedLegacy ? " \u2014 legacy GitHub Packages lines replaced" : ""}` : `\u2713 npm already seeded (${npmrc.path}) \u2014 no change needed`
|
|
1057
1183
|
);
|
|
1058
1184
|
}
|
|
@@ -1148,7 +1274,7 @@ async function runSetup(argv, io = defaultIo()) {
|
|
|
1148
1274
|
`the download may be corrupted or tampered \u2014 rerun to retry: ${RERUN_SETUP_CMD} (if it persists, contact your Forge administrator)`
|
|
1149
1275
|
);
|
|
1150
1276
|
}
|
|
1151
|
-
|
|
1277
|
+
print(`\u2713 plugin bundle ${manifest.version} verified (sha256) and staged at ${staged.target}`);
|
|
1152
1278
|
const root = bundleRoot(io.homedir);
|
|
1153
1279
|
const addRes = runShim(io, "claude", ["plugin", "marketplace", "add", root]);
|
|
1154
1280
|
const installRes = addRes.status === 0 ? runShim(io, "claude", ["plugin", "install", "forge@forge"]) : addRes;
|
|
@@ -1157,19 +1283,19 @@ async function runSetup(argv, io = defaultIo()) {
|
|
|
1157
1283
|
${installRes.stderr || ""}`
|
|
1158
1284
|
);
|
|
1159
1285
|
if (addRes.status === 0 && installRes.status === 0) {
|
|
1160
|
-
|
|
1286
|
+
print("\u2713 plugin installed from the local Forge marketplace");
|
|
1161
1287
|
} else if (alreadyRegistered) {
|
|
1162
|
-
|
|
1288
|
+
print("\u2713 plugin already installed from the local Forge marketplace \u2014 nothing to do");
|
|
1163
1289
|
} else {
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1290
|
+
print("");
|
|
1291
|
+
print("! Claude Code CLI not reachable from this shell \u2014 run these two commands yourself:");
|
|
1292
|
+
print("");
|
|
1293
|
+
print(` claude plugin marketplace add "${root}"`);
|
|
1294
|
+
print(" claude plugin install forge@forge");
|
|
1295
|
+
print("");
|
|
1170
1296
|
}
|
|
1171
1297
|
const bundledBootstrap = path.join(staged.target, "scripts", "bootstrap.js");
|
|
1172
|
-
|
|
1298
|
+
print("Handing off to the Forge plugin bootstrap (server check + issue tracker)\u2026");
|
|
1173
1299
|
const hand = io.run(process.execPath, [bundledBootstrap], { stdio: "inherit" });
|
|
1174
1300
|
if (hand.status !== 0) {
|
|
1175
1301
|
return fail(
|
|
@@ -1179,15 +1305,15 @@ ${installRes.stderr || ""}`
|
|
|
1179
1305
|
`rerun it yourself: node "${bundledBootstrap}"`
|
|
1180
1306
|
);
|
|
1181
1307
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1308
|
+
print("");
|
|
1309
|
+
print("Done. Open Claude Code \u2014 Forge is ready.");
|
|
1184
1310
|
return 0;
|
|
1185
1311
|
}
|
|
1186
|
-
module.exports = { runSetup, LICENSE_RE, defaultIo, RERUN_SETUP_CMD };
|
|
1312
|
+
module.exports = { runSetup, LICENSE_RE, defaultIo, promptHidden, RERUN_SETUP_CMD };
|
|
1187
1313
|
if (require.main === module) {
|
|
1188
1314
|
runSetup(process.argv.slice(2)).then((code) => process.exit(code)).catch((err) => {
|
|
1189
1315
|
process.stderr.write(`
|
|
1190
|
-
\u2717 [unexpected] ${errMessage(err)}
|
|
1316
|
+
\u2717 [unexpected] ${sanitizeForTerminal(errMessage(err))}
|
|
1191
1317
|
`);
|
|
1192
1318
|
process.stderr.write(
|
|
1193
1319
|
" fix: this is unexpected \u2014 please report it (including this message) to your Forge administrator\n"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bigbrainforge/setup",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.21.0",
|
|
4
4
|
"description": "Forge workstation setup — paste one license, get a working Forge install.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"homepage": "https://github.com/bigbrainforge/forge",
|
|
@@ -12,5 +12,5 @@
|
|
|
12
12
|
},
|
|
13
13
|
"engines": { "node": ">=24.18.1", "pnpm": ">=11.0.0" },
|
|
14
14
|
"publishConfig": { "registry": "https://registry.npmjs.org", "access": "public" },
|
|
15
|
-
"devDependencies": { "esbuild": "0.28.
|
|
15
|
+
"devDependencies": { "esbuild": "0.28.2" }
|
|
16
16
|
}
|