theforge-cli 0.2.4 → 0.2.6
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 +12 -0
- package/dist/index.js +603 -122
- package/docs/installation.md +48 -0
- package/package.json +2 -2
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:
|
|
@@ -15272,6 +15288,8 @@ async function checkEnvironment() {
|
|
|
15272
15288
|
return env;
|
|
15273
15289
|
}
|
|
15274
15290
|
function emitJson(data, exitCode = 0) {
|
|
15291
|
+
const handle = process.stdout._handle;
|
|
15292
|
+
handle?.setBlocking?.(true);
|
|
15275
15293
|
process.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
15276
15294
|
`);
|
|
15277
15295
|
process.exit(exitCode);
|
|
@@ -19705,7 +19723,7 @@ function errMsg(error) {
|
|
|
19705
19723
|
}
|
|
19706
19724
|
|
|
19707
19725
|
// src/doctor2/domains.ts
|
|
19708
|
-
import { existsSync as
|
|
19726
|
+
import { existsSync as existsSync25, readdirSync as readdirSync11, rmSync as rmSync6, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
19709
19727
|
import os4 from "os";
|
|
19710
19728
|
import path34 from "path";
|
|
19711
19729
|
|
|
@@ -20186,6 +20204,303 @@ function analyzeBumps(current, candidates) {
|
|
|
20186
20204
|
}).sort((a3, b) => a3.to.localeCompare(b.to, void 0, { numeric: true }));
|
|
20187
20205
|
}
|
|
20188
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
|
+
|
|
20189
20504
|
// src/doctor2/domains.ts
|
|
20190
20505
|
function systemChecks(ctx) {
|
|
20191
20506
|
const checks = [];
|
|
@@ -20277,7 +20592,7 @@ function systemChecks(ctx) {
|
|
|
20277
20592
|
const dirEvidence = [];
|
|
20278
20593
|
let dirFailed = 0;
|
|
20279
20594
|
for (const { label, dir: d } of required) {
|
|
20280
|
-
if (!
|
|
20595
|
+
if (!existsSync25(d)) {
|
|
20281
20596
|
dirFailed += 1;
|
|
20282
20597
|
dirEvidence.push(`${label}=${d}: ausente`);
|
|
20283
20598
|
continue;
|
|
@@ -20348,7 +20663,7 @@ function backupScan(ctx) {
|
|
|
20348
20663
|
scan(
|
|
20349
20664
|
path34.join(ctx.home, ".forge-cli", "backups"),
|
|
20350
20665
|
"host-backup",
|
|
20351
|
-
(full) =>
|
|
20666
|
+
(full) => existsSync25(path34.join(full, "manifest.json"))
|
|
20352
20667
|
);
|
|
20353
20668
|
for (const repo of ctx.catalog) {
|
|
20354
20669
|
const dir = resolveInside(ctx.root, repo.name);
|
|
@@ -20361,7 +20676,7 @@ function backupScan(ctx) {
|
|
|
20361
20676
|
function installationChecks(ctx) {
|
|
20362
20677
|
const checks = [];
|
|
20363
20678
|
const forgeDir = artifactsRoot(ctx.root);
|
|
20364
|
-
const forgeDirPresent =
|
|
20679
|
+
const forgeDirPresent = existsSync25(forgeDir);
|
|
20365
20680
|
const stateFile = stateFileFor(ctx.root);
|
|
20366
20681
|
checks.push(
|
|
20367
20682
|
mkCheck(
|
|
@@ -20374,7 +20689,7 @@ function installationChecks(ctx) {
|
|
|
20374
20689
|
diagnosis: "diret\xF3rio de estado .forge-cli presente",
|
|
20375
20690
|
evidence: [
|
|
20376
20691
|
`${forgeDir} presente`,
|
|
20377
|
-
|
|
20692
|
+
existsSync25(stateFile) ? "state.json presente" : "state.json ausente"
|
|
20378
20693
|
],
|
|
20379
20694
|
reversible: null
|
|
20380
20695
|
} : {
|
|
@@ -20390,7 +20705,7 @@ function installationChecks(ctx) {
|
|
|
20390
20705
|
)
|
|
20391
20706
|
);
|
|
20392
20707
|
const forgeJson = path34.join(ctx.root, "forge.json");
|
|
20393
|
-
if (!
|
|
20708
|
+
if (!existsSync25(forgeJson)) {
|
|
20394
20709
|
checks.push(
|
|
20395
20710
|
mkCheck(ctx.now, {
|
|
20396
20711
|
id: "installation.forge-json.present",
|
|
@@ -20440,9 +20755,9 @@ function installationChecks(ctx) {
|
|
|
20440
20755
|
}
|
|
20441
20756
|
}
|
|
20442
20757
|
const lockFile = path34.join(forgeDir, "env.lock.json");
|
|
20443
|
-
const lockParsed =
|
|
20758
|
+
const lockParsed = existsSync25(lockFile) ? readJson(lockFile) : null;
|
|
20444
20759
|
const lockObj = lockParsed?.ok && lockParsed.value !== null && typeof lockParsed.value === "object" ? lockParsed.value : null;
|
|
20445
|
-
if (!forgeDirPresent || !
|
|
20760
|
+
if (!forgeDirPresent || !existsSync25(lockFile)) {
|
|
20446
20761
|
checks.push(
|
|
20447
20762
|
mkCheck(ctx.now, {
|
|
20448
20763
|
id: "installation.lockfile.present",
|
|
@@ -20487,7 +20802,7 @@ function installationChecks(ctx) {
|
|
|
20487
20802
|
);
|
|
20488
20803
|
}
|
|
20489
20804
|
const trackedForges = lockObj ? Object.keys(lockObj.forges ?? {}) : [];
|
|
20490
|
-
if (!
|
|
20805
|
+
if (!existsSync25(lockFile) || !lockObj || !trackedForges.length) {
|
|
20491
20806
|
checks.push(
|
|
20492
20807
|
mkCheck(ctx.now, {
|
|
20493
20808
|
id: "installation.lockfile.drift",
|
|
@@ -20496,7 +20811,7 @@ function installationChecks(ctx) {
|
|
|
20496
20811
|
severity: "info",
|
|
20497
20812
|
diagnosis: "deriva do lockfile n\xE3o verific\xE1vel",
|
|
20498
20813
|
evidence: [
|
|
20499
|
-
!
|
|
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"
|
|
20500
20815
|
],
|
|
20501
20816
|
reversible: null
|
|
20502
20817
|
})
|
|
@@ -20525,10 +20840,10 @@ function installationChecks(ctx) {
|
|
|
20525
20840
|
mkCheck(ctx.now, {
|
|
20526
20841
|
id: "installation.ledger.readable",
|
|
20527
20842
|
domain: "installation",
|
|
20528
|
-
result:
|
|
20529
|
-
severity:
|
|
20530
|
-
diagnosis:
|
|
20531
|
-
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"}`],
|
|
20532
20847
|
reversible: null
|
|
20533
20848
|
})
|
|
20534
20849
|
);
|
|
@@ -20610,11 +20925,11 @@ function installationChecks(ctx) {
|
|
|
20610
20925
|
} else {
|
|
20611
20926
|
const lines = localEntries.map(([name, r2]) => {
|
|
20612
20927
|
const src = typeof r2.source_dir === "string" ? r2.source_dir : "?";
|
|
20613
|
-
const srcOk = typeof r2.source_dir === "string" &&
|
|
20928
|
+
const srcOk = typeof r2.source_dir === "string" && existsSync25(r2.source_dir);
|
|
20614
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)"}`;
|
|
20615
20930
|
});
|
|
20616
20931
|
const missing = localEntries.filter(
|
|
20617
|
-
([, r2]) => typeof r2.source_dir !== "string" || !
|
|
20932
|
+
([, r2]) => typeof r2.source_dir !== "string" || !existsSync25(r2.source_dir)
|
|
20618
20933
|
);
|
|
20619
20934
|
checks.push(
|
|
20620
20935
|
mkCheck(ctx.now, {
|
|
@@ -20688,6 +21003,58 @@ function installationChecks(ctx) {
|
|
|
20688
21003
|
}
|
|
20689
21004
|
)
|
|
20690
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
|
+
}
|
|
20691
21058
|
return checks;
|
|
20692
21059
|
}
|
|
20693
21060
|
function environmentChecks(ctx) {
|
|
@@ -20695,7 +21062,7 @@ function environmentChecks(ctx) {
|
|
|
20695
21062
|
const targets = [{ label: "<workspace>", dir: ctx.root }];
|
|
20696
21063
|
for (const repo of ctx.catalog) {
|
|
20697
21064
|
const dir = resolveInside(ctx.root, repo.name);
|
|
20698
|
-
if (dir &&
|
|
21065
|
+
if (dir && existsSync25(dir)) targets.push({ label: repo.name, dir, envKeys: repo.envKeys });
|
|
20699
21066
|
}
|
|
20700
21067
|
const inspected = targets.map((t2) => ({
|
|
20701
21068
|
...t2,
|
|
@@ -20917,7 +21284,7 @@ function operationsChecks(ctx) {
|
|
|
20917
21284
|
checks.push(
|
|
20918
21285
|
mkCheck(
|
|
20919
21286
|
ctx.now,
|
|
20920
|
-
!
|
|
21287
|
+
!existsSync25(reportsDir) ? {
|
|
20921
21288
|
id: "operations.receipts.recent",
|
|
20922
21289
|
domain: "operations",
|
|
20923
21290
|
result: "skip",
|
|
@@ -20953,7 +21320,7 @@ function operationsChecks(ctx) {
|
|
|
20953
21320
|
if (entry2.status === "error") failures.set(name, (failures.get(name) ?? 0) + 1);
|
|
20954
21321
|
}
|
|
20955
21322
|
const recurring = [...failures.entries()].filter(([, n4]) => n4 >= 2);
|
|
20956
|
-
const scannedAnything =
|
|
21323
|
+
const scannedAnything = existsSync25(reportsDir) || ctx.state !== null;
|
|
20957
21324
|
if (!scannedAnything) {
|
|
20958
21325
|
checks.push(
|
|
20959
21326
|
mkCheck(ctx.now, {
|
|
@@ -21229,7 +21596,7 @@ async function runDoctor2(root, opts = {}) {
|
|
|
21229
21596
|
}
|
|
21230
21597
|
|
|
21231
21598
|
// src/heal/fixes.ts
|
|
21232
|
-
import { existsSync as
|
|
21599
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync8, readdirSync as readdirSync12, readFileSync as readFileSync19, statSync as statSync5 } from "fs";
|
|
21233
21600
|
import path36 from "path";
|
|
21234
21601
|
var rel = (root, abs) => path36.relative(root, abs) || ".";
|
|
21235
21602
|
function stampOf(iso) {
|
|
@@ -21248,7 +21615,7 @@ var forgeDirRepair = {
|
|
|
21248
21615
|
return ["", ...FORGE_DIR_SUBDIRS].map((sub) => {
|
|
21249
21616
|
const dir = sub ? path36.join(base, sub) : base;
|
|
21250
21617
|
return {
|
|
21251
|
-
description: `create directory ${rel(ctx.root, dir)}${
|
|
21618
|
+
description: `create directory ${rel(ctx.root, dir)}${existsSync26(dir) ? " (already present)" : ""}`,
|
|
21252
21619
|
touches: [rel(ctx.root, dir)]
|
|
21253
21620
|
};
|
|
21254
21621
|
});
|
|
@@ -21259,7 +21626,7 @@ var forgeDirRepair = {
|
|
|
21259
21626
|
const created = [];
|
|
21260
21627
|
for (const sub of ["", ...FORGE_DIR_SUBDIRS]) {
|
|
21261
21628
|
const dir = sub ? path36.join(base, sub) : base;
|
|
21262
|
-
if (
|
|
21629
|
+
if (existsSync26(dir)) continue;
|
|
21263
21630
|
mkdirSync8(dir, { recursive: true });
|
|
21264
21631
|
created.push(rel(ctx.root, dir));
|
|
21265
21632
|
}
|
|
@@ -21283,7 +21650,7 @@ function envPathOf(root) {
|
|
|
21283
21650
|
}
|
|
21284
21651
|
function presentKeys(root) {
|
|
21285
21652
|
const file = envPathOf(root);
|
|
21286
|
-
if (!
|
|
21653
|
+
if (!existsSync26(file)) return /* @__PURE__ */ new Set();
|
|
21287
21654
|
return new Set(parseEnv(readFileSync19(file, "utf8")).entries.map((e) => e.key));
|
|
21288
21655
|
}
|
|
21289
21656
|
function missingEnvKeys(root, check) {
|
|
@@ -21319,7 +21686,7 @@ var envMissingKeyRepair = {
|
|
|
21319
21686
|
const root = ctx.root;
|
|
21320
21687
|
const toAppend = keysToAppend(root, ctx.check);
|
|
21321
21688
|
const steps = [];
|
|
21322
|
-
if (
|
|
21689
|
+
if (existsSync26(envPathOf(root))) {
|
|
21323
21690
|
steps.push({
|
|
21324
21691
|
description: `backup .env to ${rel(root, path36.join(artifactsRoot(root), "backups"))}/env-<ts>/.env`,
|
|
21325
21692
|
touches: [".env", `${rel(root, artifactsRoot(root))}/backups/`]
|
|
@@ -21338,7 +21705,7 @@ var envMissingKeyRepair = {
|
|
|
21338
21705
|
const root = ctx.root;
|
|
21339
21706
|
if (ctx.dryRun) return "dry-run \u2014 not applied";
|
|
21340
21707
|
const file = envPathOf(root);
|
|
21341
|
-
const exists =
|
|
21708
|
+
const exists = existsSync26(file);
|
|
21342
21709
|
const current = exists ? readFileSync19(file, "utf8") : "";
|
|
21343
21710
|
const env = parseEnv(current);
|
|
21344
21711
|
const present = new Set(env.entries.map((e) => e.key));
|
|
@@ -21356,7 +21723,7 @@ var envMissingKeyRepair = {
|
|
|
21356
21723
|
},
|
|
21357
21724
|
async verify(ctx) {
|
|
21358
21725
|
const file = envPathOf(ctx.root);
|
|
21359
|
-
if (!
|
|
21726
|
+
if (!existsSync26(file)) return false;
|
|
21360
21727
|
const present = new Set(parseEnv(readFileSync19(file, "utf8")).entries.map((e) => e.key));
|
|
21361
21728
|
return missingEnvKeys(ctx.root, ctx.check).every((k) => present.has(k));
|
|
21362
21729
|
}
|
|
@@ -21394,7 +21761,7 @@ var ENV_BACKUP_RE = /^env-\d{8}-\d{6}$/;
|
|
|
21394
21761
|
var REPO_BACKUP_RE = /^([a-z0-9][a-z0-9._-]*)-\d{10,}$/i;
|
|
21395
21762
|
function listBackups(root) {
|
|
21396
21763
|
const base = path36.join(artifactsRoot(root), "backups");
|
|
21397
|
-
if (!
|
|
21764
|
+
if (!existsSync26(base)) return [];
|
|
21398
21765
|
const out = [];
|
|
21399
21766
|
for (const entry2 of readdirSync12(base, { withFileTypes: true })) {
|
|
21400
21767
|
if (!entry2.isDirectory()) continue;
|
|
@@ -21423,7 +21790,7 @@ function pickBackupFile(ctx) {
|
|
|
21423
21790
|
if (inside.startsWith("..") || path36.isAbsolute(inside)) {
|
|
21424
21791
|
throw new Error(`selected backup escapes .forge-cli/backups: ${ctx.backup}`);
|
|
21425
21792
|
}
|
|
21426
|
-
if (!
|
|
21793
|
+
if (!existsSync26(abs) || !statSync5(abs).isFile()) {
|
|
21427
21794
|
throw new Error(`selected backup not found: ${ctx.backup}`);
|
|
21428
21795
|
}
|
|
21429
21796
|
const dirName = inside.split(path36.sep)[0] ?? "";
|
|
@@ -21491,7 +21858,7 @@ var backupRestoreRepair = {
|
|
|
21491
21858
|
try {
|
|
21492
21859
|
const picked = pickBackupFile(ctx);
|
|
21493
21860
|
const dest = path36.resolve(ctx.root, picked.dest);
|
|
21494
|
-
return
|
|
21861
|
+
return existsSync26(dest) && readFileSync19(dest, "utf8") === readFileSync19(picked.src, "utf8");
|
|
21495
21862
|
} catch {
|
|
21496
21863
|
return false;
|
|
21497
21864
|
}
|
|
@@ -21746,7 +22113,7 @@ async function runDoctor2Command(cli) {
|
|
|
21746
22113
|
|
|
21747
22114
|
// src/cmd/registry.ts
|
|
21748
22115
|
import path38 from "path";
|
|
21749
|
-
import { existsSync as
|
|
22116
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync10 } from "fs";
|
|
21750
22117
|
|
|
21751
22118
|
// src/registry/catalog.ts
|
|
21752
22119
|
var REGISTRY_PACKAGES = [
|
|
@@ -22160,7 +22527,14 @@ async function runRegistryInstall(cli, names, deps = {}) {
|
|
|
22160
22527
|
...receipt.native ? { native: receipt.native } : {}
|
|
22161
22528
|
});
|
|
22162
22529
|
}
|
|
22163
|
-
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
|
+
};
|
|
22164
22538
|
reportTo2(doc);
|
|
22165
22539
|
if (cli.reportPath !== void 0) {
|
|
22166
22540
|
log.info(`${S.reportSaved}: ${theme.dim(path38.resolve(cli.reportPath))}`);
|
|
@@ -22182,8 +22556,62 @@ async function runRegistryInstall(cli, names, deps = {}) {
|
|
|
22182
22556
|
"Launchers em ~/.forge-cli/bin (no PATH do forge-cli); sem shim, invoque o caminho da venv"
|
|
22183
22557
|
);
|
|
22184
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
|
+
}
|
|
22185
22586
|
return results;
|
|
22186
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
|
+
}
|
|
22187
22615
|
async function runRegistryLock(cli, names, deps = {}) {
|
|
22188
22616
|
const home = deps.home ?? cliHome();
|
|
22189
22617
|
const quiet2 = registryQuiet(cli);
|
|
@@ -22304,7 +22732,7 @@ function runRegistryList(cli, deps = {}) {
|
|
|
22304
22732
|
}
|
|
22305
22733
|
const rows = REGISTRY_PACKAGES.filter((e) => !wanted || wanted.has(e.name)).map((e) => {
|
|
22306
22734
|
const inst = state.forges[e.name];
|
|
22307
|
-
const binOk = inst ? typeof inst.bin === "string" &&
|
|
22735
|
+
const binOk = inst ? typeof inst.bin === "string" && existsSync27(inst.bin) : null;
|
|
22308
22736
|
const consistency = installConsistency(home, e.name);
|
|
22309
22737
|
const relevant = consistency.status !== "not-installed";
|
|
22310
22738
|
return {
|
|
@@ -22333,7 +22761,7 @@ function runRegistryList(cli, deps = {}) {
|
|
|
22333
22761
|
installed: inst.version ?? null,
|
|
22334
22762
|
method: inst.method ?? null,
|
|
22335
22763
|
verifyOk: inst.verify?.ok ?? null,
|
|
22336
|
-
binPresent: typeof inst.bin === "string" ?
|
|
22764
|
+
binPresent: typeof inst.bin === "string" ? existsSync27(inst.bin) : null,
|
|
22337
22765
|
bin: inst.bin ?? null,
|
|
22338
22766
|
consistent: null,
|
|
22339
22767
|
consistency: null,
|
|
@@ -23101,7 +23529,7 @@ async function runRegistryPin(cli, names, pin, deps = {}) {
|
|
|
23101
23529
|
|
|
23102
23530
|
// src/source/detect.ts
|
|
23103
23531
|
import { createHash as createHash5 } from "crypto";
|
|
23104
|
-
import { existsSync as
|
|
23532
|
+
import { existsSync as existsSync28, lstatSync as lstatSync3, readFileSync as readFileSync20, statSync as statSync6 } from "fs";
|
|
23105
23533
|
import path39 from "path";
|
|
23106
23534
|
var MANIFEST_MAX_BYTES = 64 * 1024;
|
|
23107
23535
|
var CHECKSUM_MAX_ENTRIES = 1e4;
|
|
@@ -23124,7 +23552,7 @@ var PROJECT_MARKERS = [
|
|
|
23124
23552
|
var CHECKSUM_FILES = ["SHA256SUMS", "checksums.sha256", ".forge-checksums.json"];
|
|
23125
23553
|
function readManifestStrict(dir) {
|
|
23126
23554
|
const file = path39.join(dir, "forge.json");
|
|
23127
|
-
if (!
|
|
23555
|
+
if (!existsSync28(file)) return { ok: false, reason: "absent", detail: "forge.json ausente" };
|
|
23128
23556
|
let raw;
|
|
23129
23557
|
try {
|
|
23130
23558
|
if (!statSync6(file).isFile() || statSync6(file).size > MANIFEST_MAX_BYTES) {
|
|
@@ -23199,7 +23627,7 @@ function scanStructure(dir) {
|
|
|
23199
23627
|
return false;
|
|
23200
23628
|
}
|
|
23201
23629
|
});
|
|
23202
|
-
const markers = PROJECT_MARKERS.filter((f) =>
|
|
23630
|
+
const markers = PROJECT_MARKERS.filter((f) => existsSync28(path39.join(dir, f)));
|
|
23203
23631
|
return { setup_scripts: [...setup], project_markers: [...markers] };
|
|
23204
23632
|
}
|
|
23205
23633
|
function verifyChecksums(dir, reasons) {
|
|
@@ -23421,7 +23849,7 @@ function detectSource(input, cwd = process.cwd()) {
|
|
|
23421
23849
|
const structure = scanStructure(dir);
|
|
23422
23850
|
const expected = structure.setup_scripts.length > 0 && structure.project_markers.length > 0;
|
|
23423
23851
|
if (gitKind === null) {
|
|
23424
|
-
if (!
|
|
23852
|
+
if (!existsSync28(path39.join(dir, "forge.json"))) {
|
|
23425
23853
|
reasons.push("forge.json=ausente");
|
|
23426
23854
|
return verdict(
|
|
23427
23855
|
"UNKNOWN_DIRECTORY",
|
|
@@ -23504,7 +23932,7 @@ function looksLikePathArg(arg, cwd = process.cwd()) {
|
|
|
23504
23932
|
import path41 from "path";
|
|
23505
23933
|
|
|
23506
23934
|
// src/source/install.ts
|
|
23507
|
-
import { existsSync as
|
|
23935
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync11, readdirSync as readdirSync13, rmSync as rmSync7, statSync as statSync7 } from "fs";
|
|
23508
23936
|
import { copyFile as copyFile3 } from "fs/promises";
|
|
23509
23937
|
import path40 from "path";
|
|
23510
23938
|
var fail = (failure, detection) => ({
|
|
@@ -23592,7 +24020,7 @@ function selectSetup(det, platform2, python, winShells) {
|
|
|
23592
24020
|
if (script) return script;
|
|
23593
24021
|
const bootstrap = path40.join("scripts", "forge_bootstrap.py");
|
|
23594
24022
|
const argv = localPythonOf(python)?.argv;
|
|
23595
|
-
if (argv?.length &&
|
|
24023
|
+
if (argv?.length && existsSync29(path40.join(det.path, bootstrap))) {
|
|
23596
24024
|
return {
|
|
23597
24025
|
file: bootstrap,
|
|
23598
24026
|
command: argv[0],
|
|
@@ -23618,14 +24046,14 @@ function pythonFloorFailure(det, python) {
|
|
|
23618
24046
|
}
|
|
23619
24047
|
async function provisionEnvironment(dir, forgeId) {
|
|
23620
24048
|
const envKeys = FORGE_REPOS.find((r2) => r2.name === forgeId)?.envKeys;
|
|
23621
|
-
if (envKeys?.length ||
|
|
24049
|
+
if (envKeys?.length || existsSync29(path40.join(dir, ".env")) || existsSync29(path40.join(dir, ".env.example"))) {
|
|
23622
24050
|
await ensureEnvFile(dir, envKeys);
|
|
23623
24051
|
}
|
|
23624
24052
|
await copyConfigExamples(dir);
|
|
23625
24053
|
}
|
|
23626
24054
|
function sourceVenv(det, platform2) {
|
|
23627
24055
|
const venv = path40.join(det.path, ".venv");
|
|
23628
|
-
return
|
|
24056
|
+
return existsSync29(venvBin(venv, platform2)) ? venv : void 0;
|
|
23629
24057
|
}
|
|
23630
24058
|
async function persistLocalReceipt(home, receipt, opts) {
|
|
23631
24059
|
const envelope = resolveInside(forgesRoot(home), receipt.forge);
|
|
@@ -23650,7 +24078,7 @@ function buildReceipt(det, deps, script, managed, sourceFiles, prior) {
|
|
|
23650
24078
|
const venv = sourceVenv(det, platform2);
|
|
23651
24079
|
const cliName = manifest?.cli;
|
|
23652
24080
|
const binCandidate = venv && cliName ? path40.join(venvBin(venv, platform2), cliName) : void 0;
|
|
23653
|
-
const bin = binCandidate &&
|
|
24081
|
+
const bin = binCandidate && existsSync29(binCandidate) ? binCandidate : cliName;
|
|
23654
24082
|
const py = localPythonOf(deps.python);
|
|
23655
24083
|
return {
|
|
23656
24084
|
forge: det.forge_id,
|
|
@@ -23805,7 +24233,7 @@ async function localInstall(dir, deps) {
|
|
|
23805
24233
|
await runVerify(receipt, det, deps, run);
|
|
23806
24234
|
try {
|
|
23807
24235
|
await persistLocalReceipt(deps.home, receipt, deps.lifecycleLock);
|
|
23808
|
-
if (receipt.bin &&
|
|
24236
|
+
if (receipt.bin && existsSync29(receipt.bin)) {
|
|
23809
24237
|
try {
|
|
23810
24238
|
receipt.shim = emitForgeShim(deps.home, receipt.bin, platform2);
|
|
23811
24239
|
} catch (shimError) {
|
|
@@ -23855,7 +24283,7 @@ async function localUpdate(dir, deps) {
|
|
|
23855
24283
|
if (gate) return fail(gate, det);
|
|
23856
24284
|
const targetDir = prior.source_dir;
|
|
23857
24285
|
if (path40.resolve(targetDir) !== det.path) {
|
|
23858
|
-
if (!
|
|
24286
|
+
if (!existsSync29(targetDir)) {
|
|
23859
24287
|
return fail(
|
|
23860
24288
|
{
|
|
23861
24289
|
code: "FORGE-SOURCE-NOT-FOUND",
|
|
@@ -23877,7 +24305,7 @@ async function localUpdate(dir, deps) {
|
|
|
23877
24305
|
for (const rel2 of prior.source_files) {
|
|
23878
24306
|
if (keep.has(rel2)) continue;
|
|
23879
24307
|
const target = safeJoinInside(targetDir, rel2);
|
|
23880
|
-
if (target &&
|
|
24308
|
+
if (target && existsSync29(target)) rmSync7(target, { force: true });
|
|
23881
24309
|
}
|
|
23882
24310
|
}
|
|
23883
24311
|
return runUpdateAt(targetDir, prior, deps, run);
|
|
@@ -23920,7 +24348,7 @@ async function runUpdateAt(dir, prior, deps, run) {
|
|
|
23920
24348
|
}
|
|
23921
24349
|
const managed = [
|
|
23922
24350
|
.../* @__PURE__ */ new Set([
|
|
23923
|
-
...prior.managed_artifacts.filter((a3) =>
|
|
24351
|
+
...prior.managed_artifacts.filter((a3) => existsSync29(path40.join(det.path, a3))),
|
|
23924
24352
|
...[...topLevelEntries(det.path)].filter((e) => !before.has(e))
|
|
23925
24353
|
])
|
|
23926
24354
|
].sort();
|
|
@@ -23972,7 +24400,7 @@ async function localRepair(name, deps) {
|
|
|
23972
24400
|
});
|
|
23973
24401
|
}
|
|
23974
24402
|
const dir = prior.source_dir;
|
|
23975
|
-
if (!dir || !
|
|
24403
|
+
if (!dir || !existsSync29(dir)) {
|
|
23976
24404
|
return fail({
|
|
23977
24405
|
code: "FORGE-SOURCE-NOT-FOUND",
|
|
23978
24406
|
cause: `${name}: fonte local ausente (${dir ?? "desconhecida"})`,
|
|
@@ -23987,7 +24415,7 @@ async function localRepair(name, deps) {
|
|
|
23987
24415
|
if (floorFailure) return fail(floorFailure, det);
|
|
23988
24416
|
for (const rel2 of prior.managed_artifacts) {
|
|
23989
24417
|
const target = safeJoinInside(dir, rel2);
|
|
23990
|
-
if (target &&
|
|
24418
|
+
if (target && existsSync29(target)) rmSync7(target, { recursive: true, force: true });
|
|
23991
24419
|
}
|
|
23992
24420
|
const before = topLevelEntries(dir);
|
|
23993
24421
|
const envFailure = await provisionOrRefuse(dir, det.forge_id);
|
|
@@ -24022,7 +24450,7 @@ async function localRepair(name, deps) {
|
|
|
24022
24450
|
}
|
|
24023
24451
|
const managed = [
|
|
24024
24452
|
.../* @__PURE__ */ new Set([
|
|
24025
|
-
...prior.managed_artifacts.filter((a3) =>
|
|
24453
|
+
...prior.managed_artifacts.filter((a3) => existsSync29(path40.join(dir, a3))),
|
|
24026
24454
|
...[...topLevelEntries(dir)].filter((e) => !before.has(e))
|
|
24027
24455
|
])
|
|
24028
24456
|
].sort();
|
|
@@ -24078,7 +24506,7 @@ async function localUninstall(name, deps) {
|
|
|
24078
24506
|
const dir = prior.source_dir;
|
|
24079
24507
|
const removed = [];
|
|
24080
24508
|
let backup;
|
|
24081
|
-
if (dir &&
|
|
24509
|
+
if (dir && existsSync29(dir)) {
|
|
24082
24510
|
const filesToBackup = prior.managed_artifacts.filter((rel2) => {
|
|
24083
24511
|
const target = safeJoinInside(dir, rel2);
|
|
24084
24512
|
try {
|
|
@@ -24103,7 +24531,7 @@ async function localUninstall(name, deps) {
|
|
|
24103
24531
|
}
|
|
24104
24532
|
for (const rel2 of prior.managed_artifacts) {
|
|
24105
24533
|
const target = safeJoinInside(dir, rel2);
|
|
24106
|
-
if (target &&
|
|
24534
|
+
if (target && existsSync29(target)) {
|
|
24107
24535
|
rmSync7(target, { recursive: true, force: true });
|
|
24108
24536
|
removed.push(rel2);
|
|
24109
24537
|
}
|
|
@@ -24116,7 +24544,7 @@ async function localUninstall(name, deps) {
|
|
|
24116
24544
|
try {
|
|
24117
24545
|
removeForgeShim(deps.home, prior.shim ?? prior.bin, deps.platform ?? process.platform);
|
|
24118
24546
|
const envelope = resolveInside(forgesRoot(deps.home), name);
|
|
24119
|
-
if (envelope &&
|
|
24547
|
+
if (envelope && existsSync29(envelope)) rmSync7(envelope, { recursive: true, force: true });
|
|
24120
24548
|
const cur = loadForgeState(deps.home);
|
|
24121
24549
|
if (Object.hasOwn(cur.forges, name)) {
|
|
24122
24550
|
delete cur.forges[name];
|
|
@@ -24481,15 +24909,15 @@ async function runLocalLifecycleCommand(cli, op, names) {
|
|
|
24481
24909
|
}
|
|
24482
24910
|
|
|
24483
24911
|
// src/lifecycle.ts
|
|
24484
|
-
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";
|
|
24485
24913
|
import path42 from "path";
|
|
24486
24914
|
function configFilesToBackup(repoDir) {
|
|
24487
24915
|
const out = /* @__PURE__ */ new Set();
|
|
24488
|
-
if (
|
|
24916
|
+
if (existsSync30(path42.join(repoDir, ".env"))) out.add(".env");
|
|
24489
24917
|
for (const file of readdirSync14(repoDir)) {
|
|
24490
24918
|
if (file === ".env.example" || !file.includes(".example.")) continue;
|
|
24491
24919
|
const real = file.replace(".example.", ".");
|
|
24492
|
-
if (
|
|
24920
|
+
if (existsSync30(path42.join(repoDir, real))) out.add(real);
|
|
24493
24921
|
}
|
|
24494
24922
|
return [...out];
|
|
24495
24923
|
}
|
|
@@ -24498,7 +24926,7 @@ async function planUninstall(targetDir, repo) {
|
|
|
24498
24926
|
if (!dir)
|
|
24499
24927
|
return { kind: "refused", dir: path42.join(targetDir, repo.name), reason: "invalid-name" };
|
|
24500
24928
|
if (isLinkOrJunction(dir)) return { kind: "refused", dir, reason: "symlink" };
|
|
24501
|
-
if (!
|
|
24929
|
+
if (!existsSync30(dir)) return { kind: "absent", dir };
|
|
24502
24930
|
if (!hasGitRepo(dir)) return { kind: "refused", dir, reason: "non-git" };
|
|
24503
24931
|
const remote = await commandOutput("git", ["-C", dir, "remote", "get-url", "origin"]);
|
|
24504
24932
|
if (remote && remote !== "ok" && !sameRemote(remote, repo.url)) {
|
|
@@ -24849,11 +25277,11 @@ async function runUninstallCommand(cli) {
|
|
|
24849
25277
|
}
|
|
24850
25278
|
|
|
24851
25279
|
// src/config.ts
|
|
24852
|
-
import { existsSync as
|
|
25280
|
+
import { existsSync as existsSync31, readFileSync as readFileSync21 } from "fs";
|
|
24853
25281
|
import path44 from "path";
|
|
24854
25282
|
function loadConfig(targetDir) {
|
|
24855
25283
|
const file = path44.join(targetDir, "forge-cli.config.json");
|
|
24856
|
-
if (!
|
|
25284
|
+
if (!existsSync31(file)) return null;
|
|
24857
25285
|
let parsed;
|
|
24858
25286
|
try {
|
|
24859
25287
|
parsed = JSON.parse(readFileSync21(file, "utf8"));
|
|
@@ -24899,12 +25327,12 @@ function loadConfig(targetDir) {
|
|
|
24899
25327
|
}
|
|
24900
25328
|
|
|
24901
25329
|
// src/plan.ts
|
|
24902
|
-
import { existsSync as
|
|
25330
|
+
import { existsSync as existsSync32, readdirSync as readdirSync15 } from "fs";
|
|
24903
25331
|
import path45 from "path";
|
|
24904
25332
|
function planLines(repo, targetDir) {
|
|
24905
25333
|
const repoDir = path45.join(targetDir, repo.name);
|
|
24906
25334
|
const lines = [];
|
|
24907
|
-
if (!
|
|
25335
|
+
if (!existsSync32(repoDir)) {
|
|
24908
25336
|
lines.push(`${S.planSync}: git clone (${S.planDetectAfter})`);
|
|
24909
25337
|
for (const step of [
|
|
24910
25338
|
S.phaseEnv,
|
|
@@ -24947,7 +25375,7 @@ async function executionSummary(repo, targetDir, native, platform2 = process.pla
|
|
|
24947
25375
|
const trust = repo.origin === "custom" ? "custom" : "builtin";
|
|
24948
25376
|
const commands = [];
|
|
24949
25377
|
let revision;
|
|
24950
|
-
if (!
|
|
25378
|
+
if (!existsSync32(repoDir) || !hasGitRepo(repoDir)) {
|
|
24951
25379
|
commands.push(`git clone -- ${repo.url}`, S.planScriptsAfterClone);
|
|
24952
25380
|
} else {
|
|
24953
25381
|
revision = (await currentHead(repoDir))?.slice(0, 7);
|
|
@@ -25181,11 +25609,59 @@ ${planLines(repo, targetDir).map((line) => ` ${theme.dim(line)}`).join("\n")}`
|
|
|
25181
25609
|
log.info(`${S.logSaved}: ${theme.dim(runLog.file)}`);
|
|
25182
25610
|
const hasErrors = results.some((r2) => r2.status === "error");
|
|
25183
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
|
+
}
|
|
25184
25615
|
if (hasErrors) process.exitCode = 1;
|
|
25185
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
|
+
}
|
|
25186
25662
|
|
|
25187
25663
|
// src/cmd/env.ts
|
|
25188
|
-
import { copyFileSync as copyFileSync4, existsSync as
|
|
25664
|
+
import { copyFileSync as copyFileSync4, existsSync as existsSync35, readFileSync as readFileSync23, readdirSync as readdirSync17 } from "fs";
|
|
25189
25665
|
import path48 from "path";
|
|
25190
25666
|
|
|
25191
25667
|
// src/env/discover.ts
|
|
@@ -25193,7 +25669,7 @@ import { accessSync as accessSync3, constants as constants6 } from "fs";
|
|
|
25193
25669
|
import os7 from "os";
|
|
25194
25670
|
|
|
25195
25671
|
// src/env/paths.ts
|
|
25196
|
-
import { accessSync as accessSync2, constants as constants5, existsSync as
|
|
25672
|
+
import { accessSync as accessSync2, constants as constants5, existsSync as existsSync33 } from "fs";
|
|
25197
25673
|
import os6 from "os";
|
|
25198
25674
|
import path46 from "path";
|
|
25199
25675
|
function resolveDeps2(deps) {
|
|
@@ -25202,7 +25678,7 @@ function resolveDeps2(deps) {
|
|
|
25202
25678
|
osHome: deps.osHome ?? os6.homedir(),
|
|
25203
25679
|
cwd: deps.cwd ?? process.cwd(),
|
|
25204
25680
|
tmpDir: deps.tmpDir ?? os6.tmpdir(),
|
|
25205
|
-
exists: deps.exists ??
|
|
25681
|
+
exists: deps.exists ?? existsSync33,
|
|
25206
25682
|
writable: deps.writable ?? ((dir) => {
|
|
25207
25683
|
try {
|
|
25208
25684
|
accessSync2(dir, constants5.W_OK);
|
|
@@ -25416,7 +25892,32 @@ function targetFindings(targets) {
|
|
|
25416
25892
|
async function discoverEnvironment(deps = {}) {
|
|
25417
25893
|
const platform2 = deps.platform ?? process.platform;
|
|
25418
25894
|
const home = deps.home ?? cliHome();
|
|
25419
|
-
const shells = await
|
|
25895
|
+
const [shells, gitProbe, pyDiscovery] = await Promise.all([
|
|
25896
|
+
discoverShells(deps),
|
|
25897
|
+
deps.gitProbe ? deps.gitProbe() : commandOutput("git", ["--version"]).then((output) => ({ output, timedOut: false })),
|
|
25898
|
+
// Pipeline lf-0017 completo — `run`/`exists`/`env`/`platform` são os
|
|
25899
|
+
// MESMOS da sonda de shells: um fake único controla a descoberta toda.
|
|
25900
|
+
// `pythonProbe` legado (testes pré-lf-0017) vira descoberta sintética.
|
|
25901
|
+
deps.pythonDiscovery ? deps.pythonDiscovery({
|
|
25902
|
+
platform: platform2,
|
|
25903
|
+
env: deps.env,
|
|
25904
|
+
run: deps.run,
|
|
25905
|
+
exists: deps.exists,
|
|
25906
|
+
osHome: deps.osHome,
|
|
25907
|
+
cwd: deps.cwd,
|
|
25908
|
+
targetDir: deps.workspace,
|
|
25909
|
+
explicit: deps.pythonExplicit
|
|
25910
|
+
}) : deps.pythonProbe ? legacyPythonDiscovery(platform2, deps.pythonProbe) : discoverPythons({
|
|
25911
|
+
platform: platform2,
|
|
25912
|
+
env: deps.env,
|
|
25913
|
+
run: deps.run,
|
|
25914
|
+
exists: deps.exists,
|
|
25915
|
+
osHome: deps.osHome,
|
|
25916
|
+
cwd: deps.cwd,
|
|
25917
|
+
targetDir: deps.workspace,
|
|
25918
|
+
explicit: deps.pythonExplicit
|
|
25919
|
+
})
|
|
25920
|
+
]);
|
|
25420
25921
|
const route = routeOf(platform2, shells);
|
|
25421
25922
|
const targets = resolveWriteTargets({
|
|
25422
25923
|
env: deps.env,
|
|
@@ -25432,31 +25933,11 @@ async function discoverEnvironment(deps = {}) {
|
|
|
25432
25933
|
workspaceDir: deps.workspace ?? deps.explicit?.workspaceDir
|
|
25433
25934
|
}
|
|
25434
25935
|
});
|
|
25435
|
-
const gitProbe = deps.gitProbe ? await deps.gitProbe() : { output: await commandOutput("git", ["--version"]), timedOut: false };
|
|
25436
25936
|
const git = {
|
|
25437
25937
|
present: gitProbe !== null && !gitProbe.timedOut && gitProbe.output !== null,
|
|
25438
25938
|
version: gitProbe?.output ?? null,
|
|
25439
25939
|
executable: gitProbe?.output ? "git" : null
|
|
25440
25940
|
};
|
|
25441
|
-
const pyDiscovery = deps.pythonDiscovery ? await deps.pythonDiscovery({
|
|
25442
|
-
platform: platform2,
|
|
25443
|
-
env: deps.env,
|
|
25444
|
-
run: deps.run,
|
|
25445
|
-
exists: deps.exists,
|
|
25446
|
-
osHome: deps.osHome,
|
|
25447
|
-
cwd: deps.cwd,
|
|
25448
|
-
targetDir: deps.workspace,
|
|
25449
|
-
explicit: deps.pythonExplicit
|
|
25450
|
-
}) : deps.pythonProbe ? await legacyPythonDiscovery(platform2, deps.pythonProbe) : await discoverPythons({
|
|
25451
|
-
platform: platform2,
|
|
25452
|
-
env: deps.env,
|
|
25453
|
-
run: deps.run,
|
|
25454
|
-
exists: deps.exists,
|
|
25455
|
-
osHome: deps.osHome,
|
|
25456
|
-
cwd: deps.cwd,
|
|
25457
|
-
targetDir: deps.workspace,
|
|
25458
|
-
explicit: deps.pythonExplicit
|
|
25459
|
-
});
|
|
25460
25941
|
const pySelected = pyDiscovery.selected;
|
|
25461
25942
|
const pyInfo = pySelected ? {
|
|
25462
25943
|
exe: pySelected.argv[0],
|
|
@@ -25719,7 +26200,7 @@ function envDoctor(repoDir, envKeys) {
|
|
|
25719
26200
|
|
|
25720
26201
|
// src/envintel/migrate.ts
|
|
25721
26202
|
import { randomBytes } from "crypto";
|
|
25722
|
-
import { existsSync as
|
|
26203
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync14, readdirSync as readdirSync16, readFileSync as readFileSync22 } from "fs";
|
|
25723
26204
|
import path47 from "path";
|
|
25724
26205
|
function timestamp2(d = /* @__PURE__ */ new Date()) {
|
|
25725
26206
|
const p = (n4) => String(n4).padStart(2, "0");
|
|
@@ -25735,11 +26216,11 @@ function addValue(spec, exampleEmpty) {
|
|
|
25735
26216
|
function planMigration(repoDir, targetSchema) {
|
|
25736
26217
|
const schema = targetSchema ?? deriveSchema(repoDir);
|
|
25737
26218
|
const envPath = path47.join(repoDir, ".env");
|
|
25738
|
-
const env =
|
|
26219
|
+
const env = existsSync34(envPath) ? parseEnv(readFileSync22(envPath, "utf8")) : null;
|
|
25739
26220
|
const present = new Set(env?.entries.map((e) => e.key) ?? []);
|
|
25740
26221
|
const examplePath = path47.join(repoDir, ".env.example");
|
|
25741
26222
|
const exampleEmpty = /* @__PURE__ */ new Set();
|
|
25742
|
-
if (
|
|
26223
|
+
if (existsSync34(examplePath)) {
|
|
25743
26224
|
for (const e of parseEnv(readFileSync22(examplePath, "utf8")).entries) {
|
|
25744
26225
|
if (e.value === "") exampleEmpty.add(e.key);
|
|
25745
26226
|
}
|
|
@@ -25779,7 +26260,7 @@ function backupEnv(repoDir, content) {
|
|
|
25779
26260
|
}
|
|
25780
26261
|
function applyMigration(repoDir, plan, opts = {}) {
|
|
25781
26262
|
const envPath = path47.join(repoDir, ".env");
|
|
25782
|
-
const exists =
|
|
26263
|
+
const exists = existsSync34(envPath);
|
|
25783
26264
|
const current = exists ? readFileSync22(envPath, "utf8") : "";
|
|
25784
26265
|
const env = parseEnv(current);
|
|
25785
26266
|
const present = new Set(env.entries.map((e) => e.key));
|
|
@@ -25811,14 +26292,14 @@ function applyMigration(repoDir, plan, opts = {}) {
|
|
|
25811
26292
|
}
|
|
25812
26293
|
function latestEnvBackup(repoDir) {
|
|
25813
26294
|
const root = path47.join(artifactsRoot(repoDir), "backups");
|
|
25814
|
-
if (!
|
|
26295
|
+
if (!existsSync34(root)) return null;
|
|
25815
26296
|
const candidates = readdirSync16(root, { withFileTypes: true }).filter((d) => d.isDirectory() && /^env-\d{8}-\d{6}$/.test(d.name)).map((d) => d.name).sort();
|
|
25816
26297
|
const last = candidates.at(-1);
|
|
25817
26298
|
return last ? path47.join(root, last) : null;
|
|
25818
26299
|
}
|
|
25819
26300
|
function rollbackEnv(repoDir, backupDir) {
|
|
25820
26301
|
const src = path47.join(backupDir, ".env");
|
|
25821
|
-
if (!
|
|
26302
|
+
if (!existsSync34(src)) return false;
|
|
25822
26303
|
writeFileAtomic(path47.join(repoDir, ".env"), readFileSync22(src, "utf8"));
|
|
25823
26304
|
return true;
|
|
25824
26305
|
}
|
|
@@ -25839,7 +26320,7 @@ function envCandidates(dir) {
|
|
|
25839
26320
|
try {
|
|
25840
26321
|
return readdirSync17(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).filter((name) => {
|
|
25841
26322
|
const r2 = resolveInside(dir, name);
|
|
25842
|
-
return r2 !== null && (
|
|
26323
|
+
return r2 !== null && (existsSync35(path48.join(r2, ".env")) || existsSync35(path48.join(r2, ".env.example")));
|
|
25843
26324
|
}).sort();
|
|
25844
26325
|
} catch {
|
|
25845
26326
|
return [];
|
|
@@ -25916,7 +26397,7 @@ async function runEnvCommand(cli, args) {
|
|
|
25916
26397
|
envKeys = catalogFor(targetDir).find((r2) => r2.name === name)?.envKeys;
|
|
25917
26398
|
} else {
|
|
25918
26399
|
const cwd = process.cwd();
|
|
25919
|
-
if (
|
|
26400
|
+
if (existsSync35(path48.join(cwd, ".env")) || existsSync35(path48.join(cwd, ".env.example"))) {
|
|
25920
26401
|
repoDir = cwd;
|
|
25921
26402
|
}
|
|
25922
26403
|
}
|
|
@@ -25968,7 +26449,7 @@ async function runEnvCommand(cli, args) {
|
|
|
25968
26449
|
note(lines.join("\n"), `${S.envDoctorTitle} \u2014 ${repo}`);
|
|
25969
26450
|
return res2.checks.some((c5) => c5.result === "fail") ? 1 : 0;
|
|
25970
26451
|
}
|
|
25971
|
-
if (!
|
|
26452
|
+
if (!existsSync35(repoDir)) {
|
|
25972
26453
|
log.error(`${repo}: ${S.statusNotInstalled}`);
|
|
25973
26454
|
return 2;
|
|
25974
26455
|
}
|
|
@@ -25984,7 +26465,7 @@ async function runEnvCommand(cli, args) {
|
|
|
25984
26465
|
log.error(S.envWizardNonInteractive);
|
|
25985
26466
|
return 2;
|
|
25986
26467
|
}
|
|
25987
|
-
const env = parseEnv(
|
|
26468
|
+
const env = parseEnv(existsSync35(envPath) ? readFileSync23(envPath, "utf8") : "");
|
|
25988
26469
|
const current = new Map(env.entries.map((e) => [e.key, e]));
|
|
25989
26470
|
const updates = /* @__PURE__ */ new Map();
|
|
25990
26471
|
for (const spec of schema.vars) {
|
|
@@ -26027,7 +26508,7 @@ async function runEnvCommand(cli, args) {
|
|
|
26027
26508
|
}
|
|
26028
26509
|
for (const [k, v] of updates) lines.push(formatEntry(k, v));
|
|
26029
26510
|
const out = serializeEnv({ ...env, lines });
|
|
26030
|
-
if (
|
|
26511
|
+
if (existsSync35(envPath)) copyFileSync4(envPath, `${envPath}.bak`);
|
|
26031
26512
|
writeFileAtomic(envPath, out.endsWith("\n") ? out : `${out}
|
|
26032
26513
|
`);
|
|
26033
26514
|
log.success(`${icons.ok} ${S.envWizardDone} ${theme.dim(`(${envPath}.bak)`)}`);
|
|
@@ -26055,7 +26536,7 @@ async function runEnvCommand(cli, args) {
|
|
|
26055
26536
|
return 0;
|
|
26056
26537
|
}
|
|
26057
26538
|
if (verb === "init") {
|
|
26058
|
-
if (
|
|
26539
|
+
if (existsSync35(envPath)) {
|
|
26059
26540
|
if (json) console.log(JSON.stringify({ command: "env init", repo, created: false }));
|
|
26060
26541
|
else log.info(S.envInitExists);
|
|
26061
26542
|
return 0;
|
|
@@ -26170,11 +26651,11 @@ async function runEnvCommand(cli, args) {
|
|
|
26170
26651
|
|
|
26171
26652
|
// src/cmd/hosts.ts
|
|
26172
26653
|
import path50 from "path";
|
|
26173
|
-
import { existsSync as
|
|
26654
|
+
import { existsSync as existsSync37, readdirSync as readdirSync18 } from "fs";
|
|
26174
26655
|
|
|
26175
26656
|
// src/mcp/handshake.ts
|
|
26176
26657
|
import { spawn as spawn2 } from "child_process";
|
|
26177
|
-
import { existsSync as
|
|
26658
|
+
import { existsSync as existsSync36 } from "fs";
|
|
26178
26659
|
import path49 from "path";
|
|
26179
26660
|
import { createInterface as createInterface2 } from "readline";
|
|
26180
26661
|
var PROTOCOL_VERSION = "2024-11-05";
|
|
@@ -26265,7 +26746,7 @@ function probeMcpServer(argv, opts = {}) {
|
|
|
26265
26746
|
const stepMs = opts.timeoutMs ?? STEP_MS;
|
|
26266
26747
|
const levels = [];
|
|
26267
26748
|
if (path49.isAbsolute(argv[0])) {
|
|
26268
|
-
if (!
|
|
26749
|
+
if (!existsSync36(argv[0])) {
|
|
26269
26750
|
resolve(err("bin-missing", `bin\xE1rio MCP ausente: ${argv[0]}`));
|
|
26270
26751
|
return;
|
|
26271
26752
|
}
|
|
@@ -26695,7 +27176,7 @@ async function runMcpCommand(cli, args, home = cliHome()) {
|
|
|
26695
27176
|
const candidates = names.length > 0 ? names : [
|
|
26696
27177
|
.../* @__PURE__ */ new Set([
|
|
26697
27178
|
...Object.keys(loadForgeState(home).forges),
|
|
26698
|
-
...
|
|
27179
|
+
...existsSync37(targetDir) ? readdirSync18(targetDir, { withFileTypes: true }).filter((d) => d.isDirectory() && isSafeRepoName(d.name)).map((d) => d.name) : []
|
|
26699
27180
|
])
|
|
26700
27181
|
].sort();
|
|
26701
27182
|
if (candidates.length === 0) {
|
|
@@ -26716,7 +27197,7 @@ async function runMcpCommand(cli, args, home = cliHome()) {
|
|
|
26716
27197
|
results.push({ forge: name, ok: false, detail: "sem mcp_command" });
|
|
26717
27198
|
continue;
|
|
26718
27199
|
}
|
|
26719
|
-
const binPresent = path50.isAbsolute(argv[0]) ?
|
|
27200
|
+
const binPresent = path50.isAbsolute(argv[0]) ? existsSync37(argv[0]) : true;
|
|
26720
27201
|
const item = {
|
|
26721
27202
|
forge: name,
|
|
26722
27203
|
forge_id: forge.forge_id,
|
|
@@ -26764,7 +27245,7 @@ async function runMcpCommand(cli, args, home = cliHome()) {
|
|
|
26764
27245
|
}
|
|
26765
27246
|
|
|
26766
27247
|
// src/discovery.ts
|
|
26767
|
-
import { existsSync as
|
|
27248
|
+
import { existsSync as existsSync38, readdirSync as readdirSync19, readFileSync as readFileSync24, statSync as statSync8 } from "fs";
|
|
26768
27249
|
import os8 from "os";
|
|
26769
27250
|
import path51 from "path";
|
|
26770
27251
|
var DEFAULT_LIMITS = {
|
|
@@ -27054,10 +27535,10 @@ function registryState(entry2) {
|
|
|
27054
27535
|
function discoverForge(name, dir, state) {
|
|
27055
27536
|
const sources = [];
|
|
27056
27537
|
const git = !!dir && hasGitRepo(dir);
|
|
27057
|
-
const manifest = dir &&
|
|
27538
|
+
const manifest = dir && existsSync38(dir) ? readForgeManifest(dir) : null;
|
|
27058
27539
|
const entry2 = state?.repos[name];
|
|
27059
27540
|
if (entry2 && entry2.phase !== "not-installed") {
|
|
27060
|
-
const dirExists = !!dir &&
|
|
27541
|
+
const dirExists = !!dir && existsSync38(dir);
|
|
27061
27542
|
sources.push({
|
|
27062
27543
|
origin: "manifest",
|
|
27063
27544
|
// registro diz instalada mas o checkout sumiu → evidência falhando
|
|
@@ -27117,7 +27598,7 @@ async function discoverEnvironment2(targetDir, opts) {
|
|
|
27117
27598
|
const dir = resolveInside(root, dirName);
|
|
27118
27599
|
if (!dir) continue;
|
|
27119
27600
|
const git = hasGitRepo(dir);
|
|
27120
|
-
const manifest =
|
|
27601
|
+
const manifest = existsSync38(path51.join(dir, "forge.json"));
|
|
27121
27602
|
if (!git && !manifest) continue;
|
|
27122
27603
|
presence.push({ name: dirName, path: dirName, git, manifest });
|
|
27123
27604
|
if (!catalogNames.has(dirName) && manifest) {
|
|
@@ -27786,7 +28267,7 @@ async function answerRepair(targetDir, repos, opts = {}) {
|
|
|
27786
28267
|
}
|
|
27787
28268
|
|
|
27788
28269
|
// src/intel/context.ts
|
|
27789
|
-
import { existsSync as
|
|
28270
|
+
import { existsSync as existsSync39, readdirSync as readdirSync20, readFileSync as readFileSync25, statSync as statSync9 } from "fs";
|
|
27790
28271
|
import path52 from "path";
|
|
27791
28272
|
|
|
27792
28273
|
// src/planner.ts
|
|
@@ -27904,7 +28385,7 @@ function safeConfig(targetDir) {
|
|
|
27904
28385
|
function readManifestObject(repoDir) {
|
|
27905
28386
|
const file = path52.join(repoDir, "forge.json");
|
|
27906
28387
|
try {
|
|
27907
|
-
if (!
|
|
28388
|
+
if (!existsSync39(file) || statSync9(file).size > MAX_MANIFEST_BYTES) return null;
|
|
27908
28389
|
const raw = JSON.parse(readFileSync25(file, "utf8"));
|
|
27909
28390
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
|
|
27910
28391
|
} catch {
|
|
@@ -28074,7 +28555,7 @@ async function buildForgeContext(targetDir, repos, opts = {}) {
|
|
|
28074
28555
|
}
|
|
28075
28556
|
|
|
28076
28557
|
// src/intel/predict.ts
|
|
28077
|
-
import { existsSync as
|
|
28558
|
+
import { existsSync as existsSync40, readFileSync as readFileSync26, statSync as statSync10 } from "fs";
|
|
28078
28559
|
import path53 from "path";
|
|
28079
28560
|
var MAX_MANIFEST_BYTES2 = 64 * 1024;
|
|
28080
28561
|
var SEMVERISH = /^v?\d+\.\d+\.\d+/;
|
|
@@ -28087,7 +28568,7 @@ var mk2 = (id, kind, subject, certainty, version, evidence, explanation) => ({ i
|
|
|
28087
28568
|
function readManifest(repoDir) {
|
|
28088
28569
|
const file = path53.join(repoDir, "forge.json");
|
|
28089
28570
|
try {
|
|
28090
|
-
if (!
|
|
28571
|
+
if (!existsSync40(file) || statSync10(file).size > MAX_MANIFEST_BYTES2) return null;
|
|
28091
28572
|
const raw = JSON.parse(readFileSync26(file, "utf8"));
|
|
28092
28573
|
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : null;
|
|
28093
28574
|
} catch {
|
|
@@ -28404,7 +28885,7 @@ async function predictCompatibility(targetDir, repos, opts = {}) {
|
|
|
28404
28885
|
const allClaims = [];
|
|
28405
28886
|
for (const repo of sorted) {
|
|
28406
28887
|
const dir = resolveInside(targetDir, repo.name);
|
|
28407
|
-
if (!dir || !
|
|
28888
|
+
if (!dir || !existsSync40(dir)) continue;
|
|
28408
28889
|
const m3 = readManifest(dir);
|
|
28409
28890
|
manifests.set(repo.name, m3);
|
|
28410
28891
|
if (m3) out.push(...schemaChange(repo.name, m3));
|
|
@@ -28416,7 +28897,7 @@ async function predictCompatibility(targetDir, repos, opts = {}) {
|
|
|
28416
28897
|
out.push(...envVarFindings("<workspace>", targetDir));
|
|
28417
28898
|
for (const repo of sorted) {
|
|
28418
28899
|
const dir = resolveInside(targetDir, repo.name);
|
|
28419
|
-
if (!dir || !
|
|
28900
|
+
if (!dir || !existsSync40(dir)) continue;
|
|
28420
28901
|
out.push(...envVarFindings(repo.name, dir, repo.envKeys));
|
|
28421
28902
|
}
|
|
28422
28903
|
out.push(...installConflict(allClaims));
|
|
@@ -28429,7 +28910,7 @@ import path55 from "path";
|
|
|
28429
28910
|
|
|
28430
28911
|
// src/ai/cache.ts
|
|
28431
28912
|
import { createHash as createHash7 } from "crypto";
|
|
28432
|
-
import { existsSync as
|
|
28913
|
+
import { existsSync as existsSync41, mkdirSync as mkdirSync15, readFileSync as readFileSync27, readdirSync as readdirSync21, rmSync as rmSync9 } from "fs";
|
|
28433
28914
|
import path54 from "path";
|
|
28434
28915
|
|
|
28435
28916
|
// src/ai/types.ts
|
|
@@ -28533,7 +29014,7 @@ function entryFile(rootDir, key) {
|
|
|
28533
29014
|
function aiCacheGet(rootDir, key, opts) {
|
|
28534
29015
|
if (opts.ttlMs <= 0) return null;
|
|
28535
29016
|
const file = entryFile(rootDir, key);
|
|
28536
|
-
if (!file || !
|
|
29017
|
+
if (!file || !existsSync41(file)) return null;
|
|
28537
29018
|
try {
|
|
28538
29019
|
const entry2 = JSON.parse(readFileSync27(file, "utf8"));
|
|
28539
29020
|
const now = opts.now ?? Date.now();
|
|
@@ -28733,7 +29214,7 @@ async function runAiBridge(req, cfg, deps) {
|
|
|
28733
29214
|
}
|
|
28734
29215
|
|
|
28735
29216
|
// src/portability/bundle.ts
|
|
28736
|
-
import { existsSync as
|
|
29217
|
+
import { existsSync as existsSync42, mkdirSync as mkdirSync17, readFileSync as readFileSync28 } from "fs";
|
|
28737
29218
|
import path56 from "path";
|
|
28738
29219
|
|
|
28739
29220
|
// src/portability/manifest.ts
|
|
@@ -28845,7 +29326,7 @@ function collectPins(targetDir, repos) {
|
|
|
28845
29326
|
const pins = /* @__PURE__ */ new Map();
|
|
28846
29327
|
for (const repo of repos) {
|
|
28847
29328
|
const repoDir = path56.join(targetDir, repo.name);
|
|
28848
|
-
const version =
|
|
29329
|
+
const version = existsSync42(repoDir) ? pinVersion(repoDir) : void 0;
|
|
28849
29330
|
const head = state?.repos[repo.name]?.head;
|
|
28850
29331
|
const ref = state?.pins?.[repo.name] ?? head;
|
|
28851
29332
|
pins.set(repo.name, {
|
|
@@ -28872,7 +29353,7 @@ async function exportBundle(targetDir, repos, dest, opts) {
|
|
|
28872
29353
|
const schemaByRepo = /* @__PURE__ */ new Map();
|
|
28873
29354
|
for (const repo of repos) {
|
|
28874
29355
|
const repoDir = path56.join(targetDir, repo.name);
|
|
28875
|
-
const schema =
|
|
29356
|
+
const schema = existsSync42(repoDir) ? deriveSchema(repoDir, repo.envKeys) : { vars: [] };
|
|
28876
29357
|
schemaByRepo.set(repo.name, schema);
|
|
28877
29358
|
for (const v of schema.vars) {
|
|
28878
29359
|
if (v.secret && whitelist.includes(v.key)) refused.add(v.key);
|
|
@@ -28914,7 +29395,7 @@ async function exportBundle(targetDir, repos, dest, opts) {
|
|
|
28914
29395
|
const values = {};
|
|
28915
29396
|
for (const repo of repos) {
|
|
28916
29397
|
const envFile = path56.join(targetDir, repo.name, ".env");
|
|
28917
|
-
if (!
|
|
29398
|
+
if (!existsSync42(envFile)) continue;
|
|
28918
29399
|
const entries = parseEnv(readFileSync28(envFile, "utf8")).entries;
|
|
28919
29400
|
const found = {};
|
|
28920
29401
|
for (const key of whitelist) {
|
|
@@ -29004,7 +29485,7 @@ function importBundle(src, opts) {
|
|
|
29004
29485
|
}
|
|
29005
29486
|
|
|
29006
29487
|
// src/intel/learning.ts
|
|
29007
|
-
import { existsSync as
|
|
29488
|
+
import { existsSync as existsSync43, mkdirSync as mkdirSync18, readFileSync as readFileSync29 } from "fs";
|
|
29008
29489
|
import path57 from "path";
|
|
29009
29490
|
var LEARNING_SCHEMA = "forge-cli/LearningStore/v1";
|
|
29010
29491
|
function learningFile(targetDir) {
|
|
@@ -29012,7 +29493,7 @@ function learningFile(targetDir) {
|
|
|
29012
29493
|
}
|
|
29013
29494
|
function readStore(targetDir) {
|
|
29014
29495
|
const file = learningFile(targetDir);
|
|
29015
|
-
if (!
|
|
29496
|
+
if (!existsSync43(file)) return { schema: LEARNING_SCHEMA, enabled: false, observations: [] };
|
|
29016
29497
|
try {
|
|
29017
29498
|
const parsed = JSON.parse(readFileSync29(file, "utf8"));
|
|
29018
29499
|
if (parsed?.schema !== LEARNING_SCHEMA || !Array.isArray(parsed.observations)) {
|
|
@@ -29272,7 +29753,7 @@ async function runLearningCommand(cli) {
|
|
|
29272
29753
|
}
|
|
29273
29754
|
|
|
29274
29755
|
// src/wizard.ts
|
|
29275
|
-
import { existsSync as
|
|
29756
|
+
import { existsSync as existsSync44 } from "fs";
|
|
29276
29757
|
import path58 from "path";
|
|
29277
29758
|
var STEP_LABELS = [
|
|
29278
29759
|
S.wizardStepDetect,
|
|
@@ -29313,7 +29794,7 @@ function renderOperationPlan(plan) {
|
|
|
29313
29794
|
}
|
|
29314
29795
|
function planActionsFor(repo, targetDir, steps) {
|
|
29315
29796
|
const repoDir = path58.join(targetDir, repo.name);
|
|
29316
|
-
const cloned =
|
|
29797
|
+
const cloned = existsSync44(repoDir) && hasGitRepo(repoDir);
|
|
29317
29798
|
const actions = [
|
|
29318
29799
|
{
|
|
29319
29800
|
kind: "clone",
|