theforge-cli 0.2.1 → 0.2.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/CHANGELOG.md +11 -0
- package/dist/index.js +482 -414
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -95,7 +95,7 @@ var require_windows = __commonJS({
|
|
|
95
95
|
module.exports = isexe;
|
|
96
96
|
isexe.sync = sync;
|
|
97
97
|
var fs2 = __require("fs");
|
|
98
|
-
function checkPathExt(
|
|
98
|
+
function checkPathExt(path53, options) {
|
|
99
99
|
var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
|
|
100
100
|
if (!pathext) {
|
|
101
101
|
return true;
|
|
@@ -106,25 +106,25 @@ var require_windows = __commonJS({
|
|
|
106
106
|
}
|
|
107
107
|
for (var i3 = 0; i3 < pathext.length; i3++) {
|
|
108
108
|
var p = pathext[i3].toLowerCase();
|
|
109
|
-
if (p &&
|
|
109
|
+
if (p && path53.substr(-p.length).toLowerCase() === p) {
|
|
110
110
|
return true;
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
return false;
|
|
114
114
|
}
|
|
115
|
-
function checkStat(stat,
|
|
115
|
+
function checkStat(stat, path53, options) {
|
|
116
116
|
if (!stat.isSymbolicLink() && !stat.isFile()) {
|
|
117
117
|
return false;
|
|
118
118
|
}
|
|
119
|
-
return checkPathExt(
|
|
119
|
+
return checkPathExt(path53, options);
|
|
120
120
|
}
|
|
121
|
-
function isexe(
|
|
122
|
-
fs2.stat(
|
|
123
|
-
cb(er, er ? false : checkStat(stat,
|
|
121
|
+
function isexe(path53, options, cb) {
|
|
122
|
+
fs2.stat(path53, function(er, stat) {
|
|
123
|
+
cb(er, er ? false : checkStat(stat, path53, options));
|
|
124
124
|
});
|
|
125
125
|
}
|
|
126
|
-
function sync(
|
|
127
|
-
return checkStat(fs2.statSync(
|
|
126
|
+
function sync(path53, options) {
|
|
127
|
+
return checkStat(fs2.statSync(path53), path53, options);
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
});
|
|
@@ -136,13 +136,13 @@ var require_mode = __commonJS({
|
|
|
136
136
|
module.exports = isexe;
|
|
137
137
|
isexe.sync = sync;
|
|
138
138
|
var fs2 = __require("fs");
|
|
139
|
-
function isexe(
|
|
140
|
-
fs2.stat(
|
|
139
|
+
function isexe(path53, options, cb) {
|
|
140
|
+
fs2.stat(path53, function(er, stat) {
|
|
141
141
|
cb(er, er ? false : checkStat(stat, options));
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
|
-
function sync(
|
|
145
|
-
return checkStat(fs2.statSync(
|
|
144
|
+
function sync(path53, options) {
|
|
145
|
+
return checkStat(fs2.statSync(path53), options);
|
|
146
146
|
}
|
|
147
147
|
function checkStat(stat, options) {
|
|
148
148
|
return stat.isFile() && checkMode(stat, options);
|
|
@@ -176,7 +176,7 @@ var require_isexe = __commonJS({
|
|
|
176
176
|
}
|
|
177
177
|
module.exports = isexe;
|
|
178
178
|
isexe.sync = sync;
|
|
179
|
-
function isexe(
|
|
179
|
+
function isexe(path53, options, cb) {
|
|
180
180
|
if (typeof options === "function") {
|
|
181
181
|
cb = options;
|
|
182
182
|
options = {};
|
|
@@ -186,7 +186,7 @@ var require_isexe = __commonJS({
|
|
|
186
186
|
throw new TypeError("callback not provided");
|
|
187
187
|
}
|
|
188
188
|
return new Promise(function(resolve, reject) {
|
|
189
|
-
isexe(
|
|
189
|
+
isexe(path53, options || {}, function(er, is) {
|
|
190
190
|
if (er) {
|
|
191
191
|
reject(er);
|
|
192
192
|
} else {
|
|
@@ -195,7 +195,7 @@ var require_isexe = __commonJS({
|
|
|
195
195
|
});
|
|
196
196
|
});
|
|
197
197
|
}
|
|
198
|
-
core(
|
|
198
|
+
core(path53, options || {}, function(er, is) {
|
|
199
199
|
if (er) {
|
|
200
200
|
if (er.code === "EACCES" || options && options.ignoreErrors) {
|
|
201
201
|
er = null;
|
|
@@ -205,9 +205,9 @@ var require_isexe = __commonJS({
|
|
|
205
205
|
cb(er, is);
|
|
206
206
|
});
|
|
207
207
|
}
|
|
208
|
-
function sync(
|
|
208
|
+
function sync(path53, options) {
|
|
209
209
|
try {
|
|
210
|
-
return core.sync(
|
|
210
|
+
return core.sync(path53, options || {});
|
|
211
211
|
} catch (er) {
|
|
212
212
|
if (options && options.ignoreErrors || er.code === "EACCES") {
|
|
213
213
|
return false;
|
|
@@ -224,7 +224,7 @@ var require_which = __commonJS({
|
|
|
224
224
|
"node_modules/which/which.js"(exports, module) {
|
|
225
225
|
"use strict";
|
|
226
226
|
var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
|
|
227
|
-
var
|
|
227
|
+
var path53 = __require("path");
|
|
228
228
|
var COLON = isWindows ? ";" : ":";
|
|
229
229
|
var isexe = require_isexe();
|
|
230
230
|
var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
|
|
@@ -262,7 +262,7 @@ var require_which = __commonJS({
|
|
|
262
262
|
return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
|
|
263
263
|
const ppRaw = pathEnv[i3];
|
|
264
264
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
265
|
-
const pCmd =
|
|
265
|
+
const pCmd = path53.join(pathPart, cmd);
|
|
266
266
|
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
267
267
|
resolve(subStep(p, i3, 0));
|
|
268
268
|
});
|
|
@@ -289,7 +289,7 @@ var require_which = __commonJS({
|
|
|
289
289
|
for (let i3 = 0; i3 < pathEnv.length; i3++) {
|
|
290
290
|
const ppRaw = pathEnv[i3];
|
|
291
291
|
const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
|
|
292
|
-
const pCmd =
|
|
292
|
+
const pCmd = path53.join(pathPart, cmd);
|
|
293
293
|
const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
|
|
294
294
|
for (let j = 0; j < pathExt.length; j++) {
|
|
295
295
|
const cur = p + pathExt[j];
|
|
@@ -337,7 +337,7 @@ var require_path_key = __commonJS({
|
|
|
337
337
|
var require_resolveCommand = __commonJS({
|
|
338
338
|
"node_modules/cross-spawn/lib/util/resolveCommand.js"(exports, module) {
|
|
339
339
|
"use strict";
|
|
340
|
-
var
|
|
340
|
+
var path53 = __require("path");
|
|
341
341
|
var which = require_which();
|
|
342
342
|
var getPathKey = require_path_key();
|
|
343
343
|
function resolveCommandAttempt(parsed, withoutPathExt) {
|
|
@@ -355,7 +355,7 @@ var require_resolveCommand = __commonJS({
|
|
|
355
355
|
try {
|
|
356
356
|
resolved = which.sync(parsed.command, {
|
|
357
357
|
path: env[getPathKey({ env })],
|
|
358
|
-
pathExt: withoutPathExt ?
|
|
358
|
+
pathExt: withoutPathExt ? path53.delimiter : void 0
|
|
359
359
|
});
|
|
360
360
|
} catch (e) {
|
|
361
361
|
} finally {
|
|
@@ -364,7 +364,7 @@ var require_resolveCommand = __commonJS({
|
|
|
364
364
|
}
|
|
365
365
|
}
|
|
366
366
|
if (resolved) {
|
|
367
|
-
resolved =
|
|
367
|
+
resolved = path53.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
|
|
368
368
|
}
|
|
369
369
|
return resolved;
|
|
370
370
|
}
|
|
@@ -418,8 +418,8 @@ var require_shebang_command = __commonJS({
|
|
|
418
418
|
if (!match) {
|
|
419
419
|
return null;
|
|
420
420
|
}
|
|
421
|
-
const [
|
|
422
|
-
const binary =
|
|
421
|
+
const [path53, argument] = match[0].replace(/#! ?/, "").split(" ");
|
|
422
|
+
const binary = path53.split("/").pop();
|
|
423
423
|
if (binary === "env") {
|
|
424
424
|
return argument;
|
|
425
425
|
}
|
|
@@ -454,7 +454,7 @@ var require_readShebang = __commonJS({
|
|
|
454
454
|
var require_parse = __commonJS({
|
|
455
455
|
"node_modules/cross-spawn/lib/parse.js"(exports, module) {
|
|
456
456
|
"use strict";
|
|
457
|
-
var
|
|
457
|
+
var path53 = __require("path");
|
|
458
458
|
var resolveCommand2 = require_resolveCommand();
|
|
459
459
|
var escape = require_escape();
|
|
460
460
|
var readShebang = require_readShebang();
|
|
@@ -479,7 +479,7 @@ var require_parse = __commonJS({
|
|
|
479
479
|
const needsShell = !isExecutableRegExp.test(commandFile);
|
|
480
480
|
if (parsed.options.forceShell || needsShell) {
|
|
481
481
|
const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
|
|
482
|
-
parsed.command =
|
|
482
|
+
parsed.command = path53.normalize(parsed.command);
|
|
483
483
|
parsed.command = escape.command(parsed.command);
|
|
484
484
|
parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
|
|
485
485
|
const shellCommand = [parsed.command].concat(parsed.args).join(" ");
|
|
@@ -7069,13 +7069,13 @@ var logOutputSync = ({ serializedResult, fdNumber, state, verboseInfo, encoding,
|
|
|
7069
7069
|
}
|
|
7070
7070
|
};
|
|
7071
7071
|
var writeToFiles = (serializedResult, stdioItems, outputFiles) => {
|
|
7072
|
-
for (const { path:
|
|
7073
|
-
const pathString = typeof
|
|
7072
|
+
for (const { path: path53, append } of stdioItems.filter(({ type }) => FILE_TYPES.has(type))) {
|
|
7073
|
+
const pathString = typeof path53 === "string" ? path53 : path53.toString();
|
|
7074
7074
|
if (append || outputFiles.has(pathString)) {
|
|
7075
|
-
appendFileSync2(
|
|
7075
|
+
appendFileSync2(path53, serializedResult);
|
|
7076
7076
|
} else {
|
|
7077
7077
|
outputFiles.add(pathString);
|
|
7078
|
-
writeFileSync2(
|
|
7078
|
+
writeFileSync2(path53, serializedResult);
|
|
7079
7079
|
}
|
|
7080
7080
|
}
|
|
7081
7081
|
};
|
|
@@ -10380,7 +10380,7 @@ function isNonInteractive(cli) {
|
|
|
10380
10380
|
return cli.all || (cli.only?.length ?? 0) > 0 || cli.retryFailed;
|
|
10381
10381
|
}
|
|
10382
10382
|
var COMMAND_USAGE = {
|
|
10383
|
-
install: "\ninstall <forjas> \u2014
|
|
10383
|
+
install: "\ninstall <forjas> \u2014 PyPI+venv (default) ou git clone + setup (--method git).\n --plan s\xF3 mostra o plano; --ref/--version fixa UMA Forja; --all/--only selecionam.",
|
|
10384
10384
|
update: "\nupdate [forjas] \u2014 pull + setup. --check s\xF3 verifica; --method registry usa o receipt.\n Sem nomes no interativo: oferece as Forjas presentes; --yes exige sele\xE7\xE3o.",
|
|
10385
10385
|
registry: "\nregistry [list|install|lock] \u2014 cat\xE1logo PyPI + receipts.\n registry install <f> --offline --lock <arquivo>: instala\xE7\xE3o travada (--no-index).\n registry lock <f> [--out <arq|dir>]: gera <forja>.offline-lock.json (online, pip>=23).",
|
|
10386
10386
|
mcp: "\nmcp [forja\u2026] \u2014 argv MCP por Forja (clone ou venv registry).\n --verify: handshake real \u2014 n\xEDveis INSTALLED\u2192EXECUTABLE\u2192INITIALIZED\u2192\n TOOLS_DISCOVERED\u2192SAFE_TOOL_CALLED\u2192HOST_VERIFIED. tools/call s\xF3 na allowlist do cat\xE1logo.",
|
|
@@ -14528,26 +14528,26 @@ var ContractValidator = class {
|
|
|
14528
14528
|
}
|
|
14529
14529
|
return this.validate(declared, document);
|
|
14530
14530
|
}
|
|
14531
|
-
check(value, schema,
|
|
14531
|
+
check(value, schema, path53, errs) {
|
|
14532
14532
|
if (typeof schema.$ref === "string") {
|
|
14533
14533
|
const target = schema.$ref;
|
|
14534
14534
|
if (!target.startsWith("forge/")) {
|
|
14535
|
-
errs.push({ code: "CONTRACT-REF", message: `external ref refused: ${target}`, path:
|
|
14535
|
+
errs.push({ code: "CONTRACT-REF", message: `external ref refused: ${target}`, path: path53 });
|
|
14536
14536
|
return;
|
|
14537
14537
|
}
|
|
14538
14538
|
const resolved = this.schemas[target];
|
|
14539
14539
|
if (!resolved) {
|
|
14540
|
-
errs.push({ code: "CONTRACT-UNKNOWN", message: `unknown contract: ${target}`, path:
|
|
14540
|
+
errs.push({ code: "CONTRACT-UNKNOWN", message: `unknown contract: ${target}`, path: path53 });
|
|
14541
14541
|
return;
|
|
14542
14542
|
}
|
|
14543
|
-
this.check(value, resolved,
|
|
14543
|
+
this.check(value, resolved, path53, errs);
|
|
14544
14544
|
return;
|
|
14545
14545
|
}
|
|
14546
14546
|
if ("const" in schema && value !== schema.const) {
|
|
14547
14547
|
errs.push({
|
|
14548
14548
|
code: "CONTRACT-CONST",
|
|
14549
14549
|
message: `expected ${JSON.stringify(schema.const)}`,
|
|
14550
|
-
path:
|
|
14550
|
+
path: path53
|
|
14551
14551
|
});
|
|
14552
14552
|
return;
|
|
14553
14553
|
}
|
|
@@ -14555,7 +14555,7 @@ var ContractValidator = class {
|
|
|
14555
14555
|
errs.push({
|
|
14556
14556
|
code: "CONTRACT-TYPE",
|
|
14557
14557
|
message: `expected type ${JSON.stringify(schema.type)}`,
|
|
14558
|
-
path:
|
|
14558
|
+
path: path53
|
|
14559
14559
|
});
|
|
14560
14560
|
return;
|
|
14561
14561
|
}
|
|
@@ -14563,27 +14563,27 @@ var ContractValidator = class {
|
|
|
14563
14563
|
errs.push({
|
|
14564
14564
|
code: "CONTRACT-ENUM",
|
|
14565
14565
|
message: `value not in ${JSON.stringify(schema.enum)}`,
|
|
14566
|
-
path:
|
|
14566
|
+
path: path53
|
|
14567
14567
|
});
|
|
14568
14568
|
return;
|
|
14569
14569
|
}
|
|
14570
14570
|
if (typeof value === "string") {
|
|
14571
14571
|
if (typeof schema.pattern === "string" && !new RegExp(schema.pattern).test(value)) {
|
|
14572
|
-
errs.push({ code: "CONTRACT-PATTERN", message: `does not match ${schema.pattern}`, path:
|
|
14572
|
+
errs.push({ code: "CONTRACT-PATTERN", message: `does not match ${schema.pattern}`, path: path53 });
|
|
14573
14573
|
}
|
|
14574
14574
|
if (typeof schema.minLength === "number" && value.length < schema.minLength) {
|
|
14575
|
-
errs.push({ code: "CONTRACT-LENGTH", message: "string too short", path:
|
|
14575
|
+
errs.push({ code: "CONTRACT-LENGTH", message: "string too short", path: path53 });
|
|
14576
14576
|
}
|
|
14577
14577
|
if (typeof schema.maxLength === "number" && value.length > schema.maxLength) {
|
|
14578
|
-
errs.push({ code: "CONTRACT-LENGTH", message: "string too long", path:
|
|
14578
|
+
errs.push({ code: "CONTRACT-LENGTH", message: "string too long", path: path53 });
|
|
14579
14579
|
}
|
|
14580
14580
|
}
|
|
14581
14581
|
if (typeof value === "number") {
|
|
14582
14582
|
if (typeof schema.min === "number" && value < schema.min) {
|
|
14583
|
-
errs.push({ code: "CONTRACT-RANGE", message: `< ${schema.min}`, path:
|
|
14583
|
+
errs.push({ code: "CONTRACT-RANGE", message: `< ${schema.min}`, path: path53 });
|
|
14584
14584
|
}
|
|
14585
14585
|
if (typeof schema.max === "number" && value > schema.max) {
|
|
14586
|
-
errs.push({ code: "CONTRACT-RANGE", message: `> ${schema.max}`, path:
|
|
14586
|
+
errs.push({ code: "CONTRACT-RANGE", message: `> ${schema.max}`, path: path53 });
|
|
14587
14587
|
}
|
|
14588
14588
|
}
|
|
14589
14589
|
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
@@ -14593,7 +14593,7 @@ var ContractValidator = class {
|
|
|
14593
14593
|
errs.push({
|
|
14594
14594
|
code: "CONTRACT-REQUIRED",
|
|
14595
14595
|
message: `missing '${req}'`,
|
|
14596
|
-
path: `${
|
|
14596
|
+
path: `${path53}/${req}`
|
|
14597
14597
|
});
|
|
14598
14598
|
}
|
|
14599
14599
|
}
|
|
@@ -14601,25 +14601,25 @@ var ContractValidator = class {
|
|
|
14601
14601
|
const additional = schema.additionalProperties;
|
|
14602
14602
|
for (const [key, val] of Object.entries(obj)) {
|
|
14603
14603
|
if (key in props) {
|
|
14604
|
-
this.check(val, props[key], `${
|
|
14604
|
+
this.check(val, props[key], `${path53}/${key}`, errs);
|
|
14605
14605
|
} else if (additional === false) {
|
|
14606
14606
|
errs.push({
|
|
14607
14607
|
code: "CONTRACT-UNKNOWN-FIELD",
|
|
14608
14608
|
message: `unexpected '${key}'`,
|
|
14609
|
-
path: `${
|
|
14609
|
+
path: `${path53}/${key}`
|
|
14610
14610
|
});
|
|
14611
14611
|
} else if (typeof additional === "object" && additional !== null) {
|
|
14612
|
-
this.check(val, additional, `${
|
|
14612
|
+
this.check(val, additional, `${path53}/${key}`, errs);
|
|
14613
14613
|
}
|
|
14614
14614
|
}
|
|
14615
14615
|
}
|
|
14616
14616
|
if (Array.isArray(value)) {
|
|
14617
14617
|
if (typeof schema.minItems === "number" && value.length < schema.minItems) {
|
|
14618
|
-
errs.push({ code: "CONTRACT-SIZE", message: "too few items", path:
|
|
14618
|
+
errs.push({ code: "CONTRACT-SIZE", message: "too few items", path: path53 });
|
|
14619
14619
|
}
|
|
14620
14620
|
const itemSchema = schema.items;
|
|
14621
14621
|
if (typeof itemSchema === "object" && itemSchema !== null) {
|
|
14622
|
-
value.forEach((item, i3) => this.check(item, itemSchema, `${
|
|
14622
|
+
value.forEach((item, i3) => this.check(item, itemSchema, `${path53}/${i3}`, errs));
|
|
14623
14623
|
}
|
|
14624
14624
|
}
|
|
14625
14625
|
}
|
|
@@ -15609,22 +15609,22 @@ async function uninstall(opts = {}) {
|
|
|
15609
15609
|
}
|
|
15610
15610
|
|
|
15611
15611
|
// src/cmd/query.ts
|
|
15612
|
-
import { existsSync as
|
|
15612
|
+
import { existsSync as existsSync15 } from "fs";
|
|
15613
15613
|
|
|
15614
15614
|
// src/registry/install.ts
|
|
15615
15615
|
import { createHash as createHash2 } from "crypto";
|
|
15616
15616
|
import {
|
|
15617
|
-
chmodSync as
|
|
15618
|
-
existsSync as
|
|
15619
|
-
mkdirSync as
|
|
15617
|
+
chmodSync as chmodSync3,
|
|
15618
|
+
existsSync as existsSync13,
|
|
15619
|
+
mkdirSync as mkdirSync5,
|
|
15620
15620
|
readdirSync as readdirSync6,
|
|
15621
15621
|
readFileSync as readFileSync11,
|
|
15622
15622
|
renameSync as renameSync2,
|
|
15623
|
-
rmSync as
|
|
15623
|
+
rmSync as rmSync4,
|
|
15624
15624
|
statSync as statSync3,
|
|
15625
|
-
writeFileSync as
|
|
15625
|
+
writeFileSync as writeFileSync5
|
|
15626
15626
|
} from "fs";
|
|
15627
|
-
import
|
|
15627
|
+
import path21 from "path";
|
|
15628
15628
|
|
|
15629
15629
|
// src/registry/lock.ts
|
|
15630
15630
|
import { createHash } from "crypto";
|
|
@@ -15919,9 +15919,58 @@ async function lockTransitiveCheck(lock, wheels, run, pythonExe) {
|
|
|
15919
15919
|
return null;
|
|
15920
15920
|
}
|
|
15921
15921
|
|
|
15922
|
-
// src/registry/
|
|
15923
|
-
import { mkdirSync as mkdirSync3,
|
|
15922
|
+
// src/registry/shims.ts
|
|
15923
|
+
import { chmodSync as chmodSync2, existsSync as existsSync12, mkdirSync as mkdirSync3, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
15924
15924
|
import path19 from "path";
|
|
15925
|
+
function shimsDir(home) {
|
|
15926
|
+
return path19.join(home, ".forge-cli", "bin");
|
|
15927
|
+
}
|
|
15928
|
+
function shimName(bin, platform2) {
|
|
15929
|
+
const base = path19.basename(bin).replace(/\.(exe|cmd|bat)$/i, "");
|
|
15930
|
+
return platform2 === "win32" ? `${base}.cmd` : base;
|
|
15931
|
+
}
|
|
15932
|
+
function shQuote(bin) {
|
|
15933
|
+
if (bin.includes("\n") || bin.includes("\r")) {
|
|
15934
|
+
throw new Error(`bin com newline n\xE3o pode virar shim: ${JSON.stringify(bin)}`);
|
|
15935
|
+
}
|
|
15936
|
+
return bin.replace(/(["\\$`])/g, "\\$1");
|
|
15937
|
+
}
|
|
15938
|
+
function emitForgeShim(home, bin, platform2) {
|
|
15939
|
+
const dir = shimsDir(home);
|
|
15940
|
+
const target = resolveInside(dir, shimName(bin, platform2));
|
|
15941
|
+
if (!target || !isSafeRepoName(path19.basename(target).replace(/\.cmd$/i, ""))) {
|
|
15942
|
+
throw new Error(`nome de shim inseguro: ${path19.basename(bin)}`);
|
|
15943
|
+
}
|
|
15944
|
+
mkdirSync3(dir, { recursive: true });
|
|
15945
|
+
if (platform2 === "win32") {
|
|
15946
|
+
if (bin.includes('"') || bin.includes("\n") || bin.includes("\r")) {
|
|
15947
|
+
throw new Error(`bin n\xE3o express\xE1vel em .cmd: ${JSON.stringify(bin)}`);
|
|
15948
|
+
}
|
|
15949
|
+
writeFileSync4(target, `@echo off\r
|
|
15950
|
+
"${bin}" %*\r
|
|
15951
|
+
`, "utf8");
|
|
15952
|
+
} else {
|
|
15953
|
+
writeFileSync4(target, `#!/bin/sh
|
|
15954
|
+
exec "${shQuote(bin)}" "$@"
|
|
15955
|
+
`, {
|
|
15956
|
+
encoding: "utf8",
|
|
15957
|
+
mode: 493
|
|
15958
|
+
});
|
|
15959
|
+
chmodSync2(target, 493);
|
|
15960
|
+
}
|
|
15961
|
+
return target;
|
|
15962
|
+
}
|
|
15963
|
+
function removeForgeShim(home, bin, platform2) {
|
|
15964
|
+
if (!bin) return false;
|
|
15965
|
+
const target = resolveInside(shimsDir(home), shimName(bin, platform2));
|
|
15966
|
+
if (!target || !existsSync12(target)) return false;
|
|
15967
|
+
rmSync3(target, { force: true });
|
|
15968
|
+
return true;
|
|
15969
|
+
}
|
|
15970
|
+
|
|
15971
|
+
// src/registry/pypi.ts
|
|
15972
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync10 } from "fs";
|
|
15973
|
+
import path20 from "path";
|
|
15925
15974
|
|
|
15926
15975
|
// src/registry/versions.ts
|
|
15927
15976
|
var VERSION_RE = /^(\d+(?:\.\d+)*)(?:(a|b|rc)(\d+))?(?:\.post(\d+))?(?:\.dev(\d+))?$/i;
|
|
@@ -16051,7 +16100,7 @@ function latestStable(versions) {
|
|
|
16051
16100
|
var PYPI_JSON = "https://pypi.org/pypi";
|
|
16052
16101
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
16053
16102
|
function pypiCacheFile(cacheDir, dist) {
|
|
16054
|
-
return
|
|
16103
|
+
return path20.join(cacheDir, `${dist}.json`);
|
|
16055
16104
|
}
|
|
16056
16105
|
function asString(v) {
|
|
16057
16106
|
return typeof v === "string" && v.length ? v : void 0;
|
|
@@ -16090,7 +16139,7 @@ function readCache(cacheDir, dist) {
|
|
|
16090
16139
|
}
|
|
16091
16140
|
function writeCache(cacheDir, dist, payload) {
|
|
16092
16141
|
try {
|
|
16093
|
-
|
|
16142
|
+
mkdirSync4(cacheDir, { recursive: true });
|
|
16094
16143
|
writeFileAtomic(
|
|
16095
16144
|
pypiCacheFile(cacheDir, dist),
|
|
16096
16145
|
JSON.stringify({ fetchedAt: Date.now(), payload })
|
|
@@ -16247,20 +16296,20 @@ async function resolveRelease(dist, opts) {
|
|
|
16247
16296
|
|
|
16248
16297
|
// src/registry/install.ts
|
|
16249
16298
|
function forgesRoot(home) {
|
|
16250
|
-
return
|
|
16299
|
+
return path21.join(home, ".forge-cli", "forges");
|
|
16251
16300
|
}
|
|
16252
16301
|
function forgeStatePath(home) {
|
|
16253
|
-
return
|
|
16302
|
+
return path21.join(forgesRoot(home), "state.json");
|
|
16254
16303
|
}
|
|
16255
16304
|
function forgeVenv(home, name) {
|
|
16256
|
-
return
|
|
16305
|
+
return path21.join(forgesRoot(home), name, "venv");
|
|
16257
16306
|
}
|
|
16258
16307
|
function receiptPath(home, name) {
|
|
16259
|
-
return
|
|
16308
|
+
return path21.join(forgesRoot(home), name, "install.json");
|
|
16260
16309
|
}
|
|
16261
16310
|
function loadForgeState(home) {
|
|
16262
16311
|
const file = forgeStatePath(home);
|
|
16263
|
-
if (!
|
|
16312
|
+
if (!existsSync13(file)) return { forges: {} };
|
|
16264
16313
|
try {
|
|
16265
16314
|
const raw = JSON.parse(readFileSync11(file, "utf8"));
|
|
16266
16315
|
if (typeof raw.forges !== "object" || !raw.forges || Array.isArray(raw.forges)) {
|
|
@@ -16279,7 +16328,7 @@ function ghostEntry(name, receipt) {
|
|
|
16279
16328
|
return {
|
|
16280
16329
|
name,
|
|
16281
16330
|
dist: receipt.dist,
|
|
16282
|
-
bin:
|
|
16331
|
+
bin: path21.basename(receipt.bin).replace(/\.exe$/i, ""),
|
|
16283
16332
|
minPython: receipt.python.split(".").slice(0, 2).join("."),
|
|
16284
16333
|
origin: "official",
|
|
16285
16334
|
// Extras instalados junto — reinstall/update/rollback do ghost pedem o
|
|
@@ -16288,22 +16337,22 @@ function ghostEntry(name, receipt) {
|
|
|
16288
16337
|
};
|
|
16289
16338
|
}
|
|
16290
16339
|
function quarantineCorruptState(file) {
|
|
16291
|
-
if (!
|
|
16340
|
+
if (!existsSync13(file)) return;
|
|
16292
16341
|
try {
|
|
16293
16342
|
JSON.parse(readFileSync11(file, "utf8"));
|
|
16294
16343
|
} catch {
|
|
16295
16344
|
try {
|
|
16296
|
-
|
|
16345
|
+
writeFileSync5(`${file}.corrupt`, readFileSync11(file), { flag: "wx" });
|
|
16297
16346
|
} catch {
|
|
16298
16347
|
}
|
|
16299
16348
|
}
|
|
16300
16349
|
}
|
|
16301
16350
|
function saveForgeState(home, state) {
|
|
16302
|
-
|
|
16351
|
+
mkdirSync5(forgesRoot(home), { recursive: true });
|
|
16303
16352
|
if (process.platform !== "win32") {
|
|
16304
16353
|
try {
|
|
16305
|
-
|
|
16306
|
-
|
|
16354
|
+
chmodSync3(forgesRoot(home), 448);
|
|
16355
|
+
chmodSync3(path21.dirname(forgesRoot(home)), 448);
|
|
16307
16356
|
} catch {
|
|
16308
16357
|
}
|
|
16309
16358
|
}
|
|
@@ -16313,7 +16362,7 @@ function saveForgeState(home, state) {
|
|
|
16313
16362
|
`);
|
|
16314
16363
|
}
|
|
16315
16364
|
function wheelCacheDir(cacheDir, dist) {
|
|
16316
|
-
return
|
|
16365
|
+
return path21.join(path21.dirname(cacheDir), "wheels", dist);
|
|
16317
16366
|
}
|
|
16318
16367
|
function entryExtras(entry2) {
|
|
16319
16368
|
return [...new Set(entry2.extras ?? [])].sort();
|
|
@@ -16326,13 +16375,13 @@ function pipSpec(entry2, version) {
|
|
|
16326
16375
|
return `${entry2.dist}${extrasSuffix(entry2)}==${version}`;
|
|
16327
16376
|
}
|
|
16328
16377
|
function venvBin(venvDir, platform2 = process.platform) {
|
|
16329
|
-
return platform2 === "win32" ?
|
|
16378
|
+
return platform2 === "win32" ? path21.join(venvDir, "Scripts") : path21.join(venvDir, "bin");
|
|
16330
16379
|
}
|
|
16331
16380
|
function venvPython(venvDir, platform2 = process.platform) {
|
|
16332
|
-
return
|
|
16381
|
+
return path21.join(venvBin(venvDir, platform2), platform2 === "win32" ? "python.exe" : "python");
|
|
16333
16382
|
}
|
|
16334
16383
|
function forgeLockDir(home) {
|
|
16335
|
-
return
|
|
16384
|
+
return path21.join(forgesRoot(home), ".forge-lifecycle.lock");
|
|
16336
16385
|
}
|
|
16337
16386
|
function pidAliveDefault(pid) {
|
|
16338
16387
|
try {
|
|
@@ -16349,12 +16398,12 @@ async function acquireForgeLock(home, opts = {}) {
|
|
|
16349
16398
|
const now = opts.now ?? Date.now;
|
|
16350
16399
|
const alive = opts.pidAlive ?? pidAliveDefault;
|
|
16351
16400
|
const dir = forgeLockDir(home);
|
|
16352
|
-
const info =
|
|
16401
|
+
const info = path21.join(dir, "info.json");
|
|
16353
16402
|
const deadline = now() + timeoutMs;
|
|
16354
16403
|
for (; ; ) {
|
|
16355
16404
|
try {
|
|
16356
|
-
|
|
16357
|
-
|
|
16405
|
+
mkdirSync5(forgesRoot(home), { recursive: true });
|
|
16406
|
+
mkdirSync5(dir);
|
|
16358
16407
|
} catch (error) {
|
|
16359
16408
|
if (error.code !== "EEXIST") {
|
|
16360
16409
|
return { ok: false, reason: "error", detail: `lifecycle lock: ${String(error)}` };
|
|
@@ -16375,7 +16424,7 @@ async function acquireForgeLock(home, opts = {}) {
|
|
|
16375
16424
|
})();
|
|
16376
16425
|
if (stale || broken) {
|
|
16377
16426
|
try {
|
|
16378
|
-
|
|
16427
|
+
rmSync4(dir, { recursive: true, force: true });
|
|
16379
16428
|
} catch {
|
|
16380
16429
|
}
|
|
16381
16430
|
continue;
|
|
@@ -16391,7 +16440,7 @@ async function acquireForgeLock(home, opts = {}) {
|
|
|
16391
16440
|
continue;
|
|
16392
16441
|
}
|
|
16393
16442
|
try {
|
|
16394
|
-
|
|
16443
|
+
writeFileSync5(info, JSON.stringify({ pid: process.pid, at: new Date(now()).toISOString() }));
|
|
16395
16444
|
} catch {
|
|
16396
16445
|
}
|
|
16397
16446
|
return {
|
|
@@ -16400,7 +16449,7 @@ async function acquireForgeLock(home, opts = {}) {
|
|
|
16400
16449
|
dir,
|
|
16401
16450
|
release: () => {
|
|
16402
16451
|
try {
|
|
16403
|
-
|
|
16452
|
+
rmSync4(dir, { recursive: true, force: true });
|
|
16404
16453
|
} catch {
|
|
16405
16454
|
}
|
|
16406
16455
|
}
|
|
@@ -16414,7 +16463,7 @@ function installConsistency(home, name) {
|
|
|
16414
16463
|
const dir = resolveInside(forgesRoot(home), name);
|
|
16415
16464
|
const venv = forgeVenv(home, name);
|
|
16416
16465
|
if (!entry2) {
|
|
16417
|
-
const leftover = dir !== null &&
|
|
16466
|
+
const leftover = dir !== null && existsSync13(dir) || existsSync13(venv);
|
|
16418
16467
|
return leftover ? {
|
|
16419
16468
|
ok: false,
|
|
16420
16469
|
status: "orphaned",
|
|
@@ -16422,7 +16471,7 @@ function installConsistency(home, name) {
|
|
|
16422
16471
|
} : { ok: false, status: "not-installed", detail: `${name}: n\xE3o instalado via registry` };
|
|
16423
16472
|
}
|
|
16424
16473
|
const rfile = receiptPath(home, name);
|
|
16425
|
-
if (!
|
|
16474
|
+
if (!existsSync13(rfile)) {
|
|
16426
16475
|
return {
|
|
16427
16476
|
ok: false,
|
|
16428
16477
|
status: "missing-receipt",
|
|
@@ -16455,7 +16504,7 @@ function installConsistency(home, name) {
|
|
|
16455
16504
|
detail: `${name}: state=${entry2.version} mas receipt=${receipt.version} \u2014 escrita interrompida no meio; ${REPAIR_HINT}`
|
|
16456
16505
|
};
|
|
16457
16506
|
}
|
|
16458
|
-
if (!
|
|
16507
|
+
if (!existsSync13(entry2.venv ?? venv)) {
|
|
16459
16508
|
return {
|
|
16460
16509
|
ok: false,
|
|
16461
16510
|
status: "missing-venv",
|
|
@@ -16511,16 +16560,16 @@ function selectInterpreter(entry2, resolved, pythons) {
|
|
|
16511
16560
|
return { python: candidates[0] };
|
|
16512
16561
|
}
|
|
16513
16562
|
function checkDistHashes(distFiles, wheels) {
|
|
16514
|
-
const have =
|
|
16563
|
+
const have = existsSync13(wheels) ? readdirSync6(wheels) : [];
|
|
16515
16564
|
let verifiedHash;
|
|
16516
16565
|
let verifiedFile;
|
|
16517
16566
|
for (const f of distFiles) {
|
|
16518
16567
|
if (!have.includes(f.filename) || !f.sha256) continue;
|
|
16519
|
-
const digest = createHash2("sha256").update(readFileSync11(
|
|
16568
|
+
const digest = createHash2("sha256").update(readFileSync11(path21.join(wheels, f.filename))).digest("hex");
|
|
16520
16569
|
if (digest !== f.sha256) {
|
|
16521
|
-
const culprit =
|
|
16570
|
+
const culprit = path21.join(wheels, f.filename);
|
|
16522
16571
|
try {
|
|
16523
|
-
|
|
16572
|
+
rmSync4(`${culprit}.rejected`, { force: true });
|
|
16524
16573
|
renameSync2(culprit, `${culprit}.rejected`);
|
|
16525
16574
|
} catch {
|
|
16526
16575
|
}
|
|
@@ -16557,7 +16606,7 @@ async function registryInstall(entry2, pin, deps) {
|
|
|
16557
16606
|
}
|
|
16558
16607
|
return { stage: "incomplete", detail: c5.detail };
|
|
16559
16608
|
};
|
|
16560
|
-
if (!deps.clean &&
|
|
16609
|
+
if (!deps.clean && existsSync13(venvDir)) return consistencyGate();
|
|
16561
16610
|
const resolved = await resolveRelease(entry2.dist, {
|
|
16562
16611
|
pin,
|
|
16563
16612
|
offline: deps.offline,
|
|
@@ -16615,20 +16664,20 @@ async function registryInstall(entry2, pin, deps) {
|
|
|
16615
16664
|
return { stage: "busy", detail: `${entry2.name}: ${lockRes.detail}` };
|
|
16616
16665
|
}
|
|
16617
16666
|
try {
|
|
16618
|
-
if (!deps.clean &&
|
|
16619
|
-
const forgeDir =
|
|
16667
|
+
if (!deps.clean && existsSync13(venvDir)) return consistencyGate();
|
|
16668
|
+
const forgeDir = path21.join(forgesRoot(deps.home), entry2.name);
|
|
16620
16669
|
let backupDir = null;
|
|
16621
|
-
if (deps.clean &&
|
|
16670
|
+
if (deps.clean && existsSync13(forgeDir)) {
|
|
16622
16671
|
backupDir = `${forgeDir}.restore-${process.pid}`;
|
|
16623
16672
|
renameSync2(forgeDir, backupDir);
|
|
16624
16673
|
}
|
|
16625
16674
|
const cleanup = () => {
|
|
16626
|
-
|
|
16627
|
-
if (
|
|
16628
|
-
|
|
16675
|
+
rmSync4(venvDir, { recursive: true, force: true });
|
|
16676
|
+
if (existsSync13(forgeDir) && !readdirSync6(forgeDir).length) {
|
|
16677
|
+
rmSync4(forgeDir, { recursive: true, force: true });
|
|
16629
16678
|
}
|
|
16630
16679
|
if (backupDir) {
|
|
16631
|
-
|
|
16680
|
+
rmSync4(forgeDir, { recursive: true, force: true });
|
|
16632
16681
|
renameSync2(backupDir, forgeDir);
|
|
16633
16682
|
backupDir = null;
|
|
16634
16683
|
}
|
|
@@ -16643,7 +16692,7 @@ async function registryInstall(entry2, pin, deps) {
|
|
|
16643
16692
|
}
|
|
16644
16693
|
const vpy = venvPython(venvDir, platform2);
|
|
16645
16694
|
const spec = pipSpec(entry2, resolved.version);
|
|
16646
|
-
|
|
16695
|
+
mkdirSync5(wheels, { recursive: true });
|
|
16647
16696
|
const distFiles = distArtifactFiles(resolved.files, entry2.dist);
|
|
16648
16697
|
let verifiedHash;
|
|
16649
16698
|
let verifiedFile;
|
|
@@ -16671,7 +16720,7 @@ async function registryInstall(entry2, pin, deps) {
|
|
|
16671
16720
|
return { stage: "lock", error: depErr };
|
|
16672
16721
|
}
|
|
16673
16722
|
} else if (deps.offline) {
|
|
16674
|
-
const have =
|
|
16723
|
+
const have = existsSync13(wheels) ? readdirSync6(wheels) : [];
|
|
16675
16724
|
if (!distFiles.length || !distFiles.some((f) => have.includes(f.filename))) {
|
|
16676
16725
|
cleanup();
|
|
16677
16726
|
return {
|
|
@@ -16739,11 +16788,11 @@ async function registryInstall(entry2, pin, deps) {
|
|
|
16739
16788
|
...lock ? (
|
|
16740
16789
|
// Lock já enumera o fecho inteiro — inclusive as deps dos extras
|
|
16741
16790
|
// declarados — então os artefatos entram por caminho puro.
|
|
16742
|
-
lock.artifacts.map((a3) =>
|
|
16791
|
+
lock.artifacts.map((a3) => path21.join(wheels, a3.filename))
|
|
16743
16792
|
) : (
|
|
16744
16793
|
// `[extras]` no caminho do artefato: pip instala a wheel verificada
|
|
16745
16794
|
// E resolve as deps do extra no índice/--find-links (PEP 508).
|
|
16746
|
-
[`${
|
|
16795
|
+
[`${path21.join(wheels, verifiedFile)}${extrasSuffix(entry2)}`]
|
|
16747
16796
|
)
|
|
16748
16797
|
];
|
|
16749
16798
|
const pip = await run(vpy, pipArgs);
|
|
@@ -16756,7 +16805,7 @@ async function registryInstall(entry2, pin, deps) {
|
|
|
16756
16805
|
}
|
|
16757
16806
|
const commitBackup = backupDir;
|
|
16758
16807
|
backupDir = null;
|
|
16759
|
-
const binPath =
|
|
16808
|
+
const binPath = path21.join(
|
|
16760
16809
|
venvBin(venvDir, platform2),
|
|
16761
16810
|
platform2 === "win32" ? `${entry2.bin}.exe` : entry2.bin
|
|
16762
16811
|
);
|
|
@@ -16793,15 +16842,24 @@ async function registryInstall(entry2, pin, deps) {
|
|
|
16793
16842
|
};
|
|
16794
16843
|
deps.receiptPatch?.(receipt);
|
|
16795
16844
|
try {
|
|
16796
|
-
|
|
16845
|
+
mkdirSync5(path21.dirname(receiptPath(deps.home, entry2.name)), { recursive: true });
|
|
16797
16846
|
writeFileAtomic(receiptPath(deps.home, entry2.name), `${JSON.stringify(receipt, null, 2)}
|
|
16798
16847
|
`);
|
|
16799
16848
|
const state = loadForgeState(deps.home);
|
|
16800
16849
|
state.forges[entry2.name] = receipt;
|
|
16801
16850
|
saveForgeState(deps.home, state);
|
|
16851
|
+
try {
|
|
16852
|
+
receipt.shim = emitForgeShim(deps.home, binPath, platform2);
|
|
16853
|
+
} catch (shimError) {
|
|
16854
|
+
receipt.shimRefused = shimError instanceof Error ? shimError.message : String(shimError);
|
|
16855
|
+
}
|
|
16856
|
+
writeFileAtomic(receiptPath(deps.home, entry2.name), `${JSON.stringify(receipt, null, 2)}
|
|
16857
|
+
`);
|
|
16858
|
+
state.forges[entry2.name] = receipt;
|
|
16859
|
+
saveForgeState(deps.home, state);
|
|
16802
16860
|
} catch (error) {
|
|
16803
16861
|
if (commitBackup) {
|
|
16804
|
-
|
|
16862
|
+
rmSync4(forgeDir, { recursive: true, force: true });
|
|
16805
16863
|
try {
|
|
16806
16864
|
renameSync2(commitBackup, forgeDir);
|
|
16807
16865
|
} catch {
|
|
@@ -16814,7 +16872,7 @@ async function registryInstall(entry2, pin, deps) {
|
|
|
16814
16872
|
detail: `${entry2.name}: pip instalou mas o receipt/state falhou (${why}) \u2014 instala\xE7\xE3o INCOMPLETA (${consistency.status}): ${consistency.detail || "reexecute install"}`
|
|
16815
16873
|
};
|
|
16816
16874
|
}
|
|
16817
|
-
if (commitBackup)
|
|
16875
|
+
if (commitBackup) rmSync4(commitBackup, { recursive: true, force: true });
|
|
16818
16876
|
if (verify.exitCode !== 0) {
|
|
16819
16877
|
return { stage: "verify", detail: `${entry2.bin} --help saiu com c\xF3digo ${verify.exitCode}` };
|
|
16820
16878
|
}
|
|
@@ -16827,22 +16885,27 @@ function registryUninstall(target, deps) {
|
|
|
16827
16885
|
const dir = resolveInside(forgesRoot(deps.home), target.name);
|
|
16828
16886
|
if (!dir)
|
|
16829
16887
|
return { removed: false, receiptRemoved: false, cacheRemoved: false, refused: "unsafe-name" };
|
|
16830
|
-
const removed =
|
|
16831
|
-
if (removed)
|
|
16832
|
-
const wheels = target.dist && isSafeRepoName(target.dist) ? resolveInside(
|
|
16833
|
-
const cacheRemoved = wheels !== null &&
|
|
16834
|
-
if (wheels)
|
|
16888
|
+
const removed = existsSync13(dir);
|
|
16889
|
+
if (removed) rmSync4(dir, { recursive: true, force: true });
|
|
16890
|
+
const wheels = target.dist && isSafeRepoName(target.dist) ? resolveInside(path21.join(deps.home, ".forge-cli", "cache", "wheels"), target.dist) : null;
|
|
16891
|
+
const cacheRemoved = wheels !== null && existsSync13(wheels);
|
|
16892
|
+
if (wheels) rmSync4(wheels, { recursive: true, force: true });
|
|
16835
16893
|
const state = loadForgeState(deps.home);
|
|
16836
16894
|
const hadReceipt = Object.hasOwn(state.forges, target.name);
|
|
16895
|
+
const shimRemoved = removeForgeShim(
|
|
16896
|
+
deps.home,
|
|
16897
|
+
hadReceipt ? state.forges[target.name].shim ?? state.forges[target.name].bin : void 0,
|
|
16898
|
+
deps.platform ?? process.platform
|
|
16899
|
+
);
|
|
16837
16900
|
if (hadReceipt) delete state.forges[target.name];
|
|
16838
16901
|
saveForgeState(deps.home, state);
|
|
16839
|
-
return { removed, receiptRemoved: hadReceipt, cacheRemoved };
|
|
16902
|
+
return { removed, receiptRemoved: hadReceipt, cacheRemoved, shimRemoved };
|
|
16840
16903
|
}
|
|
16841
16904
|
|
|
16842
16905
|
// src/compat.ts
|
|
16843
16906
|
import { createHash as createHash3 } from "crypto";
|
|
16844
|
-
import { existsSync as
|
|
16845
|
-
import
|
|
16907
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
16908
|
+
import path22 from "path";
|
|
16846
16909
|
var ECOSYSTEM_SCHEMA = "forge/EcosystemCompatibility/v1";
|
|
16847
16910
|
function isRecord(v) {
|
|
16848
16911
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
@@ -16929,7 +16992,7 @@ function manifestProblems(doc) {
|
|
|
16929
16992
|
return problems;
|
|
16930
16993
|
}
|
|
16931
16994
|
function loadCompatManifest(manifestPath) {
|
|
16932
|
-
if (!
|
|
16995
|
+
if (!existsSync14(manifestPath)) return null;
|
|
16933
16996
|
let raw;
|
|
16934
16997
|
try {
|
|
16935
16998
|
raw = readFileSync12(manifestPath, "utf-8");
|
|
@@ -16954,7 +17017,7 @@ function loadCompatManifest(manifestPath) {
|
|
|
16954
17017
|
};
|
|
16955
17018
|
}
|
|
16956
17019
|
function defaultManifestPath(workspaceDir) {
|
|
16957
|
-
return
|
|
17020
|
+
return path22.join(workspaceDir, "the-forge", "ecosystem-compatibility.json");
|
|
16958
17021
|
}
|
|
16959
17022
|
function compatVerdict(manifest, component, version) {
|
|
16960
17023
|
const entry2 = manifest.components.find((c5) => c5.component === component);
|
|
@@ -16962,8 +17025,8 @@ function compatVerdict(manifest, component, version) {
|
|
|
16962
17025
|
return entry2.version === version ? "certified" : "incompatible";
|
|
16963
17026
|
}
|
|
16964
17027
|
function readDeclaredVersion(repoDir) {
|
|
16965
|
-
const pkg =
|
|
16966
|
-
if (
|
|
17028
|
+
const pkg = path22.join(repoDir, "package.json");
|
|
17029
|
+
if (existsSync14(pkg)) {
|
|
16967
17030
|
try {
|
|
16968
17031
|
const parsed = JSON.parse(readFileSync12(pkg, "utf-8"));
|
|
16969
17032
|
if (isRecord(parsed) && typeof parsed.version === "string" && parsed.version) {
|
|
@@ -16972,8 +17035,8 @@ function readDeclaredVersion(repoDir) {
|
|
|
16972
17035
|
} catch {
|
|
16973
17036
|
}
|
|
16974
17037
|
}
|
|
16975
|
-
const pyproject =
|
|
16976
|
-
if (
|
|
17038
|
+
const pyproject = path22.join(repoDir, "pyproject.toml");
|
|
17039
|
+
if (existsSync14(pyproject)) {
|
|
16977
17040
|
try {
|
|
16978
17041
|
const table = /^\[project\][^\n]*\n((?:(?!\[)[^\n]*(?:\n|$))*)/m.exec(
|
|
16979
17042
|
readFileSync12(pyproject, "utf-8")
|
|
@@ -17015,7 +17078,7 @@ function mergeRegistryPhase(infos, registry) {
|
|
|
17015
17078
|
const reg = Object.hasOwn(registry, i3.name) ? registry[i3.name] : void 0;
|
|
17016
17079
|
if (!reg || typeof reg !== "object") continue;
|
|
17017
17080
|
const tag2 = `pypi ${reg.dist ?? "?"}==${reg.version ?? "?"}`;
|
|
17018
|
-
const binMissing = typeof reg.bin !== "string" || !
|
|
17081
|
+
const binMissing = typeof reg.bin !== "string" || !existsSync15(reg.bin);
|
|
17019
17082
|
if (binMissing) {
|
|
17020
17083
|
if (i3.phase === "not-installed" || i3.phase === "installed") i3.phase = "drifted";
|
|
17021
17084
|
i3.describe = [i3.describe, `${tag2} (bin ausente)`].filter(Boolean).join(" + ");
|
|
@@ -17154,15 +17217,15 @@ async function runVersionsCommand(cli, names) {
|
|
|
17154
17217
|
|
|
17155
17218
|
// src/doctor2/index.ts
|
|
17156
17219
|
import os4 from "os";
|
|
17157
|
-
import
|
|
17220
|
+
import path33 from "path";
|
|
17158
17221
|
|
|
17159
17222
|
// src/hosts/claude.ts
|
|
17160
|
-
import
|
|
17223
|
+
import path24 from "path";
|
|
17161
17224
|
|
|
17162
17225
|
// src/hosts/types.ts
|
|
17163
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
17226
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync13, readdirSync as readdirSync7, rmSync as rmSync5 } from "fs";
|
|
17164
17227
|
import os2 from "os";
|
|
17165
|
-
import
|
|
17228
|
+
import path23 from "path";
|
|
17166
17229
|
var defaultProbe = {
|
|
17167
17230
|
output: (command, args) => commandOutput(command, args),
|
|
17168
17231
|
detailed: async (command, args) => {
|
|
@@ -17181,7 +17244,7 @@ function parseVersionOutput(out) {
|
|
|
17181
17244
|
return first && first !== "ok" ? first : null;
|
|
17182
17245
|
}
|
|
17183
17246
|
function existingConfigPaths(home, rels) {
|
|
17184
|
-
return rels.map((rel2) =>
|
|
17247
|
+
return rels.map((rel2) => path23.join(home, rel2)).filter((p) => existsSync16(p));
|
|
17185
17248
|
}
|
|
17186
17249
|
async function detectHost(home, spec) {
|
|
17187
17250
|
const runProbe = async (argv) => spec.probe.detailed ? spec.probe.detailed(argv[0], argv.slice(1)) : { out: await spec.probe.output(argv[0], argv.slice(1)), timedOut: false };
|
|
@@ -17237,7 +17300,7 @@ async function detectHost(home, spec) {
|
|
|
17237
17300
|
};
|
|
17238
17301
|
}
|
|
17239
17302
|
function readJsonObject(file) {
|
|
17240
|
-
if (!
|
|
17303
|
+
if (!existsSync16(file)) return null;
|
|
17241
17304
|
try {
|
|
17242
17305
|
const parsed = JSON.parse(readFileSync13(file, "utf8"));
|
|
17243
17306
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : "invalid";
|
|
@@ -17275,22 +17338,22 @@ function backupTimestamp(d = /* @__PURE__ */ new Date()) {
|
|
|
17275
17338
|
return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}${p(d.getMilliseconds(), 3)}`;
|
|
17276
17339
|
}
|
|
17277
17340
|
function backupsRoot(home) {
|
|
17278
|
-
return
|
|
17341
|
+
return path23.join(artifactsRoot(home), "backups");
|
|
17279
17342
|
}
|
|
17280
17343
|
function backupDirFor(home, hostId) {
|
|
17281
|
-
return
|
|
17344
|
+
return path23.join(backupsRoot(home), `hosts-${hostId}-${backupTimestamp()}`);
|
|
17282
17345
|
}
|
|
17283
17346
|
function backupTargets(dir, hostId, targets) {
|
|
17284
|
-
|
|
17347
|
+
mkdirSync6(dir, { recursive: true });
|
|
17285
17348
|
const files = targets.map((target) => {
|
|
17286
|
-
const name =
|
|
17287
|
-
const absent = !
|
|
17288
|
-
if (absent) writeFileAtomic(
|
|
17289
|
-
else copyFileSync2(target,
|
|
17349
|
+
const name = path23.basename(target);
|
|
17350
|
+
const absent = !existsSync16(target);
|
|
17351
|
+
if (absent) writeFileAtomic(path23.join(dir, `${name}.absent`), "");
|
|
17352
|
+
else copyFileSync2(target, path23.join(dir, name));
|
|
17290
17353
|
return { backup: name, target, absent };
|
|
17291
17354
|
});
|
|
17292
17355
|
writeFileAtomic(
|
|
17293
|
-
|
|
17356
|
+
path23.join(dir, "manifest.json"),
|
|
17294
17357
|
`${JSON.stringify({ host: hostId, files }, null, 2)}
|
|
17295
17358
|
`
|
|
17296
17359
|
);
|
|
@@ -17303,18 +17366,18 @@ function latestBackupDir(home, hostId) {
|
|
|
17303
17366
|
return null;
|
|
17304
17367
|
}
|
|
17305
17368
|
const hit = names.filter((n4) => n4.startsWith(`hosts-${hostId}-`)).sort().reverse()[0];
|
|
17306
|
-
return hit ?
|
|
17369
|
+
return hit ? path23.join(backupsRoot(home), hit) : null;
|
|
17307
17370
|
}
|
|
17308
17371
|
function restoreBackupDir(dir) {
|
|
17309
|
-
const manifest = JSON.parse(readFileSync13(
|
|
17372
|
+
const manifest = JSON.parse(readFileSync13(path23.join(dir, "manifest.json"), "utf8"));
|
|
17310
17373
|
const restored = [];
|
|
17311
17374
|
const removed = [];
|
|
17312
17375
|
for (const entry2 of manifest.files ?? []) {
|
|
17313
17376
|
if (entry2.absent) {
|
|
17314
|
-
|
|
17377
|
+
rmSync5(entry2.target, { force: true });
|
|
17315
17378
|
removed.push(entry2.target);
|
|
17316
17379
|
} else {
|
|
17317
|
-
writeFileAtomic(entry2.target, readFileSync13(
|
|
17380
|
+
writeFileAtomic(entry2.target, readFileSync13(path23.join(dir, entry2.backup), "utf8"));
|
|
17318
17381
|
restored.push(entry2.target);
|
|
17319
17382
|
}
|
|
17320
17383
|
}
|
|
@@ -17363,7 +17426,7 @@ async function applyHostPlan(home, plan, resolveWrite, opts = {}) {
|
|
|
17363
17426
|
);
|
|
17364
17427
|
for (const w of writes) {
|
|
17365
17428
|
try {
|
|
17366
|
-
|
|
17429
|
+
mkdirSync6(path23.dirname(w.target), { recursive: true });
|
|
17367
17430
|
writeFileAtomic(w.target, w.content);
|
|
17368
17431
|
applied.push(w.target);
|
|
17369
17432
|
} catch (error) {
|
|
@@ -17410,9 +17473,9 @@ async function rollbackHostPlan(home, plan, backup) {
|
|
|
17410
17473
|
|
|
17411
17474
|
// src/hosts/claude.ts
|
|
17412
17475
|
var CONFIG_FILE = ".claude.json";
|
|
17413
|
-
var CONFIG_REL = [CONFIG_FILE, ".claude",
|
|
17476
|
+
var CONFIG_REL = [CONFIG_FILE, ".claude", path24.join(".claude", "settings.json")];
|
|
17414
17477
|
function targetFor(home) {
|
|
17415
|
-
return
|
|
17478
|
+
return path24.join(home, CONFIG_FILE);
|
|
17416
17479
|
}
|
|
17417
17480
|
function createClaudeAdapter(probe = defaultProbe) {
|
|
17418
17481
|
return {
|
|
@@ -17480,12 +17543,12 @@ function createClaudeAdapter(probe = defaultProbe) {
|
|
|
17480
17543
|
var claudeAdapter = createClaudeAdapter();
|
|
17481
17544
|
|
|
17482
17545
|
// src/hosts/codex.ts
|
|
17483
|
-
import { existsSync as
|
|
17484
|
-
import
|
|
17485
|
-
var CONFIG_FILE2 =
|
|
17546
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
|
|
17547
|
+
import path25 from "path";
|
|
17548
|
+
var CONFIG_FILE2 = path25.join(".codex", "config.toml");
|
|
17486
17549
|
var CONFIG_REL2 = [CONFIG_FILE2, ".codex"];
|
|
17487
17550
|
function targetFor2(home) {
|
|
17488
|
-
return
|
|
17551
|
+
return path25.join(home, CONFIG_FILE2);
|
|
17489
17552
|
}
|
|
17490
17553
|
function escapeRe(s) {
|
|
17491
17554
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -17551,7 +17614,7 @@ function createCodexAdapter(probe = defaultProbe) {
|
|
|
17551
17614
|
authArgv: ["codex", "login", "status"],
|
|
17552
17615
|
hasIntegration: (dir) => {
|
|
17553
17616
|
const file = targetFor2(dir);
|
|
17554
|
-
return
|
|
17617
|
+
return existsSync17(file) && hasAnyCodexMcpSection(readFileSync14(file, "utf8"));
|
|
17555
17618
|
}
|
|
17556
17619
|
});
|
|
17557
17620
|
},
|
|
@@ -17560,7 +17623,7 @@ function createCodexAdapter(probe = defaultProbe) {
|
|
|
17560
17623
|
if (!argv?.length) return null;
|
|
17561
17624
|
const h3 = hostHome(home);
|
|
17562
17625
|
const target = targetFor2(h3);
|
|
17563
|
-
const existing =
|
|
17626
|
+
const existing = existsSync17(target) ? readFileSync14(target, "utf8") : null;
|
|
17564
17627
|
if (existing !== null && hasCodexMcpSection(existing, forge.forge_id)) {
|
|
17565
17628
|
return {
|
|
17566
17629
|
host: "codex",
|
|
@@ -17591,7 +17654,7 @@ function createCodexAdapter(probe = defaultProbe) {
|
|
|
17591
17654
|
plan,
|
|
17592
17655
|
(action) => {
|
|
17593
17656
|
if (!action.target || !action.argv?.length) return "skip";
|
|
17594
|
-
const existing =
|
|
17657
|
+
const existing = existsSync17(action.target) ? readFileSync14(action.target, "utf8") : null;
|
|
17595
17658
|
if (existing !== null && hasCodexMcpSection(existing, plan.forge_id)) {
|
|
17596
17659
|
return "duplicate";
|
|
17597
17660
|
}
|
|
@@ -17608,7 +17671,7 @@ ${block2}
|
|
|
17608
17671
|
validateIntegration: async (forge_id, home) => {
|
|
17609
17672
|
const file = targetFor2(hostHome(home));
|
|
17610
17673
|
const checks = [];
|
|
17611
|
-
if (!
|
|
17674
|
+
if (!existsSync17(file)) {
|
|
17612
17675
|
checks.push({ id: "config-file", result: "fail", detail: "absent" });
|
|
17613
17676
|
checks.push({ id: "mcp-section", result: "fail", detail: forge_id });
|
|
17614
17677
|
return { ok: false, checks };
|
|
@@ -17635,12 +17698,12 @@ ${block2}
|
|
|
17635
17698
|
var codexAdapter = createCodexAdapter();
|
|
17636
17699
|
|
|
17637
17700
|
// src/hosts/copilot.ts
|
|
17638
|
-
import
|
|
17639
|
-
import { existsSync as
|
|
17640
|
-
var CONFIG_FILE3 =
|
|
17641
|
-
var CONFIG_REL3 = [".copilot", CONFIG_FILE3,
|
|
17701
|
+
import path26 from "path";
|
|
17702
|
+
import { existsSync as existsSync18 } from "fs";
|
|
17703
|
+
var CONFIG_FILE3 = path26.join(".copilot", "mcp-config.json");
|
|
17704
|
+
var CONFIG_REL3 = [".copilot", CONFIG_FILE3, path26.join(".config", "gh")];
|
|
17642
17705
|
function targetFor3(home) {
|
|
17643
|
-
return
|
|
17706
|
+
return path26.join(home, CONFIG_FILE3);
|
|
17644
17707
|
}
|
|
17645
17708
|
function createCopilotAdapter(probe = defaultProbe) {
|
|
17646
17709
|
return {
|
|
@@ -17664,7 +17727,7 @@ function createCopilotAdapter(probe = defaultProbe) {
|
|
|
17664
17727
|
if (!argv?.length) return null;
|
|
17665
17728
|
const h3 = hostHome(home);
|
|
17666
17729
|
const target = targetFor3(h3);
|
|
17667
|
-
if (!
|
|
17730
|
+
if (!existsSync18(target)) return null;
|
|
17668
17731
|
if (hasMcpEntry(target, forge.forge_id)) {
|
|
17669
17732
|
return {
|
|
17670
17733
|
host: "copilot",
|
|
@@ -17710,11 +17773,11 @@ function createCopilotAdapter(probe = defaultProbe) {
|
|
|
17710
17773
|
var copilotAdapter = createCopilotAdapter();
|
|
17711
17774
|
|
|
17712
17775
|
// src/hosts/cursor.ts
|
|
17713
|
-
import
|
|
17714
|
-
var CONFIG_FILE4 =
|
|
17776
|
+
import path27 from "path";
|
|
17777
|
+
var CONFIG_FILE4 = path27.join(".cursor", "mcp.json");
|
|
17715
17778
|
var CONFIG_REL4 = [CONFIG_FILE4, ".cursor"];
|
|
17716
17779
|
function targetFor4(home) {
|
|
17717
|
-
return
|
|
17780
|
+
return path27.join(home, CONFIG_FILE4);
|
|
17718
17781
|
}
|
|
17719
17782
|
function createCursorAdapter(probe = defaultProbe) {
|
|
17720
17783
|
return {
|
|
@@ -17780,8 +17843,8 @@ function createCursorAdapter(probe = defaultProbe) {
|
|
|
17780
17843
|
var cursorAdapter = createCursorAdapter();
|
|
17781
17844
|
|
|
17782
17845
|
// src/hosts/devin.ts
|
|
17783
|
-
import
|
|
17784
|
-
var CONFIG_REL5 = [".devin",
|
|
17846
|
+
import path28 from "path";
|
|
17847
|
+
var CONFIG_REL5 = [".devin", path28.join(".config", "devin")];
|
|
17785
17848
|
function createDevinAdapter(probe = defaultProbe) {
|
|
17786
17849
|
return {
|
|
17787
17850
|
id: "devin",
|
|
@@ -17862,13 +17925,13 @@ function errMsg(error) {
|
|
|
17862
17925
|
}
|
|
17863
17926
|
|
|
17864
17927
|
// src/doctor2/domains.ts
|
|
17865
|
-
import { existsSync as
|
|
17928
|
+
import { existsSync as existsSync22, readdirSync as readdirSync10, rmSync as rmSync6, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
17866
17929
|
import os3 from "os";
|
|
17867
|
-
import
|
|
17930
|
+
import path32 from "path";
|
|
17868
17931
|
|
|
17869
17932
|
// src/envintel/validate.ts
|
|
17870
|
-
import { existsSync as
|
|
17871
|
-
import
|
|
17933
|
+
import { existsSync as existsSync20, readFileSync as readFileSync17 } from "fs";
|
|
17934
|
+
import path30 from "path";
|
|
17872
17935
|
|
|
17873
17936
|
// src/envintel/parse.ts
|
|
17874
17937
|
var ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -17957,8 +18020,8 @@ function formatEntry(key, value) {
|
|
|
17957
18020
|
}
|
|
17958
18021
|
|
|
17959
18022
|
// src/envintel/schema.ts
|
|
17960
|
-
import { existsSync as
|
|
17961
|
-
import
|
|
18023
|
+
import { existsSync as existsSync19, readFileSync as readFileSync16 } from "fs";
|
|
18024
|
+
import path29 from "path";
|
|
17962
18025
|
var SECRET_RE = /KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL/;
|
|
17963
18026
|
var KIND_RULES = [
|
|
17964
18027
|
{ kind: "port", re: /(^|_)PORT($|_)/ },
|
|
@@ -18005,8 +18068,8 @@ function validPattern(p) {
|
|
|
18005
18068
|
}
|
|
18006
18069
|
}
|
|
18007
18070
|
function readSchemaFile(repoDir) {
|
|
18008
|
-
const file =
|
|
18009
|
-
if (!
|
|
18071
|
+
const file = path29.join(repoDir, "forge.env.schema.json");
|
|
18072
|
+
if (!existsSync19(file)) return [];
|
|
18010
18073
|
try {
|
|
18011
18074
|
const parsed = JSON.parse(readFileSync16(file, "utf8"));
|
|
18012
18075
|
const list = Array.isArray(parsed) ? parsed : parsed && typeof parsed === "object" && Array.isArray(parsed.vars) ? parsed.vars : parsed && typeof parsed === "object" ? Object.entries(parsed).map(([key, v]) => ({
|
|
@@ -18020,8 +18083,8 @@ function readSchemaFile(repoDir) {
|
|
|
18020
18083
|
}
|
|
18021
18084
|
function deriveSchema(repoDir, envKeys) {
|
|
18022
18085
|
const specs = /* @__PURE__ */ new Map();
|
|
18023
|
-
const exampleFile =
|
|
18024
|
-
if (
|
|
18086
|
+
const exampleFile = path29.join(repoDir, ".env.example");
|
|
18087
|
+
if (existsSync19(exampleFile)) {
|
|
18025
18088
|
for (const entry2 of parseEnv(readFileSync16(exampleFile, "utf8")).entries) {
|
|
18026
18089
|
if (specs.has(entry2.key)) continue;
|
|
18027
18090
|
const kind = inferKind(entry2.key, entry2.value || void 0);
|
|
@@ -18085,8 +18148,8 @@ function checkValue(spec, value, opts = {}) {
|
|
|
18085
18148
|
case "path": {
|
|
18086
18149
|
if (!value.trim()) return false;
|
|
18087
18150
|
if (!opts.checkPaths) return true;
|
|
18088
|
-
const p =
|
|
18089
|
-
return
|
|
18151
|
+
const p = path30.isAbsolute(value) ? value : path30.resolve(opts.baseDir ?? ".", value);
|
|
18152
|
+
return existsSync20(p);
|
|
18090
18153
|
}
|
|
18091
18154
|
case "bool":
|
|
18092
18155
|
return /^(true|false|0|1|yes|no)$/i.test(value.trim());
|
|
@@ -18137,9 +18200,9 @@ function analyzeKey(key, spec, entries, opts) {
|
|
|
18137
18200
|
}
|
|
18138
18201
|
function analyzeEnv(repoDir, envKeys, opts = {}) {
|
|
18139
18202
|
const schema = deriveSchema(repoDir, envKeys);
|
|
18140
|
-
const envPath =
|
|
18203
|
+
const envPath = path30.join(repoDir, ".env");
|
|
18141
18204
|
const specByKey = new Map(schema.vars.map((v) => [v.key, v]));
|
|
18142
|
-
const envFile =
|
|
18205
|
+
const envFile = existsSync20(envPath) ? parseEnv(readFileSync17(envPath, "utf8")) : null;
|
|
18143
18206
|
const findings = [];
|
|
18144
18207
|
const vars = [];
|
|
18145
18208
|
const byKey = /* @__PURE__ */ new Map();
|
|
@@ -18220,9 +18283,9 @@ function inspectEnv(repoDir, envKeys) {
|
|
|
18220
18283
|
}
|
|
18221
18284
|
|
|
18222
18285
|
// src/maintenance.ts
|
|
18223
|
-
import { existsSync as
|
|
18286
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync7, readFileSync as readFileSync18, readdirSync as readdirSync9 } from "fs";
|
|
18224
18287
|
import { createHash as createHash4 } from "crypto";
|
|
18225
|
-
import
|
|
18288
|
+
import path31 from "path";
|
|
18226
18289
|
function envLockDigest(repoDir) {
|
|
18227
18290
|
const names = [
|
|
18228
18291
|
".env.example",
|
|
@@ -18234,8 +18297,8 @@ function envLockDigest(repoDir) {
|
|
|
18234
18297
|
const files = [];
|
|
18235
18298
|
const hash = createHash4("sha256");
|
|
18236
18299
|
for (const name of names) {
|
|
18237
|
-
const file =
|
|
18238
|
-
if (!
|
|
18300
|
+
const file = path31.join(repoDir, name);
|
|
18301
|
+
if (!existsSync21(file)) continue;
|
|
18239
18302
|
files.push(name);
|
|
18240
18303
|
const content = readFileSync18(file);
|
|
18241
18304
|
hash.update(name).update("\0").update(content).update("\0");
|
|
@@ -18249,8 +18312,8 @@ function buildEnvLock(targetDir, repos) {
|
|
|
18249
18312
|
forges: {}
|
|
18250
18313
|
};
|
|
18251
18314
|
for (const repo of repos) {
|
|
18252
|
-
const dir =
|
|
18253
|
-
if (!
|
|
18315
|
+
const dir = path31.join(targetDir, repo.name);
|
|
18316
|
+
if (!existsSync21(dir)) continue;
|
|
18254
18317
|
const { files, digest } = envLockDigest(dir);
|
|
18255
18318
|
const head = loadState(targetDir)?.repos[repo.name]?.head;
|
|
18256
18319
|
lock.forges[repo.name] = { env_files: files, digest, ...head ? { head } : {} };
|
|
@@ -18259,14 +18322,14 @@ function buildEnvLock(targetDir, repos) {
|
|
|
18259
18322
|
}
|
|
18260
18323
|
function writeEnvLock(targetDir, repos) {
|
|
18261
18324
|
const lock = buildEnvLock(targetDir, repos);
|
|
18262
|
-
const file =
|
|
18263
|
-
|
|
18325
|
+
const file = path31.join(artifactsRoot(targetDir), "env.lock.json");
|
|
18326
|
+
mkdirSync7(path31.dirname(file), { recursive: true });
|
|
18264
18327
|
writeFileAtomic(file, JSON.stringify(lock, null, 2));
|
|
18265
18328
|
return lock;
|
|
18266
18329
|
}
|
|
18267
18330
|
function checkEnvLock(targetDir, repos) {
|
|
18268
|
-
const file =
|
|
18269
|
-
if (!
|
|
18331
|
+
const file = path31.join(artifactsRoot(targetDir), "env.lock.json");
|
|
18332
|
+
if (!existsSync21(file)) return repos.map((r2) => ({ forge: r2.name, state: "untracked" }));
|
|
18270
18333
|
let lock;
|
|
18271
18334
|
try {
|
|
18272
18335
|
lock = JSON.parse(readFileSync18(file, "utf8"));
|
|
@@ -18276,8 +18339,8 @@ function checkEnvLock(targetDir, repos) {
|
|
|
18276
18339
|
return repos.map((repo) => {
|
|
18277
18340
|
const entry2 = lock.forges?.[repo.name];
|
|
18278
18341
|
if (!entry2) return { forge: repo.name, state: "untracked" };
|
|
18279
|
-
const dir =
|
|
18280
|
-
if (!
|
|
18342
|
+
const dir = path31.join(targetDir, repo.name);
|
|
18343
|
+
if (!existsSync21(dir)) return { forge: repo.name, state: "absent" };
|
|
18281
18344
|
const { digest } = envLockDigest(dir);
|
|
18282
18345
|
return { forge: repo.name, state: digest === entry2.digest ? "match" : "drifted" };
|
|
18283
18346
|
});
|
|
@@ -18298,7 +18361,7 @@ function buildProfile(targetDir) {
|
|
|
18298
18361
|
}
|
|
18299
18362
|
function exportProfile(targetDir, dest) {
|
|
18300
18363
|
const profile = buildProfile(targetDir);
|
|
18301
|
-
|
|
18364
|
+
mkdirSync7(path31.dirname(path31.resolve(dest)), { recursive: true });
|
|
18302
18365
|
writeFileAtomic(dest, JSON.stringify(profile, null, 2));
|
|
18303
18366
|
return profile;
|
|
18304
18367
|
}
|
|
@@ -18399,16 +18462,16 @@ function systemChecks(ctx) {
|
|
|
18399
18462
|
readEvidence = `read=falha (${errMsg(error)})`;
|
|
18400
18463
|
}
|
|
18401
18464
|
if (readable2) {
|
|
18402
|
-
const probe =
|
|
18465
|
+
const probe = path32.join(dir, `.forge-doctor2-probe-${process.pid}`);
|
|
18403
18466
|
try {
|
|
18404
|
-
|
|
18405
|
-
|
|
18467
|
+
writeFileSync6(probe, "probe\n", "utf8");
|
|
18468
|
+
rmSync6(probe, { force: true });
|
|
18406
18469
|
writable2 = true;
|
|
18407
18470
|
writeEvidence = "write=ok";
|
|
18408
18471
|
} catch (error) {
|
|
18409
18472
|
writeEvidence = `write=falha (${errMsg(error)})`;
|
|
18410
18473
|
try {
|
|
18411
|
-
|
|
18474
|
+
rmSync6(probe, { force: true });
|
|
18412
18475
|
} catch {
|
|
18413
18476
|
}
|
|
18414
18477
|
}
|
|
@@ -18434,7 +18497,7 @@ function systemChecks(ctx) {
|
|
|
18434
18497
|
const dirEvidence = [];
|
|
18435
18498
|
let dirFailed = 0;
|
|
18436
18499
|
for (const { label, dir: d } of required) {
|
|
18437
|
-
if (!
|
|
18500
|
+
if (!existsSync22(d)) {
|
|
18438
18501
|
dirFailed += 1;
|
|
18439
18502
|
dirEvidence.push(`${label}=${d}: ausente`);
|
|
18440
18503
|
continue;
|
|
@@ -18492,7 +18555,7 @@ function backupScan(ctx) {
|
|
|
18492
18555
|
over = true;
|
|
18493
18556
|
break;
|
|
18494
18557
|
}
|
|
18495
|
-
const full =
|
|
18558
|
+
const full = path32.join(dir, name);
|
|
18496
18559
|
try {
|
|
18497
18560
|
if (!statSync4(full).isDirectory()) continue;
|
|
18498
18561
|
} catch {
|
|
@@ -18501,16 +18564,16 @@ function backupScan(ctx) {
|
|
|
18501
18564
|
if (restorableIf(full)) restorable.push(`${kind}: ${full}`);
|
|
18502
18565
|
}
|
|
18503
18566
|
};
|
|
18504
|
-
scan(
|
|
18567
|
+
scan(path32.join(artifactsRoot(ctx.root), "backups"), "workspace-backup", dirHasFiles);
|
|
18505
18568
|
scan(
|
|
18506
|
-
|
|
18569
|
+
path32.join(ctx.home, ".forge-cli", "backups"),
|
|
18507
18570
|
"host-backup",
|
|
18508
|
-
(full) =>
|
|
18571
|
+
(full) => existsSync22(path32.join(full, "manifest.json"))
|
|
18509
18572
|
);
|
|
18510
18573
|
for (const repo of ctx.catalog) {
|
|
18511
18574
|
const dir = resolveInside(ctx.root, repo.name);
|
|
18512
18575
|
if (!dir || over) continue;
|
|
18513
|
-
scan(
|
|
18576
|
+
scan(path32.join(dir, ".forge-cli", "backups"), `env-backup(${repo.name})`, dirHasFiles);
|
|
18514
18577
|
}
|
|
18515
18578
|
if (over) ctx.truncated.value = true;
|
|
18516
18579
|
return { restorable, scanned };
|
|
@@ -18518,7 +18581,7 @@ function backupScan(ctx) {
|
|
|
18518
18581
|
function installationChecks(ctx) {
|
|
18519
18582
|
const checks = [];
|
|
18520
18583
|
const forgeDir = artifactsRoot(ctx.root);
|
|
18521
|
-
const forgeDirPresent =
|
|
18584
|
+
const forgeDirPresent = existsSync22(forgeDir);
|
|
18522
18585
|
const stateFile = stateFileFor(ctx.root);
|
|
18523
18586
|
checks.push(
|
|
18524
18587
|
mkCheck(
|
|
@@ -18531,7 +18594,7 @@ function installationChecks(ctx) {
|
|
|
18531
18594
|
diagnosis: "diret\xF3rio de estado .forge-cli presente",
|
|
18532
18595
|
evidence: [
|
|
18533
18596
|
`${forgeDir} presente`,
|
|
18534
|
-
|
|
18597
|
+
existsSync22(stateFile) ? "state.json presente" : "state.json ausente"
|
|
18535
18598
|
],
|
|
18536
18599
|
reversible: null
|
|
18537
18600
|
} : {
|
|
@@ -18546,8 +18609,8 @@ function installationChecks(ctx) {
|
|
|
18546
18609
|
}
|
|
18547
18610
|
)
|
|
18548
18611
|
);
|
|
18549
|
-
const forgeJson =
|
|
18550
|
-
if (!
|
|
18612
|
+
const forgeJson = path32.join(ctx.root, "forge.json");
|
|
18613
|
+
if (!existsSync22(forgeJson)) {
|
|
18551
18614
|
checks.push(
|
|
18552
18615
|
mkCheck(ctx.now, {
|
|
18553
18616
|
id: "installation.forge-json.present",
|
|
@@ -18596,10 +18659,10 @@ function installationChecks(ctx) {
|
|
|
18596
18659
|
);
|
|
18597
18660
|
}
|
|
18598
18661
|
}
|
|
18599
|
-
const lockFile =
|
|
18600
|
-
const lockParsed =
|
|
18662
|
+
const lockFile = path32.join(forgeDir, "env.lock.json");
|
|
18663
|
+
const lockParsed = existsSync22(lockFile) ? readJson(lockFile) : null;
|
|
18601
18664
|
const lockObj = lockParsed?.ok && lockParsed.value !== null && typeof lockParsed.value === "object" ? lockParsed.value : null;
|
|
18602
|
-
if (!forgeDirPresent || !
|
|
18665
|
+
if (!forgeDirPresent || !existsSync22(lockFile)) {
|
|
18603
18666
|
checks.push(
|
|
18604
18667
|
mkCheck(ctx.now, {
|
|
18605
18668
|
id: "installation.lockfile.present",
|
|
@@ -18644,7 +18707,7 @@ function installationChecks(ctx) {
|
|
|
18644
18707
|
);
|
|
18645
18708
|
}
|
|
18646
18709
|
const trackedForges = lockObj ? Object.keys(lockObj.forges ?? {}) : [];
|
|
18647
|
-
if (!
|
|
18710
|
+
if (!existsSync22(lockFile) || !lockObj || !trackedForges.length) {
|
|
18648
18711
|
checks.push(
|
|
18649
18712
|
mkCheck(ctx.now, {
|
|
18650
18713
|
id: "installation.lockfile.drift",
|
|
@@ -18653,7 +18716,7 @@ function installationChecks(ctx) {
|
|
|
18653
18716
|
severity: "info",
|
|
18654
18717
|
diagnosis: "deriva do lockfile n\xE3o verific\xE1vel",
|
|
18655
18718
|
evidence: [
|
|
18656
|
-
!
|
|
18719
|
+
!existsSync22(lockFile) ? "sem lockfile para recomputar (.forge-cli/env.lock.json ausente)" : !lockObj ? "lockfile ileg\xEDvel \u2014 digest n\xE3o pode ser recomputado" : "lockfile n\xE3o cobre nenhuma Forja \u2014 nada para recomputar"
|
|
18657
18720
|
],
|
|
18658
18721
|
reversible: null
|
|
18659
18722
|
})
|
|
@@ -18675,17 +18738,17 @@ function installationChecks(ctx) {
|
|
|
18675
18738
|
})
|
|
18676
18739
|
);
|
|
18677
18740
|
}
|
|
18678
|
-
const ledgerDir =
|
|
18741
|
+
const ledgerDir = path32.join(ctx.home, ".forge", "installations");
|
|
18679
18742
|
const ledgerNames = listDir(ledgerDir);
|
|
18680
18743
|
if (ledgerNames === null) {
|
|
18681
18744
|
checks.push(
|
|
18682
18745
|
mkCheck(ctx.now, {
|
|
18683
18746
|
id: "installation.ledger.readable",
|
|
18684
18747
|
domain: "installation",
|
|
18685
|
-
result:
|
|
18686
|
-
severity:
|
|
18687
|
-
diagnosis:
|
|
18688
|
-
evidence: [`${ledgerDir} ${
|
|
18748
|
+
result: existsSync22(ledgerDir) ? "warn" : "skip",
|
|
18749
|
+
severity: existsSync22(ledgerDir) ? "medium" : "info",
|
|
18750
|
+
diagnosis: existsSync22(ledgerDir) ? "ledger de instala\xE7\xF5es existe mas n\xE3o p\xF4de ser lido" : "sem ledger de instala\xE7\xF5es (~/.forge/installations ausente)",
|
|
18751
|
+
evidence: [`${ledgerDir} ${existsSync22(ledgerDir) ? "ileg\xEDvel" : "ausente"}`],
|
|
18689
18752
|
reversible: null
|
|
18690
18753
|
})
|
|
18691
18754
|
);
|
|
@@ -18813,7 +18876,7 @@ function environmentChecks(ctx) {
|
|
|
18813
18876
|
const targets = [{ label: "<workspace>", dir: ctx.root }];
|
|
18814
18877
|
for (const repo of ctx.catalog) {
|
|
18815
18878
|
const dir = resolveInside(ctx.root, repo.name);
|
|
18816
|
-
if (dir &&
|
|
18879
|
+
if (dir && existsSync22(dir)) targets.push({ label: repo.name, dir, envKeys: repo.envKeys });
|
|
18817
18880
|
}
|
|
18818
18881
|
const inspected = targets.map((t2) => ({
|
|
18819
18882
|
...t2,
|
|
@@ -19026,16 +19089,16 @@ async function hostsChecks(ctx) {
|
|
|
19026
19089
|
}
|
|
19027
19090
|
function operationsChecks(ctx) {
|
|
19028
19091
|
const checks = [];
|
|
19029
|
-
const reportsDir =
|
|
19092
|
+
const reportsDir = path32.join(artifactsRoot(ctx.root), "reports");
|
|
19030
19093
|
const allReports = (listDir(reportsDir) ?? []).filter((n4) => n4.endsWith(".json"));
|
|
19031
19094
|
const capped = allReports.length > ctx.maxReceipts;
|
|
19032
19095
|
if (capped) ctx.truncated.value = true;
|
|
19033
19096
|
const scannedNames = [...allReports].sort().reverse().slice(0, ctx.maxReceipts);
|
|
19034
|
-
const parsedReports = scannedNames.map((n4) => readJson(
|
|
19097
|
+
const parsedReports = scannedNames.map((n4) => readJson(path32.join(reportsDir, n4))).filter((r2) => r2.ok).map((r2) => r2.value);
|
|
19035
19098
|
checks.push(
|
|
19036
19099
|
mkCheck(
|
|
19037
19100
|
ctx.now,
|
|
19038
|
-
!
|
|
19101
|
+
!existsSync22(reportsDir) ? {
|
|
19039
19102
|
id: "operations.receipts.recent",
|
|
19040
19103
|
domain: "operations",
|
|
19041
19104
|
result: "skip",
|
|
@@ -19071,7 +19134,7 @@ function operationsChecks(ctx) {
|
|
|
19071
19134
|
if (entry2.status === "error") failures.set(name, (failures.get(name) ?? 0) + 1);
|
|
19072
19135
|
}
|
|
19073
19136
|
const recurring = [...failures.entries()].filter(([, n4]) => n4 >= 2);
|
|
19074
|
-
const scannedAnything =
|
|
19137
|
+
const scannedAnything = existsSync22(reportsDir) || ctx.state !== null;
|
|
19075
19138
|
if (!scannedAnything) {
|
|
19076
19139
|
checks.push(
|
|
19077
19140
|
mkCheck(ctx.now, {
|
|
@@ -19107,7 +19170,7 @@ function operationsChecks(ctx) {
|
|
|
19107
19170
|
);
|
|
19108
19171
|
const recFiles = boundEntries(ctx, allRecFiles).shown;
|
|
19109
19172
|
const recEvidence = recFiles.map((f) => {
|
|
19110
|
-
const parsed = readJson(
|
|
19173
|
+
const parsed = readJson(path32.join(artifactsRoot(ctx.root), f));
|
|
19111
19174
|
return `recovery file: ${f}${parsed.ok ? "" : " (ileg\xEDvel)"}`;
|
|
19112
19175
|
});
|
|
19113
19176
|
checks.push(
|
|
@@ -19287,7 +19350,7 @@ function defaultCatalog(root) {
|
|
|
19287
19350
|
}
|
|
19288
19351
|
async function runDoctor2(root, opts = {}) {
|
|
19289
19352
|
const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
19290
|
-
const resolved =
|
|
19353
|
+
const resolved = path33.resolve(root);
|
|
19291
19354
|
const ctx = {
|
|
19292
19355
|
root: resolved,
|
|
19293
19356
|
home: opts.home ?? os4.homedir(),
|
|
@@ -19347,9 +19410,9 @@ async function runDoctor2(root, opts = {}) {
|
|
|
19347
19410
|
}
|
|
19348
19411
|
|
|
19349
19412
|
// src/heal/fixes.ts
|
|
19350
|
-
import { existsSync as
|
|
19351
|
-
import
|
|
19352
|
-
var rel = (root, abs) =>
|
|
19413
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync8, readdirSync as readdirSync11, readFileSync as readFileSync19, statSync as statSync5 } from "fs";
|
|
19414
|
+
import path34 from "path";
|
|
19415
|
+
var rel = (root, abs) => path34.relative(root, abs) || ".";
|
|
19353
19416
|
function stampOf(iso) {
|
|
19354
19417
|
const m3 = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/.exec(iso);
|
|
19355
19418
|
return m3 ? `${m3[1]}${m3[2]}${m3[3]}-${m3[4]}${m3[5]}${m3[6]}` : iso.replace(/[^\d]/g, "").slice(0, 14);
|
|
@@ -19364,9 +19427,9 @@ var forgeDirRepair = {
|
|
|
19364
19427
|
plan(ctx) {
|
|
19365
19428
|
const base = artifactsRoot(ctx.root);
|
|
19366
19429
|
return ["", ...FORGE_DIR_SUBDIRS].map((sub) => {
|
|
19367
|
-
const dir = sub ?
|
|
19430
|
+
const dir = sub ? path34.join(base, sub) : base;
|
|
19368
19431
|
return {
|
|
19369
|
-
description: `create directory ${rel(ctx.root, dir)}${
|
|
19432
|
+
description: `create directory ${rel(ctx.root, dir)}${existsSync23(dir) ? " (already present)" : ""}`,
|
|
19370
19433
|
touches: [rel(ctx.root, dir)]
|
|
19371
19434
|
};
|
|
19372
19435
|
});
|
|
@@ -19376,9 +19439,9 @@ var forgeDirRepair = {
|
|
|
19376
19439
|
const base = artifactsRoot(ctx.root);
|
|
19377
19440
|
const created = [];
|
|
19378
19441
|
for (const sub of ["", ...FORGE_DIR_SUBDIRS]) {
|
|
19379
|
-
const dir = sub ?
|
|
19380
|
-
if (
|
|
19381
|
-
|
|
19442
|
+
const dir = sub ? path34.join(base, sub) : base;
|
|
19443
|
+
if (existsSync23(dir)) continue;
|
|
19444
|
+
mkdirSync8(dir, { recursive: true });
|
|
19382
19445
|
created.push(rel(ctx.root, dir));
|
|
19383
19446
|
}
|
|
19384
19447
|
return created.length ? `created ${created.length} directorie(s): ${created.join(", ")}` : "all directories already present";
|
|
@@ -19386,7 +19449,7 @@ var forgeDirRepair = {
|
|
|
19386
19449
|
async verify(ctx) {
|
|
19387
19450
|
const base = artifactsRoot(ctx.root);
|
|
19388
19451
|
return ["", ...FORGE_DIR_SUBDIRS].every((sub) => {
|
|
19389
|
-
const dir = sub ?
|
|
19452
|
+
const dir = sub ? path34.join(base, sub) : base;
|
|
19390
19453
|
try {
|
|
19391
19454
|
return statSync5(dir).isDirectory();
|
|
19392
19455
|
} catch {
|
|
@@ -19397,11 +19460,11 @@ var forgeDirRepair = {
|
|
|
19397
19460
|
};
|
|
19398
19461
|
var EVIDENCE_KEY_RE = /^[A-Z_][A-Z0-9_]*$/;
|
|
19399
19462
|
function envPathOf(root) {
|
|
19400
|
-
return
|
|
19463
|
+
return path34.join(root, ".env");
|
|
19401
19464
|
}
|
|
19402
19465
|
function presentKeys(root) {
|
|
19403
19466
|
const file = envPathOf(root);
|
|
19404
|
-
if (!
|
|
19467
|
+
if (!existsSync23(file)) return /* @__PURE__ */ new Set();
|
|
19405
19468
|
return new Set(parseEnv(readFileSync19(file, "utf8")).entries.map((e) => e.key));
|
|
19406
19469
|
}
|
|
19407
19470
|
function missingEnvKeys(root, check) {
|
|
@@ -19422,9 +19485,9 @@ function keysToAppend(root, check) {
|
|
|
19422
19485
|
}
|
|
19423
19486
|
function backupEnvFile(ctx, current) {
|
|
19424
19487
|
const stamp = stampOf(ctx.now?.() ?? (/* @__PURE__ */ new Date()).toISOString());
|
|
19425
|
-
const dir =
|
|
19426
|
-
|
|
19427
|
-
writeFileAtomic(
|
|
19488
|
+
const dir = path34.join(artifactsRoot(ctx.root), "backups", `env-${stamp}`);
|
|
19489
|
+
mkdirSync8(dir, { recursive: true });
|
|
19490
|
+
writeFileAtomic(path34.join(dir, ".env"), current);
|
|
19428
19491
|
return dir;
|
|
19429
19492
|
}
|
|
19430
19493
|
var envMissingKeyRepair = {
|
|
@@ -19437,9 +19500,9 @@ var envMissingKeyRepair = {
|
|
|
19437
19500
|
const root = ctx.root;
|
|
19438
19501
|
const toAppend = keysToAppend(root, ctx.check);
|
|
19439
19502
|
const steps = [];
|
|
19440
|
-
if (
|
|
19503
|
+
if (existsSync23(envPathOf(root))) {
|
|
19441
19504
|
steps.push({
|
|
19442
|
-
description: `backup .env to ${rel(root,
|
|
19505
|
+
description: `backup .env to ${rel(root, path34.join(artifactsRoot(root), "backups"))}/env-<ts>/.env`,
|
|
19443
19506
|
touches: [".env", `${rel(root, artifactsRoot(root))}/backups/`]
|
|
19444
19507
|
});
|
|
19445
19508
|
}
|
|
@@ -19456,7 +19519,7 @@ var envMissingKeyRepair = {
|
|
|
19456
19519
|
const root = ctx.root;
|
|
19457
19520
|
if (ctx.dryRun) return "dry-run \u2014 not applied";
|
|
19458
19521
|
const file = envPathOf(root);
|
|
19459
|
-
const exists =
|
|
19522
|
+
const exists = existsSync23(file);
|
|
19460
19523
|
const current = exists ? readFileSync19(file, "utf8") : "";
|
|
19461
19524
|
const env = parseEnv(current);
|
|
19462
19525
|
const present = new Set(env.entries.map((e) => e.key));
|
|
@@ -19474,7 +19537,7 @@ var envMissingKeyRepair = {
|
|
|
19474
19537
|
},
|
|
19475
19538
|
async verify(ctx) {
|
|
19476
19539
|
const file = envPathOf(ctx.root);
|
|
19477
|
-
if (!
|
|
19540
|
+
if (!existsSync23(file)) return false;
|
|
19478
19541
|
const present = new Set(parseEnv(readFileSync19(file, "utf8")).entries.map((e) => e.key));
|
|
19479
19542
|
return missingEnvKeys(ctx.root, ctx.check).every((k) => present.has(k));
|
|
19480
19543
|
}
|
|
@@ -19491,7 +19554,7 @@ var lockfileDriftRepair = {
|
|
|
19491
19554
|
return [
|
|
19492
19555
|
{
|
|
19493
19556
|
description: `run \`${LOCKFILE_REGEN_COMMAND}\` to regenerate .forge-cli/env.lock.json`,
|
|
19494
|
-
touches: [
|
|
19557
|
+
touches: [path34.join(".forge-cli", "env.lock.json")]
|
|
19495
19558
|
},
|
|
19496
19559
|
{
|
|
19497
19560
|
description: `run \`${LOCKFILE_CHECK_COMMAND}\` to confirm the drift cleared`,
|
|
@@ -19511,12 +19574,12 @@ var lockfileDriftRepair = {
|
|
|
19511
19574
|
var ENV_BACKUP_RE = /^env-\d{8}-\d{6}$/;
|
|
19512
19575
|
var REPO_BACKUP_RE = /^([a-z0-9][a-z0-9._-]*)-\d{10,}$/i;
|
|
19513
19576
|
function listBackups(root) {
|
|
19514
|
-
const base =
|
|
19515
|
-
if (!
|
|
19577
|
+
const base = path34.join(artifactsRoot(root), "backups");
|
|
19578
|
+
if (!existsSync23(base)) return [];
|
|
19516
19579
|
const out = [];
|
|
19517
19580
|
for (const entry2 of readdirSync11(base, { withFileTypes: true })) {
|
|
19518
19581
|
if (!entry2.isDirectory()) continue;
|
|
19519
|
-
const dir =
|
|
19582
|
+
const dir = path34.join(base, entry2.name);
|
|
19520
19583
|
try {
|
|
19521
19584
|
const files = readdirSync11(dir, { withFileTypes: true }).filter((f) => f.isFile()).map((f) => f.name).sort();
|
|
19522
19585
|
if (!files.length) continue;
|
|
@@ -19534,18 +19597,18 @@ function restoreTargetFor(backupDirName, fileName) {
|
|
|
19534
19597
|
return fileName;
|
|
19535
19598
|
}
|
|
19536
19599
|
function pickBackupFile(ctx) {
|
|
19537
|
-
const backupsBase =
|
|
19600
|
+
const backupsBase = path34.join(artifactsRoot(ctx.root), "backups");
|
|
19538
19601
|
if (ctx.backup) {
|
|
19539
|
-
const abs =
|
|
19540
|
-
const inside =
|
|
19541
|
-
if (inside.startsWith("..") ||
|
|
19602
|
+
const abs = path34.resolve(ctx.root, ctx.backup);
|
|
19603
|
+
const inside = path34.relative(backupsBase, abs);
|
|
19604
|
+
if (inside.startsWith("..") || path34.isAbsolute(inside)) {
|
|
19542
19605
|
throw new Error(`selected backup escapes .forge-cli/backups: ${ctx.backup}`);
|
|
19543
19606
|
}
|
|
19544
|
-
if (!
|
|
19607
|
+
if (!existsSync23(abs) || !statSync5(abs).isFile()) {
|
|
19545
19608
|
throw new Error(`selected backup not found: ${ctx.backup}`);
|
|
19546
19609
|
}
|
|
19547
|
-
const dirName = inside.split(
|
|
19548
|
-
const fileName2 =
|
|
19610
|
+
const dirName = inside.split(path34.sep)[0] ?? "";
|
|
19611
|
+
const fileName2 = path34.basename(abs);
|
|
19549
19612
|
return { src: abs, rel: inside, dest: restoreTargetFor(dirName, fileName2) };
|
|
19550
19613
|
}
|
|
19551
19614
|
const candidates = listBackups(ctx.root);
|
|
@@ -19554,7 +19617,7 @@ function pickBackupFile(ctx) {
|
|
|
19554
19617
|
if (!cand) throw new Error("no backups available under .forge-cli/backups");
|
|
19555
19618
|
const fileName = cand.files.includes(".env") ? ".env" : cand.files[0];
|
|
19556
19619
|
return {
|
|
19557
|
-
src:
|
|
19620
|
+
src: path34.join(cand.dir, fileName),
|
|
19558
19621
|
rel: `${cand.name}/${fileName}`,
|
|
19559
19622
|
dest: restoreTargetFor(cand.name, fileName)
|
|
19560
19623
|
};
|
|
@@ -19570,7 +19633,7 @@ var backupRestoreRepair = {
|
|
|
19570
19633
|
if (!candidates.length) {
|
|
19571
19634
|
return [
|
|
19572
19635
|
{
|
|
19573
|
-
description: `no backups found under ${rel(ctx.root,
|
|
19636
|
+
description: `no backups found under ${rel(ctx.root, path34.join(artifactsRoot(ctx.root), "backups"))}/`,
|
|
19574
19637
|
touches: []
|
|
19575
19638
|
}
|
|
19576
19639
|
];
|
|
@@ -19596,20 +19659,20 @@ var backupRestoreRepair = {
|
|
|
19596
19659
|
async apply(ctx) {
|
|
19597
19660
|
if (ctx.dryRun) return "dry-run \u2014 not applied";
|
|
19598
19661
|
const picked = pickBackupFile(ctx);
|
|
19599
|
-
const dest =
|
|
19600
|
-
const relDest =
|
|
19601
|
-
if (relDest.startsWith("..") ||
|
|
19662
|
+
const dest = path34.resolve(ctx.root, picked.dest);
|
|
19663
|
+
const relDest = path34.relative(ctx.root, dest);
|
|
19664
|
+
if (relDest.startsWith("..") || path34.isAbsolute(relDest)) {
|
|
19602
19665
|
throw new Error(`restore target escapes root: ${picked.dest}`);
|
|
19603
19666
|
}
|
|
19604
|
-
|
|
19667
|
+
mkdirSync8(path34.dirname(dest), { recursive: true });
|
|
19605
19668
|
writeFileAtomic(dest, readFileSync19(picked.src, "utf8"));
|
|
19606
19669
|
return `restored ${picked.rel} \u2192 ${relDest}`;
|
|
19607
19670
|
},
|
|
19608
19671
|
async verify(ctx) {
|
|
19609
19672
|
try {
|
|
19610
19673
|
const picked = pickBackupFile(ctx);
|
|
19611
|
-
const dest =
|
|
19612
|
-
return
|
|
19674
|
+
const dest = path34.resolve(ctx.root, picked.dest);
|
|
19675
|
+
return existsSync23(dest) && readFileSync19(dest, "utf8") === readFileSync19(picked.src, "utf8");
|
|
19613
19676
|
} catch {
|
|
19614
19677
|
return false;
|
|
19615
19678
|
}
|
|
@@ -19639,11 +19702,11 @@ function classifyFindings(checks) {
|
|
|
19639
19702
|
}
|
|
19640
19703
|
|
|
19641
19704
|
// src/heal/flow.ts
|
|
19642
|
-
import { mkdirSync as
|
|
19643
|
-
import
|
|
19705
|
+
import { mkdirSync as mkdirSync9 } from "fs";
|
|
19706
|
+
import path35 from "path";
|
|
19644
19707
|
function healReceiptPath(root, generatedAt) {
|
|
19645
19708
|
const stamp = generatedAt.replace(/[:.]/g, "-");
|
|
19646
|
-
return
|
|
19709
|
+
return path35.join(artifactsRoot(root), "receipts", `heal-${stamp}.json`);
|
|
19647
19710
|
}
|
|
19648
19711
|
async function planRepairs(checks, opts) {
|
|
19649
19712
|
const classified = classifyFindings(checks);
|
|
@@ -19778,7 +19841,7 @@ async function runHealFlow(checks, opts) {
|
|
|
19778
19841
|
};
|
|
19779
19842
|
if (!dryRun) {
|
|
19780
19843
|
const file = healReceiptPath(opts.root, generatedAt);
|
|
19781
|
-
|
|
19844
|
+
mkdirSync9(path35.dirname(file), { recursive: true });
|
|
19782
19845
|
writeFileAtomic(file, redactSecrets(JSON.stringify(receipt, null, 2)));
|
|
19783
19846
|
}
|
|
19784
19847
|
return receipt;
|
|
@@ -19863,8 +19926,8 @@ async function runDoctor2Command(cli) {
|
|
|
19863
19926
|
}
|
|
19864
19927
|
|
|
19865
19928
|
// src/cmd/registry.ts
|
|
19866
|
-
import
|
|
19867
|
-
import { existsSync as
|
|
19929
|
+
import path36 from "path";
|
|
19930
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync10 } from "fs";
|
|
19868
19931
|
|
|
19869
19932
|
// src/registry/catalog.ts
|
|
19870
19933
|
var REGISTRY_PACKAGES = [
|
|
@@ -19998,7 +20061,7 @@ function describeInstallError(e) {
|
|
|
19998
20061
|
return `${e.stage}: ${e.detail}`;
|
|
19999
20062
|
}
|
|
20000
20063
|
function registryCacheDir(home = cliHome()) {
|
|
20001
|
-
return
|
|
20064
|
+
return path36.join(home, ".forge-cli", "cache", "pypi");
|
|
20002
20065
|
}
|
|
20003
20066
|
var REGISTRY_IGNORED_FLAGS = [
|
|
20004
20067
|
["dir", "--dir"],
|
|
@@ -20092,8 +20155,8 @@ async function persistReceipt(home, receipt) {
|
|
|
20092
20155
|
}
|
|
20093
20156
|
}
|
|
20094
20157
|
function writeRegistryReport(reportPath, doc) {
|
|
20095
|
-
const file =
|
|
20096
|
-
|
|
20158
|
+
const file = path36.resolve(reportPath);
|
|
20159
|
+
mkdirSync10(path36.dirname(file), { recursive: true });
|
|
20097
20160
|
writeFileAtomic(file, redactSecrets(JSON.stringify(doc, null, 2)));
|
|
20098
20161
|
}
|
|
20099
20162
|
async function runRegistryInstall(cli, names, deps = {}) {
|
|
@@ -20272,7 +20335,7 @@ async function runRegistryInstall(cli, names, deps = {}) {
|
|
|
20272
20335
|
const doc = { method: "pypi", results, warnings };
|
|
20273
20336
|
reportTo(doc);
|
|
20274
20337
|
if (cli.reportPath !== void 0) {
|
|
20275
|
-
log.info(`${S.reportSaved}: ${theme.dim(
|
|
20338
|
+
log.info(`${S.reportSaved}: ${theme.dim(path36.resolve(cli.reportPath))}`);
|
|
20276
20339
|
}
|
|
20277
20340
|
if (quiet) {
|
|
20278
20341
|
emitJson(doc, results.every((r2) => r2.ok) ? 0 : 1);
|
|
@@ -20283,9 +20346,13 @@ async function runRegistryInstall(cli, names, deps = {}) {
|
|
|
20283
20346
|
if (results.some((r2) => !r2.ok)) process.exit(1);
|
|
20284
20347
|
const bins = results.map((r2) => {
|
|
20285
20348
|
const inst = loadForgeState(home).forges[r2.forge];
|
|
20286
|
-
|
|
20349
|
+
if (!inst) return "";
|
|
20350
|
+
return inst.shim ? `${r2.forge} \u2192 ${path36.basename(inst.shim)} (em ${path36.dirname(inst.shim)})` : `${r2.forge} \u2192 ${inst.bin}`;
|
|
20287
20351
|
});
|
|
20288
|
-
note(
|
|
20352
|
+
note(
|
|
20353
|
+
bins.filter(Boolean).join("\n"),
|
|
20354
|
+
"Launchers em ~/.forge-cli/bin (no PATH do forge-cli); sem shim, invoque o caminho da venv"
|
|
20355
|
+
);
|
|
20289
20356
|
outro(`${icons.ok} instala\xE7\xE3o registry-first conclu\xEDda`);
|
|
20290
20357
|
return results;
|
|
20291
20358
|
}
|
|
@@ -20347,7 +20414,7 @@ async function runRegistryLock(cli, names, deps = {}) {
|
|
|
20347
20414
|
results.push({ forge: e.name, ok: false, detail: describeResolveError(picked.error) });
|
|
20348
20415
|
continue;
|
|
20349
20416
|
}
|
|
20350
|
-
const out = cli.out ? entries.length === 1 ?
|
|
20417
|
+
const out = cli.out ? entries.length === 1 ? path36.resolve(cli.out) : path36.join(path36.resolve(cli.out), `${e.name}.offline-lock.json`) : path36.resolve(`${e.name}.offline-lock.json`);
|
|
20351
20418
|
if (isPlan) {
|
|
20352
20419
|
results.push({
|
|
20353
20420
|
forge: e.name,
|
|
@@ -20366,7 +20433,7 @@ async function runRegistryLock(cli, names, deps = {}) {
|
|
|
20366
20433
|
results.push({ forge: e.name, ok: false, detail: `${lock.kind}: ${lock.detail}` });
|
|
20367
20434
|
continue;
|
|
20368
20435
|
}
|
|
20369
|
-
|
|
20436
|
+
mkdirSync10(path36.dirname(out), { recursive: true });
|
|
20370
20437
|
writeFileAtomic(out, `${JSON.stringify(lock, null, 2)}
|
|
20371
20438
|
`);
|
|
20372
20439
|
locks.push({ forge: e.name, lock, file: out });
|
|
@@ -20407,7 +20474,7 @@ function runRegistryList(cli, deps = {}) {
|
|
|
20407
20474
|
}
|
|
20408
20475
|
const rows = REGISTRY_PACKAGES.filter((e) => !wanted || wanted.has(e.name)).map((e) => {
|
|
20409
20476
|
const inst = state.forges[e.name];
|
|
20410
|
-
const binOk = inst ? typeof inst.bin === "string" &&
|
|
20477
|
+
const binOk = inst ? typeof inst.bin === "string" && existsSync24(inst.bin) : null;
|
|
20411
20478
|
const consistency = installConsistency(home, e.name);
|
|
20412
20479
|
const relevant = consistency.status !== "not-installed";
|
|
20413
20480
|
return {
|
|
@@ -21132,24 +21199,24 @@ async function runRegistryPin(cli, names, pin, deps = {}) {
|
|
|
21132
21199
|
}
|
|
21133
21200
|
|
|
21134
21201
|
// src/lifecycle.ts
|
|
21135
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
21136
|
-
import
|
|
21202
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync25, mkdirSync as mkdirSync11, readdirSync as readdirSync12, rmSync as rmSync7 } from "fs";
|
|
21203
|
+
import path37 from "path";
|
|
21137
21204
|
function configFilesToBackup(repoDir) {
|
|
21138
21205
|
const out = /* @__PURE__ */ new Set();
|
|
21139
|
-
if (
|
|
21206
|
+
if (existsSync25(path37.join(repoDir, ".env"))) out.add(".env");
|
|
21140
21207
|
for (const file of readdirSync12(repoDir)) {
|
|
21141
21208
|
if (file === ".env.example" || !file.includes(".example.")) continue;
|
|
21142
21209
|
const real = file.replace(".example.", ".");
|
|
21143
|
-
if (
|
|
21210
|
+
if (existsSync25(path37.join(repoDir, real))) out.add(real);
|
|
21144
21211
|
}
|
|
21145
21212
|
return [...out];
|
|
21146
21213
|
}
|
|
21147
21214
|
async function planUninstall(targetDir, repo) {
|
|
21148
21215
|
const dir = resolveInside(targetDir, repo.name);
|
|
21149
21216
|
if (!dir)
|
|
21150
|
-
return { kind: "refused", dir:
|
|
21217
|
+
return { kind: "refused", dir: path37.join(targetDir, repo.name), reason: "invalid-name" };
|
|
21151
21218
|
if (isLinkOrJunction(dir)) return { kind: "refused", dir, reason: "symlink" };
|
|
21152
|
-
if (!
|
|
21219
|
+
if (!existsSync25(dir)) return { kind: "absent", dir };
|
|
21153
21220
|
if (!hasGitRepo(dir)) return { kind: "refused", dir, reason: "non-git" };
|
|
21154
21221
|
const remote = await commandOutput("git", ["-C", dir, "remote", "get-url", "origin"]);
|
|
21155
21222
|
if (remote && remote !== "ok" && !sameRemote(remote, repo.url)) {
|
|
@@ -21163,14 +21230,14 @@ function executeUninstall(targetDir, repo, plan) {
|
|
|
21163
21230
|
if (!expected || expected !== plan.dir) throw new Error("plano de remo\xE7\xE3o inconsistente");
|
|
21164
21231
|
let backupDir = null;
|
|
21165
21232
|
if (plan.backups.length) {
|
|
21166
|
-
backupDir =
|
|
21233
|
+
backupDir = path37.join(artifactsRoot(targetDir), "backups", `${repo.name}-${Date.now()}`);
|
|
21167
21234
|
for (const file of plan.backups) {
|
|
21168
|
-
const dest =
|
|
21169
|
-
|
|
21170
|
-
copyFileSync3(
|
|
21235
|
+
const dest = path37.join(backupDir, file);
|
|
21236
|
+
mkdirSync11(path37.dirname(dest), { recursive: true });
|
|
21237
|
+
copyFileSync3(path37.join(plan.dir, file), dest);
|
|
21171
21238
|
}
|
|
21172
21239
|
}
|
|
21173
|
-
|
|
21240
|
+
rmSync7(plan.dir, { recursive: true, force: true });
|
|
21174
21241
|
forgetRepo(targetDir, repo.name);
|
|
21175
21242
|
return backupDir;
|
|
21176
21243
|
}
|
|
@@ -21199,13 +21266,13 @@ async function rollbackRepo(targetDir, repo) {
|
|
|
21199
21266
|
}
|
|
21200
21267
|
|
|
21201
21268
|
// src/self.ts
|
|
21202
|
-
import
|
|
21269
|
+
import path38 from "path";
|
|
21203
21270
|
function norm(p) {
|
|
21204
|
-
return
|
|
21271
|
+
return path38.resolve(p).replace(/\\/g, "/").toLowerCase();
|
|
21205
21272
|
}
|
|
21206
21273
|
function detectInstallMethod(scriptPath, home) {
|
|
21207
21274
|
const script = norm(scriptPath);
|
|
21208
|
-
if (script.startsWith(`${norm(
|
|
21275
|
+
if (script.startsWith(`${norm(path38.join(home, ".forge-cli", "bin"))}/`)) return "shim";
|
|
21209
21276
|
if (script.includes("/node_modules/")) return "npm-global";
|
|
21210
21277
|
return "source";
|
|
21211
21278
|
}
|
|
@@ -21500,11 +21567,11 @@ async function runUninstallCommand(cli) {
|
|
|
21500
21567
|
}
|
|
21501
21568
|
|
|
21502
21569
|
// src/config.ts
|
|
21503
|
-
import { existsSync as
|
|
21504
|
-
import
|
|
21570
|
+
import { existsSync as existsSync26, readFileSync as readFileSync20 } from "fs";
|
|
21571
|
+
import path39 from "path";
|
|
21505
21572
|
function loadConfig(targetDir) {
|
|
21506
|
-
const file =
|
|
21507
|
-
if (!
|
|
21573
|
+
const file = path39.join(targetDir, "forge-cli.config.json");
|
|
21574
|
+
if (!existsSync26(file)) return null;
|
|
21508
21575
|
let parsed;
|
|
21509
21576
|
try {
|
|
21510
21577
|
parsed = JSON.parse(readFileSync20(file, "utf8"));
|
|
@@ -21550,12 +21617,12 @@ function loadConfig(targetDir) {
|
|
|
21550
21617
|
}
|
|
21551
21618
|
|
|
21552
21619
|
// src/plan.ts
|
|
21553
|
-
import { existsSync as
|
|
21554
|
-
import
|
|
21620
|
+
import { existsSync as existsSync27, readdirSync as readdirSync13 } from "fs";
|
|
21621
|
+
import path40 from "path";
|
|
21555
21622
|
function planLines(repo, targetDir) {
|
|
21556
|
-
const repoDir =
|
|
21623
|
+
const repoDir = path40.join(targetDir, repo.name);
|
|
21557
21624
|
const lines = [];
|
|
21558
|
-
if (!
|
|
21625
|
+
if (!existsSync27(repoDir)) {
|
|
21559
21626
|
lines.push(`${S.planSync}: git clone (${S.planDetectAfter})`);
|
|
21560
21627
|
for (const step of [
|
|
21561
21628
|
S.phaseEnv,
|
|
@@ -21594,11 +21661,11 @@ function planLines(repo, targetDir) {
|
|
|
21594
21661
|
return lines;
|
|
21595
21662
|
}
|
|
21596
21663
|
async function executionSummary(repo, targetDir, native, platform2 = process.platform) {
|
|
21597
|
-
const repoDir =
|
|
21664
|
+
const repoDir = path40.join(targetDir, repo.name);
|
|
21598
21665
|
const trust = repo.origin === "custom" ? "custom" : "builtin";
|
|
21599
21666
|
const commands = [];
|
|
21600
21667
|
let revision;
|
|
21601
|
-
if (!
|
|
21668
|
+
if (!existsSync27(repoDir) || !hasGitRepo(repoDir)) {
|
|
21602
21669
|
commands.push(`git clone -- ${repo.url}`, S.planScriptsAfterClone);
|
|
21603
21670
|
} else {
|
|
21604
21671
|
revision = (await currentHead(repoDir))?.slice(0, 7);
|
|
@@ -21833,8 +21900,8 @@ ${planLines(repo, targetDir).map((line) => ` ${theme.dim(line)}`).join("\n")}`
|
|
|
21833
21900
|
}
|
|
21834
21901
|
|
|
21835
21902
|
// src/cmd/env.ts
|
|
21836
|
-
import { copyFileSync as copyFileSync4, existsSync as
|
|
21837
|
-
import
|
|
21903
|
+
import { copyFileSync as copyFileSync4, existsSync as existsSync29, readFileSync as readFileSync22, readdirSync as readdirSync15 } from "fs";
|
|
21904
|
+
import path42 from "path";
|
|
21838
21905
|
|
|
21839
21906
|
// src/envintel/doctor.ts
|
|
21840
21907
|
function envDoctor(repoDir, envKeys) {
|
|
@@ -21883,8 +21950,8 @@ function envDoctor(repoDir, envKeys) {
|
|
|
21883
21950
|
|
|
21884
21951
|
// src/envintel/migrate.ts
|
|
21885
21952
|
import { randomBytes } from "crypto";
|
|
21886
|
-
import { existsSync as
|
|
21887
|
-
import
|
|
21953
|
+
import { existsSync as existsSync28, mkdirSync as mkdirSync12, readdirSync as readdirSync14, readFileSync as readFileSync21 } from "fs";
|
|
21954
|
+
import path41 from "path";
|
|
21888
21955
|
function timestamp2(d = /* @__PURE__ */ new Date()) {
|
|
21889
21956
|
const p = (n4) => String(n4).padStart(2, "0");
|
|
21890
21957
|
return `${d.getFullYear()}${p(d.getMonth() + 1)}${p(d.getDate())}-${p(d.getHours())}${p(d.getMinutes())}${p(d.getSeconds())}`;
|
|
@@ -21898,12 +21965,12 @@ function addValue(spec, exampleEmpty) {
|
|
|
21898
21965
|
}
|
|
21899
21966
|
function planMigration(repoDir, targetSchema) {
|
|
21900
21967
|
const schema = targetSchema ?? deriveSchema(repoDir);
|
|
21901
|
-
const envPath =
|
|
21902
|
-
const env =
|
|
21968
|
+
const envPath = path41.join(repoDir, ".env");
|
|
21969
|
+
const env = existsSync28(envPath) ? parseEnv(readFileSync21(envPath, "utf8")) : null;
|
|
21903
21970
|
const present = new Set(env?.entries.map((e) => e.key) ?? []);
|
|
21904
|
-
const examplePath =
|
|
21971
|
+
const examplePath = path41.join(repoDir, ".env.example");
|
|
21905
21972
|
const exampleEmpty = /* @__PURE__ */ new Set();
|
|
21906
|
-
if (
|
|
21973
|
+
if (existsSync28(examplePath)) {
|
|
21907
21974
|
for (const e of parseEnv(readFileSync21(examplePath, "utf8")).entries) {
|
|
21908
21975
|
if (e.value === "") exampleEmpty.add(e.key);
|
|
21909
21976
|
}
|
|
@@ -21932,18 +21999,18 @@ function planMigration(repoDir, targetSchema) {
|
|
|
21932
21999
|
return { actions, removesRequireApproval: true };
|
|
21933
22000
|
}
|
|
21934
22001
|
function backupDirFor2(repoDir, ts) {
|
|
21935
|
-
return
|
|
22002
|
+
return path41.join(artifactsRoot(repoDir), "backups", `env-${ts}`);
|
|
21936
22003
|
}
|
|
21937
22004
|
function backupEnv(repoDir, content) {
|
|
21938
22005
|
const dir = backupDirFor2(repoDir, timestamp2());
|
|
21939
|
-
|
|
21940
|
-
const file =
|
|
22006
|
+
mkdirSync12(dir, { recursive: true });
|
|
22007
|
+
const file = path41.join(dir, ".env");
|
|
21941
22008
|
writeFileAtomic(file, content);
|
|
21942
22009
|
return dir;
|
|
21943
22010
|
}
|
|
21944
22011
|
function applyMigration(repoDir, plan, opts = {}) {
|
|
21945
|
-
const envPath =
|
|
21946
|
-
const exists =
|
|
22012
|
+
const envPath = path41.join(repoDir, ".env");
|
|
22013
|
+
const exists = existsSync28(envPath);
|
|
21947
22014
|
const current = exists ? readFileSync21(envPath, "utf8") : "";
|
|
21948
22015
|
const env = parseEnv(current);
|
|
21949
22016
|
const present = new Set(env.entries.map((e) => e.key));
|
|
@@ -21974,16 +22041,16 @@ function applyMigration(repoDir, plan, opts = {}) {
|
|
|
21974
22041
|
};
|
|
21975
22042
|
}
|
|
21976
22043
|
function latestEnvBackup(repoDir) {
|
|
21977
|
-
const root =
|
|
21978
|
-
if (!
|
|
22044
|
+
const root = path41.join(artifactsRoot(repoDir), "backups");
|
|
22045
|
+
if (!existsSync28(root)) return null;
|
|
21979
22046
|
const candidates = readdirSync14(root, { withFileTypes: true }).filter((d) => d.isDirectory() && /^env-\d{8}-\d{6}$/.test(d.name)).map((d) => d.name).sort();
|
|
21980
22047
|
const last = candidates.at(-1);
|
|
21981
|
-
return last ?
|
|
22048
|
+
return last ? path41.join(root, last) : null;
|
|
21982
22049
|
}
|
|
21983
22050
|
function rollbackEnv(repoDir, backupDir) {
|
|
21984
|
-
const src =
|
|
21985
|
-
if (!
|
|
21986
|
-
writeFileAtomic(
|
|
22051
|
+
const src = path41.join(backupDir, ".env");
|
|
22052
|
+
if (!existsSync28(src)) return false;
|
|
22053
|
+
writeFileAtomic(path41.join(repoDir, ".env"), readFileSync21(src, "utf8"));
|
|
21987
22054
|
return true;
|
|
21988
22055
|
}
|
|
21989
22056
|
|
|
@@ -21993,7 +22060,7 @@ function envCandidates(dir) {
|
|
|
21993
22060
|
try {
|
|
21994
22061
|
return readdirSync15(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).filter((name) => {
|
|
21995
22062
|
const r2 = resolveInside(dir, name);
|
|
21996
|
-
return r2 !== null && (
|
|
22063
|
+
return r2 !== null && (existsSync29(path42.join(r2, ".env")) || existsSync29(path42.join(r2, ".env.example")));
|
|
21997
22064
|
}).sort();
|
|
21998
22065
|
} catch {
|
|
21999
22066
|
return [];
|
|
@@ -22040,7 +22107,7 @@ async function runEnvCommand(cli, args) {
|
|
|
22040
22107
|
envKeys = catalogFor(targetDir).find((r2) => r2.name === name)?.envKeys;
|
|
22041
22108
|
} else {
|
|
22042
22109
|
const cwd = process.cwd();
|
|
22043
|
-
if (
|
|
22110
|
+
if (existsSync29(path42.join(cwd, ".env")) || existsSync29(path42.join(cwd, ".env.example"))) {
|
|
22044
22111
|
repoDir = cwd;
|
|
22045
22112
|
}
|
|
22046
22113
|
}
|
|
@@ -22050,7 +22117,7 @@ async function runEnvCommand(cli, args) {
|
|
|
22050
22117
|
if (found.length) note(found.join("\n"), S.envTitle);
|
|
22051
22118
|
return 2;
|
|
22052
22119
|
}
|
|
22053
|
-
const repo = name ??
|
|
22120
|
+
const repo = name ?? path42.basename(repoDir);
|
|
22054
22121
|
if (verb === "inspect") {
|
|
22055
22122
|
const res2 = inspectEnv(repoDir, envKeys);
|
|
22056
22123
|
if (json) {
|
|
@@ -22092,13 +22159,13 @@ async function runEnvCommand(cli, args) {
|
|
|
22092
22159
|
note(lines.join("\n"), `${S.envDoctorTitle} \u2014 ${repo}`);
|
|
22093
22160
|
return res2.checks.some((c5) => c5.result === "fail") ? 1 : 0;
|
|
22094
22161
|
}
|
|
22095
|
-
if (!
|
|
22162
|
+
if (!existsSync29(repoDir)) {
|
|
22096
22163
|
log.error(`${repo}: ${S.statusNotInstalled}`);
|
|
22097
22164
|
return 2;
|
|
22098
22165
|
}
|
|
22099
22166
|
const schema = deriveSchema(repoDir, envKeys);
|
|
22100
22167
|
const secretKeys = new Set(schema.vars.filter((v) => v.secret).map((v) => v.key));
|
|
22101
|
-
const envPath =
|
|
22168
|
+
const envPath = path42.join(repoDir, ".env");
|
|
22102
22169
|
if (verb === "wizard") {
|
|
22103
22170
|
if (dryRun) {
|
|
22104
22171
|
log.error(S.errPlanUnsupported);
|
|
@@ -22108,7 +22175,7 @@ async function runEnvCommand(cli, args) {
|
|
|
22108
22175
|
log.error(S.envWizardNonInteractive);
|
|
22109
22176
|
return 2;
|
|
22110
22177
|
}
|
|
22111
|
-
const env = parseEnv(
|
|
22178
|
+
const env = parseEnv(existsSync29(envPath) ? readFileSync22(envPath, "utf8") : "");
|
|
22112
22179
|
const current = new Map(env.entries.map((e) => [e.key, e]));
|
|
22113
22180
|
const updates = /* @__PURE__ */ new Map();
|
|
22114
22181
|
for (const spec of schema.vars) {
|
|
@@ -22151,7 +22218,7 @@ async function runEnvCommand(cli, args) {
|
|
|
22151
22218
|
}
|
|
22152
22219
|
for (const [k, v] of updates) lines.push(formatEntry(k, v));
|
|
22153
22220
|
const out = serializeEnv({ ...env, lines });
|
|
22154
|
-
if (
|
|
22221
|
+
if (existsSync29(envPath)) copyFileSync4(envPath, `${envPath}.bak`);
|
|
22155
22222
|
writeFileAtomic(envPath, out.endsWith("\n") ? out : `${out}
|
|
22156
22223
|
`);
|
|
22157
22224
|
log.success(`${icons.ok} ${S.envWizardDone} ${theme.dim(`(${envPath}.bak)`)}`);
|
|
@@ -22179,7 +22246,7 @@ async function runEnvCommand(cli, args) {
|
|
|
22179
22246
|
return 0;
|
|
22180
22247
|
}
|
|
22181
22248
|
if (verb === "init") {
|
|
22182
|
-
if (
|
|
22249
|
+
if (existsSync29(envPath)) {
|
|
22183
22250
|
if (json) console.log(JSON.stringify({ command: "env init", repo, created: false }));
|
|
22184
22251
|
else log.info(S.envInitExists);
|
|
22185
22252
|
return 0;
|
|
@@ -22293,13 +22360,13 @@ async function runEnvCommand(cli, args) {
|
|
|
22293
22360
|
}
|
|
22294
22361
|
|
|
22295
22362
|
// src/cmd/hosts.ts
|
|
22296
|
-
import
|
|
22297
|
-
import { existsSync as
|
|
22363
|
+
import path44 from "path";
|
|
22364
|
+
import { existsSync as existsSync31, readdirSync as readdirSync16 } from "fs";
|
|
22298
22365
|
|
|
22299
22366
|
// src/mcp/handshake.ts
|
|
22300
22367
|
import { spawn as spawn2 } from "child_process";
|
|
22301
|
-
import { existsSync as
|
|
22302
|
-
import
|
|
22368
|
+
import { existsSync as existsSync30 } from "fs";
|
|
22369
|
+
import path43 from "path";
|
|
22303
22370
|
import { createInterface as createInterface2 } from "readline";
|
|
22304
22371
|
var PROTOCOL_VERSION = "2024-11-05";
|
|
22305
22372
|
var STEP_MS = 8e3;
|
|
@@ -22388,8 +22455,8 @@ function probeMcpServer(argv, opts = {}) {
|
|
|
22388
22455
|
}
|
|
22389
22456
|
const stepMs = opts.timeoutMs ?? STEP_MS;
|
|
22390
22457
|
const levels = [];
|
|
22391
|
-
if (
|
|
22392
|
-
if (!
|
|
22458
|
+
if (path43.isAbsolute(argv[0])) {
|
|
22459
|
+
if (!existsSync30(argv[0])) {
|
|
22393
22460
|
resolve(err("bin-missing", `bin\xE1rio MCP ausente: ${argv[0]}`));
|
|
22394
22461
|
return;
|
|
22395
22462
|
}
|
|
@@ -22678,12 +22745,12 @@ function forgeDescriptor(targetDir, name, home = cliHome()) {
|
|
|
22678
22745
|
const receipt = loadForgeState(home).forges[name];
|
|
22679
22746
|
const entry2 = registryEntry(name);
|
|
22680
22747
|
if (receipt?.bin && entry2?.mcp?.length) {
|
|
22681
|
-
const binDir =
|
|
22748
|
+
const binDir = path44.dirname(receipt.bin);
|
|
22682
22749
|
const mcp_command = entry2.mcp.map((tok) => {
|
|
22683
22750
|
if (tok === "{bin}") return receipt.bin;
|
|
22684
22751
|
if (tok.startsWith("{binDir}/")) {
|
|
22685
22752
|
const script = tok.slice("{binDir}/".length);
|
|
22686
|
-
return
|
|
22753
|
+
return path44.join(binDir, process.platform === "win32" ? `${script}.exe` : script);
|
|
22687
22754
|
}
|
|
22688
22755
|
return tok;
|
|
22689
22756
|
});
|
|
@@ -22818,7 +22885,7 @@ async function runMcpCommand(cli, args, home = cliHome()) {
|
|
|
22818
22885
|
const candidates = names.length > 0 ? names : [
|
|
22819
22886
|
.../* @__PURE__ */ new Set([
|
|
22820
22887
|
...Object.keys(loadForgeState(home).forges),
|
|
22821
|
-
...
|
|
22888
|
+
...existsSync31(targetDir) ? readdirSync16(targetDir, { withFileTypes: true }).filter((d) => d.isDirectory() && isSafeRepoName(d.name)).map((d) => d.name) : []
|
|
22822
22889
|
])
|
|
22823
22890
|
].sort();
|
|
22824
22891
|
if (candidates.length === 0) {
|
|
@@ -22839,7 +22906,7 @@ async function runMcpCommand(cli, args, home = cliHome()) {
|
|
|
22839
22906
|
results.push({ forge: name, ok: false, detail: "sem mcp_command" });
|
|
22840
22907
|
continue;
|
|
22841
22908
|
}
|
|
22842
|
-
const binPresent =
|
|
22909
|
+
const binPresent = path44.isAbsolute(argv[0]) ? existsSync31(argv[0]) : true;
|
|
22843
22910
|
const item = {
|
|
22844
22911
|
forge: name,
|
|
22845
22912
|
forge_id: forge.forge_id,
|
|
@@ -22848,7 +22915,7 @@ async function runMcpCommand(cli, args, home = cliHome()) {
|
|
|
22848
22915
|
ok: binPresent
|
|
22849
22916
|
};
|
|
22850
22917
|
if (!binPresent) item.detail = "bin\xE1rio MCP ausente na venv";
|
|
22851
|
-
if (verify && binPresent &&
|
|
22918
|
+
if (verify && binPresent && path44.isAbsolute(argv[0])) {
|
|
22852
22919
|
const safeTools = registryEntry(name)?.mcp_safe_tools;
|
|
22853
22920
|
const probe = await probeMcpServer(argv, { safeTools });
|
|
22854
22921
|
let levels = probe.levels;
|
|
@@ -22887,9 +22954,9 @@ async function runMcpCommand(cli, args, home = cliHome()) {
|
|
|
22887
22954
|
}
|
|
22888
22955
|
|
|
22889
22956
|
// src/discovery.ts
|
|
22890
|
-
import { existsSync as
|
|
22957
|
+
import { existsSync as existsSync32, readdirSync as readdirSync17, readFileSync as readFileSync23, statSync as statSync6 } from "fs";
|
|
22891
22958
|
import os5 from "os";
|
|
22892
|
-
import
|
|
22959
|
+
import path45 from "path";
|
|
22893
22960
|
var DEFAULT_LIMITS = {
|
|
22894
22961
|
maxDepth: 3,
|
|
22895
22962
|
maxEntriesPerDir: 500,
|
|
@@ -23054,7 +23121,7 @@ function walkWorkspace(root, limits, budgetLeft) {
|
|
|
23054
23121
|
return { markers, envFiles, topDirs, signals: signals2, truncated };
|
|
23055
23122
|
}
|
|
23056
23123
|
const lower = entry2.name.toLowerCase();
|
|
23057
|
-
const full =
|
|
23124
|
+
const full = path45.join(dir, entry2.name);
|
|
23058
23125
|
const relPath = rel2 ? `${rel2}/${entry2.name}` : entry2.name;
|
|
23059
23126
|
if (entry2.isDirectory()) {
|
|
23060
23127
|
if (skip.has(lower) || isLinkOrJunction(full)) continue;
|
|
@@ -23177,10 +23244,10 @@ function registryState(entry2) {
|
|
|
23177
23244
|
function discoverForge(name, dir, state) {
|
|
23178
23245
|
const sources = [];
|
|
23179
23246
|
const git = !!dir && hasGitRepo(dir);
|
|
23180
|
-
const manifest = dir &&
|
|
23247
|
+
const manifest = dir && existsSync32(dir) ? readForgeManifest(dir) : null;
|
|
23181
23248
|
const entry2 = state?.repos[name];
|
|
23182
23249
|
if (entry2 && entry2.phase !== "not-installed") {
|
|
23183
|
-
const dirExists = !!dir &&
|
|
23250
|
+
const dirExists = !!dir && existsSync32(dir);
|
|
23184
23251
|
sources.push({
|
|
23185
23252
|
origin: "manifest",
|
|
23186
23253
|
// registro diz instalada mas o checkout sumiu → evidência falhando
|
|
@@ -23223,7 +23290,7 @@ async function discoverEnvironment(targetDir, opts) {
|
|
|
23223
23290
|
const start = now();
|
|
23224
23291
|
const limits = { ...DEFAULT_LIMITS, ...opts?.limits };
|
|
23225
23292
|
const budgetLeft = () => limits.timeoutMs - (now() - start);
|
|
23226
|
-
const root =
|
|
23293
|
+
const root = path45.resolve(targetDir);
|
|
23227
23294
|
const walk = walkWorkspace(root, limits, budgetLeft);
|
|
23228
23295
|
const tools = await probeTools(process.platform, budgetLeft);
|
|
23229
23296
|
const hosts = await probeHosts(budgetLeft);
|
|
@@ -23240,7 +23307,7 @@ async function discoverEnvironment(targetDir, opts) {
|
|
|
23240
23307
|
const dir = resolveInside(root, dirName);
|
|
23241
23308
|
if (!dir) continue;
|
|
23242
23309
|
const git = hasGitRepo(dir);
|
|
23243
|
-
const manifest =
|
|
23310
|
+
const manifest = existsSync32(path45.join(dir, "forge.json"));
|
|
23244
23311
|
if (!git && !manifest) continue;
|
|
23245
23312
|
presence.push({ name: dirName, path: dirName, git, manifest });
|
|
23246
23313
|
if (!catalogNames.has(dirName) && manifest) {
|
|
@@ -23909,8 +23976,8 @@ async function answerRepair(targetDir, repos, opts = {}) {
|
|
|
23909
23976
|
}
|
|
23910
23977
|
|
|
23911
23978
|
// src/intel/context.ts
|
|
23912
|
-
import { existsSync as
|
|
23913
|
-
import
|
|
23979
|
+
import { existsSync as existsSync33, readdirSync as readdirSync18, readFileSync as readFileSync24, statSync as statSync7 } from "fs";
|
|
23980
|
+
import path46 from "path";
|
|
23914
23981
|
|
|
23915
23982
|
// src/planner.ts
|
|
23916
23983
|
import { createHash as createHash5 } from "crypto";
|
|
@@ -24008,7 +24075,7 @@ function safeScalar(value) {
|
|
|
24008
24075
|
return redactSecrets(value) === value ? value : null;
|
|
24009
24076
|
}
|
|
24010
24077
|
function safeConfig(targetDir) {
|
|
24011
|
-
const file =
|
|
24078
|
+
const file = path46.join(targetDir, "forge-cli.config.json");
|
|
24012
24079
|
let raw;
|
|
24013
24080
|
try {
|
|
24014
24081
|
raw = JSON.parse(readFileSync24(file, "utf8"));
|
|
@@ -24025,9 +24092,9 @@ function safeConfig(targetDir) {
|
|
|
24025
24092
|
return out;
|
|
24026
24093
|
}
|
|
24027
24094
|
function readManifestObject(repoDir) {
|
|
24028
|
-
const file =
|
|
24095
|
+
const file = path46.join(repoDir, "forge.json");
|
|
24029
24096
|
try {
|
|
24030
|
-
if (!
|
|
24097
|
+
if (!existsSync33(file) || statSync7(file).size > MAX_MANIFEST_BYTES) return null;
|
|
24031
24098
|
const raw = JSON.parse(readFileSync24(file, "utf8"));
|
|
24032
24099
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
|
|
24033
24100
|
} catch {
|
|
@@ -24061,7 +24128,7 @@ function manifestCapabilities(repoDir, repoName, out) {
|
|
|
24061
24128
|
}
|
|
24062
24129
|
}
|
|
24063
24130
|
function activePlans(targetDir, nowIso) {
|
|
24064
|
-
const dir =
|
|
24131
|
+
const dir = path46.join(artifactsRoot(targetDir), "plans");
|
|
24065
24132
|
let names;
|
|
24066
24133
|
try {
|
|
24067
24134
|
names = readdirSync18(dir).filter((n4) => n4.endsWith(".json")).sort();
|
|
@@ -24072,7 +24139,7 @@ function activePlans(targetDir, nowIso) {
|
|
|
24072
24139
|
const out = [];
|
|
24073
24140
|
for (const name of names.slice(0, MAX_PLAN_FILES)) {
|
|
24074
24141
|
try {
|
|
24075
|
-
const doc = JSON.parse(readFileSync24(
|
|
24142
|
+
const doc = JSON.parse(readFileSync24(path46.join(dir, name), "utf8"));
|
|
24076
24143
|
if (!doc || typeof doc !== "object" || Array.isArray(doc)) continue;
|
|
24077
24144
|
const plan = doc;
|
|
24078
24145
|
if (typeof plan.plan_id !== "string" || !plan.plan_id) continue;
|
|
@@ -24143,7 +24210,7 @@ async function buildForgeContext(targetDir, repos, opts = {}) {
|
|
|
24143
24210
|
const byName = new Map(report.forges.map((f) => [f.name, f]));
|
|
24144
24211
|
const capabilities = {};
|
|
24145
24212
|
const forjas = [...repos].sort((a3, b) => cmp2(a3.name, b.name)).map((repo) => {
|
|
24146
|
-
const dir =
|
|
24213
|
+
const dir = path46.join(targetDir, repo.name);
|
|
24147
24214
|
manifestCapabilities(dir, repo.name, capabilities);
|
|
24148
24215
|
const discovered = byName.get(repo.name);
|
|
24149
24216
|
return {
|
|
@@ -24197,8 +24264,8 @@ async function buildForgeContext(targetDir, repos, opts = {}) {
|
|
|
24197
24264
|
}
|
|
24198
24265
|
|
|
24199
24266
|
// src/intel/predict.ts
|
|
24200
|
-
import { existsSync as
|
|
24201
|
-
import
|
|
24267
|
+
import { existsSync as existsSync34, readFileSync as readFileSync25, statSync as statSync8 } from "fs";
|
|
24268
|
+
import path47 from "path";
|
|
24202
24269
|
var MAX_MANIFEST_BYTES2 = 64 * 1024;
|
|
24203
24270
|
var SEMVERISH = /^v?\d+\.\d+\.\d+/;
|
|
24204
24271
|
var ev2 = (source, fact) => ({
|
|
@@ -24208,9 +24275,9 @@ var ev2 = (source, fact) => ({
|
|
|
24208
24275
|
var cmp3 = (a3, b) => a3 < b ? -1 : a3 > b ? 1 : 0;
|
|
24209
24276
|
var mk2 = (id, kind, subject, certainty, version, evidence, explanation) => ({ id, kind, subject, certainty, version, evidence, explanation });
|
|
24210
24277
|
function readManifest(repoDir) {
|
|
24211
|
-
const file =
|
|
24278
|
+
const file = path47.join(repoDir, "forge.json");
|
|
24212
24279
|
try {
|
|
24213
|
-
if (!
|
|
24280
|
+
if (!existsSync34(file) || statSync8(file).size > MAX_MANIFEST_BYTES2) return null;
|
|
24214
24281
|
const raw = JSON.parse(readFileSync25(file, "utf8"));
|
|
24215
24282
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
|
|
24216
24283
|
} catch {
|
|
@@ -24260,7 +24327,7 @@ function declaredRefs(targetDir, repoName, m3) {
|
|
|
24260
24327
|
}
|
|
24261
24328
|
try {
|
|
24262
24329
|
const lock = JSON.parse(
|
|
24263
|
-
readFileSync25(
|
|
24330
|
+
readFileSync25(path47.join(artifactsRoot(targetDir), "env.lock.json"), "utf8")
|
|
24264
24331
|
);
|
|
24265
24332
|
const head = lock?.forges?.[repoName]?.head;
|
|
24266
24333
|
if (typeof head === "string" && SEMVERISH.test(head)) {
|
|
@@ -24527,7 +24594,7 @@ async function predictCompatibility(targetDir, repos, opts = {}) {
|
|
|
24527
24594
|
const allClaims = [];
|
|
24528
24595
|
for (const repo of sorted) {
|
|
24529
24596
|
const dir = resolveInside(targetDir, repo.name);
|
|
24530
|
-
if (!dir || !
|
|
24597
|
+
if (!dir || !existsSync34(dir)) continue;
|
|
24531
24598
|
const m3 = readManifest(dir);
|
|
24532
24599
|
manifests.set(repo.name, m3);
|
|
24533
24600
|
if (m3) out.push(...schemaChange(repo.name, m3));
|
|
@@ -24539,7 +24606,7 @@ async function predictCompatibility(targetDir, repos, opts = {}) {
|
|
|
24539
24606
|
out.push(...envVarFindings("<workspace>", targetDir));
|
|
24540
24607
|
for (const repo of sorted) {
|
|
24541
24608
|
const dir = resolveInside(targetDir, repo.name);
|
|
24542
|
-
if (!dir || !
|
|
24609
|
+
if (!dir || !existsSync34(dir)) continue;
|
|
24543
24610
|
out.push(...envVarFindings(repo.name, dir, repo.envKeys));
|
|
24544
24611
|
}
|
|
24545
24612
|
out.push(...installConflict(allClaims));
|
|
@@ -24547,13 +24614,13 @@ async function predictCompatibility(targetDir, repos, opts = {}) {
|
|
|
24547
24614
|
}
|
|
24548
24615
|
|
|
24549
24616
|
// src/ai/bridge.ts
|
|
24550
|
-
import { mkdirSync as
|
|
24551
|
-
import
|
|
24617
|
+
import { mkdirSync as mkdirSync14 } from "fs";
|
|
24618
|
+
import path49 from "path";
|
|
24552
24619
|
|
|
24553
24620
|
// src/ai/cache.ts
|
|
24554
24621
|
import { createHash as createHash6 } from "crypto";
|
|
24555
|
-
import { existsSync as
|
|
24556
|
-
import
|
|
24622
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync13, readFileSync as readFileSync26, readdirSync as readdirSync19, rmSync as rmSync8 } from "fs";
|
|
24623
|
+
import path48 from "path";
|
|
24557
24624
|
|
|
24558
24625
|
// src/ai/types.ts
|
|
24559
24626
|
var AI_PROPOSAL_SCHEMA = "forge/AiProposal/v1";
|
|
@@ -24636,7 +24703,7 @@ function validateProposal(proposal, catalog) {
|
|
|
24636
24703
|
var AI_CACHE_MAX_ENTRIES = 32;
|
|
24637
24704
|
var KEY_RE = /^[a-f0-9]{64}$/;
|
|
24638
24705
|
function aiCacheDir(rootDir) {
|
|
24639
|
-
return
|
|
24706
|
+
return path48.join(artifactsRoot(rootDir), "ai", "cache");
|
|
24640
24707
|
}
|
|
24641
24708
|
function canonical(value) {
|
|
24642
24709
|
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
@@ -24651,12 +24718,12 @@ function aiCacheKey(payload) {
|
|
|
24651
24718
|
}
|
|
24652
24719
|
function entryFile(rootDir, key) {
|
|
24653
24720
|
if (!KEY_RE.test(key)) return null;
|
|
24654
|
-
return
|
|
24721
|
+
return path48.join(aiCacheDir(rootDir), `${key}.json`);
|
|
24655
24722
|
}
|
|
24656
24723
|
function aiCacheGet(rootDir, key, opts) {
|
|
24657
24724
|
if (opts.ttlMs <= 0) return null;
|
|
24658
24725
|
const file = entryFile(rootDir, key);
|
|
24659
|
-
if (!file || !
|
|
24726
|
+
if (!file || !existsSync35(file)) return null;
|
|
24660
24727
|
try {
|
|
24661
24728
|
const entry2 = JSON.parse(readFileSync26(file, "utf8"));
|
|
24662
24729
|
const now = opts.now ?? Date.now();
|
|
@@ -24672,7 +24739,7 @@ function aiCacheSet(rootDir, key, proposal, opts = {}) {
|
|
|
24672
24739
|
const file = entryFile(rootDir, key);
|
|
24673
24740
|
if (!file) return;
|
|
24674
24741
|
const dir = aiCacheDir(rootDir);
|
|
24675
|
-
|
|
24742
|
+
mkdirSync13(dir, { recursive: true });
|
|
24676
24743
|
const entry2 = { at: opts.now ?? Date.now(), proposal };
|
|
24677
24744
|
writeFileAtomic(file, JSON.stringify(entry2));
|
|
24678
24745
|
evictOldest(dir, opts.maxEntries ?? AI_CACHE_MAX_ENTRIES);
|
|
@@ -24681,14 +24748,14 @@ function evictOldest(dir, maxEntries) {
|
|
|
24681
24748
|
if (maxEntries < 1) return;
|
|
24682
24749
|
let files;
|
|
24683
24750
|
try {
|
|
24684
|
-
files = readdirSync19(dir).filter((f) => KEY_RE.test(
|
|
24751
|
+
files = readdirSync19(dir).filter((f) => KEY_RE.test(path48.basename(f, ".json")));
|
|
24685
24752
|
} catch {
|
|
24686
24753
|
return;
|
|
24687
24754
|
}
|
|
24688
24755
|
if (files.length <= maxEntries) return;
|
|
24689
|
-
const aged = files.map((file) => ({ file, at: entryAge(
|
|
24756
|
+
const aged = files.map((file) => ({ file, at: entryAge(path48.join(dir, file)) })).sort((a3, b) => a3.at - b.at || a3.file.localeCompare(b.file));
|
|
24690
24757
|
for (const { file } of aged.slice(0, aged.length - maxEntries)) {
|
|
24691
|
-
|
|
24758
|
+
rmSync8(path48.join(dir, file), { force: true });
|
|
24692
24759
|
}
|
|
24693
24760
|
}
|
|
24694
24761
|
function entryAge(file) {
|
|
@@ -24745,18 +24812,18 @@ function sanitizeForAi(input) {
|
|
|
24745
24812
|
}
|
|
24746
24813
|
function assertNoSecrets(input) {
|
|
24747
24814
|
const hits = /* @__PURE__ */ new Set();
|
|
24748
|
-
const visit = (value,
|
|
24815
|
+
const visit = (value, path53) => {
|
|
24749
24816
|
if (typeof value === "string") {
|
|
24750
|
-
if (redactSecrets(value) !== value) hits.add(
|
|
24817
|
+
if (redactSecrets(value) !== value) hits.add(path53 || "<root>");
|
|
24751
24818
|
return;
|
|
24752
24819
|
}
|
|
24753
24820
|
if (Array.isArray(value)) {
|
|
24754
|
-
value.forEach((item, index) => visit(item, `${
|
|
24821
|
+
value.forEach((item, index) => visit(item, `${path53}[${index}]`));
|
|
24755
24822
|
return;
|
|
24756
24823
|
}
|
|
24757
24824
|
if (value && typeof value === "object") {
|
|
24758
24825
|
for (const [key, child] of Object.entries(value)) {
|
|
24759
|
-
const childPath =
|
|
24826
|
+
const childPath = path53 ? `${path53}.${key}` : key;
|
|
24760
24827
|
if (isDeniedKey(key)) {
|
|
24761
24828
|
hits.add(childPath);
|
|
24762
24829
|
continue;
|
|
@@ -24813,9 +24880,9 @@ async function runAiBridge(req, cfg, deps) {
|
|
|
24813
24880
|
if (Buffer.byteLength(body, "utf8") > cfg.maxContextBytes) {
|
|
24814
24881
|
return { ok: false, reason: "bridge-error", detail: "context-too-large" };
|
|
24815
24882
|
}
|
|
24816
|
-
const aiDir =
|
|
24817
|
-
|
|
24818
|
-
const contextFile =
|
|
24883
|
+
const aiDir = path49.join(artifactsRoot(rootDir), "ai");
|
|
24884
|
+
mkdirSync14(aiDir, { recursive: true });
|
|
24885
|
+
const contextFile = path49.join(aiDir, `context-${digest}.json`);
|
|
24819
24886
|
writeFileAtomic(contextFile, body);
|
|
24820
24887
|
const argv = command.map((part) => part === "{context}" ? contextFile : part);
|
|
24821
24888
|
if (!command.includes("{context}")) argv.push(contextFile);
|
|
@@ -24856,8 +24923,8 @@ async function runAiBridge(req, cfg, deps) {
|
|
|
24856
24923
|
}
|
|
24857
24924
|
|
|
24858
24925
|
// src/portability/bundle.ts
|
|
24859
|
-
import { existsSync as
|
|
24860
|
-
import
|
|
24926
|
+
import { existsSync as existsSync36, mkdirSync as mkdirSync15, readFileSync as readFileSync27 } from "fs";
|
|
24927
|
+
import path50 from "path";
|
|
24861
24928
|
|
|
24862
24929
|
// src/portability/manifest.ts
|
|
24863
24930
|
import { createHash as createHash7 } from "crypto";
|
|
@@ -24944,19 +25011,19 @@ var SecretKeysRefusedError = class extends Error {
|
|
|
24944
25011
|
function writeJson(dest, doc) {
|
|
24945
25012
|
const content = JSON.stringify(doc, null, 2);
|
|
24946
25013
|
writeFileAtomic(dest, content);
|
|
24947
|
-
return { path:
|
|
25014
|
+
return { path: path50.basename(dest), content };
|
|
24948
25015
|
}
|
|
24949
25016
|
function pinVersion(repoDir) {
|
|
24950
25017
|
for (const file of ["forge.json", "package.json"]) {
|
|
24951
25018
|
try {
|
|
24952
|
-
const parsed = JSON.parse(readFileSync27(
|
|
25019
|
+
const parsed = JSON.parse(readFileSync27(path50.join(repoDir, file), "utf8"));
|
|
24953
25020
|
const v = parsed?.version;
|
|
24954
25021
|
if (typeof v === "string" && v) return v;
|
|
24955
25022
|
} catch {
|
|
24956
25023
|
}
|
|
24957
25024
|
}
|
|
24958
25025
|
try {
|
|
24959
|
-
const pyproject = readFileSync27(
|
|
25026
|
+
const pyproject = readFileSync27(path50.join(repoDir, "pyproject.toml"), "utf8");
|
|
24960
25027
|
const m3 = /^version\s*=\s*"([^"]+)"/m.exec(pyproject);
|
|
24961
25028
|
if (m3) return m3[1];
|
|
24962
25029
|
} catch {
|
|
@@ -24967,8 +25034,8 @@ function collectPins(targetDir, repos) {
|
|
|
24967
25034
|
const state = loadState(targetDir);
|
|
24968
25035
|
const pins = /* @__PURE__ */ new Map();
|
|
24969
25036
|
for (const repo of repos) {
|
|
24970
|
-
const repoDir =
|
|
24971
|
-
const version =
|
|
25037
|
+
const repoDir = path50.join(targetDir, repo.name);
|
|
25038
|
+
const version = existsSync36(repoDir) ? pinVersion(repoDir) : void 0;
|
|
24972
25039
|
const head = state?.repos[repo.name]?.head;
|
|
24973
25040
|
const ref = state?.pins?.[repo.name] ?? head;
|
|
24974
25041
|
pins.set(repo.name, {
|
|
@@ -24994,15 +25061,15 @@ async function exportBundle(targetDir, repos, dest, opts) {
|
|
|
24994
25061
|
}
|
|
24995
25062
|
const schemaByRepo = /* @__PURE__ */ new Map();
|
|
24996
25063
|
for (const repo of repos) {
|
|
24997
|
-
const repoDir =
|
|
24998
|
-
const schema =
|
|
25064
|
+
const repoDir = path50.join(targetDir, repo.name);
|
|
25065
|
+
const schema = existsSync36(repoDir) ? deriveSchema(repoDir, repo.envKeys) : { vars: [] };
|
|
24999
25066
|
schemaByRepo.set(repo.name, schema);
|
|
25000
25067
|
for (const v of schema.vars) {
|
|
25001
25068
|
if (v.secret && whitelist.includes(v.key)) refused.add(v.key);
|
|
25002
25069
|
}
|
|
25003
25070
|
}
|
|
25004
25071
|
if (refused.size) throw new SecretKeysRefusedError([...refused]);
|
|
25005
|
-
|
|
25072
|
+
mkdirSync15(dest, { recursive: true });
|
|
25006
25073
|
const inputs = [];
|
|
25007
25074
|
const produced = [
|
|
25008
25075
|
BUNDLE_FILES.envelope,
|
|
@@ -25015,29 +25082,29 @@ async function exportBundle(targetDir, repos, dest, opts) {
|
|
|
25015
25082
|
BUNDLE_FILES.integrity
|
|
25016
25083
|
].sort();
|
|
25017
25084
|
inputs.push(
|
|
25018
|
-
writeJson(
|
|
25085
|
+
writeJson(path50.join(dest, BUNDLE_FILES.envelope), {
|
|
25019
25086
|
schema: BUNDLE_SCHEMA,
|
|
25020
25087
|
generatedAt: now,
|
|
25021
25088
|
files: produced
|
|
25022
25089
|
})
|
|
25023
25090
|
);
|
|
25024
|
-
const profile = { ...exportProfile(targetDir,
|
|
25091
|
+
const profile = { ...exportProfile(targetDir, path50.join(dest, BUNDLE_FILES.profile)) };
|
|
25025
25092
|
const normalizedProfile = { ...profile, exported_at: now };
|
|
25026
|
-
inputs.push(writeJson(
|
|
25093
|
+
inputs.push(writeJson(path50.join(dest, BUNDLE_FILES.profile), normalizedProfile));
|
|
25027
25094
|
const lock = { ...writeEnvLock(targetDir, repos), generated_at: now };
|
|
25028
|
-
inputs.push(writeJson(
|
|
25029
|
-
inputs.push(writeJson(
|
|
25095
|
+
inputs.push(writeJson(path50.join(dest, BUNDLE_FILES.lock), lock));
|
|
25096
|
+
inputs.push(writeJson(path50.join(dest, BUNDLE_FILES.lockCheck), checkEnvLock(targetDir, repos)));
|
|
25030
25097
|
const envKeys = {};
|
|
25031
25098
|
for (const repo of repos) {
|
|
25032
25099
|
const keys = [...new Set((schemaByRepo.get(repo.name)?.vars ?? []).map((v) => v.key))].sort();
|
|
25033
25100
|
envKeys[repo.name] = keys;
|
|
25034
25101
|
}
|
|
25035
|
-
inputs.push(writeJson(
|
|
25102
|
+
inputs.push(writeJson(path50.join(dest, BUNDLE_FILES.envKeys), envKeys));
|
|
25036
25103
|
if (whitelist.length) {
|
|
25037
25104
|
const values = {};
|
|
25038
25105
|
for (const repo of repos) {
|
|
25039
|
-
const envFile =
|
|
25040
|
-
if (!
|
|
25106
|
+
const envFile = path50.join(targetDir, repo.name, ".env");
|
|
25107
|
+
if (!existsSync36(envFile)) continue;
|
|
25041
25108
|
const entries = parseEnv(readFileSync27(envFile, "utf8")).entries;
|
|
25042
25109
|
const found = {};
|
|
25043
25110
|
for (const key of whitelist) {
|
|
@@ -25046,11 +25113,11 @@ async function exportBundle(targetDir, repos, dest, opts) {
|
|
|
25046
25113
|
}
|
|
25047
25114
|
if (Object.keys(found).length) values[repo.name] = found;
|
|
25048
25115
|
}
|
|
25049
|
-
inputs.push(writeJson(
|
|
25116
|
+
inputs.push(writeJson(path50.join(dest, BUNDLE_FILES.envValues), values));
|
|
25050
25117
|
}
|
|
25051
|
-
inputs.push(writeJson(
|
|
25118
|
+
inputs.push(writeJson(path50.join(dest, BUNDLE_FILES.pins), collectPins(targetDir, repos)));
|
|
25052
25119
|
const manifest = buildIntegrityManifest(inputs, { now });
|
|
25053
|
-
inputs.push(writeJson(
|
|
25120
|
+
inputs.push(writeJson(path50.join(dest, BUNDLE_FILES.integrity), manifest));
|
|
25054
25121
|
return { ok: true, dir: dest, files: produced, manifest };
|
|
25055
25122
|
}
|
|
25056
25123
|
function readJsonFile(file) {
|
|
@@ -25104,38 +25171,38 @@ function readPins(file) {
|
|
|
25104
25171
|
function importBundle(src, opts) {
|
|
25105
25172
|
const read = (p) => {
|
|
25106
25173
|
try {
|
|
25107
|
-
return readFileSync27(
|
|
25174
|
+
return readFileSync27(path50.join(src, p));
|
|
25108
25175
|
} catch {
|
|
25109
25176
|
return null;
|
|
25110
25177
|
}
|
|
25111
25178
|
};
|
|
25112
|
-
const manifestRaw = readJsonFile(
|
|
25179
|
+
const manifestRaw = readJsonFile(path50.join(src, BUNDLE_FILES.integrity));
|
|
25113
25180
|
const manifest = manifestRaw && typeof manifestRaw === "object" && manifestRaw.schema === "forge/IntegrityManifest/v1" ? manifestRaw : null;
|
|
25114
25181
|
const verify = opts?.verifyIntegrity === false ? { ok: true } : verifyIntegrityManifest(manifest, read);
|
|
25115
|
-
const profile = importProfile(
|
|
25182
|
+
const profile = importProfile(path50.join(src, BUNDLE_FILES.profile));
|
|
25116
25183
|
return {
|
|
25117
25184
|
dir: src,
|
|
25118
25185
|
verify,
|
|
25119
25186
|
manifest,
|
|
25120
25187
|
profile: "error" in profile ? null : profile,
|
|
25121
|
-
lockfile: readLockfile(
|
|
25122
|
-
lockCheck: readLockCheck(
|
|
25123
|
-
envKeys: readStringMap(
|
|
25124
|
-
envValues: readValueMap(
|
|
25125
|
-
pins: readPins(
|
|
25188
|
+
lockfile: readLockfile(path50.join(src, BUNDLE_FILES.lock)),
|
|
25189
|
+
lockCheck: readLockCheck(path50.join(src, BUNDLE_FILES.lockCheck)),
|
|
25190
|
+
envKeys: readStringMap(path50.join(src, BUNDLE_FILES.envKeys)),
|
|
25191
|
+
envValues: readValueMap(path50.join(src, BUNDLE_FILES.envValues)),
|
|
25192
|
+
pins: readPins(path50.join(src, BUNDLE_FILES.pins))
|
|
25126
25193
|
};
|
|
25127
25194
|
}
|
|
25128
25195
|
|
|
25129
25196
|
// src/intel/learning.ts
|
|
25130
|
-
import { existsSync as
|
|
25131
|
-
import
|
|
25197
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync16, readFileSync as readFileSync28 } from "fs";
|
|
25198
|
+
import path51 from "path";
|
|
25132
25199
|
var LEARNING_SCHEMA = "forge-cli/LearningStore/v1";
|
|
25133
25200
|
function learningFile(targetDir) {
|
|
25134
|
-
return
|
|
25201
|
+
return path51.join(artifactsRoot(targetDir), "learning.json");
|
|
25135
25202
|
}
|
|
25136
25203
|
function readStore(targetDir) {
|
|
25137
25204
|
const file = learningFile(targetDir);
|
|
25138
|
-
if (!
|
|
25205
|
+
if (!existsSync37(file)) return { schema: LEARNING_SCHEMA, enabled: false, observations: [] };
|
|
25139
25206
|
try {
|
|
25140
25207
|
const parsed = JSON.parse(readFileSync28(file, "utf8"));
|
|
25141
25208
|
if (parsed?.schema !== LEARNING_SCHEMA || !Array.isArray(parsed.observations)) {
|
|
@@ -25154,7 +25221,7 @@ function readStore(targetDir) {
|
|
|
25154
25221
|
}
|
|
25155
25222
|
function writeStore(targetDir, store) {
|
|
25156
25223
|
const file = learningFile(targetDir);
|
|
25157
|
-
|
|
25224
|
+
mkdirSync16(path51.dirname(file), { recursive: true });
|
|
25158
25225
|
writeFileAtomic(file, JSON.stringify(store, null, 2));
|
|
25159
25226
|
}
|
|
25160
25227
|
function learningStatus(targetDir) {
|
|
@@ -25395,8 +25462,8 @@ async function runLearningCommand(cli) {
|
|
|
25395
25462
|
}
|
|
25396
25463
|
|
|
25397
25464
|
// src/wizard.ts
|
|
25398
|
-
import { existsSync as
|
|
25399
|
-
import
|
|
25465
|
+
import { existsSync as existsSync38 } from "fs";
|
|
25466
|
+
import path52 from "path";
|
|
25400
25467
|
var STEP_LABELS = [
|
|
25401
25468
|
S.wizardStepDetect,
|
|
25402
25469
|
S.wizardStepRecommend,
|
|
@@ -25435,8 +25502,8 @@ function renderOperationPlan(plan) {
|
|
|
25435
25502
|
return lines.join("\n");
|
|
25436
25503
|
}
|
|
25437
25504
|
function planActionsFor(repo, targetDir, steps) {
|
|
25438
|
-
const repoDir =
|
|
25439
|
-
const cloned =
|
|
25505
|
+
const repoDir = path52.join(targetDir, repo.name);
|
|
25506
|
+
const cloned = existsSync38(repoDir) && hasGitRepo(repoDir);
|
|
25440
25507
|
const actions = [
|
|
25441
25508
|
{
|
|
25442
25509
|
kind: "clone",
|
|
@@ -25456,7 +25523,7 @@ function planActionsFor(repo, targetDir, steps) {
|
|
|
25456
25523
|
actions.push({
|
|
25457
25524
|
kind: "env",
|
|
25458
25525
|
label: "provisionar .env (nunca sobrescreve)",
|
|
25459
|
-
paths: [
|
|
25526
|
+
paths: [path52.join(repo.name, ".env")],
|
|
25460
25527
|
reversible: false
|
|
25461
25528
|
});
|
|
25462
25529
|
if (steps.includes("skills")) {
|
|
@@ -25606,7 +25673,7 @@ async function runWizard(cli) {
|
|
|
25606
25673
|
const planForges = repos.map((repo) => ({
|
|
25607
25674
|
forge_id: repo.name,
|
|
25608
25675
|
repo: repo.name,
|
|
25609
|
-
target_dir:
|
|
25676
|
+
target_dir: path52.join(targetDir, repo.name),
|
|
25610
25677
|
actions: filterSteps(planActionsFor(repo, targetDir, steps), steps)
|
|
25611
25678
|
}));
|
|
25612
25679
|
const plan = buildPlan({
|
|
@@ -26000,7 +26067,8 @@ async function main() {
|
|
|
26000
26067
|
log.error(S.errReinstallNeedsNames);
|
|
26001
26068
|
process.exit(2);
|
|
26002
26069
|
}
|
|
26003
|
-
|
|
26070
|
+
const installWantsRegistry = resolved.command === "install" && cli.method !== "git" && (cli.method === "registry" || resolved.names.length > 0 || cli.all === true || (cli.only?.length ?? 0) > 0 || isNonInteractive(cli));
|
|
26071
|
+
if (installWantsRegistry) {
|
|
26004
26072
|
const names = resolved.names.length ? resolved.names : cli.all ? registryNames() : cli.only ?? [];
|
|
26005
26073
|
if (!names.length) {
|
|
26006
26074
|
log.error(S.errRegistryNeedsNames);
|