theforge-cli 0.2.5 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/index.js +575 -101
- package/docs/installation.md +48 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2664,6 +2664,19 @@ var FORGE_REPOS = [
|
|
|
2664
2664
|
kind: "node",
|
|
2665
2665
|
verify: ["npx", "--no-install", "ng", "version"],
|
|
2666
2666
|
nextSteps: ["npm start"]
|
|
2667
|
+
},
|
|
2668
|
+
{
|
|
2669
|
+
// lf-0031 (gap G7): o repositório existe e é clonável, mas ainda não tem
|
|
2670
|
+
// rota de instalação — sem setup.sh/forge_bootstrap.py na fonte e sem
|
|
2671
|
+
// dist no PyPI (verificado). Sem `nativeInstall`: o fluxo git reporta
|
|
2672
|
+
// `noSetupSteps` honestamente; o registry recusa com "sem dist no
|
|
2673
|
+
// catálogo". Nenhum caminho finge uma instalação que não existe.
|
|
2674
|
+
name: "data-sentinel-forge",
|
|
2675
|
+
url: "https://github.com/EdgarSocrates98/data-sentinel-forge",
|
|
2676
|
+
description: "Diagn\xF3stico de runtime (logs/plans Spark, doctor-bundles)",
|
|
2677
|
+
kind: "python",
|
|
2678
|
+
verify: ["data-sentinel", "--help"],
|
|
2679
|
+
nextSteps: ["data-sentinel --help"]
|
|
2667
2680
|
}
|
|
2668
2681
|
];
|
|
2669
2682
|
|
|
@@ -10787,6 +10800,9 @@ var S = {
|
|
|
10787
10800
|
recoveredRun: "Execu\xE7\xE3o anterior interrompida recuperada do estado persistido",
|
|
10788
10801
|
breakingTitle: "An\xE1lise de breaking changes",
|
|
10789
10802
|
rollbackScopeNote: "Rollback cobre apenas efeitos revers\xEDveis documentados",
|
|
10803
|
+
integrationsTitle: "Integra\xE7\xF5es opcionais",
|
|
10804
|
+
integrationsOffer: "Integra\xE7\xF5es opcionais \u2014 marque para instalar (nada entra sem sua sele\xE7\xE3o)",
|
|
10805
|
+
integrationNoInstallRoute: "sem dist no cat\xE1logo registry \u2014 instale via git (`--method git`) ou pelo m\xE9todo do reposit\xF3rio",
|
|
10790
10806
|
usage: `forge-cli \u2014 instalador do ecossistema Forge
|
|
10791
10807
|
|
|
10792
10808
|
Uso:
|
|
@@ -19707,7 +19723,7 @@ function errMsg(error) {
|
|
|
19707
19723
|
}
|
|
19708
19724
|
|
|
19709
19725
|
// src/doctor2/domains.ts
|
|
19710
|
-
import { existsSync as
|
|
19726
|
+
import { existsSync as existsSync25, readdirSync as readdirSync11, rmSync as rmSync6, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
19711
19727
|
import os4 from "os";
|
|
19712
19728
|
import path34 from "path";
|
|
19713
19729
|
|
|
@@ -20188,6 +20204,303 @@ function analyzeBumps(current, candidates) {
|
|
|
20188
20204
|
}).sort((a3, b) => a3.to.localeCompare(b.to, void 0, { numeric: true }));
|
|
20189
20205
|
}
|
|
20190
20206
|
|
|
20207
|
+
// src/integrations.ts
|
|
20208
|
+
import { existsSync as existsSync24 } from "fs";
|
|
20209
|
+
var OPTIONAL_INTEGRATIONS = {
|
|
20210
|
+
"the-forge": [
|
|
20211
|
+
{
|
|
20212
|
+
name: "spark-forge-aws",
|
|
20213
|
+
reason: "an\xE1lise est\xE1tica/runtime PySpark \u2014 delegate do orquestrador",
|
|
20214
|
+
adapter: true,
|
|
20215
|
+
installable: true
|
|
20216
|
+
},
|
|
20217
|
+
{
|
|
20218
|
+
name: "spark-forge-azure",
|
|
20219
|
+
reason: "an\xE1lise PySpark em Azure \u2014 delegate do orquestrador",
|
|
20220
|
+
adapter: true,
|
|
20221
|
+
installable: true
|
|
20222
|
+
},
|
|
20223
|
+
{
|
|
20224
|
+
name: "api-forge",
|
|
20225
|
+
reason: "constru\xE7\xE3o/contratos de API \u2014 delegate do orquestrador",
|
|
20226
|
+
adapter: true,
|
|
20227
|
+
installable: true
|
|
20228
|
+
},
|
|
20229
|
+
{
|
|
20230
|
+
name: "platform-forge",
|
|
20231
|
+
reason: "postura de plataforma \u2014 delegate do orquestrador",
|
|
20232
|
+
adapter: true,
|
|
20233
|
+
installable: true
|
|
20234
|
+
},
|
|
20235
|
+
{
|
|
20236
|
+
name: "forge-doctor-data",
|
|
20237
|
+
reason: "verifica\xE7\xE3o independente de dados \u2014 delegate do orquestrador",
|
|
20238
|
+
adapter: true,
|
|
20239
|
+
installable: true
|
|
20240
|
+
},
|
|
20241
|
+
{
|
|
20242
|
+
name: "forge-doctor-api",
|
|
20243
|
+
reason: "verifica\xE7\xE3o independente de API \u2014 delegate do orquestrador",
|
|
20244
|
+
adapter: true,
|
|
20245
|
+
installable: true
|
|
20246
|
+
},
|
|
20247
|
+
{
|
|
20248
|
+
name: "data-sentinel-forge",
|
|
20249
|
+
reason: "diagn\xF3stico de runtime (logs/plans) \u2014 sem adapter the-forge e sem rota de install: gap documentado, nunca obrigat\xF3rio",
|
|
20250
|
+
adapter: false,
|
|
20251
|
+
installable: false
|
|
20252
|
+
}
|
|
20253
|
+
],
|
|
20254
|
+
"spark-forge-aws": [
|
|
20255
|
+
{
|
|
20256
|
+
name: "forge-doctor-data",
|
|
20257
|
+
reason: "verificador independente das an\xE1lises produzidas",
|
|
20258
|
+
adapter: true,
|
|
20259
|
+
installable: true
|
|
20260
|
+
},
|
|
20261
|
+
{
|
|
20262
|
+
name: "platform-forge",
|
|
20263
|
+
reason: "estado da plataforma onde o job roda",
|
|
20264
|
+
adapter: true,
|
|
20265
|
+
installable: true
|
|
20266
|
+
},
|
|
20267
|
+
{
|
|
20268
|
+
name: "spark-forge-azure",
|
|
20269
|
+
reason: "fallback declarado (mesma fam\xEDlia spark)",
|
|
20270
|
+
adapter: true,
|
|
20271
|
+
installable: true
|
|
20272
|
+
},
|
|
20273
|
+
{
|
|
20274
|
+
name: "api-forge",
|
|
20275
|
+
reason: "contratos de API adjacentes ao pipeline",
|
|
20276
|
+
adapter: true,
|
|
20277
|
+
installable: true
|
|
20278
|
+
},
|
|
20279
|
+
{
|
|
20280
|
+
name: "data-sentinel-forge",
|
|
20281
|
+
reason: "diagn\xF3stico de runtime \u2014 sem adapter e sem rota de install: gap documentado, nunca obrigat\xF3rio",
|
|
20282
|
+
adapter: false,
|
|
20283
|
+
installable: false
|
|
20284
|
+
}
|
|
20285
|
+
],
|
|
20286
|
+
"spark-forge-azure": [
|
|
20287
|
+
{
|
|
20288
|
+
name: "forge-doctor-data",
|
|
20289
|
+
reason: "verificador independente das an\xE1lises produzidas",
|
|
20290
|
+
adapter: true,
|
|
20291
|
+
installable: true
|
|
20292
|
+
},
|
|
20293
|
+
{
|
|
20294
|
+
name: "platform-forge",
|
|
20295
|
+
reason: "estado da plataforma onde o job roda",
|
|
20296
|
+
adapter: true,
|
|
20297
|
+
installable: true
|
|
20298
|
+
},
|
|
20299
|
+
{
|
|
20300
|
+
name: "spark-forge-aws",
|
|
20301
|
+
reason: "fallback declarado (mesma fam\xEDlia spark)",
|
|
20302
|
+
adapter: true,
|
|
20303
|
+
installable: true
|
|
20304
|
+
},
|
|
20305
|
+
{
|
|
20306
|
+
name: "data-sentinel-forge",
|
|
20307
|
+
reason: "diagn\xF3stico de runtime \u2014 sem adapter e sem rota de install: gap documentado, nunca obrigat\xF3rio",
|
|
20308
|
+
adapter: false,
|
|
20309
|
+
installable: false
|
|
20310
|
+
}
|
|
20311
|
+
],
|
|
20312
|
+
"api-forge": [
|
|
20313
|
+
{
|
|
20314
|
+
name: "forge-doctor-api",
|
|
20315
|
+
reason: "verificador independente (producer\u2260verifier)",
|
|
20316
|
+
adapter: true,
|
|
20317
|
+
installable: true
|
|
20318
|
+
},
|
|
20319
|
+
{
|
|
20320
|
+
name: "platform-forge",
|
|
20321
|
+
reason: "governan\xE7a/estado da plataforma consumidora",
|
|
20322
|
+
adapter: true,
|
|
20323
|
+
installable: true
|
|
20324
|
+
}
|
|
20325
|
+
],
|
|
20326
|
+
"platform-forge": [
|
|
20327
|
+
{
|
|
20328
|
+
name: "api-forge",
|
|
20329
|
+
reason: "comp\xF5e governan\xE7a de APIs da plataforma",
|
|
20330
|
+
adapter: true,
|
|
20331
|
+
installable: true
|
|
20332
|
+
},
|
|
20333
|
+
{
|
|
20334
|
+
name: "forge-doctor-data",
|
|
20335
|
+
reason: "verificador independente declarado",
|
|
20336
|
+
adapter: true,
|
|
20337
|
+
installable: true
|
|
20338
|
+
}
|
|
20339
|
+
],
|
|
20340
|
+
"forge-doctor-api": [
|
|
20341
|
+
{
|
|
20342
|
+
name: "api-forge",
|
|
20343
|
+
reason: "produtor dos artefatos que ele verifica",
|
|
20344
|
+
adapter: true,
|
|
20345
|
+
installable: true
|
|
20346
|
+
},
|
|
20347
|
+
{
|
|
20348
|
+
name: "forge-doctor-data",
|
|
20349
|
+
reason: "fallback declarado entre verificadores",
|
|
20350
|
+
adapter: true,
|
|
20351
|
+
installable: true
|
|
20352
|
+
}
|
|
20353
|
+
],
|
|
20354
|
+
"forge-doctor-data": [
|
|
20355
|
+
{
|
|
20356
|
+
name: "spark-forge-aws",
|
|
20357
|
+
reason: "produtor das an\xE1lises que ele verifica",
|
|
20358
|
+
adapter: true,
|
|
20359
|
+
installable: true
|
|
20360
|
+
},
|
|
20361
|
+
{
|
|
20362
|
+
name: "spark-forge-azure",
|
|
20363
|
+
reason: "produtor das an\xE1lises que ele verifica",
|
|
20364
|
+
adapter: true,
|
|
20365
|
+
installable: true
|
|
20366
|
+
},
|
|
20367
|
+
{
|
|
20368
|
+
name: "platform-forge",
|
|
20369
|
+
reason: "estado da plataforma verificada",
|
|
20370
|
+
adapter: true,
|
|
20371
|
+
installable: true
|
|
20372
|
+
},
|
|
20373
|
+
{
|
|
20374
|
+
name: "forge-doctor-api",
|
|
20375
|
+
reason: "fallback declarado entre verificadores",
|
|
20376
|
+
adapter: true,
|
|
20377
|
+
installable: true
|
|
20378
|
+
},
|
|
20379
|
+
{
|
|
20380
|
+
name: "data-sentinel-forge",
|
|
20381
|
+
reason: "importa doctor-bundles para diagn\xF3stico de runtime \u2014 sem adapter e sem rota de install: gap documentado",
|
|
20382
|
+
adapter: false,
|
|
20383
|
+
installable: false
|
|
20384
|
+
}
|
|
20385
|
+
],
|
|
20386
|
+
"forge-portal": [
|
|
20387
|
+
{
|
|
20388
|
+
name: "the-forge",
|
|
20389
|
+
reason: "control plane que o portal renderiza",
|
|
20390
|
+
adapter: true,
|
|
20391
|
+
installable: true
|
|
20392
|
+
}
|
|
20393
|
+
],
|
|
20394
|
+
"data-sentinel-forge": [
|
|
20395
|
+
{
|
|
20396
|
+
name: "forge-doctor-data",
|
|
20397
|
+
reason: "consome doctor-bundles para diagn\xF3stico independente",
|
|
20398
|
+
adapter: true,
|
|
20399
|
+
installable: true
|
|
20400
|
+
},
|
|
20401
|
+
{
|
|
20402
|
+
name: "spark-forge-aws",
|
|
20403
|
+
reason: "fonte de evid\xEAncia Spark para diagn\xF3stico",
|
|
20404
|
+
adapter: true,
|
|
20405
|
+
installable: true
|
|
20406
|
+
},
|
|
20407
|
+
{
|
|
20408
|
+
name: "spark-forge-azure",
|
|
20409
|
+
reason: "fonte de evid\xEAncia Spark para diagn\xF3stico",
|
|
20410
|
+
adapter: true,
|
|
20411
|
+
installable: true
|
|
20412
|
+
}
|
|
20413
|
+
]
|
|
20414
|
+
};
|
|
20415
|
+
function integrationsOf(forge) {
|
|
20416
|
+
return OPTIONAL_INTEGRATIONS[forge] ?? [];
|
|
20417
|
+
}
|
|
20418
|
+
function detectLocal(integration, deps) {
|
|
20419
|
+
const { name } = integration;
|
|
20420
|
+
const evidence = [];
|
|
20421
|
+
const receipt = loadForgeState(deps.home).forges[name];
|
|
20422
|
+
if (receipt) {
|
|
20423
|
+
evidence.push(`receipt=${receipt.method}@${receipt.venv ?? receipt.bin ?? "?"}`);
|
|
20424
|
+
if (receipt.version) evidence.push(`version=${receipt.version}`);
|
|
20425
|
+
const localMissing = receipt.method === "local" && (typeof receipt.source_dir !== "string" || !existsSync24(receipt.source_dir));
|
|
20426
|
+
if (localMissing) {
|
|
20427
|
+
evidence.push(`source_dir=${receipt.source_dir ?? "?"} (AUSENTE)`);
|
|
20428
|
+
return { integration, state: "degraded", evidence };
|
|
20429
|
+
}
|
|
20430
|
+
if (receipt.verify?.ok === false) {
|
|
20431
|
+
evidence.push(`verify=${receipt.verify.command.join(" ") || "?"} falhou`);
|
|
20432
|
+
return { integration, state: "degraded", evidence };
|
|
20433
|
+
}
|
|
20434
|
+
evidence.push(`verify=${receipt.verify?.ok ? "ok" : "n\xE3o executado"}`);
|
|
20435
|
+
return { integration, state: receipt.verify?.ok ? "verified" : "installed", evidence };
|
|
20436
|
+
}
|
|
20437
|
+
const dir = resolveInside(deps.root, name);
|
|
20438
|
+
if (dir && existsSync24(dir)) {
|
|
20439
|
+
evidence.push(`checkout=${dir}${hasGitRepo(dir) ? " (git)" : " (sem .git)"}`);
|
|
20440
|
+
return { integration, state: "checkout", evidence };
|
|
20441
|
+
}
|
|
20442
|
+
evidence.push("sem receipt, checkout ou bin\xE1rio gerenciado detectado");
|
|
20443
|
+
return { integration, state: "absent", evidence };
|
|
20444
|
+
}
|
|
20445
|
+
async function detect(integration, deps) {
|
|
20446
|
+
const local = detectLocal(integration, deps);
|
|
20447
|
+
if (local.state !== "absent" || !deps.which) return local;
|
|
20448
|
+
const bin = deps.bin?.(integration.name);
|
|
20449
|
+
if (!bin) return local;
|
|
20450
|
+
const found = await deps.which(bin);
|
|
20451
|
+
if (!found) return local;
|
|
20452
|
+
return {
|
|
20453
|
+
integration,
|
|
20454
|
+
state: "installed",
|
|
20455
|
+
evidence: [`bin=${found} (PATH, n\xE3o-gerenciado)`]
|
|
20456
|
+
};
|
|
20457
|
+
}
|
|
20458
|
+
async function classifyIntegrations(forge, deps) {
|
|
20459
|
+
const out = [];
|
|
20460
|
+
for (const integration of integrationsOf(forge)) {
|
|
20461
|
+
out.push(await detect(integration, deps));
|
|
20462
|
+
}
|
|
20463
|
+
return out;
|
|
20464
|
+
}
|
|
20465
|
+
function classifyIntegrationsSync(forge, deps) {
|
|
20466
|
+
return integrationsOf(forge).map((i3) => detectLocal(i3, deps));
|
|
20467
|
+
}
|
|
20468
|
+
function pendingIntegrations(statuses) {
|
|
20469
|
+
return statuses.filter((s) => s.state === "absent" || s.state === "checkout");
|
|
20470
|
+
}
|
|
20471
|
+
function installableNames(statuses) {
|
|
20472
|
+
return statuses.filter((s) => s.integration.installable).map((s) => s.integration.name);
|
|
20473
|
+
}
|
|
20474
|
+
async function defaultWhich(bin) {
|
|
20475
|
+
const isWin = process.platform === "win32";
|
|
20476
|
+
try {
|
|
20477
|
+
const res = await execa(isWin ? "where" : "sh", isWin ? [bin] : ["-c", `command -v ${bin}`], {
|
|
20478
|
+
reject: false
|
|
20479
|
+
});
|
|
20480
|
+
const first = (res.stdout ?? "").split("\n")[0]?.trim();
|
|
20481
|
+
return res.exitCode === 0 && first ? first : null;
|
|
20482
|
+
} catch {
|
|
20483
|
+
return null;
|
|
20484
|
+
}
|
|
20485
|
+
}
|
|
20486
|
+
async function askOptionalIntegrations(statuses, ask) {
|
|
20487
|
+
const pending = pendingIntegrations(statuses);
|
|
20488
|
+
const chosen = await ask(
|
|
20489
|
+
pending.map((s) => ({
|
|
20490
|
+
value: s.integration.name,
|
|
20491
|
+
label: s.integration.name,
|
|
20492
|
+
hint: s.integration.reason
|
|
20493
|
+
}))
|
|
20494
|
+
) ?? [];
|
|
20495
|
+
const installable = new Set(installableNames(pending));
|
|
20496
|
+
return {
|
|
20497
|
+
offered: pending,
|
|
20498
|
+
consented: chosen,
|
|
20499
|
+
installable: chosen.filter((n4) => installable.has(n4)),
|
|
20500
|
+
notInstallable: chosen.filter((n4) => !installable.has(n4))
|
|
20501
|
+
};
|
|
20502
|
+
}
|
|
20503
|
+
|
|
20191
20504
|
// src/doctor2/domains.ts
|
|
20192
20505
|
function systemChecks(ctx) {
|
|
20193
20506
|
const checks = [];
|
|
@@ -20279,7 +20592,7 @@ function systemChecks(ctx) {
|
|
|
20279
20592
|
const dirEvidence = [];
|
|
20280
20593
|
let dirFailed = 0;
|
|
20281
20594
|
for (const { label, dir: d } of required) {
|
|
20282
|
-
if (!
|
|
20595
|
+
if (!existsSync25(d)) {
|
|
20283
20596
|
dirFailed += 1;
|
|
20284
20597
|
dirEvidence.push(`${label}=${d}: ausente`);
|
|
20285
20598
|
continue;
|
|
@@ -20350,7 +20663,7 @@ function backupScan(ctx) {
|
|
|
20350
20663
|
scan(
|
|
20351
20664
|
path34.join(ctx.home, ".forge-cli", "backups"),
|
|
20352
20665
|
"host-backup",
|
|
20353
|
-
(full) =>
|
|
20666
|
+
(full) => existsSync25(path34.join(full, "manifest.json"))
|
|
20354
20667
|
);
|
|
20355
20668
|
for (const repo of ctx.catalog) {
|
|
20356
20669
|
const dir = resolveInside(ctx.root, repo.name);
|
|
@@ -20363,7 +20676,7 @@ function backupScan(ctx) {
|
|
|
20363
20676
|
function installationChecks(ctx) {
|
|
20364
20677
|
const checks = [];
|
|
20365
20678
|
const forgeDir = artifactsRoot(ctx.root);
|
|
20366
|
-
const forgeDirPresent =
|
|
20679
|
+
const forgeDirPresent = existsSync25(forgeDir);
|
|
20367
20680
|
const stateFile = stateFileFor(ctx.root);
|
|
20368
20681
|
checks.push(
|
|
20369
20682
|
mkCheck(
|
|
@@ -20376,7 +20689,7 @@ function installationChecks(ctx) {
|
|
|
20376
20689
|
diagnosis: "diret\xF3rio de estado .forge-cli presente",
|
|
20377
20690
|
evidence: [
|
|
20378
20691
|
`${forgeDir} presente`,
|
|
20379
|
-
|
|
20692
|
+
existsSync25(stateFile) ? "state.json presente" : "state.json ausente"
|
|
20380
20693
|
],
|
|
20381
20694
|
reversible: null
|
|
20382
20695
|
} : {
|
|
@@ -20392,7 +20705,7 @@ function installationChecks(ctx) {
|
|
|
20392
20705
|
)
|
|
20393
20706
|
);
|
|
20394
20707
|
const forgeJson = path34.join(ctx.root, "forge.json");
|
|
20395
|
-
if (!
|
|
20708
|
+
if (!existsSync25(forgeJson)) {
|
|
20396
20709
|
checks.push(
|
|
20397
20710
|
mkCheck(ctx.now, {
|
|
20398
20711
|
id: "installation.forge-json.present",
|
|
@@ -20442,9 +20755,9 @@ function installationChecks(ctx) {
|
|
|
20442
20755
|
}
|
|
20443
20756
|
}
|
|
20444
20757
|
const lockFile = path34.join(forgeDir, "env.lock.json");
|
|
20445
|
-
const lockParsed =
|
|
20758
|
+
const lockParsed = existsSync25(lockFile) ? readJson(lockFile) : null;
|
|
20446
20759
|
const lockObj = lockParsed?.ok && lockParsed.value !== null && typeof lockParsed.value === "object" ? lockParsed.value : null;
|
|
20447
|
-
if (!forgeDirPresent || !
|
|
20760
|
+
if (!forgeDirPresent || !existsSync25(lockFile)) {
|
|
20448
20761
|
checks.push(
|
|
20449
20762
|
mkCheck(ctx.now, {
|
|
20450
20763
|
id: "installation.lockfile.present",
|
|
@@ -20489,7 +20802,7 @@ function installationChecks(ctx) {
|
|
|
20489
20802
|
);
|
|
20490
20803
|
}
|
|
20491
20804
|
const trackedForges = lockObj ? Object.keys(lockObj.forges ?? {}) : [];
|
|
20492
|
-
if (!
|
|
20805
|
+
if (!existsSync25(lockFile) || !lockObj || !trackedForges.length) {
|
|
20493
20806
|
checks.push(
|
|
20494
20807
|
mkCheck(ctx.now, {
|
|
20495
20808
|
id: "installation.lockfile.drift",
|
|
@@ -20498,7 +20811,7 @@ function installationChecks(ctx) {
|
|
|
20498
20811
|
severity: "info",
|
|
20499
20812
|
diagnosis: "deriva do lockfile n\xE3o verific\xE1vel",
|
|
20500
20813
|
evidence: [
|
|
20501
|
-
!
|
|
20814
|
+
!existsSync25(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"
|
|
20502
20815
|
],
|
|
20503
20816
|
reversible: null
|
|
20504
20817
|
})
|
|
@@ -20527,10 +20840,10 @@ function installationChecks(ctx) {
|
|
|
20527
20840
|
mkCheck(ctx.now, {
|
|
20528
20841
|
id: "installation.ledger.readable",
|
|
20529
20842
|
domain: "installation",
|
|
20530
|
-
result:
|
|
20531
|
-
severity:
|
|
20532
|
-
diagnosis:
|
|
20533
|
-
evidence: [`${ledgerDir} ${
|
|
20843
|
+
result: existsSync25(ledgerDir) ? "warn" : "skip",
|
|
20844
|
+
severity: existsSync25(ledgerDir) ? "medium" : "info",
|
|
20845
|
+
diagnosis: existsSync25(ledgerDir) ? "ledger de instala\xE7\xF5es existe mas n\xE3o p\xF4de ser lido" : "sem ledger de instala\xE7\xF5es (~/.forge/installations ausente)",
|
|
20846
|
+
evidence: [`${ledgerDir} ${existsSync25(ledgerDir) ? "ileg\xEDvel" : "ausente"}`],
|
|
20534
20847
|
reversible: null
|
|
20535
20848
|
})
|
|
20536
20849
|
);
|
|
@@ -20612,11 +20925,11 @@ function installationChecks(ctx) {
|
|
|
20612
20925
|
} else {
|
|
20613
20926
|
const lines = localEntries.map(([name, r2]) => {
|
|
20614
20927
|
const src = typeof r2.source_dir === "string" ? r2.source_dir : "?";
|
|
20615
|
-
const srcOk = typeof r2.source_dir === "string" &&
|
|
20928
|
+
const srcOk = typeof r2.source_dir === "string" && existsSync25(r2.source_dir);
|
|
20616
20929
|
return `${name}: is_git=${r2.is_git === true} trust=${r2.source_trust ?? "?"} manifest_valid=${r2.manifest_valid === true} integrity=${r2.integrity ?? "?"} env=${r2.execution_environment?.platform ?? "?"} dir=${src}${srcOk ? "" : " (AUSENTE)"}`;
|
|
20617
20930
|
});
|
|
20618
20931
|
const missing = localEntries.filter(
|
|
20619
|
-
([, r2]) => typeof r2.source_dir !== "string" || !
|
|
20932
|
+
([, r2]) => typeof r2.source_dir !== "string" || !existsSync25(r2.source_dir)
|
|
20620
20933
|
);
|
|
20621
20934
|
checks.push(
|
|
20622
20935
|
mkCheck(ctx.now, {
|
|
@@ -20690,6 +21003,58 @@ function installationChecks(ctx) {
|
|
|
20690
21003
|
}
|
|
20691
21004
|
)
|
|
20692
21005
|
);
|
|
21006
|
+
const forgeState = loadForgeState(ctx.home).forges;
|
|
21007
|
+
for (const repo of [...ctx.catalog].sort((a3, b) => a3.name.localeCompare(b.name))) {
|
|
21008
|
+
const dir = resolveInside(ctx.root, repo.name);
|
|
21009
|
+
const consumerPresent = !!dir && hasGitRepo(dir) || !!forgeState[repo.name];
|
|
21010
|
+
if (!consumerPresent) continue;
|
|
21011
|
+
const statuses = classifyIntegrationsSync(repo.name, { root: ctx.root, home: ctx.home });
|
|
21012
|
+
if (!statuses.length) continue;
|
|
21013
|
+
const lines = statuses.map((s) => {
|
|
21014
|
+
const sdir = resolveInside(ctx.root, s.integration.name);
|
|
21015
|
+
let contract = "contract=n\xE3o sondado";
|
|
21016
|
+
if (sdir && existsSync25(sdir)) {
|
|
21017
|
+
const manifest = path34.join(sdir, "forge.json");
|
|
21018
|
+
contract = !existsSync25(manifest) ? "contract=sem forge.json" : readJson(manifest).ok ? "contract=forge.json v\xE1lido" : "contract=forge.json inv\xE1lido";
|
|
21019
|
+
} else if (s.state === "verified" || s.state === "installed") {
|
|
21020
|
+
contract = "contract=manifesto dentro da venv (n\xE3o sondado)";
|
|
21021
|
+
}
|
|
21022
|
+
return `${s.integration.name}: state=${s.state} capability=${s.integration.adapter ? "deriv\xE1vel (adapter declarado)" : "n\xE3o declarada"} adapter=${s.integration.adapter ? "the-forge" : "ausente"} ${contract} [${s.evidence.join(" \xB7 ")}]`;
|
|
21023
|
+
});
|
|
21024
|
+
const degraded = statuses.filter((s) => s.state === "degraded").length;
|
|
21025
|
+
const present = statuses.filter((s) => s.state !== "absent").length;
|
|
21026
|
+
checks.push(
|
|
21027
|
+
mkCheck(
|
|
21028
|
+
ctx.now,
|
|
21029
|
+
degraded ? {
|
|
21030
|
+
id: `installation.integrations.${repo.name}`,
|
|
21031
|
+
domain: "installation",
|
|
21032
|
+
result: "warn",
|
|
21033
|
+
severity: "low",
|
|
21034
|
+
diagnosis: `${degraded} integra\xE7\xE3o(\xF5es) opcional(is) degradada(s)`,
|
|
21035
|
+
evidence: boundEntries(ctx, lines).shown,
|
|
21036
|
+
correctiveAction: "reinstall or verify degraded optional specialists",
|
|
21037
|
+
reversible: true
|
|
21038
|
+
} : present ? {
|
|
21039
|
+
id: `installation.integrations.${repo.name}`,
|
|
21040
|
+
domain: "installation",
|
|
21041
|
+
result: "pass",
|
|
21042
|
+
severity: "info",
|
|
21043
|
+
diagnosis: `${present} integra\xE7\xE3o(\xF5es) opcional(is) presente(s) e saud\xE1vel(is)`,
|
|
21044
|
+
evidence: boundEntries(ctx, lines).shown,
|
|
21045
|
+
reversible: null
|
|
21046
|
+
} : {
|
|
21047
|
+
id: `installation.integrations.${repo.name}`,
|
|
21048
|
+
domain: "installation",
|
|
21049
|
+
result: "skip",
|
|
21050
|
+
severity: "info",
|
|
21051
|
+
diagnosis: "integra\xE7\xF5es opcionais ausentes \u2014 nada exigido, nunca falha a Forja principal",
|
|
21052
|
+
evidence: boundEntries(ctx, lines).shown,
|
|
21053
|
+
reversible: null
|
|
21054
|
+
}
|
|
21055
|
+
)
|
|
21056
|
+
);
|
|
21057
|
+
}
|
|
20693
21058
|
return checks;
|
|
20694
21059
|
}
|
|
20695
21060
|
function environmentChecks(ctx) {
|
|
@@ -20697,7 +21062,7 @@ function environmentChecks(ctx) {
|
|
|
20697
21062
|
const targets = [{ label: "<workspace>", dir: ctx.root }];
|
|
20698
21063
|
for (const repo of ctx.catalog) {
|
|
20699
21064
|
const dir = resolveInside(ctx.root, repo.name);
|
|
20700
|
-
if (dir &&
|
|
21065
|
+
if (dir && existsSync25(dir)) targets.push({ label: repo.name, dir, envKeys: repo.envKeys });
|
|
20701
21066
|
}
|
|
20702
21067
|
const inspected = targets.map((t2) => ({
|
|
20703
21068
|
...t2,
|
|
@@ -20919,7 +21284,7 @@ function operationsChecks(ctx) {
|
|
|
20919
21284
|
checks.push(
|
|
20920
21285
|
mkCheck(
|
|
20921
21286
|
ctx.now,
|
|
20922
|
-
!
|
|
21287
|
+
!existsSync25(reportsDir) ? {
|
|
20923
21288
|
id: "operations.receipts.recent",
|
|
20924
21289
|
domain: "operations",
|
|
20925
21290
|
result: "skip",
|
|
@@ -20955,7 +21320,7 @@ function operationsChecks(ctx) {
|
|
|
20955
21320
|
if (entry2.status === "error") failures.set(name, (failures.get(name) ?? 0) + 1);
|
|
20956
21321
|
}
|
|
20957
21322
|
const recurring = [...failures.entries()].filter(([, n4]) => n4 >= 2);
|
|
20958
|
-
const scannedAnything =
|
|
21323
|
+
const scannedAnything = existsSync25(reportsDir) || ctx.state !== null;
|
|
20959
21324
|
if (!scannedAnything) {
|
|
20960
21325
|
checks.push(
|
|
20961
21326
|
mkCheck(ctx.now, {
|
|
@@ -21231,7 +21596,7 @@ async function runDoctor2(root, opts = {}) {
|
|
|
21231
21596
|
}
|
|
21232
21597
|
|
|
21233
21598
|
// src/heal/fixes.ts
|
|
21234
|
-
import { existsSync as
|
|
21599
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync8, readdirSync as readdirSync12, readFileSync as readFileSync19, statSync as statSync5 } from "fs";
|
|
21235
21600
|
import path36 from "path";
|
|
21236
21601
|
var rel = (root, abs) => path36.relative(root, abs) || ".";
|
|
21237
21602
|
function stampOf(iso) {
|
|
@@ -21250,7 +21615,7 @@ var forgeDirRepair = {
|
|
|
21250
21615
|
return ["", ...FORGE_DIR_SUBDIRS].map((sub) => {
|
|
21251
21616
|
const dir = sub ? path36.join(base, sub) : base;
|
|
21252
21617
|
return {
|
|
21253
|
-
description: `create directory ${rel(ctx.root, dir)}${
|
|
21618
|
+
description: `create directory ${rel(ctx.root, dir)}${existsSync26(dir) ? " (already present)" : ""}`,
|
|
21254
21619
|
touches: [rel(ctx.root, dir)]
|
|
21255
21620
|
};
|
|
21256
21621
|
});
|
|
@@ -21261,7 +21626,7 @@ var forgeDirRepair = {
|
|
|
21261
21626
|
const created = [];
|
|
21262
21627
|
for (const sub of ["", ...FORGE_DIR_SUBDIRS]) {
|
|
21263
21628
|
const dir = sub ? path36.join(base, sub) : base;
|
|
21264
|
-
if (
|
|
21629
|
+
if (existsSync26(dir)) continue;
|
|
21265
21630
|
mkdirSync8(dir, { recursive: true });
|
|
21266
21631
|
created.push(rel(ctx.root, dir));
|
|
21267
21632
|
}
|
|
@@ -21285,7 +21650,7 @@ function envPathOf(root) {
|
|
|
21285
21650
|
}
|
|
21286
21651
|
function presentKeys(root) {
|
|
21287
21652
|
const file = envPathOf(root);
|
|
21288
|
-
if (!
|
|
21653
|
+
if (!existsSync26(file)) return /* @__PURE__ */ new Set();
|
|
21289
21654
|
return new Set(parseEnv(readFileSync19(file, "utf8")).entries.map((e) => e.key));
|
|
21290
21655
|
}
|
|
21291
21656
|
function missingEnvKeys(root, check) {
|
|
@@ -21321,7 +21686,7 @@ var envMissingKeyRepair = {
|
|
|
21321
21686
|
const root = ctx.root;
|
|
21322
21687
|
const toAppend = keysToAppend(root, ctx.check);
|
|
21323
21688
|
const steps = [];
|
|
21324
|
-
if (
|
|
21689
|
+
if (existsSync26(envPathOf(root))) {
|
|
21325
21690
|
steps.push({
|
|
21326
21691
|
description: `backup .env to ${rel(root, path36.join(artifactsRoot(root), "backups"))}/env-<ts>/.env`,
|
|
21327
21692
|
touches: [".env", `${rel(root, artifactsRoot(root))}/backups/`]
|
|
@@ -21340,7 +21705,7 @@ var envMissingKeyRepair = {
|
|
|
21340
21705
|
const root = ctx.root;
|
|
21341
21706
|
if (ctx.dryRun) return "dry-run \u2014 not applied";
|
|
21342
21707
|
const file = envPathOf(root);
|
|
21343
|
-
const exists =
|
|
21708
|
+
const exists = existsSync26(file);
|
|
21344
21709
|
const current = exists ? readFileSync19(file, "utf8") : "";
|
|
21345
21710
|
const env = parseEnv(current);
|
|
21346
21711
|
const present = new Set(env.entries.map((e) => e.key));
|
|
@@ -21358,7 +21723,7 @@ var envMissingKeyRepair = {
|
|
|
21358
21723
|
},
|
|
21359
21724
|
async verify(ctx) {
|
|
21360
21725
|
const file = envPathOf(ctx.root);
|
|
21361
|
-
if (!
|
|
21726
|
+
if (!existsSync26(file)) return false;
|
|
21362
21727
|
const present = new Set(parseEnv(readFileSync19(file, "utf8")).entries.map((e) => e.key));
|
|
21363
21728
|
return missingEnvKeys(ctx.root, ctx.check).every((k) => present.has(k));
|
|
21364
21729
|
}
|
|
@@ -21396,7 +21761,7 @@ var ENV_BACKUP_RE = /^env-\d{8}-\d{6}$/;
|
|
|
21396
21761
|
var REPO_BACKUP_RE = /^([a-z0-9][a-z0-9._-]*)-\d{10,}$/i;
|
|
21397
21762
|
function listBackups(root) {
|
|
21398
21763
|
const base = path36.join(artifactsRoot(root), "backups");
|
|
21399
|
-
if (!
|
|
21764
|
+
if (!existsSync26(base)) return [];
|
|
21400
21765
|
const out = [];
|
|
21401
21766
|
for (const entry2 of readdirSync12(base, { withFileTypes: true })) {
|
|
21402
21767
|
if (!entry2.isDirectory()) continue;
|
|
@@ -21425,7 +21790,7 @@ function pickBackupFile(ctx) {
|
|
|
21425
21790
|
if (inside.startsWith("..") || path36.isAbsolute(inside)) {
|
|
21426
21791
|
throw new Error(`selected backup escapes .forge-cli/backups: ${ctx.backup}`);
|
|
21427
21792
|
}
|
|
21428
|
-
if (!
|
|
21793
|
+
if (!existsSync26(abs) || !statSync5(abs).isFile()) {
|
|
21429
21794
|
throw new Error(`selected backup not found: ${ctx.backup}`);
|
|
21430
21795
|
}
|
|
21431
21796
|
const dirName = inside.split(path36.sep)[0] ?? "";
|
|
@@ -21493,7 +21858,7 @@ var backupRestoreRepair = {
|
|
|
21493
21858
|
try {
|
|
21494
21859
|
const picked = pickBackupFile(ctx);
|
|
21495
21860
|
const dest = path36.resolve(ctx.root, picked.dest);
|
|
21496
|
-
return
|
|
21861
|
+
return existsSync26(dest) && readFileSync19(dest, "utf8") === readFileSync19(picked.src, "utf8");
|
|
21497
21862
|
} catch {
|
|
21498
21863
|
return false;
|
|
21499
21864
|
}
|
|
@@ -21748,7 +22113,7 @@ async function runDoctor2Command(cli) {
|
|
|
21748
22113
|
|
|
21749
22114
|
// src/cmd/registry.ts
|
|
21750
22115
|
import path38 from "path";
|
|
21751
|
-
import { existsSync as
|
|
22116
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync10 } from "fs";
|
|
21752
22117
|
|
|
21753
22118
|
// src/registry/catalog.ts
|
|
21754
22119
|
var REGISTRY_PACKAGES = [
|
|
@@ -22162,7 +22527,14 @@ async function runRegistryInstall(cli, names, deps = {}) {
|
|
|
22162
22527
|
...receipt.native ? { native: receipt.native } : {}
|
|
22163
22528
|
});
|
|
22164
22529
|
}
|
|
22165
|
-
const
|
|
22530
|
+
const integrationStatuses = deps.skipIntegrations ? [] : await collectIntegrationStatuses(results, home, cli, deps.which);
|
|
22531
|
+
const pending = pendingIntegrations(integrationStatuses);
|
|
22532
|
+
const doc = {
|
|
22533
|
+
method: "pypi",
|
|
22534
|
+
results,
|
|
22535
|
+
warnings,
|
|
22536
|
+
...pending.length ? { optional_integrations: pending.map(integrationJson) } : {}
|
|
22537
|
+
};
|
|
22166
22538
|
reportTo2(doc);
|
|
22167
22539
|
if (cli.reportPath !== void 0) {
|
|
22168
22540
|
log.info(`${S.reportSaved}: ${theme.dim(path38.resolve(cli.reportPath))}`);
|
|
@@ -22184,8 +22556,62 @@ async function runRegistryInstall(cli, names, deps = {}) {
|
|
|
22184
22556
|
"Launchers em ~/.forge-cli/bin (no PATH do forge-cli); sem shim, invoque o caminho da venv"
|
|
22185
22557
|
);
|
|
22186
22558
|
outro(`${icons.ok} instala\xE7\xE3o registry-first conclu\xEDda`);
|
|
22559
|
+
if (pending.length && !deps.skipIntegrations) {
|
|
22560
|
+
const canAsk = !quiet2 && !cli.yes && process.stdout.isTTY && process.stdin.isTTY;
|
|
22561
|
+
if (canAsk) {
|
|
22562
|
+
const offer = await askOptionalIntegrations(pending, async (options) => {
|
|
22563
|
+
const picked = await multiselect({
|
|
22564
|
+
message: S.integrationsOffer,
|
|
22565
|
+
options,
|
|
22566
|
+
required: false
|
|
22567
|
+
});
|
|
22568
|
+
if (isCancel(picked)) exitCancelled();
|
|
22569
|
+
return picked;
|
|
22570
|
+
});
|
|
22571
|
+
for (const n4 of offer.notInstallable) {
|
|
22572
|
+
log.warn(`${n4}: ${S.integrationNoInstallRoute}`);
|
|
22573
|
+
}
|
|
22574
|
+
if (offer.installable.length) {
|
|
22575
|
+
await runRegistryInstall(cli, offer.installable, { ...deps, skipIntegrations: true });
|
|
22576
|
+
}
|
|
22577
|
+
} else if (!quiet2) {
|
|
22578
|
+
note(
|
|
22579
|
+
pending.map(
|
|
22580
|
+
(s) => `${s.integration.name} \u2014 ${s.integration.reason}${s.integration.installable ? "" : ` (${S.integrationNoInstallRoute})`}`
|
|
22581
|
+
).join("\n"),
|
|
22582
|
+
S.integrationsTitle
|
|
22583
|
+
);
|
|
22584
|
+
}
|
|
22585
|
+
}
|
|
22187
22586
|
return results;
|
|
22188
22587
|
}
|
|
22588
|
+
function integrationJson(s) {
|
|
22589
|
+
return {
|
|
22590
|
+
name: s.integration.name,
|
|
22591
|
+
state: s.state,
|
|
22592
|
+
adapter: s.integration.adapter,
|
|
22593
|
+
installable: s.integration.installable,
|
|
22594
|
+
reason: s.integration.reason,
|
|
22595
|
+
evidence: s.evidence
|
|
22596
|
+
};
|
|
22597
|
+
}
|
|
22598
|
+
async function collectIntegrationStatuses(results, home, cli, which) {
|
|
22599
|
+
const root = resolveTargetDir(cli.dir);
|
|
22600
|
+
const bin = (n4) => FORGE_REPOS.find((r2) => r2.name === n4)?.verify?.[0];
|
|
22601
|
+
const statuses = [];
|
|
22602
|
+
for (const r2 of results) {
|
|
22603
|
+
if (!r2.ok) continue;
|
|
22604
|
+
statuses.push(
|
|
22605
|
+
...await classifyIntegrations(r2.forge, {
|
|
22606
|
+
root,
|
|
22607
|
+
home,
|
|
22608
|
+
which: which ?? defaultWhich,
|
|
22609
|
+
bin
|
|
22610
|
+
})
|
|
22611
|
+
);
|
|
22612
|
+
}
|
|
22613
|
+
return statuses;
|
|
22614
|
+
}
|
|
22189
22615
|
async function runRegistryLock(cli, names, deps = {}) {
|
|
22190
22616
|
const home = deps.home ?? cliHome();
|
|
22191
22617
|
const quiet2 = registryQuiet(cli);
|
|
@@ -22306,7 +22732,7 @@ function runRegistryList(cli, deps = {}) {
|
|
|
22306
22732
|
}
|
|
22307
22733
|
const rows = REGISTRY_PACKAGES.filter((e) => !wanted || wanted.has(e.name)).map((e) => {
|
|
22308
22734
|
const inst = state.forges[e.name];
|
|
22309
|
-
const binOk = inst ? typeof inst.bin === "string" &&
|
|
22735
|
+
const binOk = inst ? typeof inst.bin === "string" && existsSync27(inst.bin) : null;
|
|
22310
22736
|
const consistency = installConsistency(home, e.name);
|
|
22311
22737
|
const relevant = consistency.status !== "not-installed";
|
|
22312
22738
|
return {
|
|
@@ -22335,7 +22761,7 @@ function runRegistryList(cli, deps = {}) {
|
|
|
22335
22761
|
installed: inst.version ?? null,
|
|
22336
22762
|
method: inst.method ?? null,
|
|
22337
22763
|
verifyOk: inst.verify?.ok ?? null,
|
|
22338
|
-
binPresent: typeof inst.bin === "string" ?
|
|
22764
|
+
binPresent: typeof inst.bin === "string" ? existsSync27(inst.bin) : null,
|
|
22339
22765
|
bin: inst.bin ?? null,
|
|
22340
22766
|
consistent: null,
|
|
22341
22767
|
consistency: null,
|
|
@@ -23103,7 +23529,7 @@ async function runRegistryPin(cli, names, pin, deps = {}) {
|
|
|
23103
23529
|
|
|
23104
23530
|
// src/source/detect.ts
|
|
23105
23531
|
import { createHash as createHash5 } from "crypto";
|
|
23106
|
-
import { existsSync as
|
|
23532
|
+
import { existsSync as existsSync28, lstatSync as lstatSync3, readFileSync as readFileSync20, statSync as statSync6 } from "fs";
|
|
23107
23533
|
import path39 from "path";
|
|
23108
23534
|
var MANIFEST_MAX_BYTES = 64 * 1024;
|
|
23109
23535
|
var CHECKSUM_MAX_ENTRIES = 1e4;
|
|
@@ -23126,7 +23552,7 @@ var PROJECT_MARKERS = [
|
|
|
23126
23552
|
var CHECKSUM_FILES = ["SHA256SUMS", "checksums.sha256", ".forge-checksums.json"];
|
|
23127
23553
|
function readManifestStrict(dir) {
|
|
23128
23554
|
const file = path39.join(dir, "forge.json");
|
|
23129
|
-
if (!
|
|
23555
|
+
if (!existsSync28(file)) return { ok: false, reason: "absent", detail: "forge.json ausente" };
|
|
23130
23556
|
let raw;
|
|
23131
23557
|
try {
|
|
23132
23558
|
if (!statSync6(file).isFile() || statSync6(file).size > MANIFEST_MAX_BYTES) {
|
|
@@ -23201,7 +23627,7 @@ function scanStructure(dir) {
|
|
|
23201
23627
|
return false;
|
|
23202
23628
|
}
|
|
23203
23629
|
});
|
|
23204
|
-
const markers = PROJECT_MARKERS.filter((f) =>
|
|
23630
|
+
const markers = PROJECT_MARKERS.filter((f) => existsSync28(path39.join(dir, f)));
|
|
23205
23631
|
return { setup_scripts: [...setup], project_markers: [...markers] };
|
|
23206
23632
|
}
|
|
23207
23633
|
function verifyChecksums(dir, reasons) {
|
|
@@ -23423,7 +23849,7 @@ function detectSource(input, cwd = process.cwd()) {
|
|
|
23423
23849
|
const structure = scanStructure(dir);
|
|
23424
23850
|
const expected = structure.setup_scripts.length > 0 && structure.project_markers.length > 0;
|
|
23425
23851
|
if (gitKind === null) {
|
|
23426
|
-
if (!
|
|
23852
|
+
if (!existsSync28(path39.join(dir, "forge.json"))) {
|
|
23427
23853
|
reasons.push("forge.json=ausente");
|
|
23428
23854
|
return verdict(
|
|
23429
23855
|
"UNKNOWN_DIRECTORY",
|
|
@@ -23506,7 +23932,7 @@ function looksLikePathArg(arg, cwd = process.cwd()) {
|
|
|
23506
23932
|
import path41 from "path";
|
|
23507
23933
|
|
|
23508
23934
|
// src/source/install.ts
|
|
23509
|
-
import { existsSync as
|
|
23935
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync11, readdirSync as readdirSync13, rmSync as rmSync7, statSync as statSync7 } from "fs";
|
|
23510
23936
|
import { copyFile as copyFile3 } from "fs/promises";
|
|
23511
23937
|
import path40 from "path";
|
|
23512
23938
|
var fail = (failure, detection) => ({
|
|
@@ -23594,7 +24020,7 @@ function selectSetup(det, platform2, python, winShells) {
|
|
|
23594
24020
|
if (script) return script;
|
|
23595
24021
|
const bootstrap = path40.join("scripts", "forge_bootstrap.py");
|
|
23596
24022
|
const argv = localPythonOf(python)?.argv;
|
|
23597
|
-
if (argv?.length &&
|
|
24023
|
+
if (argv?.length && existsSync29(path40.join(det.path, bootstrap))) {
|
|
23598
24024
|
return {
|
|
23599
24025
|
file: bootstrap,
|
|
23600
24026
|
command: argv[0],
|
|
@@ -23620,14 +24046,14 @@ function pythonFloorFailure(det, python) {
|
|
|
23620
24046
|
}
|
|
23621
24047
|
async function provisionEnvironment(dir, forgeId) {
|
|
23622
24048
|
const envKeys = FORGE_REPOS.find((r2) => r2.name === forgeId)?.envKeys;
|
|
23623
|
-
if (envKeys?.length ||
|
|
24049
|
+
if (envKeys?.length || existsSync29(path40.join(dir, ".env")) || existsSync29(path40.join(dir, ".env.example"))) {
|
|
23624
24050
|
await ensureEnvFile(dir, envKeys);
|
|
23625
24051
|
}
|
|
23626
24052
|
await copyConfigExamples(dir);
|
|
23627
24053
|
}
|
|
23628
24054
|
function sourceVenv(det, platform2) {
|
|
23629
24055
|
const venv = path40.join(det.path, ".venv");
|
|
23630
|
-
return
|
|
24056
|
+
return existsSync29(venvBin(venv, platform2)) ? venv : void 0;
|
|
23631
24057
|
}
|
|
23632
24058
|
async function persistLocalReceipt(home, receipt, opts) {
|
|
23633
24059
|
const envelope = resolveInside(forgesRoot(home), receipt.forge);
|
|
@@ -23652,7 +24078,7 @@ function buildReceipt(det, deps, script, managed, sourceFiles, prior) {
|
|
|
23652
24078
|
const venv = sourceVenv(det, platform2);
|
|
23653
24079
|
const cliName = manifest?.cli;
|
|
23654
24080
|
const binCandidate = venv && cliName ? path40.join(venvBin(venv, platform2), cliName) : void 0;
|
|
23655
|
-
const bin = binCandidate &&
|
|
24081
|
+
const bin = binCandidate && existsSync29(binCandidate) ? binCandidate : cliName;
|
|
23656
24082
|
const py = localPythonOf(deps.python);
|
|
23657
24083
|
return {
|
|
23658
24084
|
forge: det.forge_id,
|
|
@@ -23807,7 +24233,7 @@ async function localInstall(dir, deps) {
|
|
|
23807
24233
|
await runVerify(receipt, det, deps, run);
|
|
23808
24234
|
try {
|
|
23809
24235
|
await persistLocalReceipt(deps.home, receipt, deps.lifecycleLock);
|
|
23810
|
-
if (receipt.bin &&
|
|
24236
|
+
if (receipt.bin && existsSync29(receipt.bin)) {
|
|
23811
24237
|
try {
|
|
23812
24238
|
receipt.shim = emitForgeShim(deps.home, receipt.bin, platform2);
|
|
23813
24239
|
} catch (shimError) {
|
|
@@ -23857,7 +24283,7 @@ async function localUpdate(dir, deps) {
|
|
|
23857
24283
|
if (gate) return fail(gate, det);
|
|
23858
24284
|
const targetDir = prior.source_dir;
|
|
23859
24285
|
if (path40.resolve(targetDir) !== det.path) {
|
|
23860
|
-
if (!
|
|
24286
|
+
if (!existsSync29(targetDir)) {
|
|
23861
24287
|
return fail(
|
|
23862
24288
|
{
|
|
23863
24289
|
code: "FORGE-SOURCE-NOT-FOUND",
|
|
@@ -23879,7 +24305,7 @@ async function localUpdate(dir, deps) {
|
|
|
23879
24305
|
for (const rel2 of prior.source_files) {
|
|
23880
24306
|
if (keep.has(rel2)) continue;
|
|
23881
24307
|
const target = safeJoinInside(targetDir, rel2);
|
|
23882
|
-
if (target &&
|
|
24308
|
+
if (target && existsSync29(target)) rmSync7(target, { force: true });
|
|
23883
24309
|
}
|
|
23884
24310
|
}
|
|
23885
24311
|
return runUpdateAt(targetDir, prior, deps, run);
|
|
@@ -23922,7 +24348,7 @@ async function runUpdateAt(dir, prior, deps, run) {
|
|
|
23922
24348
|
}
|
|
23923
24349
|
const managed = [
|
|
23924
24350
|
.../* @__PURE__ */ new Set([
|
|
23925
|
-
...prior.managed_artifacts.filter((a3) =>
|
|
24351
|
+
...prior.managed_artifacts.filter((a3) => existsSync29(path40.join(det.path, a3))),
|
|
23926
24352
|
...[...topLevelEntries(det.path)].filter((e) => !before.has(e))
|
|
23927
24353
|
])
|
|
23928
24354
|
].sort();
|
|
@@ -23974,7 +24400,7 @@ async function localRepair(name, deps) {
|
|
|
23974
24400
|
});
|
|
23975
24401
|
}
|
|
23976
24402
|
const dir = prior.source_dir;
|
|
23977
|
-
if (!dir || !
|
|
24403
|
+
if (!dir || !existsSync29(dir)) {
|
|
23978
24404
|
return fail({
|
|
23979
24405
|
code: "FORGE-SOURCE-NOT-FOUND",
|
|
23980
24406
|
cause: `${name}: fonte local ausente (${dir ?? "desconhecida"})`,
|
|
@@ -23989,7 +24415,7 @@ async function localRepair(name, deps) {
|
|
|
23989
24415
|
if (floorFailure) return fail(floorFailure, det);
|
|
23990
24416
|
for (const rel2 of prior.managed_artifacts) {
|
|
23991
24417
|
const target = safeJoinInside(dir, rel2);
|
|
23992
|
-
if (target &&
|
|
24418
|
+
if (target && existsSync29(target)) rmSync7(target, { recursive: true, force: true });
|
|
23993
24419
|
}
|
|
23994
24420
|
const before = topLevelEntries(dir);
|
|
23995
24421
|
const envFailure = await provisionOrRefuse(dir, det.forge_id);
|
|
@@ -24024,7 +24450,7 @@ async function localRepair(name, deps) {
|
|
|
24024
24450
|
}
|
|
24025
24451
|
const managed = [
|
|
24026
24452
|
.../* @__PURE__ */ new Set([
|
|
24027
|
-
...prior.managed_artifacts.filter((a3) =>
|
|
24453
|
+
...prior.managed_artifacts.filter((a3) => existsSync29(path40.join(dir, a3))),
|
|
24028
24454
|
...[...topLevelEntries(dir)].filter((e) => !before.has(e))
|
|
24029
24455
|
])
|
|
24030
24456
|
].sort();
|
|
@@ -24080,7 +24506,7 @@ async function localUninstall(name, deps) {
|
|
|
24080
24506
|
const dir = prior.source_dir;
|
|
24081
24507
|
const removed = [];
|
|
24082
24508
|
let backup;
|
|
24083
|
-
if (dir &&
|
|
24509
|
+
if (dir && existsSync29(dir)) {
|
|
24084
24510
|
const filesToBackup = prior.managed_artifacts.filter((rel2) => {
|
|
24085
24511
|
const target = safeJoinInside(dir, rel2);
|
|
24086
24512
|
try {
|
|
@@ -24105,7 +24531,7 @@ async function localUninstall(name, deps) {
|
|
|
24105
24531
|
}
|
|
24106
24532
|
for (const rel2 of prior.managed_artifacts) {
|
|
24107
24533
|
const target = safeJoinInside(dir, rel2);
|
|
24108
|
-
if (target &&
|
|
24534
|
+
if (target && existsSync29(target)) {
|
|
24109
24535
|
rmSync7(target, { recursive: true, force: true });
|
|
24110
24536
|
removed.push(rel2);
|
|
24111
24537
|
}
|
|
@@ -24118,7 +24544,7 @@ async function localUninstall(name, deps) {
|
|
|
24118
24544
|
try {
|
|
24119
24545
|
removeForgeShim(deps.home, prior.shim ?? prior.bin, deps.platform ?? process.platform);
|
|
24120
24546
|
const envelope = resolveInside(forgesRoot(deps.home), name);
|
|
24121
|
-
if (envelope &&
|
|
24547
|
+
if (envelope && existsSync29(envelope)) rmSync7(envelope, { recursive: true, force: true });
|
|
24122
24548
|
const cur = loadForgeState(deps.home);
|
|
24123
24549
|
if (Object.hasOwn(cur.forges, name)) {
|
|
24124
24550
|
delete cur.forges[name];
|
|
@@ -24483,15 +24909,15 @@ async function runLocalLifecycleCommand(cli, op, names) {
|
|
|
24483
24909
|
}
|
|
24484
24910
|
|
|
24485
24911
|
// src/lifecycle.ts
|
|
24486
|
-
import { copyFileSync as copyFileSync3, existsSync as
|
|
24912
|
+
import { copyFileSync as copyFileSync3, existsSync as existsSync30, mkdirSync as mkdirSync13, readdirSync as readdirSync14, rmSync as rmSync8 } from "fs";
|
|
24487
24913
|
import path42 from "path";
|
|
24488
24914
|
function configFilesToBackup(repoDir) {
|
|
24489
24915
|
const out = /* @__PURE__ */ new Set();
|
|
24490
|
-
if (
|
|
24916
|
+
if (existsSync30(path42.join(repoDir, ".env"))) out.add(".env");
|
|
24491
24917
|
for (const file of readdirSync14(repoDir)) {
|
|
24492
24918
|
if (file === ".env.example" || !file.includes(".example.")) continue;
|
|
24493
24919
|
const real = file.replace(".example.", ".");
|
|
24494
|
-
if (
|
|
24920
|
+
if (existsSync30(path42.join(repoDir, real))) out.add(real);
|
|
24495
24921
|
}
|
|
24496
24922
|
return [...out];
|
|
24497
24923
|
}
|
|
@@ -24500,7 +24926,7 @@ async function planUninstall(targetDir, repo) {
|
|
|
24500
24926
|
if (!dir)
|
|
24501
24927
|
return { kind: "refused", dir: path42.join(targetDir, repo.name), reason: "invalid-name" };
|
|
24502
24928
|
if (isLinkOrJunction(dir)) return { kind: "refused", dir, reason: "symlink" };
|
|
24503
|
-
if (!
|
|
24929
|
+
if (!existsSync30(dir)) return { kind: "absent", dir };
|
|
24504
24930
|
if (!hasGitRepo(dir)) return { kind: "refused", dir, reason: "non-git" };
|
|
24505
24931
|
const remote = await commandOutput("git", ["-C", dir, "remote", "get-url", "origin"]);
|
|
24506
24932
|
if (remote && remote !== "ok" && !sameRemote(remote, repo.url)) {
|
|
@@ -24851,11 +25277,11 @@ async function runUninstallCommand(cli) {
|
|
|
24851
25277
|
}
|
|
24852
25278
|
|
|
24853
25279
|
// src/config.ts
|
|
24854
|
-
import { existsSync as
|
|
25280
|
+
import { existsSync as existsSync31, readFileSync as readFileSync21 } from "fs";
|
|
24855
25281
|
import path44 from "path";
|
|
24856
25282
|
function loadConfig(targetDir) {
|
|
24857
25283
|
const file = path44.join(targetDir, "forge-cli.config.json");
|
|
24858
|
-
if (!
|
|
25284
|
+
if (!existsSync31(file)) return null;
|
|
24859
25285
|
let parsed;
|
|
24860
25286
|
try {
|
|
24861
25287
|
parsed = JSON.parse(readFileSync21(file, "utf8"));
|
|
@@ -24901,12 +25327,12 @@ function loadConfig(targetDir) {
|
|
|
24901
25327
|
}
|
|
24902
25328
|
|
|
24903
25329
|
// src/plan.ts
|
|
24904
|
-
import { existsSync as
|
|
25330
|
+
import { existsSync as existsSync32, readdirSync as readdirSync15 } from "fs";
|
|
24905
25331
|
import path45 from "path";
|
|
24906
25332
|
function planLines(repo, targetDir) {
|
|
24907
25333
|
const repoDir = path45.join(targetDir, repo.name);
|
|
24908
25334
|
const lines = [];
|
|
24909
|
-
if (!
|
|
25335
|
+
if (!existsSync32(repoDir)) {
|
|
24910
25336
|
lines.push(`${S.planSync}: git clone (${S.planDetectAfter})`);
|
|
24911
25337
|
for (const step of [
|
|
24912
25338
|
S.phaseEnv,
|
|
@@ -24949,7 +25375,7 @@ async function executionSummary(repo, targetDir, native, platform2 = process.pla
|
|
|
24949
25375
|
const trust = repo.origin === "custom" ? "custom" : "builtin";
|
|
24950
25376
|
const commands = [];
|
|
24951
25377
|
let revision;
|
|
24952
|
-
if (!
|
|
25378
|
+
if (!existsSync32(repoDir) || !hasGitRepo(repoDir)) {
|
|
24953
25379
|
commands.push(`git clone -- ${repo.url}`, S.planScriptsAfterClone);
|
|
24954
25380
|
} else {
|
|
24955
25381
|
revision = (await currentHead(repoDir))?.slice(0, 7);
|
|
@@ -25183,11 +25609,59 @@ ${planLines(repo, targetDir).map((line) => ` ${theme.dim(line)}`).join("\n")}`
|
|
|
25183
25609
|
log.info(`${S.logSaved}: ${theme.dim(runLog.file)}`);
|
|
25184
25610
|
const hasErrors = results.some((r2) => r2.status === "error");
|
|
25185
25611
|
outro(`${icons.rocket} ${hasErrors ? S.outroWithErrors : S.outroOk}`);
|
|
25612
|
+
if (!hasErrors && (resolved.command === "install" || resolved.command === "clone")) {
|
|
25613
|
+
await offerOptionalIntegrations(cli, results, catalog, targetDir, opts, runLog, interactive);
|
|
25614
|
+
}
|
|
25186
25615
|
if (hasErrors) process.exitCode = 1;
|
|
25187
25616
|
}
|
|
25617
|
+
async function offerOptionalIntegrations(cli, results, catalog, targetDir, opts, runLog, interactive) {
|
|
25618
|
+
const present = new Set(results.filter((r2) => r2.status !== "error").map((r2) => r2.repo.name));
|
|
25619
|
+
const statuses = [];
|
|
25620
|
+
for (const r2 of results) {
|
|
25621
|
+
if (!present.has(r2.repo.name)) continue;
|
|
25622
|
+
statuses.push(
|
|
25623
|
+
...await classifyIntegrations(r2.repo.name, {
|
|
25624
|
+
root: targetDir,
|
|
25625
|
+
home: cliHome(),
|
|
25626
|
+
which: defaultWhich,
|
|
25627
|
+
bin: (n4) => catalog.find((c5) => c5.name === n4)?.verify?.[0]
|
|
25628
|
+
})
|
|
25629
|
+
);
|
|
25630
|
+
}
|
|
25631
|
+
const pending = pendingIntegrations(statuses);
|
|
25632
|
+
if (!pending.length) return;
|
|
25633
|
+
if (interactive && !cli.yes) {
|
|
25634
|
+
const offer = await askOptionalIntegrations(pending, async (options) => {
|
|
25635
|
+
const picked = await multiselect({
|
|
25636
|
+
message: S.integrationsOffer,
|
|
25637
|
+
options,
|
|
25638
|
+
required: false
|
|
25639
|
+
});
|
|
25640
|
+
if (isCancel(picked)) {
|
|
25641
|
+
cancel(S.cancelled);
|
|
25642
|
+
return null;
|
|
25643
|
+
}
|
|
25644
|
+
return picked;
|
|
25645
|
+
});
|
|
25646
|
+
const selected = catalog.filter((c5) => offer.consented.includes(c5.name));
|
|
25647
|
+
const unrouted = offer.consented.filter((n4) => !catalog.some((c5) => c5.name === n4));
|
|
25648
|
+
for (const n4 of unrouted) log.warn(`${n4}: ${S.integrationNoInstallRoute}`);
|
|
25649
|
+
if (selected.length) {
|
|
25650
|
+
const more = await processBatch(selected, targetDir, opts, runLog.write, 0, selected.length);
|
|
25651
|
+
renderReport(more);
|
|
25652
|
+
}
|
|
25653
|
+
} else {
|
|
25654
|
+
note(
|
|
25655
|
+
pending.map(
|
|
25656
|
+
(s) => `${s.integration.name} \u2014 ${s.integration.reason}${catalog.some((c5) => c5.name === s.integration.name) ? "" : ` (${S.integrationNoInstallRoute})`}`
|
|
25657
|
+
).join("\n"),
|
|
25658
|
+
S.integrationsTitle
|
|
25659
|
+
);
|
|
25660
|
+
}
|
|
25661
|
+
}
|
|
25188
25662
|
|
|
25189
25663
|
// src/cmd/env.ts
|
|
25190
|
-
import { copyFileSync as copyFileSync4, existsSync as
|
|
25664
|
+
import { copyFileSync as copyFileSync4, existsSync as existsSync35, readFileSync as readFileSync23, readdirSync as readdirSync17 } from "fs";
|
|
25191
25665
|
import path48 from "path";
|
|
25192
25666
|
|
|
25193
25667
|
// src/env/discover.ts
|
|
@@ -25195,7 +25669,7 @@ import { accessSync as accessSync3, constants as constants6 } from "fs";
|
|
|
25195
25669
|
import os7 from "os";
|
|
25196
25670
|
|
|
25197
25671
|
// src/env/paths.ts
|
|
25198
|
-
import { accessSync as accessSync2, constants as constants5, existsSync as
|
|
25672
|
+
import { accessSync as accessSync2, constants as constants5, existsSync as existsSync33 } from "fs";
|
|
25199
25673
|
import os6 from "os";
|
|
25200
25674
|
import path46 from "path";
|
|
25201
25675
|
function resolveDeps2(deps) {
|
|
@@ -25204,7 +25678,7 @@ function resolveDeps2(deps) {
|
|
|
25204
25678
|
osHome: deps.osHome ?? os6.homedir(),
|
|
25205
25679
|
cwd: deps.cwd ?? process.cwd(),
|
|
25206
25680
|
tmpDir: deps.tmpDir ?? os6.tmpdir(),
|
|
25207
|
-
exists: deps.exists ??
|
|
25681
|
+
exists: deps.exists ?? existsSync33,
|
|
25208
25682
|
writable: deps.writable ?? ((dir) => {
|
|
25209
25683
|
try {
|
|
25210
25684
|
accessSync2(dir, constants5.W_OK);
|
|
@@ -25726,7 +26200,7 @@ function envDoctor(repoDir, envKeys) {
|
|
|
25726
26200
|
|
|
25727
26201
|
// src/envintel/migrate.ts
|
|
25728
26202
|
import { randomBytes } from "crypto";
|
|
25729
|
-
import { existsSync as
|
|
26203
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync14, readdirSync as readdirSync16, readFileSync as readFileSync22 } from "fs";
|
|
25730
26204
|
import path47 from "path";
|
|
25731
26205
|
function timestamp2(d = /* @__PURE__ */ new Date()) {
|
|
25732
26206
|
const p = (n4) => String(n4).padStart(2, "0");
|
|
@@ -25742,11 +26216,11 @@ function addValue(spec, exampleEmpty) {
|
|
|
25742
26216
|
function planMigration(repoDir, targetSchema) {
|
|
25743
26217
|
const schema = targetSchema ?? deriveSchema(repoDir);
|
|
25744
26218
|
const envPath = path47.join(repoDir, ".env");
|
|
25745
|
-
const env =
|
|
26219
|
+
const env = existsSync34(envPath) ? parseEnv(readFileSync22(envPath, "utf8")) : null;
|
|
25746
26220
|
const present = new Set(env?.entries.map((e) => e.key) ?? []);
|
|
25747
26221
|
const examplePath = path47.join(repoDir, ".env.example");
|
|
25748
26222
|
const exampleEmpty = /* @__PURE__ */ new Set();
|
|
25749
|
-
if (
|
|
26223
|
+
if (existsSync34(examplePath)) {
|
|
25750
26224
|
for (const e of parseEnv(readFileSync22(examplePath, "utf8")).entries) {
|
|
25751
26225
|
if (e.value === "") exampleEmpty.add(e.key);
|
|
25752
26226
|
}
|
|
@@ -25786,7 +26260,7 @@ function backupEnv(repoDir, content) {
|
|
|
25786
26260
|
}
|
|
25787
26261
|
function applyMigration(repoDir, plan, opts = {}) {
|
|
25788
26262
|
const envPath = path47.join(repoDir, ".env");
|
|
25789
|
-
const exists =
|
|
26263
|
+
const exists = existsSync34(envPath);
|
|
25790
26264
|
const current = exists ? readFileSync22(envPath, "utf8") : "";
|
|
25791
26265
|
const env = parseEnv(current);
|
|
25792
26266
|
const present = new Set(env.entries.map((e) => e.key));
|
|
@@ -25818,14 +26292,14 @@ function applyMigration(repoDir, plan, opts = {}) {
|
|
|
25818
26292
|
}
|
|
25819
26293
|
function latestEnvBackup(repoDir) {
|
|
25820
26294
|
const root = path47.join(artifactsRoot(repoDir), "backups");
|
|
25821
|
-
if (!
|
|
26295
|
+
if (!existsSync34(root)) return null;
|
|
25822
26296
|
const candidates = readdirSync16(root, { withFileTypes: true }).filter((d) => d.isDirectory() && /^env-\d{8}-\d{6}$/.test(d.name)).map((d) => d.name).sort();
|
|
25823
26297
|
const last = candidates.at(-1);
|
|
25824
26298
|
return last ? path47.join(root, last) : null;
|
|
25825
26299
|
}
|
|
25826
26300
|
function rollbackEnv(repoDir, backupDir) {
|
|
25827
26301
|
const src = path47.join(backupDir, ".env");
|
|
25828
|
-
if (!
|
|
26302
|
+
if (!existsSync34(src)) return false;
|
|
25829
26303
|
writeFileAtomic(path47.join(repoDir, ".env"), readFileSync22(src, "utf8"));
|
|
25830
26304
|
return true;
|
|
25831
26305
|
}
|
|
@@ -25846,7 +26320,7 @@ function envCandidates(dir) {
|
|
|
25846
26320
|
try {
|
|
25847
26321
|
return readdirSync17(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).filter((name) => {
|
|
25848
26322
|
const r2 = resolveInside(dir, name);
|
|
25849
|
-
return r2 !== null && (
|
|
26323
|
+
return r2 !== null && (existsSync35(path48.join(r2, ".env")) || existsSync35(path48.join(r2, ".env.example")));
|
|
25850
26324
|
}).sort();
|
|
25851
26325
|
} catch {
|
|
25852
26326
|
return [];
|
|
@@ -25923,7 +26397,7 @@ async function runEnvCommand(cli, args) {
|
|
|
25923
26397
|
envKeys = catalogFor(targetDir).find((r2) => r2.name === name)?.envKeys;
|
|
25924
26398
|
} else {
|
|
25925
26399
|
const cwd = process.cwd();
|
|
25926
|
-
if (
|
|
26400
|
+
if (existsSync35(path48.join(cwd, ".env")) || existsSync35(path48.join(cwd, ".env.example"))) {
|
|
25927
26401
|
repoDir = cwd;
|
|
25928
26402
|
}
|
|
25929
26403
|
}
|
|
@@ -25975,7 +26449,7 @@ async function runEnvCommand(cli, args) {
|
|
|
25975
26449
|
note(lines.join("\n"), `${S.envDoctorTitle} \u2014 ${repo}`);
|
|
25976
26450
|
return res2.checks.some((c5) => c5.result === "fail") ? 1 : 0;
|
|
25977
26451
|
}
|
|
25978
|
-
if (!
|
|
26452
|
+
if (!existsSync35(repoDir)) {
|
|
25979
26453
|
log.error(`${repo}: ${S.statusNotInstalled}`);
|
|
25980
26454
|
return 2;
|
|
25981
26455
|
}
|
|
@@ -25991,7 +26465,7 @@ async function runEnvCommand(cli, args) {
|
|
|
25991
26465
|
log.error(S.envWizardNonInteractive);
|
|
25992
26466
|
return 2;
|
|
25993
26467
|
}
|
|
25994
|
-
const env = parseEnv(
|
|
26468
|
+
const env = parseEnv(existsSync35(envPath) ? readFileSync23(envPath, "utf8") : "");
|
|
25995
26469
|
const current = new Map(env.entries.map((e) => [e.key, e]));
|
|
25996
26470
|
const updates = /* @__PURE__ */ new Map();
|
|
25997
26471
|
for (const spec of schema.vars) {
|
|
@@ -26034,7 +26508,7 @@ async function runEnvCommand(cli, args) {
|
|
|
26034
26508
|
}
|
|
26035
26509
|
for (const [k, v] of updates) lines.push(formatEntry(k, v));
|
|
26036
26510
|
const out = serializeEnv({ ...env, lines });
|
|
26037
|
-
if (
|
|
26511
|
+
if (existsSync35(envPath)) copyFileSync4(envPath, `${envPath}.bak`);
|
|
26038
26512
|
writeFileAtomic(envPath, out.endsWith("\n") ? out : `${out}
|
|
26039
26513
|
`);
|
|
26040
26514
|
log.success(`${icons.ok} ${S.envWizardDone} ${theme.dim(`(${envPath}.bak)`)}`);
|
|
@@ -26062,7 +26536,7 @@ async function runEnvCommand(cli, args) {
|
|
|
26062
26536
|
return 0;
|
|
26063
26537
|
}
|
|
26064
26538
|
if (verb === "init") {
|
|
26065
|
-
if (
|
|
26539
|
+
if (existsSync35(envPath)) {
|
|
26066
26540
|
if (json) console.log(JSON.stringify({ command: "env init", repo, created: false }));
|
|
26067
26541
|
else log.info(S.envInitExists);
|
|
26068
26542
|
return 0;
|
|
@@ -26177,11 +26651,11 @@ async function runEnvCommand(cli, args) {
|
|
|
26177
26651
|
|
|
26178
26652
|
// src/cmd/hosts.ts
|
|
26179
26653
|
import path50 from "path";
|
|
26180
|
-
import { existsSync as
|
|
26654
|
+
import { existsSync as existsSync37, readdirSync as readdirSync18 } from "fs";
|
|
26181
26655
|
|
|
26182
26656
|
// src/mcp/handshake.ts
|
|
26183
26657
|
import { spawn as spawn2 } from "child_process";
|
|
26184
|
-
import { existsSync as
|
|
26658
|
+
import { existsSync as existsSync36 } from "fs";
|
|
26185
26659
|
import path49 from "path";
|
|
26186
26660
|
import { createInterface as createInterface2 } from "readline";
|
|
26187
26661
|
var PROTOCOL_VERSION = "2024-11-05";
|
|
@@ -26272,7 +26746,7 @@ function probeMcpServer(argv, opts = {}) {
|
|
|
26272
26746
|
const stepMs = opts.timeoutMs ?? STEP_MS;
|
|
26273
26747
|
const levels = [];
|
|
26274
26748
|
if (path49.isAbsolute(argv[0])) {
|
|
26275
|
-
if (!
|
|
26749
|
+
if (!existsSync36(argv[0])) {
|
|
26276
26750
|
resolve(err("bin-missing", `bin\xE1rio MCP ausente: ${argv[0]}`));
|
|
26277
26751
|
return;
|
|
26278
26752
|
}
|
|
@@ -26702,7 +27176,7 @@ async function runMcpCommand(cli, args, home = cliHome()) {
|
|
|
26702
27176
|
const candidates = names.length > 0 ? names : [
|
|
26703
27177
|
.../* @__PURE__ */ new Set([
|
|
26704
27178
|
...Object.keys(loadForgeState(home).forges),
|
|
26705
|
-
...
|
|
27179
|
+
...existsSync37(targetDir) ? readdirSync18(targetDir, { withFileTypes: true }).filter((d) => d.isDirectory() && isSafeRepoName(d.name)).map((d) => d.name) : []
|
|
26706
27180
|
])
|
|
26707
27181
|
].sort();
|
|
26708
27182
|
if (candidates.length === 0) {
|
|
@@ -26723,7 +27197,7 @@ async function runMcpCommand(cli, args, home = cliHome()) {
|
|
|
26723
27197
|
results.push({ forge: name, ok: false, detail: "sem mcp_command" });
|
|
26724
27198
|
continue;
|
|
26725
27199
|
}
|
|
26726
|
-
const binPresent = path50.isAbsolute(argv[0]) ?
|
|
27200
|
+
const binPresent = path50.isAbsolute(argv[0]) ? existsSync37(argv[0]) : true;
|
|
26727
27201
|
const item = {
|
|
26728
27202
|
forge: name,
|
|
26729
27203
|
forge_id: forge.forge_id,
|
|
@@ -26771,7 +27245,7 @@ async function runMcpCommand(cli, args, home = cliHome()) {
|
|
|
26771
27245
|
}
|
|
26772
27246
|
|
|
26773
27247
|
// src/discovery.ts
|
|
26774
|
-
import { existsSync as
|
|
27248
|
+
import { existsSync as existsSync38, readdirSync as readdirSync19, readFileSync as readFileSync24, statSync as statSync8 } from "fs";
|
|
26775
27249
|
import os8 from "os";
|
|
26776
27250
|
import path51 from "path";
|
|
26777
27251
|
var DEFAULT_LIMITS = {
|
|
@@ -27061,10 +27535,10 @@ function registryState(entry2) {
|
|
|
27061
27535
|
function discoverForge(name, dir, state) {
|
|
27062
27536
|
const sources = [];
|
|
27063
27537
|
const git = !!dir && hasGitRepo(dir);
|
|
27064
|
-
const manifest = dir &&
|
|
27538
|
+
const manifest = dir && existsSync38(dir) ? readForgeManifest(dir) : null;
|
|
27065
27539
|
const entry2 = state?.repos[name];
|
|
27066
27540
|
if (entry2 && entry2.phase !== "not-installed") {
|
|
27067
|
-
const dirExists = !!dir &&
|
|
27541
|
+
const dirExists = !!dir && existsSync38(dir);
|
|
27068
27542
|
sources.push({
|
|
27069
27543
|
origin: "manifest",
|
|
27070
27544
|
// registro diz instalada mas o checkout sumiu → evidência falhando
|
|
@@ -27124,7 +27598,7 @@ async function discoverEnvironment2(targetDir, opts) {
|
|
|
27124
27598
|
const dir = resolveInside(root, dirName);
|
|
27125
27599
|
if (!dir) continue;
|
|
27126
27600
|
const git = hasGitRepo(dir);
|
|
27127
|
-
const manifest =
|
|
27601
|
+
const manifest = existsSync38(path51.join(dir, "forge.json"));
|
|
27128
27602
|
if (!git && !manifest) continue;
|
|
27129
27603
|
presence.push({ name: dirName, path: dirName, git, manifest });
|
|
27130
27604
|
if (!catalogNames.has(dirName) && manifest) {
|
|
@@ -27793,7 +28267,7 @@ async function answerRepair(targetDir, repos, opts = {}) {
|
|
|
27793
28267
|
}
|
|
27794
28268
|
|
|
27795
28269
|
// src/intel/context.ts
|
|
27796
|
-
import { existsSync as
|
|
28270
|
+
import { existsSync as existsSync39, readdirSync as readdirSync20, readFileSync as readFileSync25, statSync as statSync9 } from "fs";
|
|
27797
28271
|
import path52 from "path";
|
|
27798
28272
|
|
|
27799
28273
|
// src/planner.ts
|
|
@@ -27911,7 +28385,7 @@ function safeConfig(targetDir) {
|
|
|
27911
28385
|
function readManifestObject(repoDir) {
|
|
27912
28386
|
const file = path52.join(repoDir, "forge.json");
|
|
27913
28387
|
try {
|
|
27914
|
-
if (!
|
|
28388
|
+
if (!existsSync39(file) || statSync9(file).size > MAX_MANIFEST_BYTES) return null;
|
|
27915
28389
|
const raw = JSON.parse(readFileSync25(file, "utf8"));
|
|
27916
28390
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
|
|
27917
28391
|
} catch {
|
|
@@ -28081,7 +28555,7 @@ async function buildForgeContext(targetDir, repos, opts = {}) {
|
|
|
28081
28555
|
}
|
|
28082
28556
|
|
|
28083
28557
|
// src/intel/predict.ts
|
|
28084
|
-
import { existsSync as
|
|
28558
|
+
import { existsSync as existsSync40, readFileSync as readFileSync26, statSync as statSync10 } from "fs";
|
|
28085
28559
|
import path53 from "path";
|
|
28086
28560
|
var MAX_MANIFEST_BYTES2 = 64 * 1024;
|
|
28087
28561
|
var SEMVERISH = /^v?\d+\.\d+\.\d+/;
|
|
@@ -28094,7 +28568,7 @@ var mk2 = (id, kind, subject, certainty, version, evidence, explanation) => ({ i
|
|
|
28094
28568
|
function readManifest(repoDir) {
|
|
28095
28569
|
const file = path53.join(repoDir, "forge.json");
|
|
28096
28570
|
try {
|
|
28097
|
-
if (!
|
|
28571
|
+
if (!existsSync40(file) || statSync10(file).size > MAX_MANIFEST_BYTES2) return null;
|
|
28098
28572
|
const raw = JSON.parse(readFileSync26(file, "utf8"));
|
|
28099
28573
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
|
|
28100
28574
|
} catch {
|
|
@@ -28411,7 +28885,7 @@ async function predictCompatibility(targetDir, repos, opts = {}) {
|
|
|
28411
28885
|
const allClaims = [];
|
|
28412
28886
|
for (const repo of sorted) {
|
|
28413
28887
|
const dir = resolveInside(targetDir, repo.name);
|
|
28414
|
-
if (!dir || !
|
|
28888
|
+
if (!dir || !existsSync40(dir)) continue;
|
|
28415
28889
|
const m3 = readManifest(dir);
|
|
28416
28890
|
manifests.set(repo.name, m3);
|
|
28417
28891
|
if (m3) out.push(...schemaChange(repo.name, m3));
|
|
@@ -28423,7 +28897,7 @@ async function predictCompatibility(targetDir, repos, opts = {}) {
|
|
|
28423
28897
|
out.push(...envVarFindings("<workspace>", targetDir));
|
|
28424
28898
|
for (const repo of sorted) {
|
|
28425
28899
|
const dir = resolveInside(targetDir, repo.name);
|
|
28426
|
-
if (!dir || !
|
|
28900
|
+
if (!dir || !existsSync40(dir)) continue;
|
|
28427
28901
|
out.push(...envVarFindings(repo.name, dir, repo.envKeys));
|
|
28428
28902
|
}
|
|
28429
28903
|
out.push(...installConflict(allClaims));
|
|
@@ -28436,7 +28910,7 @@ import path55 from "path";
|
|
|
28436
28910
|
|
|
28437
28911
|
// src/ai/cache.ts
|
|
28438
28912
|
import { createHash as createHash7 } from "crypto";
|
|
28439
|
-
import { existsSync as
|
|
28913
|
+
import { existsSync as existsSync41, mkdirSync as mkdirSync15, readFileSync as readFileSync27, readdirSync as readdirSync21, rmSync as rmSync9 } from "fs";
|
|
28440
28914
|
import path54 from "path";
|
|
28441
28915
|
|
|
28442
28916
|
// src/ai/types.ts
|
|
@@ -28540,7 +29014,7 @@ function entryFile(rootDir, key) {
|
|
|
28540
29014
|
function aiCacheGet(rootDir, key, opts) {
|
|
28541
29015
|
if (opts.ttlMs <= 0) return null;
|
|
28542
29016
|
const file = entryFile(rootDir, key);
|
|
28543
|
-
if (!file || !
|
|
29017
|
+
if (!file || !existsSync41(file)) return null;
|
|
28544
29018
|
try {
|
|
28545
29019
|
const entry2 = JSON.parse(readFileSync27(file, "utf8"));
|
|
28546
29020
|
const now = opts.now ?? Date.now();
|
|
@@ -28740,7 +29214,7 @@ async function runAiBridge(req, cfg, deps) {
|
|
|
28740
29214
|
}
|
|
28741
29215
|
|
|
28742
29216
|
// src/portability/bundle.ts
|
|
28743
|
-
import { existsSync as
|
|
29217
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync17, readFileSync as readFileSync28 } from "fs";
|
|
28744
29218
|
import path56 from "path";
|
|
28745
29219
|
|
|
28746
29220
|
// src/portability/manifest.ts
|
|
@@ -28852,7 +29326,7 @@ function collectPins(targetDir, repos) {
|
|
|
28852
29326
|
const pins = /* @__PURE__ */ new Map();
|
|
28853
29327
|
for (const repo of repos) {
|
|
28854
29328
|
const repoDir = path56.join(targetDir, repo.name);
|
|
28855
|
-
const version =
|
|
29329
|
+
const version = existsSync42(repoDir) ? pinVersion(repoDir) : void 0;
|
|
28856
29330
|
const head = state?.repos[repo.name]?.head;
|
|
28857
29331
|
const ref = state?.pins?.[repo.name] ?? head;
|
|
28858
29332
|
pins.set(repo.name, {
|
|
@@ -28879,7 +29353,7 @@ async function exportBundle(targetDir, repos, dest, opts) {
|
|
|
28879
29353
|
const schemaByRepo = /* @__PURE__ */ new Map();
|
|
28880
29354
|
for (const repo of repos) {
|
|
28881
29355
|
const repoDir = path56.join(targetDir, repo.name);
|
|
28882
|
-
const schema =
|
|
29356
|
+
const schema = existsSync42(repoDir) ? deriveSchema(repoDir, repo.envKeys) : { vars: [] };
|
|
28883
29357
|
schemaByRepo.set(repo.name, schema);
|
|
28884
29358
|
for (const v of schema.vars) {
|
|
28885
29359
|
if (v.secret && whitelist.includes(v.key)) refused.add(v.key);
|
|
@@ -28921,7 +29395,7 @@ async function exportBundle(targetDir, repos, dest, opts) {
|
|
|
28921
29395
|
const values = {};
|
|
28922
29396
|
for (const repo of repos) {
|
|
28923
29397
|
const envFile = path56.join(targetDir, repo.name, ".env");
|
|
28924
|
-
if (!
|
|
29398
|
+
if (!existsSync42(envFile)) continue;
|
|
28925
29399
|
const entries = parseEnv(readFileSync28(envFile, "utf8")).entries;
|
|
28926
29400
|
const found = {};
|
|
28927
29401
|
for (const key of whitelist) {
|
|
@@ -29011,7 +29485,7 @@ function importBundle(src, opts) {
|
|
|
29011
29485
|
}
|
|
29012
29486
|
|
|
29013
29487
|
// src/intel/learning.ts
|
|
29014
|
-
import { existsSync as
|
|
29488
|
+
import { existsSync as existsSync43, mkdirSync as mkdirSync18, readFileSync as readFileSync29 } from "fs";
|
|
29015
29489
|
import path57 from "path";
|
|
29016
29490
|
var LEARNING_SCHEMA = "forge-cli/LearningStore/v1";
|
|
29017
29491
|
function learningFile(targetDir) {
|
|
@@ -29019,7 +29493,7 @@ function learningFile(targetDir) {
|
|
|
29019
29493
|
}
|
|
29020
29494
|
function readStore(targetDir) {
|
|
29021
29495
|
const file = learningFile(targetDir);
|
|
29022
|
-
if (!
|
|
29496
|
+
if (!existsSync43(file)) return { schema: LEARNING_SCHEMA, enabled: false, observations: [] };
|
|
29023
29497
|
try {
|
|
29024
29498
|
const parsed = JSON.parse(readFileSync29(file, "utf8"));
|
|
29025
29499
|
if (parsed?.schema !== LEARNING_SCHEMA || !Array.isArray(parsed.observations)) {
|
|
@@ -29279,7 +29753,7 @@ async function runLearningCommand(cli) {
|
|
|
29279
29753
|
}
|
|
29280
29754
|
|
|
29281
29755
|
// src/wizard.ts
|
|
29282
|
-
import { existsSync as
|
|
29756
|
+
import { existsSync as existsSync44 } from "fs";
|
|
29283
29757
|
import path58 from "path";
|
|
29284
29758
|
var STEP_LABELS = [
|
|
29285
29759
|
S.wizardStepDetect,
|
|
@@ -29320,7 +29794,7 @@ function renderOperationPlan(plan) {
|
|
|
29320
29794
|
}
|
|
29321
29795
|
function planActionsFor(repo, targetDir, steps) {
|
|
29322
29796
|
const repoDir = path58.join(targetDir, repo.name);
|
|
29323
|
-
const cloned =
|
|
29797
|
+
const cloned = existsSync44(repoDir) && hasGitRepo(repoDir);
|
|
29324
29798
|
const actions = [
|
|
29325
29799
|
{
|
|
29326
29800
|
kind: "clone",
|