@vibgrate/cli 2026.817.1 → 2026.817.2
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/DOCS.md +61 -0
- package/README.md +1 -0
- package/dist/agent-UQC7QN77.js +27 -0
- package/dist/{agent-CUYQKC6S.js.map → agent-UQC7QN77.js.map} +1 -1
- package/dist/{approvals-IPMUDEDV.js → approvals-ASPJQUZK.js} +6 -5
- package/dist/{approvals-IPMUDEDV.js.map → approvals-ASPJQUZK.js.map} +1 -1
- package/dist/baseline-FIV6PR2M.js +9 -0
- package/dist/{baseline-NZFGLMER.js.map → baseline-FIV6PR2M.js.map} +1 -1
- package/dist/{chunk-4W5SYK2M.js → chunk-2FEB5KJS.js} +3 -3
- package/dist/{chunk-4W5SYK2M.js.map → chunk-2FEB5KJS.js.map} +1 -1
- package/dist/{chunk-MIYXVY57.js → chunk-3FMRFECC.js} +4 -4
- package/dist/{chunk-MIYXVY57.js.map → chunk-3FMRFECC.js.map} +1 -1
- package/dist/{chunk-NQUWC2IA.js → chunk-3H3SVJUY.js} +4 -4
- package/dist/{chunk-NQUWC2IA.js.map → chunk-3H3SVJUY.js.map} +1 -1
- package/dist/{chunk-UBEVY5UT.js → chunk-3NJUOIZQ.js} +5 -5
- package/dist/{chunk-UBEVY5UT.js.map → chunk-3NJUOIZQ.js.map} +1 -1
- package/dist/{chunk-3GZSDY35.js → chunk-5OL2TSHK.js} +4 -4
- package/dist/{chunk-3GZSDY35.js.map → chunk-5OL2TSHK.js.map} +1 -1
- package/dist/{chunk-2BURPMYA.js → chunk-6K3XK7C5.js} +69 -976
- package/dist/chunk-6K3XK7C5.js.map +1 -0
- package/dist/{chunk-2GSMCOIC.js → chunk-AXZZ5CCG.js} +4 -4
- package/dist/{chunk-2GSMCOIC.js.map → chunk-AXZZ5CCG.js.map} +1 -1
- package/dist/{chunk-NHB53JTV.js → chunk-C3LMWZKF.js} +3 -3
- package/dist/{chunk-NHB53JTV.js.map → chunk-C3LMWZKF.js.map} +1 -1
- package/dist/{chunk-FD2HPGX2.js → chunk-DVW3SVCW.js} +9 -8
- package/dist/chunk-DVW3SVCW.js.map +1 -0
- package/dist/{chunk-RJCRWQNS.js → chunk-E2FAQREZ.js} +164 -221
- package/dist/chunk-E2FAQREZ.js.map +1 -0
- package/dist/{chunk-45AKSS4A.js → chunk-EXV4QSAD.js} +4 -4
- package/dist/{chunk-45AKSS4A.js.map → chunk-EXV4QSAD.js.map} +1 -1
- package/dist/{chunk-OWSXI3PJ.js → chunk-GOGMS3ST.js} +5 -5
- package/dist/{chunk-OWSXI3PJ.js.map → chunk-GOGMS3ST.js.map} +1 -1
- package/dist/{chunk-YL4OIUA5.js → chunk-K6DXETMC.js} +11 -11
- package/dist/{chunk-YL4OIUA5.js.map → chunk-K6DXETMC.js.map} +1 -1
- package/dist/{chunk-NKRWKCME.js → chunk-KJT2R6WW.js} +4 -4
- package/dist/{chunk-NKRWKCME.js.map → chunk-KJT2R6WW.js.map} +1 -1
- package/dist/{chunk-FMS6G67U.js → chunk-KNGAZDGF.js} +5 -5
- package/dist/{chunk-FMS6G67U.js.map → chunk-KNGAZDGF.js.map} +1 -1
- package/dist/{chunk-JHLCKMKF.js → chunk-LHKQGVRF.js} +9 -8
- package/dist/chunk-LHKQGVRF.js.map +1 -0
- package/dist/{chunk-A7EKFLAN.js → chunk-M7IO3DXH.js} +9 -8
- package/dist/chunk-M7IO3DXH.js.map +1 -0
- package/dist/{chunk-QPXVTQ7O.js → chunk-MVPRPSXU.js} +7 -4
- package/dist/chunk-MVPRPSXU.js.map +1 -0
- package/dist/{chunk-DT4KIVRI.js → chunk-NG4KIJW6.js} +34 -2
- package/dist/chunk-NG4KIJW6.js.map +1 -0
- package/dist/{chunk-K267DHCF.js → chunk-O2DL2Q55.js} +6 -6
- package/dist/{chunk-K267DHCF.js.map → chunk-O2DL2Q55.js.map} +1 -1
- package/dist/chunk-O3LABX6I.js +43 -0
- package/dist/chunk-O3LABX6I.js.map +1 -0
- package/dist/chunk-PRG2ULPK.js +6 -0
- package/dist/{chunk-PV76EO4G.js.map → chunk-PRG2ULPK.js.map} +1 -1
- package/dist/{chunk-ZSECAGSU.js → chunk-Q3M6K3HY.js} +3 -3
- package/dist/{chunk-ZSECAGSU.js.map → chunk-Q3M6K3HY.js.map} +1 -1
- package/dist/{chunk-2TJ5EY4I.js → chunk-Q4G2BWE3.js} +4 -4
- package/dist/{chunk-2TJ5EY4I.js.map → chunk-Q4G2BWE3.js.map} +1 -1
- package/dist/chunk-T7RSQ5WD.js +227 -0
- package/dist/chunk-T7RSQ5WD.js.map +1 -0
- package/dist/{chunk-RQAKBRAV.js → chunk-TVDMNIUT.js} +3 -3
- package/dist/{chunk-RQAKBRAV.js.map → chunk-TVDMNIUT.js.map} +1 -1
- package/dist/{chunk-ANYL7SUI.js → chunk-WAKP7SK7.js} +4584 -976
- package/dist/chunk-WAKP7SK7.js.map +1 -0
- package/dist/{chunk-ENFSSUNK.js → chunk-WVMOGIPC.js} +5 -43
- package/dist/chunk-WVMOGIPC.js.map +1 -0
- package/dist/{chunk-6NOGNOE4.js → chunk-XLXJC7H3.js} +141 -13
- package/dist/chunk-XLXJC7H3.js.map +1 -0
- package/dist/cli.js +1476 -575
- package/dist/cli.js.map +1 -1
- package/dist/{daemon-J2HX4GY3.js → daemon-FPTRHO5N.js} +11 -10
- package/dist/{daemon-J2HX4GY3.js.map → daemon-FPTRHO5N.js.map} +1 -1
- package/dist/embed-worker-main.js +5 -4
- package/dist/embed-worker-main.js.map +1 -1
- package/dist/ensure-map-342G2IV3.js +33 -0
- package/dist/{ensure-map-5C6KG66X.js.map → ensure-map-342G2IV3.js.map} +1 -1
- package/dist/{fs-NTQUFCXV.js → fs-TLPOWC2D.js} +3 -3
- package/dist/fs-TLPOWC2D.js.map +1 -0
- package/dist/graph-backend-RNWQBT3P.js +15 -0
- package/dist/{graph-backend-HH5IIQ55.js.map → graph-backend-RNWQBT3P.js.map} +1 -1
- package/dist/index.d.ts +22 -3
- package/dist/index.js +18 -17
- package/dist/index.js.map +1 -1
- package/dist/{interactive-FXPAZAUJ.js → interactive-SR7TMF6M.js} +28 -27
- package/dist/interactive-SR7TMF6M.js.map +1 -0
- package/dist/load-AYFECIQB.js +11 -0
- package/dist/{load-OR2XVGAV.js.map → load-AYFECIQB.js.map} +1 -1
- package/dist/parse-worker.d.ts +1 -1
- package/dist/parse-worker.js +5 -2
- package/dist/parse-worker.js.map +1 -1
- package/dist/{run-outcome-M4546HLJ.js → run-outcome-YQQJG3LW.js} +7 -6
- package/dist/run-outcome-YQQJG3LW.js.map +1 -0
- package/dist/runtime-session-CNJ4DAV5.js +22 -0
- package/dist/{runtime-session-6KKMFS6Q.js.map → runtime-session-CNJ4DAV5.js.map} +1 -1
- package/dist/session-H34HUV2Y.js +17 -0
- package/dist/{session-HCFJUSKN.js.map → session-H34HUV2Y.js.map} +1 -1
- package/dist/{stream-json-4DL65D6U.js → stream-json-55YDNAFX.js} +15 -14
- package/dist/stream-json-55YDNAFX.js.map +1 -0
- package/dist/{types-8SYlXhzm.d.ts → types-DHqH0XIE.d.ts} +11 -0
- package/dist/version-WSE3QOPK.js +4 -0
- package/dist/{version-UPIC3F4P.js.map → version-WSE3QOPK.js.map} +1 -1
- package/dist/{vg-mcp-bridge-FZGX6DYO.js → vg-mcp-bridge-2CNLHNEL.js} +13 -12
- package/dist/vg-mcp-bridge-2CNLHNEL.js.map +1 -0
- package/dist/{vgd-QR26FAZD.js → vgd-URUZQORN.js} +10 -9
- package/dist/vgd-URUZQORN.js.map +1 -0
- package/package.json +7 -7
- package/dist/agent-CUYQKC6S.js +0 -26
- package/dist/baseline-NZFGLMER.js +0 -9
- package/dist/chunk-2BURPMYA.js.map +0 -1
- package/dist/chunk-6NOGNOE4.js.map +0 -1
- package/dist/chunk-A7EKFLAN.js.map +0 -1
- package/dist/chunk-ANYL7SUI.js.map +0 -1
- package/dist/chunk-DT4KIVRI.js.map +0 -1
- package/dist/chunk-ENFSSUNK.js.map +0 -1
- package/dist/chunk-FD2HPGX2.js.map +0 -1
- package/dist/chunk-JHLCKMKF.js.map +0 -1
- package/dist/chunk-PV76EO4G.js +0 -6
- package/dist/chunk-QPXVTQ7O.js.map +0 -1
- package/dist/chunk-RJCRWQNS.js.map +0 -1
- package/dist/chunk-RXP66R2E.js +0 -39
- package/dist/chunk-RXP66R2E.js.map +0 -1
- package/dist/ensure-map-5C6KG66X.js +0 -32
- package/dist/fs-NTQUFCXV.js.map +0 -1
- package/dist/graph-backend-HH5IIQ55.js +0 -14
- package/dist/interactive-FXPAZAUJ.js.map +0 -1
- package/dist/load-OR2XVGAV.js +0 -10
- package/dist/run-outcome-M4546HLJ.js.map +0 -1
- package/dist/runtime-session-6KKMFS6Q.js +0 -21
- package/dist/session-HCFJUSKN.js +0 -16
- package/dist/stream-json-4DL65D6U.js.map +0 -1
- package/dist/version-UPIC3F4P.js +0 -4
- package/dist/vg-mcp-bridge-FZGX6DYO.js.map +0 -1
- package/dist/vgd-QR26FAZD.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { fetchCatalog, resolveProviders } from './chunk-
|
|
3
|
-
import { telemetryOptOut, StatsSharer, statsEndpoint, isCI } from './chunk-
|
|
4
|
-
import { loadAdvancedScanHook, baselineCommand } from './chunk-
|
|
5
|
-
import { sha256Hex, generateKeypair, dssePae, DSSE_PAYLOAD_TYPE, keyId, registerBuild, ProgressBar, printLogo, runBuild } from './chunk-
|
|
6
|
-
export { runBuild } from './chunk-
|
|
7
|
-
import { restartVgdIfRunning, stopVgd, registerDaemon, rootOf, requireGraph } from './chunk-
|
|
2
|
+
import { fetchCatalog, resolveProviders } from './chunk-C3LMWZKF.js';
|
|
3
|
+
import { telemetryOptOut, StatsSharer, statsEndpoint, isCI } from './chunk-EXV4QSAD.js';
|
|
4
|
+
import { loadAdvancedScanHook, baselineCommand } from './chunk-6K3XK7C5.js';
|
|
5
|
+
import { sha256Hex, generateKeypair, dssePae, DSSE_PAYLOAD_TYPE, keyId, registerBuild, ProgressBar, printLogo, runBuild } from './chunk-LHKQGVRF.js';
|
|
6
|
+
export { runBuild } from './chunk-LHKQGVRF.js';
|
|
7
|
+
import { restartVgdIfRunning, stopVgd, registerDaemon, rootOf, requireGraph } from './chunk-KNGAZDGF.js';
|
|
8
8
|
import { readGlobal, applyGlobalOptions } from './chunk-OZ3IFXRD.js';
|
|
9
9
|
import { disableColor, info, c, json, out } from './chunk-CT5BS56U.js';
|
|
10
|
-
import './chunk-
|
|
10
|
+
import './chunk-GOGMS3ST.js';
|
|
11
11
|
import './chunk-IS7VTH2Q.js';
|
|
12
12
|
import { defaultProcessEndpoint, startProcessHostServer, resolveInferenceIsolation } from './chunk-JFE7XCAE.js';
|
|
13
13
|
import { clearHostSessionPool, acquireHostSession, generateOnSession, listHostSessions, hostSessionPoolSize } from './chunk-3LOWJSAF.js';
|
|
14
|
-
import { runCodeSession } from './chunk-
|
|
15
|
-
import { CONTEXT_POLICY_VERSION, computeFcsReport, renderFcsReportMarkdown, createTrajectoryCollector, runAgent } from './chunk-
|
|
14
|
+
import { runCodeSession } from './chunk-3NJUOIZQ.js';
|
|
15
|
+
import { CONTEXT_POLICY_VERSION, computeFcsReport, renderFcsReportMarkdown, createTrajectoryCollector, runAgent } from './chunk-K6DXETMC.js';
|
|
16
16
|
import './chunk-543E6WDJ.js';
|
|
17
|
-
import './chunk-
|
|
18
|
-
import './chunk-
|
|
17
|
+
import './chunk-Q3M6K3HY.js';
|
|
18
|
+
import './chunk-TVDMNIUT.js';
|
|
19
19
|
import './chunk-RBIRXQR6.js';
|
|
20
20
|
import { buildIdentifierTrieFromGraph } from './chunk-5HYCB55P.js';
|
|
21
21
|
import { parseReasoningEffort, summarizeDiffs, ScriptedProvider } from './chunk-74UBGF4P.js';
|
|
@@ -26,39 +26,40 @@ import { init_resolve_ollama, gatherSystemMemory, resolveOllamaBinary, hasOllama
|
|
|
26
26
|
import { readOrchestratorState, writeOrchestratorState, parseMode, setDefaultMode, recommendMode, activePin, resolveMode, providerForBackend, QUALIFIED_PACKS, pinPack, unpinPack, buildOrchestratorStatus } from './chunk-NZELMYT4.js';
|
|
27
27
|
import { fmt, estimateModelBytes } from './chunk-7N4Y3E47.js';
|
|
28
28
|
import './chunk-IZDH67DX.js';
|
|
29
|
-
import { serveStdio, isRelevantChange, exportGraph, GraphSource, createServer, formatForExt, RefreshScheduler } from './chunk-
|
|
30
|
-
import { refreshIfStale } from './chunk-
|
|
31
|
-
import { driftCount, ASSISTANTS, detectAssistants, isAssistantInstalled, detectServeLaunch, installAssistant, writeNavigationConfig, uninstallAssistant,
|
|
32
|
-
import { resolveDsn, createWorkspaceDsn, writeStoredCredentials, credentialsPath, findGitRoot, ensureGitignored, gitignoreEntryForCredentials, clearStoredCredentials, parseDsn as parseDsn$1, uploadScanArtifact, dsnCommand, pushCommand, countTokens, shortestPath, pathDisconnect, testsToRun, detectRunner, coveringTests, FREE_PACK, readScanArtifact, publishPrivateLibrary, assessDocQuality, symbolsFromApi, fetchHostedDocsCached, selectForBudget, hostedBase, readStoredCredentials, projectCredentialsPath, homeCredentialsPath } from './chunk-
|
|
29
|
+
import { serveStdio, isRelevantChange, exportGraph, GraphSource, createServer, formatForExt, RefreshScheduler } from './chunk-M7IO3DXH.js';
|
|
30
|
+
import { refreshIfStale } from './chunk-O2DL2Q55.js';
|
|
31
|
+
import { driftCount, ASSISTANTS, detectAssistants, isAssistantInstalled, detectServeLaunch, installAssistant, writeNavigationConfig, uninstallAssistant, buildGraph, writeSnapshot, probeFreshness, discover, assistantById, SMALL_REPO_FILES, isSkippedDirName, SKIP_FILES, SKIP_DIRS } from './chunk-E2FAQREZ.js';
|
|
32
|
+
import { resolveDsn, createWorkspaceDsn, writeStoredCredentials, credentialsPath, findGitRoot, ensureGitignored, gitignoreEntryForCredentials, clearStoredCredentials, parseDsn as parseDsn$1, uploadScanArtifact, dsnCommand, pushCommand, countTokens, shortestPath, pathDisconnect, testsToRun, detectRunner, coveringTests, FREE_PACK, readScanArtifact, publishPrivateLibrary, assessDocQuality, symbolsFromApi, fetchHostedDocsCached, selectForBudget, hostedBase, readStoredCredentials, projectCredentialsPath, homeCredentialsPath } from './chunk-DVW3SVCW.js';
|
|
33
33
|
import { resolveIngestHost, dashHostForIngestHost, availableRegionIds, ingestHostForRegionId } from './chunk-HTLDXEHW.js';
|
|
34
|
-
import { writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, loadConfig, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, PROJECT_TYPE_TO_OSV_ECOSYSTEM, fetchRiskySymbols, perDependencyDrift } from './chunk-
|
|
34
|
+
import { writeDefaultConfig, parseDsn, detectVcs, computeRepoFingerprint, resolveRepositoryName, fetchScanPreflight, runCoreScan, loadConfig, prepareCompressedUpload, computeDriftScore, titleBox, projectTypeToVulnEcosystem, driftBar, gitHistoryAvailable, buildVersionTimelines, findPackageAnyEcosystem, normalizeConstraint, findVersionCrossings, versionSatisfies, refineArchitectureWithGraph, refineArchitectureWithAstRoles, PROJECT_TYPE_TO_OSV_ECOSYSTEM, fetchRiskySymbols, perDependencyDrift } from './chunk-WAKP7SK7.js';
|
|
35
35
|
import { CliError, ExitCode, usageError, engineUnavailable } from './chunk-MS6V5XKB.js';
|
|
36
36
|
import './chunk-VIWZ4QT7.js';
|
|
37
|
-
import { inventory, enrichOnline, ECOSYSTEMS, catalogPath, libDir, loadCatalog, driftFor, addLibrary, resolveLib, libId, resolveVersion, readDoc, localPackageDocs, localApiSurface } from './chunk-
|
|
37
|
+
import { inventory, enrichOnline, ECOSYSTEMS, catalogPath, libDir, loadCatalog, driftFor, addLibrary, resolveLib, libId, resolveVersion, readDoc, localPackageDocs, localApiSurface } from './chunk-3H3SVJUY.js';
|
|
38
38
|
import { resolveOne } from './chunk-J7LKEB2R.js';
|
|
39
|
-
import { recordCliCall, CLI_TOOL_ALIASES, savingsLedgerPath, clearSavings, readSavings, readUsage, readModelSavings, CAPSULE_RANKING_VERSION, SAVINGS_TOOLS, sanitizeClient } from './chunk-
|
|
40
|
-
import { queryGraphSemantic, queryGraph, indexFor, impactOf } from './chunk-
|
|
39
|
+
import { recordCliCall, CLI_TOOL_ALIASES, savingsLedgerPath, clearSavings, readSavings, readUsage, readModelSavings, CAPSULE_RANKING_VERSION, SAVINGS_TOOLS, sanitizeClient } from './chunk-KJT2R6WW.js';
|
|
40
|
+
import { queryGraphSemantic, queryGraph, indexFor, impactOf } from './chunk-Q4G2BWE3.js';
|
|
41
41
|
import { discoverModels } from './chunk-CQDUBAQY.js';
|
|
42
42
|
import { freeBytesOnVolume, weightStoreDir, listCachedWeights, ensureWeightCached, catalogIntegrityReport } from './chunk-OV4KGXPD.js';
|
|
43
|
-
import { loadGraph, loadGraphPreferIndex, SCHEMA_VERSION } from './chunk-
|
|
43
|
+
import { loadGraph, loadGraphPreferIndex, SCHEMA_VERSION } from './chunk-3FMRFECC.js';
|
|
44
44
|
import './chunk-5I4VSBWQ.js';
|
|
45
|
-
import { pathExists as pathExists$1, parseExcludePatterns } from './chunk-
|
|
45
|
+
import { pathExists as pathExists$1, parseExcludePatterns } from './chunk-NG4KIJW6.js';
|
|
46
46
|
import { ensureDir, pathExists, readJsonFile, writeTextFile, writeJsonFile } from './chunk-CRAMOEBE.js';
|
|
47
47
|
import { init_cli_invocation, resolveCliInvocation, NPX_INVOCATION } from './chunk-EZ7UC5Y5.js';
|
|
48
|
-
import { resolvedGrammarFiles, grammarSetVersion } from './chunk-
|
|
49
|
-
import './chunk-
|
|
50
|
-
import { embeddingsCached, resolveEmbedModel, loadEmbedder, getNodeEmbeddings, unavailableMessage, countPending, modelCacheInfo, embeddingsPath, clearModelCache, withTimeout } from './chunk-
|
|
51
|
-
import {
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
48
|
+
import { resolvedGrammarFiles, TOOLCHAIN_GRAMMARS, resolveToolchainGrammar, grammarSetVersion, fileRolesFromParseCache } from './chunk-XLXJC7H3.js';
|
|
49
|
+
import './chunk-T7RSQ5WD.js';
|
|
50
|
+
import { embeddingsCached, resolveEmbedModel, loadEmbedder, getNodeEmbeddings, unavailableMessage, countPending, modelCacheInfo, embeddingsPath, clearModelCache, withTimeout } from './chunk-AXZZ5CCG.js';
|
|
51
|
+
import { resetRelevanceProviderCache, loadRelevanceProvider, relevanceModuleDir, resolveGraphPath, analyzeQuestion, loadTopicTags, renderReport, loadGraphFileWithSnapshot, writeArtifacts, vibgrateDir, mapFileExists, ensureVibgrateGitignore } from './chunk-WVMOGIPC.js';
|
|
52
|
+
import { cacheDir } from './chunk-MVPRPSXU.js';
|
|
53
|
+
import { serializeGraph, stableStringify, parseGraph } from './chunk-O3LABX6I.js';
|
|
54
|
+
import { VERSION } from './chunk-PRG2ULPK.js';
|
|
54
55
|
import './chunk-BWPBB45M.js';
|
|
55
56
|
import { canonicalize, hashString, hashBytes } from './chunk-JFGYT6BI.js';
|
|
56
57
|
import './chunk-BPF3YX2U.js';
|
|
57
58
|
import { __toCommonJS } from './chunk-Z6ZWNWWR.js';
|
|
58
59
|
import * as fs14 from 'fs';
|
|
59
60
|
import fs14__default, { realpathSync } from 'fs';
|
|
60
|
-
import * as
|
|
61
|
-
import
|
|
61
|
+
import * as path46 from 'path';
|
|
62
|
+
import path46__default from 'path';
|
|
62
63
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
63
64
|
import { Command, CommanderError, Option } from 'commander';
|
|
64
65
|
import { execSync, spawn, spawnSync, execFileSync } from 'child_process';
|
|
@@ -71,6 +72,7 @@ import * as crypto3 from 'crypto';
|
|
|
71
72
|
import { randomUUID, createHash } from 'crypto';
|
|
72
73
|
import chalk7 from 'chalk';
|
|
73
74
|
import * as fs28 from 'fs/promises';
|
|
75
|
+
import { parse, stringify } from 'yaml';
|
|
74
76
|
|
|
75
77
|
init_cli_invocation();
|
|
76
78
|
function registerStatus(program) {
|
|
@@ -80,19 +82,19 @@ function registerStatus(program) {
|
|
|
80
82
|
applyGlobalOptions(cmd);
|
|
81
83
|
}
|
|
82
84
|
async function runStatus(global) {
|
|
83
|
-
const
|
|
84
|
-
const graphPath = resolveGraphPath(
|
|
85
|
-
const graph = loadGraph(
|
|
86
|
-
const hasCache = fs14.existsSync(
|
|
85
|
+
const root3 = path46.resolve(global.cwd ?? ".");
|
|
86
|
+
const graphPath = resolveGraphPath(root3, global.graph);
|
|
87
|
+
const graph = loadGraph(root3, graphPath);
|
|
88
|
+
const hasCache = fs14.existsSync(path46.join(cacheDir(root3), "parse-cache.json"));
|
|
87
89
|
let stale = null;
|
|
88
90
|
let staleExact = false;
|
|
89
91
|
if (graph) {
|
|
90
|
-
const probe = probeFreshness(
|
|
92
|
+
const probe = probeFreshness(root3);
|
|
91
93
|
if (probe) {
|
|
92
94
|
stale = driftCount(probe.drift);
|
|
93
95
|
staleExact = true;
|
|
94
96
|
} else {
|
|
95
|
-
const files = discover({ root:
|
|
97
|
+
const files = discover({ root: root3 });
|
|
96
98
|
let changed = 0;
|
|
97
99
|
const graphFiles = new Set(graph.nodes.filter((n) => n.kind === "file").map((n) => n.file));
|
|
98
100
|
const currentFiles = new Set(files.map((f) => f.rel));
|
|
@@ -103,8 +105,8 @@ async function runStatus(global) {
|
|
|
103
105
|
}
|
|
104
106
|
if (global.json) {
|
|
105
107
|
json({
|
|
106
|
-
root:
|
|
107
|
-
graphPath:
|
|
108
|
+
root: path46.relative(process.cwd(), root3) || ".",
|
|
109
|
+
graphPath: path46.relative(root3, graphPath),
|
|
108
110
|
built: graph !== null,
|
|
109
111
|
generatedAt: graph?.generatedAt ?? null,
|
|
110
112
|
counts: graph?.meta.counts ?? null,
|
|
@@ -119,11 +121,11 @@ async function runStatus(global) {
|
|
|
119
121
|
return;
|
|
120
122
|
}
|
|
121
123
|
if (!graph) {
|
|
122
|
-
info(`${c.cyan("vg")} \xB7 no map yet at ${c.dim(
|
|
124
|
+
info(`${c.cyan("vg")} \xB7 no map yet at ${c.dim(path46.relative(root3, graphPath))}`);
|
|
123
125
|
info(` run ${c.bold("vg")} to build one`);
|
|
124
126
|
return;
|
|
125
127
|
}
|
|
126
|
-
info(`${c.cyan("vg")} \xB7 ${
|
|
128
|
+
info(`${c.cyan("vg")} \xB7 ${path46.relative(root3, graphPath)}`);
|
|
127
129
|
info(` generated ${graph.generatedAt}`);
|
|
128
130
|
info(
|
|
129
131
|
` nodes ${c.bold(String(graph.meta.counts.nodes))} edges ${c.bold(
|
|
@@ -137,17 +139,17 @@ async function runStatus(global) {
|
|
|
137
139
|
} else {
|
|
138
140
|
info(c.dim(" up to date"));
|
|
139
141
|
}
|
|
140
|
-
if (vibgrateDir(
|
|
142
|
+
if (vibgrateDir(root3)) ;
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
// src/commands/ask.ts
|
|
144
146
|
function registerAsk(program) {
|
|
145
147
|
const cmd = program.command("ask").description("ask the map \u2014 returns a budget-bounded context block").argument("<question...>", 'your question (a bare quoted string also works: vg "\u2026")').option("-b, --budget <n>", "approx token budget for the answer", "2000").option("--no-semantic", "lexical only (skip the local-embedding pass)").option("--no-refresh", "answer from the map as built \u2014 skip the auto-rebuild when files change").action(async function(question, opts) {
|
|
146
148
|
const global = readGlobal(this);
|
|
147
|
-
const
|
|
149
|
+
const root3 = rootOf(global);
|
|
148
150
|
if (opts.refresh !== false && !global.graph) {
|
|
149
|
-
const graphPath = resolveGraphPath(
|
|
150
|
-
const refreshed = await refreshIfStale(
|
|
151
|
+
const graphPath = resolveGraphPath(root3, global.graph);
|
|
152
|
+
const refreshed = await refreshIfStale(root3, { graphPath });
|
|
151
153
|
if (refreshed.status === "refreshed" && !global.json) {
|
|
152
154
|
const n = driftCount(refreshed.drift);
|
|
153
155
|
info(c.dim(` map refreshed \u2014 ${n} file(s) drifted (${(refreshed.ms / 1e3).toFixed(2)}s)`));
|
|
@@ -163,9 +165,9 @@ function registerAsk(program) {
|
|
|
163
165
|
let mode = "lexical";
|
|
164
166
|
let note = "";
|
|
165
167
|
const relevance = await analyzeQuestion(q);
|
|
166
|
-
const topicTags = relevance ? await loadTopicTags(graph,
|
|
168
|
+
const topicTags = relevance ? await loadTopicTags(graph, root3, resolveGraphPath(root3, global.graph)) : null;
|
|
167
169
|
if (wantSemantic) {
|
|
168
|
-
const firstRun = !embeddingsCached(
|
|
170
|
+
const firstRun = !embeddingsCached(root3, resolveEmbedModel());
|
|
169
171
|
if (!global.json && firstRun) {
|
|
170
172
|
info(c.dim(" preparing semantic search (first use embeds the map; cached, resumable & offline after)\u2026"));
|
|
171
173
|
}
|
|
@@ -180,7 +182,7 @@ function registerAsk(program) {
|
|
|
180
182
|
});
|
|
181
183
|
if (embedder) {
|
|
182
184
|
const bar = !global.json && firstRun ? new ProgressBar(c.dim("embedding")) : void 0;
|
|
183
|
-
const vectors = await getNodeEmbeddings(graph, embedder,
|
|
185
|
+
const vectors = await getNodeEmbeddings(graph, embedder, root3, bar ? (d, t) => bar.update(d, t) : void 0);
|
|
184
186
|
bar?.done();
|
|
185
187
|
result = await queryGraphSemantic(graph, q, { budget, embedder, nodeVectors: vectors, relevance, topicTags });
|
|
186
188
|
mode = `semantic (${embedder.id})`;
|
|
@@ -196,7 +198,7 @@ function registerAsk(program) {
|
|
|
196
198
|
if (global.client) {
|
|
197
199
|
const files = new Set(result.matches.map((m) => m.node.file).filter(Boolean));
|
|
198
200
|
recordCliCall(
|
|
199
|
-
|
|
201
|
+
root3,
|
|
200
202
|
{
|
|
201
203
|
tool: CLI_TOOL_ALIASES.ask,
|
|
202
204
|
client: global.client,
|
|
@@ -237,11 +239,11 @@ function crashedMessage(signal) {
|
|
|
237
239
|
return `semantic search is disabled: its native backend (onnxruntime-node, via the vendored fastembed path) crashed with ${signal} \u2014 using fast lexical search instead. This usually means the installed native binaries are broken (an interrupted install, or a platform without prebuilt CPU binaries). Reinstall to repair \u2014 ${envPrefix}npm install -g --allow-scripts=@vibgrate/cli,onnxruntime-node,msgpackr-extract @vibgrate/cli \u2014 or run with --local to stay lexical quietly.`;
|
|
238
240
|
}
|
|
239
241
|
function defaultCacheDir() {
|
|
240
|
-
const base = process.env.XDG_CACHE_HOME ||
|
|
241
|
-
return
|
|
242
|
+
const base = process.env.XDG_CACHE_HOME || path46.join(os.homedir(), ".cache");
|
|
243
|
+
return path46.join(base, "vibgrate", "models");
|
|
242
244
|
}
|
|
243
245
|
function verdictPath(dir) {
|
|
244
|
-
return
|
|
246
|
+
return path46.join(dir, `embed-health-${process.platform}-${process.arch}.json`);
|
|
245
247
|
}
|
|
246
248
|
function readVerdict(dir) {
|
|
247
249
|
try {
|
|
@@ -261,12 +263,12 @@ function writeVerdict(dir, verdict) {
|
|
|
261
263
|
}
|
|
262
264
|
}
|
|
263
265
|
function probeModuleUrl(from = import.meta.url) {
|
|
264
|
-
const dir =
|
|
266
|
+
const dir = path46.dirname(fileURLToPath(from));
|
|
265
267
|
for (const name of ["embeddings.js", "index.js"]) {
|
|
266
|
-
const candidate =
|
|
268
|
+
const candidate = path46.join(dir, name);
|
|
267
269
|
if (fs14.existsSync(candidate)) return pathToFileURL(candidate).href;
|
|
268
270
|
}
|
|
269
|
-
return pathToFileURL(
|
|
271
|
+
return pathToFileURL(path46.join(dir, "index.js")).href;
|
|
270
272
|
}
|
|
271
273
|
function defaultRunProbe(timeoutMs) {
|
|
272
274
|
const moduleUrl = probeModuleUrl();
|
|
@@ -285,7 +287,7 @@ function defaultRunProbe(timeoutMs) {
|
|
|
285
287
|
})
|
|
286
288
|
.catch(() => process.stdout.write('{"ok":false}'));
|
|
287
289
|
`;
|
|
288
|
-
return new Promise((
|
|
290
|
+
return new Promise((resolve23) => {
|
|
289
291
|
const child = spawn(process.execPath, ["--input-type=module", "-e", script], {
|
|
290
292
|
stdio: ["ignore", "pipe", "ignore"],
|
|
291
293
|
// The probe must never inherit a debugger port or loader flags that would
|
|
@@ -298,7 +300,7 @@ function defaultRunProbe(timeoutMs) {
|
|
|
298
300
|
if (settled) return;
|
|
299
301
|
settled = true;
|
|
300
302
|
clearTimeout(timer);
|
|
301
|
-
|
|
303
|
+
resolve23({ signal, status, stdout });
|
|
302
304
|
};
|
|
303
305
|
const timer = setTimeout(() => {
|
|
304
306
|
child.kill("SIGTERM");
|
|
@@ -359,7 +361,7 @@ function registerEmbed(program) {
|
|
|
359
361
|
if (opts.where) return showWhere(rootOf(global), modelId, global.json);
|
|
360
362
|
if (opts.clear) return doClear(global.json);
|
|
361
363
|
const { graph } = requireGraph(global);
|
|
362
|
-
const
|
|
364
|
+
const root3 = rootOf(global);
|
|
363
365
|
const bg = opts.bg === true;
|
|
364
366
|
const speak = !bg && !global.json;
|
|
365
367
|
if (global.local) {
|
|
@@ -367,7 +369,7 @@ function registerEmbed(program) {
|
|
|
367
369
|
else if (speak) info(c.dim("vg embed \xB7 semantic is off (--local) \u2014 lexical search only"));
|
|
368
370
|
return;
|
|
369
371
|
}
|
|
370
|
-
if (countPending(graph,
|
|
372
|
+
if (countPending(graph, root3, modelId) === 0) {
|
|
371
373
|
if (global.json) json({ embedded: 0, pending: 0, upToDate: true, model: modelId, semantic: true });
|
|
372
374
|
else if (speak) info(`${c.cyan("vg embed")} \xB7 semantic index already up to date`);
|
|
373
375
|
return;
|
|
@@ -400,19 +402,19 @@ function registerEmbed(program) {
|
|
|
400
402
|
}
|
|
401
403
|
return;
|
|
402
404
|
}
|
|
403
|
-
const pending = countPending(graph,
|
|
405
|
+
const pending = countPending(graph, root3, modelId);
|
|
404
406
|
const bar = speak ? new ProgressBar(c.dim("embedding")) : void 0;
|
|
405
|
-
await getNodeEmbeddings(graph, embedder,
|
|
407
|
+
await getNodeEmbeddings(graph, embedder, root3, bar ? (d, t) => bar.update(d, t) : void 0);
|
|
406
408
|
bar?.done();
|
|
407
409
|
if (global.json) json({ embedded: pending, model: modelId, semantic: true });
|
|
408
410
|
else if (speak) info(`${c.green("\u2714")} vg embed \xB7 semantic index ready (~${pending} node(s) \xB7 model ${modelId})`);
|
|
409
411
|
});
|
|
410
412
|
applyGlobalOptions(cmd);
|
|
411
413
|
}
|
|
412
|
-
function showWhere(
|
|
414
|
+
function showWhere(root3, modelId, asJson) {
|
|
413
415
|
const m = modelCacheInfo(modelId);
|
|
414
|
-
const repoCached = embeddingsCached(
|
|
415
|
-
const repoPath = embeddingsPath(
|
|
416
|
+
const repoCached = embeddingsCached(root3, modelId);
|
|
417
|
+
const repoPath = embeddingsPath(root3);
|
|
416
418
|
if (asJson) {
|
|
417
419
|
json({
|
|
418
420
|
model: modelId,
|
|
@@ -1010,8 +1012,8 @@ function isLoopbackOrigin(origin) {
|
|
|
1010
1012
|
var PUBLISH_INTERVAL_MS = 2e3;
|
|
1011
1013
|
var KEEPALIVE_MS = 1e4;
|
|
1012
1014
|
var STALE_MS = 6e4;
|
|
1013
|
-
function liveStatsDir(
|
|
1014
|
-
return
|
|
1015
|
+
function liveStatsDir(root3) {
|
|
1016
|
+
return path46.join(cacheDir(root3), "serve-live");
|
|
1015
1017
|
}
|
|
1016
1018
|
function reapOtherVersionServers(dir, keepVersion, now = Date.now(), kill = (pid, signal) => process.kill(pid, signal), selfPid = process.pid) {
|
|
1017
1019
|
let entries;
|
|
@@ -1025,7 +1027,7 @@ function reapOtherVersionServers(dir, keepVersion, now = Date.now(), kill = (pid
|
|
|
1025
1027
|
if (!/^serve-\d+\.json$/.test(name)) continue;
|
|
1026
1028
|
let parsed;
|
|
1027
1029
|
try {
|
|
1028
|
-
parsed = JSON.parse(fs14.readFileSync(
|
|
1030
|
+
parsed = JSON.parse(fs14.readFileSync(path46.join(dir, name), "utf8"));
|
|
1029
1031
|
} catch {
|
|
1030
1032
|
continue;
|
|
1031
1033
|
}
|
|
@@ -1047,7 +1049,7 @@ var LiveStatsBus = class {
|
|
|
1047
1049
|
this.dir = dir;
|
|
1048
1050
|
this.stats = stats;
|
|
1049
1051
|
this.pid = pid;
|
|
1050
|
-
this.file =
|
|
1052
|
+
this.file = path46.join(dir, `serve-${this.pid}.json`);
|
|
1051
1053
|
}
|
|
1052
1054
|
dir;
|
|
1053
1055
|
stats;
|
|
@@ -1100,7 +1102,7 @@ var LiveStatsBus = class {
|
|
|
1100
1102
|
const out2 = [];
|
|
1101
1103
|
for (const name of entries.sort()) {
|
|
1102
1104
|
if (!/^serve-\d+\.json$/.test(name)) continue;
|
|
1103
|
-
const file =
|
|
1105
|
+
const file = path46.join(this.dir, name);
|
|
1104
1106
|
let parsed;
|
|
1105
1107
|
try {
|
|
1106
1108
|
parsed = JSON.parse(fs14.readFileSync(file, "utf8"));
|
|
@@ -1487,8 +1489,8 @@ var SHARE_FLUSH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
|
1487
1489
|
function registerServe(program) {
|
|
1488
1490
|
const cmd = program.command("serve").description("start Vibgrate AI Context \u2014 local-first MCP serving your code map, drift & version-correct docs to your AI").option("--http", "serve over streamable HTTP instead of stdio").option("--port <n>", "port for --http", "7437").option("--host <h>", "host for --http", "127.0.0.1").option("--savings", "record local, counts-only usage savings (opt-in; off by default)").option("--share-stats", "ALSO upload the counts-only usage ledger to Vibgrate to improve the local MCP (opt-in; off by default; implies --savings; disabled under --local)").option("--dedup", "collapse a node's heavy relation lists on repeat reads within a session (opt-in; saves tokens)").option("--no-refresh", "serve the map as built \u2014 skip the auto-rebuild when files change").option("--no-watch", "disable the event-driven file watcher \u2014 freshness falls back to the periodic poll").option("--surface <mode>", 'tool listing surface: "hot" lists only the navigation core (orient/search_symbols/query_graph/get_node); "full" lists all tools. Every tool stays callable either way. Env: VG_MCP_SURFACE').option("--tools <names>", "comma-separated tool names to list (listing only \u2014 all tools remain callable). Env: VG_MCP_TOOLS").action(async function(opts) {
|
|
1489
1491
|
const global = readGlobal(this);
|
|
1490
|
-
const
|
|
1491
|
-
const graphPath = resolveGraphPath(
|
|
1492
|
+
const root3 = rootOf(global);
|
|
1493
|
+
const graphPath = resolveGraphPath(root3, global.graph);
|
|
1492
1494
|
const refresh = opts.refresh !== false && !global.graph;
|
|
1493
1495
|
const local = global.local === true;
|
|
1494
1496
|
const optOut = telemetryOptOut();
|
|
@@ -1496,7 +1498,7 @@ function registerServe(program) {
|
|
|
1496
1498
|
const quiet = global.quiet === true;
|
|
1497
1499
|
const stats = quiet ? void 0 : new SessionStats();
|
|
1498
1500
|
if (!quiet) {
|
|
1499
|
-
printLogo(
|
|
1501
|
+
printLogo(root3, { product: "AI Context", tagline: "Local-first MCP for your AI" });
|
|
1500
1502
|
}
|
|
1501
1503
|
const serveOpts = {
|
|
1502
1504
|
// A *requested* --share-stats always implies local recording, even when
|
|
@@ -1509,7 +1511,7 @@ function registerServe(program) {
|
|
|
1509
1511
|
dedup: opts.dedup === true,
|
|
1510
1512
|
refresh,
|
|
1511
1513
|
watch: opts.watch !== false,
|
|
1512
|
-
root:
|
|
1514
|
+
root: root3,
|
|
1513
1515
|
stats,
|
|
1514
1516
|
// Listing surface: flags win over env; unknown values fall back to the
|
|
1515
1517
|
// full surface inside listedToolNames (fail-open).
|
|
@@ -1518,18 +1520,18 @@ function registerServe(program) {
|
|
|
1518
1520
|
tools: (opts.tools ?? process.env.VG_MCP_TOOLS)?.split(",").map((s) => s.trim()).filter(Boolean)
|
|
1519
1521
|
}
|
|
1520
1522
|
};
|
|
1521
|
-
await ensureServableGraph(
|
|
1523
|
+
await ensureServableGraph(root3, graphPath, global, refresh);
|
|
1522
1524
|
if (opts.shareStats === true && local) {
|
|
1523
1525
|
info(c.dim("vg \xB7 --share-stats ignored under --local (air-gapped): recording locally, not uploading."));
|
|
1524
1526
|
} else if (opts.shareStats === true && optOut !== null) {
|
|
1525
1527
|
info(c.dim(`vg \xB7 --share-stats disabled by ${optOut}: recording locally, not uploading.`));
|
|
1526
1528
|
}
|
|
1527
|
-
if (shareStats) startSharing(
|
|
1529
|
+
if (shareStats) startSharing(root3);
|
|
1528
1530
|
const interactive = process.stderr.isTTY === true;
|
|
1529
|
-
const bus = stats ? new LiveStatsBus(liveStatsDir(
|
|
1531
|
+
const bus = stats ? new LiveStatsBus(liveStatsDir(root3), stats) : void 0;
|
|
1530
1532
|
if (bus && !interactive) bus.start();
|
|
1531
1533
|
const display = stats ? new ServeStatusDisplay(stats, process.stderr, bus && interactive ? () => bus.siblings() : void 0) : void 0;
|
|
1532
|
-
if (stats && interactive) new LedgerTail(savingsLedgerPath(
|
|
1534
|
+
if (stats && interactive) new LedgerTail(savingsLedgerPath(root3), stats).start();
|
|
1533
1535
|
const freshness = refresh ? "auto-refresh" : "as built";
|
|
1534
1536
|
if (opts.http) {
|
|
1535
1537
|
await serveHttp(graphPath, opts.host ?? "127.0.0.1", Number(opts.port) || 7437, serveOpts, freshness, () => display?.start());
|
|
@@ -1541,7 +1543,7 @@ function registerServe(program) {
|
|
|
1541
1543
|
});
|
|
1542
1544
|
applyGlobalOptions(cmd);
|
|
1543
1545
|
}
|
|
1544
|
-
async function ensureServableGraph(
|
|
1546
|
+
async function ensureServableGraph(root3, graphPath, global, refresh, opts = {}) {
|
|
1545
1547
|
if (refresh) {
|
|
1546
1548
|
if (!mapFileExists(graphPath)) {
|
|
1547
1549
|
info(c.dim("vg \xB7 no map found \u2014 building it before serving\u2026"));
|
|
@@ -1551,7 +1553,7 @@ async function ensureServableGraph(root2, graphPath, global, refresh, opts = {})
|
|
|
1551
1553
|
{ ...global, json: false }
|
|
1552
1554
|
);
|
|
1553
1555
|
} else {
|
|
1554
|
-
const kick = refreshIfStale(
|
|
1556
|
+
const kick = refreshIfStale(root3, { inline: opts.inline, graphPath }).then((refreshed) => {
|
|
1555
1557
|
if (refreshed.status === "refreshed") {
|
|
1556
1558
|
const n = driftCount(refreshed.drift);
|
|
1557
1559
|
info(
|
|
@@ -1576,8 +1578,8 @@ async function ensureServableGraph(root2, graphPath, global, refresh, opts = {})
|
|
|
1576
1578
|
);
|
|
1577
1579
|
}
|
|
1578
1580
|
}
|
|
1579
|
-
function startSharing(
|
|
1580
|
-
const sharer = new StatsSharer(
|
|
1581
|
+
function startSharing(root3) {
|
|
1582
|
+
const sharer = new StatsSharer(root3);
|
|
1581
1583
|
info(
|
|
1582
1584
|
c.dim(
|
|
1583
1585
|
`vg \xB7 sharing counts-only usage stats with Vibgrate (${statsEndpoint()}) to improve the local MCP. No code, paths, or questions are sent. Stop by omitting --share-stats.`
|
|
@@ -1632,7 +1634,7 @@ async function serveHttp(graphPath, host, port, opts, freshness, onReady) {
|
|
|
1632
1634
|
info(c.red(`vg serve: request error: ${err.message}`));
|
|
1633
1635
|
}
|
|
1634
1636
|
});
|
|
1635
|
-
await new Promise((
|
|
1637
|
+
await new Promise((resolve23) => httpServer.listen(port, host, resolve23));
|
|
1636
1638
|
info(c.dim(`vg \xB7 MCP server on http://${host}:${port}/mcp (read-only, local, ${freshness})`));
|
|
1637
1639
|
onReady?.();
|
|
1638
1640
|
await new Promise(() => {
|
|
@@ -1845,10 +1847,10 @@ function endOfLine(text, line) {
|
|
|
1845
1847
|
return (lines[line] ?? "").length;
|
|
1846
1848
|
}
|
|
1847
1849
|
var CACHE_VERSION = "vg-lsp-scan-cache/2";
|
|
1848
|
-
function cachePath(
|
|
1849
|
-
return
|
|
1850
|
+
function cachePath(root3) {
|
|
1851
|
+
return path46.join(cacheDir(root3), "lsp-scan.json");
|
|
1850
1852
|
}
|
|
1851
|
-
function manifestHash(
|
|
1853
|
+
function manifestHash(root3) {
|
|
1852
1854
|
const files = [];
|
|
1853
1855
|
const walk2 = (dir) => {
|
|
1854
1856
|
let entries;
|
|
@@ -1858,7 +1860,7 @@ function manifestHash(root2) {
|
|
|
1858
1860
|
return;
|
|
1859
1861
|
}
|
|
1860
1862
|
for (const entry of entries) {
|
|
1861
|
-
const abs =
|
|
1863
|
+
const abs = path46.join(dir, entry.name);
|
|
1862
1864
|
if (entry.isDirectory()) {
|
|
1863
1865
|
if (isSkippedDirName(entry.name)) continue;
|
|
1864
1866
|
walk2(abs);
|
|
@@ -1868,27 +1870,27 @@ function manifestHash(root2) {
|
|
|
1868
1870
|
const base = entry.name.toLowerCase();
|
|
1869
1871
|
if (!isManifest(abs) && !SKIP_FILES.has(base)) continue;
|
|
1870
1872
|
try {
|
|
1871
|
-
const rel =
|
|
1873
|
+
const rel = path46.relative(root3, abs).split(path46.sep).join("/");
|
|
1872
1874
|
files.push({ rel, hash: hashBytes(fs14.readFileSync(abs)) });
|
|
1873
1875
|
} catch {
|
|
1874
1876
|
}
|
|
1875
1877
|
}
|
|
1876
1878
|
};
|
|
1877
|
-
walk2(
|
|
1879
|
+
walk2(root3);
|
|
1878
1880
|
files.sort((a, b) => a.rel < b.rel ? -1 : a.rel > b.rel ? 1 : 0);
|
|
1879
1881
|
return hashString(canonicalize(files));
|
|
1880
1882
|
}
|
|
1881
|
-
function isDependencyFile(
|
|
1882
|
-
const rel =
|
|
1883
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
1884
|
-
const parts = rel.split(
|
|
1883
|
+
function isDependencyFile(root3, filePath) {
|
|
1884
|
+
const rel = path46.relative(path46.resolve(root3), path46.resolve(filePath));
|
|
1885
|
+
if (rel === "" || rel.startsWith("..") || path46.isAbsolute(rel)) return false;
|
|
1886
|
+
const parts = rel.split(path46.sep);
|
|
1885
1887
|
if (parts.slice(0, -1).some((dir) => isSkippedDirName(dir))) return false;
|
|
1886
1888
|
const base = parts[parts.length - 1].toLowerCase();
|
|
1887
1889
|
return isManifest(filePath) || SKIP_FILES.has(base);
|
|
1888
1890
|
}
|
|
1889
|
-
function loadScanCache(
|
|
1891
|
+
function loadScanCache(root3, key2) {
|
|
1890
1892
|
try {
|
|
1891
|
-
const raw = JSON.parse(fs14.readFileSync(cachePath(
|
|
1893
|
+
const raw = JSON.parse(fs14.readFileSync(cachePath(root3), "utf8"));
|
|
1892
1894
|
if (raw.version === CACHE_VERSION && raw.manifestHash === key2.manifestHash && raw.toolVersion === key2.toolVersion && raw.offline === key2.offline && raw.artifact) {
|
|
1893
1895
|
return { manifestHash: raw.manifestHash, artifact: raw.artifact };
|
|
1894
1896
|
}
|
|
@@ -1896,11 +1898,11 @@ function loadScanCache(root2, key2) {
|
|
|
1896
1898
|
}
|
|
1897
1899
|
return null;
|
|
1898
1900
|
}
|
|
1899
|
-
function writeScanCache(
|
|
1901
|
+
function writeScanCache(root3, key2, artifact) {
|
|
1900
1902
|
try {
|
|
1901
|
-
fs14.mkdirSync(cacheDir(
|
|
1903
|
+
fs14.mkdirSync(cacheDir(root3), { recursive: true });
|
|
1902
1904
|
fs14.writeFileSync(
|
|
1903
|
-
cachePath(
|
|
1905
|
+
cachePath(root3),
|
|
1904
1906
|
stableStringify(
|
|
1905
1907
|
{ version: CACHE_VERSION, manifestHash: key2.manifestHash, toolVersion: key2.toolVersion, offline: key2.offline, artifact },
|
|
1906
1908
|
0
|
|
@@ -1911,8 +1913,8 @@ function writeScanCache(root2, key2, artifact) {
|
|
|
1911
1913
|
}
|
|
1912
1914
|
var MAX_ENTRIES = 1e3;
|
|
1913
1915
|
var KEEP_ENTRIES = 500;
|
|
1914
|
-
function historyPath(
|
|
1915
|
-
return
|
|
1916
|
+
function historyPath(root3) {
|
|
1917
|
+
return path46.join(root3, ".vibgrate", "score-history.jsonl");
|
|
1916
1918
|
}
|
|
1917
1919
|
function parseLine(line) {
|
|
1918
1920
|
try {
|
|
@@ -1924,27 +1926,27 @@ function parseLine(line) {
|
|
|
1924
1926
|
}
|
|
1925
1927
|
return null;
|
|
1926
1928
|
}
|
|
1927
|
-
function readHistory(
|
|
1929
|
+
function readHistory(root3) {
|
|
1928
1930
|
try {
|
|
1929
|
-
return fs14.readFileSync(historyPath(
|
|
1931
|
+
return fs14.readFileSync(historyPath(root3), "utf8").split("\n").filter((l) => l.trim()).map(parseLine).filter((e) => e !== null);
|
|
1930
1932
|
} catch {
|
|
1931
1933
|
return [];
|
|
1932
1934
|
}
|
|
1933
1935
|
}
|
|
1934
|
-
function lastEntry(
|
|
1935
|
-
const all = readHistory(
|
|
1936
|
+
function lastEntry(root3) {
|
|
1937
|
+
const all = readHistory(root3);
|
|
1936
1938
|
return all.length ? all[all.length - 1] : null;
|
|
1937
1939
|
}
|
|
1938
|
-
function recentHistory(
|
|
1940
|
+
function recentHistory(root3, days, now = Date.now()) {
|
|
1939
1941
|
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
1940
|
-
return readHistory(
|
|
1942
|
+
return readHistory(root3).filter((e) => {
|
|
1941
1943
|
const t = Date.parse(e.ts);
|
|
1942
1944
|
return Number.isFinite(t) && t >= cutoff;
|
|
1943
1945
|
});
|
|
1944
1946
|
}
|
|
1945
|
-
function recordScore(
|
|
1947
|
+
function recordScore(root3, entry) {
|
|
1946
1948
|
try {
|
|
1947
|
-
const prev = lastEntry(
|
|
1949
|
+
const prev = lastEntry(root3);
|
|
1948
1950
|
if (prev && prev.score === entry.score && prev.band === entry.band && prev.mode === entry.mode && prev.methodology === entry.methodology) {
|
|
1949
1951
|
const prevT = Date.parse(prev.ts);
|
|
1950
1952
|
const nextT = Date.parse(entry.ts);
|
|
@@ -1952,12 +1954,12 @@ function recordScore(root2, entry) {
|
|
|
1952
1954
|
return;
|
|
1953
1955
|
}
|
|
1954
1956
|
}
|
|
1955
|
-
const file = historyPath(
|
|
1956
|
-
fs14.mkdirSync(
|
|
1957
|
-
ensureVibgrateGitignore(
|
|
1957
|
+
const file = historyPath(root3);
|
|
1958
|
+
fs14.mkdirSync(path46.dirname(file), { recursive: true });
|
|
1959
|
+
ensureVibgrateGitignore(root3);
|
|
1958
1960
|
fs14.appendFileSync(file, `${JSON.stringify(entry)}
|
|
1959
1961
|
`);
|
|
1960
|
-
const all = readHistory(
|
|
1962
|
+
const all = readHistory(root3);
|
|
1961
1963
|
if (all.length > MAX_ENTRIES) {
|
|
1962
1964
|
const kept = all.slice(-KEEP_ENTRIES);
|
|
1963
1965
|
fs14.writeFileSync(file, kept.map((e) => JSON.stringify(e)).join("\n") + "\n");
|
|
@@ -1970,8 +1972,8 @@ function deltaFrom(prev, current) {
|
|
|
1970
1972
|
if (prev.methodology !== current.methodology) return void 0;
|
|
1971
1973
|
return current.score - prev.score;
|
|
1972
1974
|
}
|
|
1973
|
-
function ignoreFilePath(
|
|
1974
|
-
return
|
|
1975
|
+
function ignoreFilePath(root3) {
|
|
1976
|
+
return path46.join(root3, ".vibgrate", "ignore.json");
|
|
1975
1977
|
}
|
|
1976
1978
|
function isActive(entry, now = Date.now()) {
|
|
1977
1979
|
if (!entry.until) return true;
|
|
@@ -1979,11 +1981,11 @@ function isActive(entry, now = Date.now()) {
|
|
|
1979
1981
|
if (!Number.isFinite(t)) return true;
|
|
1980
1982
|
return now <= t + 24 * 60 * 60 * 1e3 - 1;
|
|
1981
1983
|
}
|
|
1982
|
-
function readIgnores(
|
|
1984
|
+
function readIgnores(root3, now = Date.now()) {
|
|
1983
1985
|
const out2 = /* @__PURE__ */ new Map();
|
|
1984
1986
|
let raw;
|
|
1985
1987
|
try {
|
|
1986
|
-
raw = fs14.readFileSync(ignoreFilePath(
|
|
1988
|
+
raw = fs14.readFileSync(ignoreFilePath(root3), "utf8");
|
|
1987
1989
|
} catch {
|
|
1988
1990
|
return out2;
|
|
1989
1991
|
}
|
|
@@ -2002,15 +2004,15 @@ function readIgnores(root2, now = Date.now()) {
|
|
|
2002
2004
|
}
|
|
2003
2005
|
return out2;
|
|
2004
2006
|
}
|
|
2005
|
-
function inventoryPath(
|
|
2006
|
-
return
|
|
2007
|
+
function inventoryPath(root3) {
|
|
2008
|
+
return path46.join(root3, ".vibgrate", "inline-inventory.json");
|
|
2007
2009
|
}
|
|
2008
2010
|
function inventoryKey(projectPath, pkg) {
|
|
2009
2011
|
return `${projectPath}::${pkg}`;
|
|
2010
2012
|
}
|
|
2011
|
-
function readInventory(
|
|
2013
|
+
function readInventory(root3) {
|
|
2012
2014
|
try {
|
|
2013
|
-
const parsed = JSON.parse(fs14.readFileSync(inventoryPath(
|
|
2015
|
+
const parsed = JSON.parse(fs14.readFileSync(inventoryPath(root3), "utf8"));
|
|
2014
2016
|
if (typeof parsed.ts === "string" && typeof parsed.methodology === "string" && Array.isArray(parsed.drifted) && parsed.drifted.every((d) => typeof d === "string")) {
|
|
2015
2017
|
return parsed;
|
|
2016
2018
|
}
|
|
@@ -2018,10 +2020,10 @@ function readInventory(root2) {
|
|
|
2018
2020
|
}
|
|
2019
2021
|
return null;
|
|
2020
2022
|
}
|
|
2021
|
-
function recordInventory(
|
|
2023
|
+
function recordInventory(root3, snap) {
|
|
2022
2024
|
try {
|
|
2023
|
-
ensureVibgrateGitignore(
|
|
2024
|
-
fs14.writeFileSync(inventoryPath(
|
|
2025
|
+
ensureVibgrateGitignore(root3);
|
|
2026
|
+
fs14.writeFileSync(inventoryPath(root3), `${JSON.stringify(snap)}
|
|
2025
2027
|
`);
|
|
2026
2028
|
} catch {
|
|
2027
2029
|
}
|
|
@@ -2530,6 +2532,37 @@ async function scanWorkspaceVulns(targets, opts = { offline: false }) {
|
|
|
2530
2532
|
}
|
|
2531
2533
|
|
|
2532
2534
|
// src/lsp/server.ts
|
|
2535
|
+
function architectureWire(arch) {
|
|
2536
|
+
const out2 = {
|
|
2537
|
+
archetype: arch.archetype,
|
|
2538
|
+
archetypeConfidence: arch.archetypeConfidence,
|
|
2539
|
+
layers: arch.layers,
|
|
2540
|
+
totalClassified: arch.totalClassified,
|
|
2541
|
+
unclassified: arch.unclassified
|
|
2542
|
+
};
|
|
2543
|
+
if (arch.folders) out2.folders = arch.folders;
|
|
2544
|
+
if (arch.unclassifiedFiles) out2.unclassifiedFiles = arch.unclassifiedFiles;
|
|
2545
|
+
if (arch.classified) out2.classified = arch.classified;
|
|
2546
|
+
if (arch.workspaceShape) out2.workspaceShape = arch.workspaceShape;
|
|
2547
|
+
if (arch.supportLevel !== void 0) out2.supportLevel = arch.supportLevel;
|
|
2548
|
+
if (arch.packIds) out2.packIds = arch.packIds;
|
|
2549
|
+
if (arch.projectKind) out2.projectKind = arch.projectKind;
|
|
2550
|
+
if (arch.artifactKind) out2.artifactKind = arch.artifactKind;
|
|
2551
|
+
if (arch.contractKind) out2.contractKind = arch.contractKind;
|
|
2552
|
+
if (arch.semanticRole) out2.semanticRole = arch.semanticRole;
|
|
2553
|
+
if (arch.evidence) out2.evidence = arch.evidence;
|
|
2554
|
+
if (arch.evidenceCapped) out2.evidenceCapped = true;
|
|
2555
|
+
if (arch.workspaceEdges) out2.workspaceEdges = arch.workspaceEdges;
|
|
2556
|
+
if (arch.workspaceEdgesCapped) out2.workspaceEdgesCapped = true;
|
|
2557
|
+
if (arch.violations) out2.violations = arch.violations;
|
|
2558
|
+
if (arch.violationsCapped) out2.violationsCapped = true;
|
|
2559
|
+
if (arch.boundaryProfile) out2.boundaryProfile = arch.boundaryProfile;
|
|
2560
|
+
if (arch.structure) out2.structure = arch.structure;
|
|
2561
|
+
if (arch.generated) out2.generated = true;
|
|
2562
|
+
if (arch.generatedBy) out2.generatedBy = arch.generatedBy;
|
|
2563
|
+
if (arch.sourceContract) out2.sourceContract = arch.sourceContract;
|
|
2564
|
+
return out2;
|
|
2565
|
+
}
|
|
2533
2566
|
function bandForDependency(dep) {
|
|
2534
2567
|
if (dep.drift === "major-behind") {
|
|
2535
2568
|
return (dep.majorsBehind ?? 1) >= 2 ? "high" : "moderate";
|
|
@@ -2580,6 +2613,8 @@ var VibgrateLanguageServer = class {
|
|
|
2580
2613
|
debounce = null;
|
|
2581
2614
|
shuttingDown = false;
|
|
2582
2615
|
graph = null;
|
|
2616
|
+
/** Role hits from the last graph build or recovered from the parse cache. */
|
|
2617
|
+
fileRoles = [];
|
|
2583
2618
|
graphBuilding = false;
|
|
2584
2619
|
/**
|
|
2585
2620
|
* Keeps the freshness probe / incremental rebuild OFF the query path. The
|
|
@@ -2689,6 +2724,7 @@ var VibgrateLanguageServer = class {
|
|
|
2689
2724
|
let fromCache = false;
|
|
2690
2725
|
if (cached2) {
|
|
2691
2726
|
this.artifact = pruneArtifactProjects(cached2.artifact);
|
|
2727
|
+
if (this.graph) refineArtifactWithGraph(this.artifact, this.graph, this.fileRoles);
|
|
2692
2728
|
fromCache = true;
|
|
2693
2729
|
} else {
|
|
2694
2730
|
const scanOpts = {
|
|
@@ -2705,10 +2741,11 @@ var VibgrateLanguageServer = class {
|
|
|
2705
2741
|
};
|
|
2706
2742
|
const advanced = await loadAdvancedScanHook();
|
|
2707
2743
|
this.artifact = pruneArtifactProjects(
|
|
2708
|
-
await runCoreScanSilently(this.opts.root, scanOpts, advanced)
|
|
2744
|
+
await runCoreScanSilently(this.opts.root, scanOpts, advanced, this.graph, this.fileRoles)
|
|
2709
2745
|
);
|
|
2710
2746
|
writeScanCache(this.opts.root, cacheKey, this.artifact);
|
|
2711
2747
|
}
|
|
2748
|
+
if (this.graph) refineArtifactWithGraph(this.artifact, this.graph, this.fileRoles);
|
|
2712
2749
|
this.publishScore();
|
|
2713
2750
|
this.publishScanArtifact(fromCache);
|
|
2714
2751
|
for (const uri of this.docs.keys()) this.publishForDoc(uri);
|
|
@@ -2923,6 +2960,8 @@ var VibgrateLanguageServer = class {
|
|
|
2923
2960
|
const existing = loadGraph(this.opts.root);
|
|
2924
2961
|
if (existing) {
|
|
2925
2962
|
this.graph = existing;
|
|
2963
|
+
this.fileRoles = fileRolesFromParseCache(this.opts.root);
|
|
2964
|
+
this.refineAndPublishArchitecture();
|
|
2926
2965
|
this.conn.notify("vibgrate/graph/status", { state: "ready" });
|
|
2927
2966
|
return;
|
|
2928
2967
|
}
|
|
@@ -2931,6 +2970,8 @@ var VibgrateLanguageServer = class {
|
|
|
2931
2970
|
writeArtifacts(result.graph, { root: this.opts.root, html: false, report: false });
|
|
2932
2971
|
writeSnapshot(this.opts.root, result.graph.provenance.corpusHash, result.fileStats, {});
|
|
2933
2972
|
this.graph = result.graph;
|
|
2973
|
+
this.fileRoles = result.fileRoles;
|
|
2974
|
+
this.refineAndPublishArchitecture();
|
|
2934
2975
|
this.conn.notify("vibgrate/graph/status", { state: "ready" });
|
|
2935
2976
|
} catch (err) {
|
|
2936
2977
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -2962,7 +3003,22 @@ var VibgrateLanguageServer = class {
|
|
|
2962
3003
|
if (outcome?.status !== "refreshed" || !outcome.wrote) return;
|
|
2963
3004
|
const graphPath = resolveGraphPath(this.opts.root);
|
|
2964
3005
|
const reloaded = loadGraphPreferIndex(this.opts.root, graphPath)?.graph ?? loadGraph(this.opts.root);
|
|
2965
|
-
if (reloaded)
|
|
3006
|
+
if (reloaded) {
|
|
3007
|
+
this.graph = reloaded;
|
|
3008
|
+
this.fileRoles = fileRolesFromParseCache(this.opts.root);
|
|
3009
|
+
this.refineAndPublishArchitecture();
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
/**
|
|
3013
|
+
* Graph / AST-role refine can land after the first `publishScore()`. The
|
|
3014
|
+
* Architecture panel only re-pulls on a score or scope change, so a silent
|
|
3015
|
+
* in-memory refine would leave the first session on the path-only snapshot.
|
|
3016
|
+
* Re-push `vibgrate/score` (no-op when the artifact is not ready yet).
|
|
3017
|
+
*/
|
|
3018
|
+
refineAndPublishArchitecture() {
|
|
3019
|
+
if (!this.artifact || !this.graph) return;
|
|
3020
|
+
refineArtifactWithGraph(this.artifact, this.graph, this.fileRoles);
|
|
3021
|
+
this.publishScore();
|
|
2966
3022
|
}
|
|
2967
3023
|
async onGraphQuery(params) {
|
|
2968
3024
|
const p = params;
|
|
@@ -3031,7 +3087,7 @@ var VibgrateLanguageServer = class {
|
|
|
3031
3087
|
const ecosystem = osvEcosystem(proj.type);
|
|
3032
3088
|
if (!ecosystem) continue;
|
|
3033
3089
|
const relManifest = manifestRelativePath(proj);
|
|
3034
|
-
const absManifest =
|
|
3090
|
+
const absManifest = path46.resolve(this.opts.root, relManifest);
|
|
3035
3091
|
let text = null;
|
|
3036
3092
|
try {
|
|
3037
3093
|
if (fs14.existsSync(absManifest)) text = fs14.readFileSync(absManifest, "utf8");
|
|
@@ -3114,21 +3170,13 @@ var VibgrateLanguageServer = class {
|
|
|
3114
3170
|
const arch = a.extended?.architecture;
|
|
3115
3171
|
if (!arch) return null;
|
|
3116
3172
|
return {
|
|
3117
|
-
|
|
3118
|
-
archetypeConfidence: arch.archetypeConfidence,
|
|
3119
|
-
layers: arch.layers,
|
|
3120
|
-
totalClassified: arch.totalClassified,
|
|
3121
|
-
unclassified: arch.unclassified
|
|
3173
|
+
...architectureWire(arch)
|
|
3122
3174
|
};
|
|
3123
3175
|
}
|
|
3124
3176
|
const project = projectByPath(a, p.path);
|
|
3125
3177
|
if (!project?.architecture) return null;
|
|
3126
3178
|
return {
|
|
3127
|
-
|
|
3128
|
-
archetypeConfidence: project.architecture.archetypeConfidence,
|
|
3129
|
-
layers: project.architecture.layers,
|
|
3130
|
-
totalClassified: project.architecture.totalClassified,
|
|
3131
|
-
unclassified: project.architecture.unclassified,
|
|
3179
|
+
...architectureWire(project.architecture),
|
|
3132
3180
|
manifestPath: manifestRelativePath(project),
|
|
3133
3181
|
lockfilePath: lockfileRelativePath(this.opts.root, project)
|
|
3134
3182
|
};
|
|
@@ -3141,7 +3189,7 @@ function uriToPath(uri) {
|
|
|
3141
3189
|
return uri;
|
|
3142
3190
|
}
|
|
3143
3191
|
}
|
|
3144
|
-
async function runCoreScanSilently(
|
|
3192
|
+
async function runCoreScanSilently(root3, opts, advanced, graph, fileRoles) {
|
|
3145
3193
|
const realLog = console.log.bind(console);
|
|
3146
3194
|
console.log = (...args) => {
|
|
3147
3195
|
if (args.length === 1 && typeof args[0] === "string" && looksLikeScanArtifactJson(args[0])) {
|
|
@@ -3150,11 +3198,34 @@ async function runCoreScanSilently(root2, opts, advanced) {
|
|
|
3150
3198
|
realLog(...args);
|
|
3151
3199
|
};
|
|
3152
3200
|
try {
|
|
3153
|
-
|
|
3201
|
+
const artifact = await runCoreScan(root3, opts, advanced);
|
|
3202
|
+
if (graph) refineArtifactWithGraph(artifact, graph, fileRoles);
|
|
3203
|
+
return artifact;
|
|
3154
3204
|
} finally {
|
|
3155
3205
|
console.log = realLog;
|
|
3156
3206
|
}
|
|
3157
3207
|
}
|
|
3208
|
+
function architectureGraphView(graph) {
|
|
3209
|
+
return {
|
|
3210
|
+
nodes: graph.nodes.map((n) => ({ id: n.id, file: n.file, kind: n.kind })),
|
|
3211
|
+
edges: graph.edges.map((e) => ({
|
|
3212
|
+
src: e.src,
|
|
3213
|
+
dst: e.dst,
|
|
3214
|
+
kind: e.kind,
|
|
3215
|
+
confidence: e.confidence,
|
|
3216
|
+
resolution: e.resolution
|
|
3217
|
+
}))
|
|
3218
|
+
};
|
|
3219
|
+
}
|
|
3220
|
+
function refineArtifactWithGraph(artifact, graph, fileRoles) {
|
|
3221
|
+
const hasArch = Boolean(artifact.extended?.architecture) || artifact.projects.some((p) => p.architecture);
|
|
3222
|
+
if (!hasArch) return;
|
|
3223
|
+
const already = artifact.extended?.architecture?.violations !== void 0 || artifact.projects.some((p) => p.architecture?.violations !== void 0);
|
|
3224
|
+
if (!already) refineArchitectureWithGraph(artifact, architectureGraphView(graph));
|
|
3225
|
+
if (fileRoles && fileRoles.length > 0) {
|
|
3226
|
+
refineArchitectureWithAstRoles(artifact, fileRoles);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3158
3229
|
function looksLikeScanArtifactJson(text) {
|
|
3159
3230
|
const t = text.trimStart();
|
|
3160
3231
|
if (!t.startsWith("{")) return false;
|
|
@@ -3183,7 +3254,7 @@ function pruneArtifactProjects(a) {
|
|
|
3183
3254
|
return { ...a, projects, findings };
|
|
3184
3255
|
}
|
|
3185
3256
|
function projectByPath(a, projectPath) {
|
|
3186
|
-
const norm = (p) =>
|
|
3257
|
+
const norm = (p) => path46.normalize((p === "" ? "." : p).replace(/\\/g, "/"));
|
|
3187
3258
|
const want = norm(projectPath);
|
|
3188
3259
|
for (const proj of a.projects ?? []) {
|
|
3189
3260
|
if (norm(proj.path) === want) return proj;
|
|
@@ -3221,13 +3292,13 @@ function buildProjectRefs(rootDir, projects) {
|
|
|
3221
3292
|
refs.sort((a, b) => a.path.localeCompare(b.path));
|
|
3222
3293
|
return refs.length > 0 ? refs : void 0;
|
|
3223
3294
|
}
|
|
3224
|
-
function projectForFile(
|
|
3225
|
-
const dir =
|
|
3295
|
+
function projectForFile(root3, a, filePath) {
|
|
3296
|
+
const dir = path46.dirname(path46.resolve(filePath));
|
|
3226
3297
|
let best;
|
|
3227
3298
|
let bestLen = -1;
|
|
3228
3299
|
for (const proj of a.projects ?? []) {
|
|
3229
|
-
const projDir =
|
|
3230
|
-
if ((dir === projDir || dir.startsWith(projDir +
|
|
3300
|
+
const projDir = path46.resolve(root3, proj.path);
|
|
3301
|
+
if ((dir === projDir || dir.startsWith(projDir + path46.sep)) && projDir.length > bestLen) {
|
|
3231
3302
|
best = proj;
|
|
3232
3303
|
bestLen = projDir.length;
|
|
3233
3304
|
}
|
|
@@ -3235,7 +3306,7 @@ function projectForFile(root2, a, filePath) {
|
|
|
3235
3306
|
return best;
|
|
3236
3307
|
}
|
|
3237
3308
|
function sameProject(location, projectPath) {
|
|
3238
|
-
return
|
|
3309
|
+
return path46.normalize(location) === path46.normalize(projectPath);
|
|
3239
3310
|
}
|
|
3240
3311
|
function hasReleaseDates(a) {
|
|
3241
3312
|
for (const proj of a.projects ?? []) {
|
|
@@ -3305,7 +3376,7 @@ function buildBreakdown(rootDir, projects) {
|
|
|
3305
3376
|
const relManifest = manifestRelativePath(p);
|
|
3306
3377
|
if (isPrunedWorkspacePath(p.path) || isPrunedWorkspacePath(relManifest)) continue;
|
|
3307
3378
|
const ecosystem = osvEcosystem(p.type);
|
|
3308
|
-
const absManifest =
|
|
3379
|
+
const absManifest = path46.resolve(rootDir, relManifest);
|
|
3309
3380
|
let text = null;
|
|
3310
3381
|
try {
|
|
3311
3382
|
if (fs14.existsSync(absManifest)) text = fs14.readFileSync(absManifest, "utf8");
|
|
@@ -3373,34 +3444,34 @@ function buildBreakdown(rootDir, projects) {
|
|
|
3373
3444
|
}
|
|
3374
3445
|
function manifestRelativePath(proj) {
|
|
3375
3446
|
const base = proj.path === "." || proj.path === "" ? "" : proj.path;
|
|
3376
|
-
const
|
|
3447
|
+
const join36 = (name) => base ? path46.join(base, name) : name;
|
|
3377
3448
|
switch (proj.type) {
|
|
3378
3449
|
case "node":
|
|
3379
3450
|
case "typescript":
|
|
3380
|
-
return
|
|
3451
|
+
return join36("package.json");
|
|
3381
3452
|
case "python":
|
|
3382
|
-
return
|
|
3453
|
+
return join36("pyproject.toml");
|
|
3383
3454
|
case "go":
|
|
3384
|
-
return
|
|
3455
|
+
return join36("go.mod");
|
|
3385
3456
|
case "rust":
|
|
3386
|
-
return
|
|
3457
|
+
return join36("Cargo.toml");
|
|
3387
3458
|
case "ruby":
|
|
3388
|
-
return
|
|
3459
|
+
return join36("Gemfile");
|
|
3389
3460
|
case "php":
|
|
3390
|
-
return
|
|
3461
|
+
return join36("composer.json");
|
|
3391
3462
|
case "java":
|
|
3392
3463
|
case "kotlin":
|
|
3393
3464
|
case "scala":
|
|
3394
3465
|
case "groovy":
|
|
3395
|
-
return
|
|
3466
|
+
return join36("pom.xml");
|
|
3396
3467
|
case "dotnet":
|
|
3397
|
-
return
|
|
3468
|
+
return join36(proj.name ? `${proj.name}.csproj` : "Directory.Build.props");
|
|
3398
3469
|
case "dart":
|
|
3399
|
-
return
|
|
3470
|
+
return join36("pubspec.yaml");
|
|
3400
3471
|
case "elixir":
|
|
3401
|
-
return
|
|
3472
|
+
return join36("mix.exs");
|
|
3402
3473
|
default:
|
|
3403
|
-
return
|
|
3474
|
+
return join36("package.json");
|
|
3404
3475
|
}
|
|
3405
3476
|
}
|
|
3406
3477
|
function lockfileRelativePath(rootDir, proj) {
|
|
@@ -3431,9 +3502,9 @@ function lockfileRelativePath(rootDir, proj) {
|
|
|
3431
3502
|
const dirsToTry = base ? [base, ""] : [""];
|
|
3432
3503
|
for (const dir of dirsToTry) {
|
|
3433
3504
|
for (const name of candidateNames) {
|
|
3434
|
-
const rel = dir ?
|
|
3505
|
+
const rel = dir ? path46.join(dir, name) : name;
|
|
3435
3506
|
try {
|
|
3436
|
-
if (fs14.existsSync(
|
|
3507
|
+
if (fs14.existsSync(path46.join(rootDir, rel))) return rel;
|
|
3437
3508
|
} catch {
|
|
3438
3509
|
}
|
|
3439
3510
|
}
|
|
@@ -3497,14 +3568,14 @@ function lookupImportFiles(map, pkg) {
|
|
|
3497
3568
|
const p = pkg.toLowerCase();
|
|
3498
3569
|
if (map.has(p)) return map.get(p);
|
|
3499
3570
|
if (!p.startsWith("@")) {
|
|
3500
|
-
const
|
|
3501
|
-
if (map.has(
|
|
3571
|
+
const root3 = p.split("/")[0];
|
|
3572
|
+
if (map.has(root3)) return map.get(root3);
|
|
3502
3573
|
}
|
|
3503
3574
|
return [];
|
|
3504
3575
|
}
|
|
3505
|
-
function firstPackageMentionLine(
|
|
3576
|
+
function firstPackageMentionLine(root3, relFile, pkg) {
|
|
3506
3577
|
try {
|
|
3507
|
-
const abs =
|
|
3578
|
+
const abs = path46.resolve(root3, relFile);
|
|
3508
3579
|
const stat2 = fs14.statSync(abs);
|
|
3509
3580
|
if (!stat2.isFile() || stat2.size > 256 * 1024) return null;
|
|
3510
3581
|
const text = fs14.readFileSync(abs, "utf8");
|
|
@@ -3730,9 +3801,9 @@ function registerLsp(program) {
|
|
|
3730
3801
|
false
|
|
3731
3802
|
).option("--no-graph", "skip the local Vibgrate Graph entirely: no background build, and graph queries report it as turned off").option("--no-semantic", "never use semantic search for graph queries (lexical only; the embedding model is not downloaded)").action(async function(opts) {
|
|
3732
3803
|
const global = readGlobal(this);
|
|
3733
|
-
const
|
|
3804
|
+
const root3 = rootOf(global);
|
|
3734
3805
|
startLanguageServer({
|
|
3735
|
-
root:
|
|
3806
|
+
root: root3,
|
|
3736
3807
|
// `--local` is the established air-gap flag across the CLI; honour it
|
|
3737
3808
|
// here too, so an offline editor session stays offline.
|
|
3738
3809
|
offline: global.local === true,
|
|
@@ -3751,9 +3822,9 @@ var HOOK_TIMEOUT_S = 10;
|
|
|
3751
3822
|
function hookCommand(vgBin) {
|
|
3752
3823
|
return `${vgBin} hook pre-tool-use`;
|
|
3753
3824
|
}
|
|
3754
|
-
function installClaudeHooks(
|
|
3755
|
-
const file =
|
|
3756
|
-
const rel =
|
|
3825
|
+
function installClaudeHooks(root3, vgBin = "vg") {
|
|
3826
|
+
const file = path46.join(root3, ".claude", "settings.json");
|
|
3827
|
+
const rel = path46.join(".claude", "settings.json");
|
|
3757
3828
|
let settings = {};
|
|
3758
3829
|
if (fs14.existsSync(file)) {
|
|
3759
3830
|
try {
|
|
@@ -3793,9 +3864,9 @@ function installClaudeHooks(root2, vgBin = "vg") {
|
|
|
3793
3864
|
writeSettings(file, settings);
|
|
3794
3865
|
return { file: rel, status: "written" };
|
|
3795
3866
|
}
|
|
3796
|
-
function uninstallClaudeHooks(
|
|
3797
|
-
const file =
|
|
3798
|
-
const rel =
|
|
3867
|
+
function uninstallClaudeHooks(root3) {
|
|
3868
|
+
const file = path46.join(root3, ".claude", "settings.json");
|
|
3869
|
+
const rel = path46.join(".claude", "settings.json");
|
|
3799
3870
|
if (!fs14.existsSync(file)) return { file: rel, status: "unchanged" };
|
|
3800
3871
|
let settings;
|
|
3801
3872
|
try {
|
|
@@ -3821,7 +3892,7 @@ function uninstallClaudeHooks(root2) {
|
|
|
3821
3892
|
return { file: rel, status: "written" };
|
|
3822
3893
|
}
|
|
3823
3894
|
function writeSettings(file, settings) {
|
|
3824
|
-
fs14.mkdirSync(
|
|
3895
|
+
fs14.mkdirSync(path46.dirname(file), { recursive: true });
|
|
3825
3896
|
fs14.writeFileSync(file, `${JSON.stringify(settings, null, 2)}
|
|
3826
3897
|
`);
|
|
3827
3898
|
}
|
|
@@ -3830,8 +3901,8 @@ function writeSettings(file, settings) {
|
|
|
3830
3901
|
function registerInstall(program) {
|
|
3831
3902
|
const install = program.command("install").description("add vg to your AI assistant(s): skill + MCP + advisory nudge").argument("[tools...]", `assistant ids: ${ASSISTANTS.map((a) => a.id).join(", ")}`).option("--all", "install for every supported assistant").option("--detect", "detect assistants in use (repo footprint, home config, PATH) and install for those; with --list, only report what was detected").option("--list", "show the support matrix and exit").option("--no-hook", "skip the advisory nudge").option("--hooks", "also wire the PreToolUse enrichment hook (Claude Code): Grep/Glob calls get graph context via `vg hook pre-tool-use` (project .claude/settings.json; opt-in)").action(function(tools, opts) {
|
|
3832
3903
|
const global = readGlobal(this);
|
|
3833
|
-
const
|
|
3834
|
-
const detected = opts.detect ? detectAssistants(
|
|
3904
|
+
const root3 = rootOf(global);
|
|
3905
|
+
const detected = opts.detect ? detectAssistants(root3) : [];
|
|
3835
3906
|
if (opts.list) {
|
|
3836
3907
|
if (opts.detect) {
|
|
3837
3908
|
if (global.json) {
|
|
@@ -3855,14 +3926,14 @@ function registerInstall(program) {
|
|
|
3855
3926
|
mcp: !!a.mcp,
|
|
3856
3927
|
skill: !!a.skill,
|
|
3857
3928
|
nudge: !!a.nudge,
|
|
3858
|
-
installed: isAssistantInstalled(a,
|
|
3929
|
+
installed: isAssistantInstalled(a, root3)
|
|
3859
3930
|
}))
|
|
3860
3931
|
);
|
|
3861
3932
|
} else {
|
|
3862
3933
|
info(`${c.cyan("vg install")} \xB7 supported assistants`);
|
|
3863
3934
|
const pad4 = Math.max(...ASSISTANTS.map((a) => a.id.length)) + 2;
|
|
3864
3935
|
for (const a of ASSISTANTS) {
|
|
3865
|
-
const on = isAssistantInstalled(a,
|
|
3936
|
+
const on = isAssistantInstalled(a, root3) ? c.green("on") : c.dim("off");
|
|
3866
3937
|
info(
|
|
3867
3938
|
` ${c.bold(a.id.padEnd(pad4))} ${a.label} ${on} ${c.dim(`mcp:${a.mcp ? "\u2713" : "\u2014"} skill:${a.skill ? "\u2713" : "\u2014"} nudge:${a.nudge ? "\u2713" : "\u2014"}`)}`
|
|
3868
3939
|
);
|
|
@@ -3873,20 +3944,20 @@ function registerInstall(program) {
|
|
|
3873
3944
|
const targets = opts.all ? ASSISTANTS : opts.detect ? detected.map((d) => d.assistant) : tools.map(resolve4);
|
|
3874
3945
|
if (targets.length === 0) {
|
|
3875
3946
|
if (opts.detect) {
|
|
3876
|
-
if (global.json) json({ root:
|
|
3947
|
+
if (global.json) json({ root: root3, detected: [], results: [] });
|
|
3877
3948
|
else info(`${c.cyan("vg install")} \xB7 no AI assistants detected here \u2014 name one (e.g. \`vg install claude\`) or use --all`);
|
|
3878
3949
|
return;
|
|
3879
3950
|
}
|
|
3880
3951
|
throw usageError("name an assistant (e.g. `vg install claude`) or use --all / --detect / --list");
|
|
3881
3952
|
}
|
|
3882
|
-
const graph = loadGraph(
|
|
3953
|
+
const graph = loadGraph(root3, global.graph);
|
|
3883
3954
|
const fileCount = graph ? graph.nodes.filter((n) => n.kind === "file").length : 0;
|
|
3884
3955
|
const smallRepo = graph !== null && fileCount > 0 && fileCount < SMALL_REPO_FILES;
|
|
3885
3956
|
const launch = detectServeLaunch();
|
|
3886
|
-
const results = targets.map((a) => ({ id: a.id, ...installAssistant(a, { root:
|
|
3887
|
-
const navConfig = writeNavigationConfig(
|
|
3957
|
+
const results = targets.map((a) => ({ id: a.id, ...installAssistant(a, { root: root3, hook: opts.hook, smallRepo, launch }) }));
|
|
3958
|
+
const navConfig = writeNavigationConfig(root3);
|
|
3888
3959
|
if (global.json) {
|
|
3889
|
-
json({ root:
|
|
3960
|
+
json({ root: root3, smallRepo, navConfig, launch: { command: launch.command, args: launch.args, note: launch.note ?? null }, results });
|
|
3890
3961
|
return;
|
|
3891
3962
|
}
|
|
3892
3963
|
for (const r of results) {
|
|
@@ -3894,7 +3965,7 @@ function registerInstall(program) {
|
|
|
3894
3965
|
}
|
|
3895
3966
|
if (opts.hooks && targets.some((a) => a.id === "claude")) {
|
|
3896
3967
|
const vgBin = launch.command === "npx" ? null : launch.command;
|
|
3897
|
-
const hooksResult = vgBin ? installClaudeHooks(
|
|
3968
|
+
const hooksResult = vgBin ? installClaudeHooks(root3, vgBin) : { file: ".claude/settings.json", status: "skipped", note: "vg is not installed on PATH \u2014 hooks need a fast binary (npm i -g @vibgrate/cli), rerun with --hooks after" };
|
|
3898
3969
|
if (hooksResult.status === "written") info(`${c.green("\u2714")} ${c.bold("hooks")} \u2014 wrote PreToolUse enrichment into ${hooksResult.file}`);
|
|
3899
3970
|
else if (hooksResult.note) info(`${c.yellow("!")} hooks \u2014 ${hooksResult.note}`);
|
|
3900
3971
|
}
|
|
@@ -3906,14 +3977,14 @@ function registerInstall(program) {
|
|
|
3906
3977
|
applyGlobalOptions(install);
|
|
3907
3978
|
const uninstall = program.command("uninstall").description("remove vg from an AI assistant").argument("<tools...>", "assistant ids").option("--purge", "also delete the skill file").action(function(tools, opts) {
|
|
3908
3979
|
const global = readGlobal(this);
|
|
3909
|
-
const
|
|
3910
|
-
const results = tools.map(resolve4).map((a) => ({ id: a.id, removed: uninstallAssistant(a,
|
|
3980
|
+
const root3 = rootOf(global);
|
|
3981
|
+
const results = tools.map(resolve4).map((a) => ({ id: a.id, removed: uninstallAssistant(a, root3, !!opts.purge) }));
|
|
3911
3982
|
if (tools.map(resolve4).some((a) => a.id === "claude")) {
|
|
3912
|
-
const hooksResult = uninstallClaudeHooks(
|
|
3983
|
+
const hooksResult = uninstallClaudeHooks(root3);
|
|
3913
3984
|
if (hooksResult.status === "written") results.find((r) => r.id === "claude")?.removed.push(`${hooksResult.file} (vg hook entries)`);
|
|
3914
3985
|
}
|
|
3915
3986
|
if (global.json) {
|
|
3916
|
-
json({ root:
|
|
3987
|
+
json({ root: root3, results });
|
|
3917
3988
|
return;
|
|
3918
3989
|
}
|
|
3919
3990
|
for (const r of results) {
|
|
@@ -3941,11 +4012,11 @@ function registryBase(opts) {
|
|
|
3941
4012
|
return (process.env.VIBGRATE_MODULE_REGISTRY || opts.registry || DEFAULT_REGISTRY).replace(/\/+$/, "");
|
|
3942
4013
|
}
|
|
3943
4014
|
function modulesBaseDir() {
|
|
3944
|
-
const base = process.env.XDG_CACHE_HOME ||
|
|
3945
|
-
return
|
|
4015
|
+
const base = process.env.XDG_CACHE_HOME || path46.join(os.homedir(), ".cache");
|
|
4016
|
+
return path46.join(base, "vibgrate", "modules");
|
|
3946
4017
|
}
|
|
3947
4018
|
function stateFile() {
|
|
3948
|
-
return
|
|
4019
|
+
return path46.join(modulesBaseDir(), "consent.json");
|
|
3949
4020
|
}
|
|
3950
4021
|
function readConsent() {
|
|
3951
4022
|
try {
|
|
@@ -3956,7 +4027,7 @@ function readConsent() {
|
|
|
3956
4027
|
}
|
|
3957
4028
|
function writeConsent(state) {
|
|
3958
4029
|
try {
|
|
3959
|
-
fs14.mkdirSync(
|
|
4030
|
+
fs14.mkdirSync(path46.dirname(stateFile()), { recursive: true });
|
|
3960
4031
|
fs14.writeFileSync(stateFile(), JSON.stringify(state));
|
|
3961
4032
|
} catch {
|
|
3962
4033
|
}
|
|
@@ -3967,8 +4038,8 @@ function kernelDisabled() {
|
|
|
3967
4038
|
}
|
|
3968
4039
|
function moduleInstalledAt(dir) {
|
|
3969
4040
|
try {
|
|
3970
|
-
const meta = JSON.parse(fs14.readFileSync(
|
|
3971
|
-
if (fs14.existsSync(
|
|
4041
|
+
const meta = JSON.parse(fs14.readFileSync(path46.join(dir, ".module.json"), "utf8"));
|
|
4042
|
+
if (fs14.existsSync(path46.join(dir, "index.js"))) {
|
|
3972
4043
|
return { installed: true, version: meta.version };
|
|
3973
4044
|
}
|
|
3974
4045
|
} catch {
|
|
@@ -4016,7 +4087,7 @@ function untar(data) {
|
|
|
4016
4087
|
if (type !== "0" && type !== "\0") continue;
|
|
4017
4088
|
const name = pendingName ?? (prefix ? `${prefix}/${rawName}` : rawName);
|
|
4018
4089
|
pendingName = null;
|
|
4019
|
-
if (name.split("/").includes("..") ||
|
|
4090
|
+
if (name.split("/").includes("..") || path46.isAbsolute(name)) {
|
|
4020
4091
|
throw new Error(`unsafe path in tarball: ${name}`);
|
|
4021
4092
|
}
|
|
4022
4093
|
out2.push({ name, body: Buffer.from(body) });
|
|
@@ -4050,17 +4121,17 @@ async function installModule(mod, opts = {}) {
|
|
|
4050
4121
|
if (e.name.startsWith("package/dist/")) rel = e.name.slice("package/dist/".length);
|
|
4051
4122
|
else if (e.name === "package/LICENSE" || e.name === "package/README.md") rel = e.name.slice("package/".length);
|
|
4052
4123
|
if (!rel) continue;
|
|
4053
|
-
const target =
|
|
4054
|
-
if (!target.startsWith(dir +
|
|
4055
|
-
fs14.mkdirSync(
|
|
4124
|
+
const target = path46.join(dir, rel);
|
|
4125
|
+
if (!target.startsWith(dir + path46.sep) && target !== dir) throw new Error(`unsafe extract path: ${rel}`);
|
|
4126
|
+
fs14.mkdirSync(path46.dirname(target), { recursive: true });
|
|
4056
4127
|
fs14.writeFileSync(target, e.body);
|
|
4057
4128
|
wrote += 1;
|
|
4058
4129
|
}
|
|
4059
|
-
if (!wrote || !fs14.existsSync(
|
|
4130
|
+
if (!wrote || !fs14.existsSync(path46.join(dir, "index.js"))) {
|
|
4060
4131
|
fs14.rmSync(dir, { recursive: true, force: true });
|
|
4061
4132
|
return { status: "unavailable", detail: "package layout not recognized (no dist/index.js)" };
|
|
4062
4133
|
}
|
|
4063
|
-
fs14.writeFileSync(
|
|
4134
|
+
fs14.writeFileSync(path46.join(dir, ".module.json"), JSON.stringify({ name: mod.npmName, version }));
|
|
4064
4135
|
mod.onChanged?.();
|
|
4065
4136
|
return { status: "installed", version };
|
|
4066
4137
|
} catch (e) {
|
|
@@ -4106,19 +4177,19 @@ function disabled() {
|
|
|
4106
4177
|
return v === "1" || v === "true";
|
|
4107
4178
|
}
|
|
4108
4179
|
function hcsModuleDir() {
|
|
4109
|
-
return
|
|
4180
|
+
return path46.join(modulesBaseDir(), "hcs");
|
|
4110
4181
|
}
|
|
4111
4182
|
function candidatePaths() {
|
|
4112
4183
|
const custom = process.env.VIBGRATE_HCS_PATH;
|
|
4113
4184
|
if (custom) {
|
|
4114
|
-
const p =
|
|
4185
|
+
const p = path46.resolve(custom);
|
|
4115
4186
|
try {
|
|
4116
|
-
if (fs14.statSync(p).isDirectory()) return [
|
|
4187
|
+
if (fs14.statSync(p).isDirectory()) return [path46.join(p, "index.js")];
|
|
4117
4188
|
} catch {
|
|
4118
4189
|
}
|
|
4119
4190
|
return [p];
|
|
4120
4191
|
}
|
|
4121
|
-
return [
|
|
4192
|
+
return [path46.join(hcsModuleDir(), "index.js")];
|
|
4122
4193
|
}
|
|
4123
4194
|
var cached;
|
|
4124
4195
|
function resetHcsEngineCache() {
|
|
@@ -4268,11 +4339,11 @@ function requireModule(name) {
|
|
|
4268
4339
|
return mod;
|
|
4269
4340
|
}
|
|
4270
4341
|
function confirm(question) {
|
|
4271
|
-
return new Promise((
|
|
4342
|
+
return new Promise((resolve23) => {
|
|
4272
4343
|
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
4273
4344
|
rl.question(question, (answer) => {
|
|
4274
4345
|
rl.close();
|
|
4275
|
-
|
|
4346
|
+
resolve23(answer.trim() === "" || /^y(es)?$/i.test(answer.trim()));
|
|
4276
4347
|
});
|
|
4277
4348
|
});
|
|
4278
4349
|
}
|
|
@@ -4281,7 +4352,7 @@ var MIN_PATTERN_CHARS = 3;
|
|
|
4281
4352
|
function patternToQuery(pattern) {
|
|
4282
4353
|
return pattern.replace(/\\[dwsbDWSB]/g, " ").replace(/[\\^$.*+?()[\]{}|]/g, " ").replace(/\s+/g, " ").trim();
|
|
4283
4354
|
}
|
|
4284
|
-
function hookContext(payloadText,
|
|
4355
|
+
function hookContext(payloadText, root3, graphOverride) {
|
|
4285
4356
|
let payload;
|
|
4286
4357
|
try {
|
|
4287
4358
|
payload = JSON.parse(payloadText);
|
|
@@ -4292,10 +4363,10 @@ function hookContext(payloadText, root2, graphOverride) {
|
|
|
4292
4363
|
const raw = payload.tool_input?.pattern ?? payload.tool_input?.query ?? "";
|
|
4293
4364
|
const query = patternToQuery(String(raw));
|
|
4294
4365
|
if (query.length < MIN_PATTERN_CHARS) return null;
|
|
4295
|
-
const graphPath = resolveGraphPath(
|
|
4366
|
+
const graphPath = resolveGraphPath(root3, graphOverride);
|
|
4296
4367
|
let graph;
|
|
4297
4368
|
try {
|
|
4298
|
-
const preferred = loadGraphPreferIndex(
|
|
4369
|
+
const preferred = loadGraphPreferIndex(root3, graphPath);
|
|
4299
4370
|
graph = preferred ? preferred.graph : parseGraph(fs14.readFileSync(graphPath, "utf8"));
|
|
4300
4371
|
} catch {
|
|
4301
4372
|
return null;
|
|
@@ -4316,10 +4387,10 @@ function registerHook(program) {
|
|
|
4316
4387
|
const cmd = program.command("hook", { hidden: true }).description("assistant-host hook endpoints (wired by `vg install --hooks`)");
|
|
4317
4388
|
const pre = cmd.command("pre-tool-use").description("PreToolUse enrichment: annotate Grep/Glob calls with graph context (stdin: host hook payload)").action(async function() {
|
|
4318
4389
|
const global = readGlobal(this);
|
|
4319
|
-
const
|
|
4390
|
+
const root3 = rootOf(global);
|
|
4320
4391
|
try {
|
|
4321
4392
|
const stdin = fs14.readFileSync(0, "utf8");
|
|
4322
|
-
const context = hookContext(stdin,
|
|
4393
|
+
const context = hookContext(stdin, root3, global.graph);
|
|
4323
4394
|
if (context) {
|
|
4324
4395
|
process.stdout.write(
|
|
4325
4396
|
`${JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: context } })}
|
|
@@ -4337,20 +4408,20 @@ var HOOK_END = "# vg:end";
|
|
|
4337
4408
|
function registerShare(program) {
|
|
4338
4409
|
const cmd = program.command("share").description("make the map committable + auto-updating for your team (git)").option("--undo", "reverse what `vg share` installed").option("--reports", "also commit graph.html / GRAPH_REPORT.md (default: gitignored)").action(function(opts) {
|
|
4339
4410
|
const global = readGlobal(this);
|
|
4340
|
-
const
|
|
4341
|
-
const gitDir =
|
|
4411
|
+
const root3 = rootOf(global);
|
|
4412
|
+
const gitDir = path46.join(root3, ".git");
|
|
4342
4413
|
if (!fs14.existsSync(gitDir)) {
|
|
4343
4414
|
throw new CliError("not a git repository \u2014 run `vg share` inside a git repo", ExitCode.USAGE_ERROR);
|
|
4344
4415
|
}
|
|
4345
4416
|
if (opts.undo) {
|
|
4346
|
-
undo(
|
|
4417
|
+
undo(root3);
|
|
4347
4418
|
info(`${c.cyan("vg share")} ${c.dim("--undo")} \xB7 removed hooks, merge driver, and attributes`);
|
|
4348
4419
|
return;
|
|
4349
4420
|
}
|
|
4350
|
-
writeVibgrateGitignore(
|
|
4351
|
-
writeGitAttributes(
|
|
4352
|
-
configureMergeDriver(
|
|
4353
|
-
installPreCommitHook(
|
|
4421
|
+
writeVibgrateGitignore(root3, !!opts.reports);
|
|
4422
|
+
writeGitAttributes(root3);
|
|
4423
|
+
configureMergeDriver(root3);
|
|
4424
|
+
installPreCommitHook(root3);
|
|
4354
4425
|
info(`${c.cyan("vg share")} \xB7 the map is now committable + auto-updating`);
|
|
4355
4426
|
info(` ${c.green("\u2714")} .vibgrate/.gitignore (cache + volatile artifacts ignored, graph.json committed)`);
|
|
4356
4427
|
info(` ${c.green("\u2714")} pre-commit hook (rebuilds + stages graph.json only when it changed)`);
|
|
@@ -4359,16 +4430,16 @@ function registerShare(program) {
|
|
|
4359
4430
|
});
|
|
4360
4431
|
applyGlobalOptions(cmd);
|
|
4361
4432
|
}
|
|
4362
|
-
function writeVibgrateGitignore(
|
|
4363
|
-
const dir =
|
|
4433
|
+
function writeVibgrateGitignore(root3, reports) {
|
|
4434
|
+
const dir = path46.join(root3, ".vibgrate");
|
|
4364
4435
|
fs14.mkdirSync(dir, { recursive: true });
|
|
4365
4436
|
const lines = ["# Managed by `vg share` \u2014 keep graph.json committed, ignore the rest", "cache/", "facts.jsonl", "graph.snap"];
|
|
4366
4437
|
if (!reports) lines.push("graph.html", "GRAPH_REPORT.md");
|
|
4367
|
-
fs14.writeFileSync(
|
|
4438
|
+
fs14.writeFileSync(path46.join(dir, ".gitignore"), `${lines.join("\n")}
|
|
4368
4439
|
`);
|
|
4369
4440
|
}
|
|
4370
|
-
function writeGitAttributes(
|
|
4371
|
-
const file =
|
|
4441
|
+
function writeGitAttributes(root3) {
|
|
4442
|
+
const file = path46.join(root3, ".gitattributes");
|
|
4372
4443
|
const line = ".vibgrate/graph.json merge=vg";
|
|
4373
4444
|
let content = fs14.existsSync(file) ? fs14.readFileSync(file, "utf8") : "";
|
|
4374
4445
|
if (!content.split("\n").some((l) => l.trim() === line)) {
|
|
@@ -4379,14 +4450,14 @@ ${line}
|
|
|
4379
4450
|
fs14.writeFileSync(file, content);
|
|
4380
4451
|
}
|
|
4381
4452
|
}
|
|
4382
|
-
function configureMergeDriver(
|
|
4383
|
-
git(
|
|
4384
|
-
git(
|
|
4453
|
+
function configureMergeDriver(root3) {
|
|
4454
|
+
git(root3, ["config", "merge.vg.name", "vg deterministic graph rebuild"]);
|
|
4455
|
+
git(root3, ["config", "merge.vg.driver", "vg build >/dev/null 2>&1; cp .vibgrate/graph.json %A"]);
|
|
4385
4456
|
}
|
|
4386
|
-
function installPreCommitHook(
|
|
4387
|
-
const hooksDir = hookDir(
|
|
4457
|
+
function installPreCommitHook(root3) {
|
|
4458
|
+
const hooksDir = hookDir(root3);
|
|
4388
4459
|
fs14.mkdirSync(hooksDir, { recursive: true });
|
|
4389
|
-
const file =
|
|
4460
|
+
const file = path46.join(hooksDir, "pre-commit");
|
|
4390
4461
|
const block = [
|
|
4391
4462
|
HOOK_BEGIN,
|
|
4392
4463
|
"if command -v vg >/dev/null 2>&1; then",
|
|
@@ -4407,34 +4478,34 @@ ${content}`;
|
|
|
4407
4478
|
fs14.writeFileSync(file, content);
|
|
4408
4479
|
fs14.chmodSync(file, 493);
|
|
4409
4480
|
}
|
|
4410
|
-
function undo(
|
|
4411
|
-
const hookFile =
|
|
4481
|
+
function undo(root3) {
|
|
4482
|
+
const hookFile = path46.join(hookDir(root3), "pre-commit");
|
|
4412
4483
|
if (fs14.existsSync(hookFile)) {
|
|
4413
4484
|
const re = new RegExp(`\\n*${escapeRe(HOOK_BEGIN)}[\\s\\S]*?${escapeRe(HOOK_END)}\\n*`);
|
|
4414
4485
|
const next2 = fs14.readFileSync(hookFile, "utf8").replace(re, "\n");
|
|
4415
4486
|
if (next2.trim() === "#!/bin/sh") fs14.rmSync(hookFile);
|
|
4416
4487
|
else fs14.writeFileSync(hookFile, next2);
|
|
4417
4488
|
}
|
|
4418
|
-
const attr =
|
|
4489
|
+
const attr = path46.join(root3, ".gitattributes");
|
|
4419
4490
|
if (fs14.existsSync(attr)) {
|
|
4420
4491
|
const next2 = fs14.readFileSync(attr, "utf8").split("\n").filter((l) => l.trim() !== ".vibgrate/graph.json merge=vg").join("\n");
|
|
4421
4492
|
fs14.writeFileSync(attr, next2);
|
|
4422
4493
|
}
|
|
4423
4494
|
try {
|
|
4424
|
-
git(
|
|
4495
|
+
git(root3, ["config", "--remove-section", "merge.vg"]);
|
|
4425
4496
|
} catch {
|
|
4426
4497
|
}
|
|
4427
4498
|
}
|
|
4428
|
-
function hookDir(
|
|
4499
|
+
function hookDir(root3) {
|
|
4429
4500
|
try {
|
|
4430
|
-
const configured = git(
|
|
4431
|
-
if (configured) return
|
|
4501
|
+
const configured = git(root3, ["config", "--get", "core.hooksPath"]).trim();
|
|
4502
|
+
if (configured) return path46.resolve(root3, configured);
|
|
4432
4503
|
} catch {
|
|
4433
4504
|
}
|
|
4434
|
-
return
|
|
4505
|
+
return path46.join(root3, ".git", "hooks");
|
|
4435
4506
|
}
|
|
4436
|
-
function git(
|
|
4437
|
-
return execFileSync("git", args, { cwd:
|
|
4507
|
+
function git(root3, args) {
|
|
4508
|
+
return execFileSync("git", args, { cwd: root3, encoding: "utf8" });
|
|
4438
4509
|
}
|
|
4439
4510
|
function escapeRe(s) {
|
|
4440
4511
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -4443,9 +4514,9 @@ var DEFAULT_DEBOUNCE_MS = 400;
|
|
|
4443
4514
|
function registerWatch(program) {
|
|
4444
4515
|
const cmd = program.command("watch").description("rebuild the code map when files change (debounced)").argument("[paths...]", "folders to watch (default: current folder)").option("--debounce <ms>", "settle time after a change before rebuild", String(DEFAULT_DEBOUNCE_MS)).option("--no-html", "do not write graph.html on each rebuild").option("--no-report", "do not write GRAPH_REPORT.md on each rebuild").option("--fast", "skip precise tsc resolve on rebuilds (heuristic only)").action(async function(paths, opts) {
|
|
4445
4516
|
const global = readGlobal(this);
|
|
4446
|
-
const
|
|
4517
|
+
const root3 = rootOf(global);
|
|
4447
4518
|
const debounceMs = Math.max(50, Number(opts.debounce) || DEFAULT_DEBOUNCE_MS);
|
|
4448
|
-
const watchRoots = (paths.length ? paths : ["."]).map((p) =>
|
|
4519
|
+
const watchRoots = (paths.length ? paths : ["."]).map((p) => path46.resolve(root3, p));
|
|
4449
4520
|
for (const w of watchRoots) {
|
|
4450
4521
|
if (!fs14.existsSync(w)) {
|
|
4451
4522
|
throw new CliError(`watch path not found: ${w}`, ExitCode.USAGE_ERROR);
|
|
@@ -4463,18 +4534,18 @@ function registerWatch(program) {
|
|
|
4463
4534
|
try {
|
|
4464
4535
|
info(c.dim(`vg watch \xB7 rebuild (${reason})`));
|
|
4465
4536
|
const result = await buildGraph({
|
|
4466
|
-
root:
|
|
4537
|
+
root: root3,
|
|
4467
4538
|
paths: paths.length ? paths : void 0,
|
|
4468
4539
|
fast: opts.fast === true,
|
|
4469
4540
|
noGround: false
|
|
4470
4541
|
});
|
|
4471
4542
|
writeArtifacts(result.graph, {
|
|
4472
|
-
root:
|
|
4543
|
+
root: root3,
|
|
4473
4544
|
html: opts.html !== false,
|
|
4474
4545
|
report: opts.report !== false,
|
|
4475
4546
|
graphPath: global.graph
|
|
4476
4547
|
});
|
|
4477
|
-
writeSnapshot(
|
|
4548
|
+
writeSnapshot(root3, result.graph.provenance.corpusHash, result.fileStats);
|
|
4478
4549
|
const { counts } = result.graph.meta;
|
|
4479
4550
|
info(
|
|
4480
4551
|
`${c.cyan("vg watch")} \xB7 ${counts.nodes} nodes \xB7 ${counts.edges} edges \xB7 ${(result.timing.totalMs / 1e3).toFixed(2)}s` + (result.statHits ? c.dim(` \xB7 ${result.statHits} mtime hits`) : "")
|
|
@@ -4513,12 +4584,12 @@ function registerWatch(program) {
|
|
|
4513
4584
|
throw new CliError(`cannot watch ${w}: ${err.message}`, ExitCode.ERROR);
|
|
4514
4585
|
}
|
|
4515
4586
|
}
|
|
4516
|
-
info(c.dim(`vg watch \xB7 watching ${watchRoots.map((p) =>
|
|
4517
|
-
await new Promise((
|
|
4587
|
+
info(c.dim(`vg watch \xB7 watching ${watchRoots.map((p) => path46.relative(root3, p) || ".").join(", ")} (debounce ${debounceMs}ms) \xB7 Ctrl+C to stop`));
|
|
4588
|
+
await new Promise((resolve23) => {
|
|
4518
4589
|
const stop = () => {
|
|
4519
4590
|
for (const w of watchers) w.close();
|
|
4520
4591
|
if (timer) clearTimeout(timer);
|
|
4521
|
-
|
|
4592
|
+
resolve23();
|
|
4522
4593
|
};
|
|
4523
4594
|
process.once("SIGINT", stop);
|
|
4524
4595
|
process.once("SIGTERM", stop);
|
|
@@ -4529,7 +4600,7 @@ function registerWatch(program) {
|
|
|
4529
4600
|
function registerTests(program) {
|
|
4530
4601
|
const cmd = program.command("tests").description("which tests cover a node (call/coverage linkage)").argument("<name>", "node to inspect").option("--missing", "show untested nodes nearby instead").option("--run", "print the command to run exactly these tests").option("--exec", "run that command").option("--pick <n>", "pick the nth candidate when ambiguous").action(function(name, opts) {
|
|
4531
4602
|
const global = readGlobal(this);
|
|
4532
|
-
const { root:
|
|
4603
|
+
const { root: root3, graph } = requireGraph(global);
|
|
4533
4604
|
const { node: node2, candidates } = resolveOne(graph, name, opts.pick ? Number(opts.pick) : void 0);
|
|
4534
4605
|
if (!node2) throw ambiguityError(`"${name}" ${candidates.length ? "is ambiguous" : "not found"}`, candidates);
|
|
4535
4606
|
if (opts.missing) {
|
|
@@ -4549,7 +4620,7 @@ function registerTests(program) {
|
|
|
4549
4620
|
const command = runner.command(testFiles);
|
|
4550
4621
|
if (opts.exec) {
|
|
4551
4622
|
info(c.dim(`$ ${command}`));
|
|
4552
|
-
execSync(command, { cwd:
|
|
4623
|
+
execSync(command, { cwd: root3, stdio: "inherit" });
|
|
4553
4624
|
return;
|
|
4554
4625
|
}
|
|
4555
4626
|
if (global.json) json({ runner: runner.name, command, testFiles });
|
|
@@ -4665,16 +4736,16 @@ function registerGuide(program) {
|
|
|
4665
4736
|
function registerWhy(program) {
|
|
4666
4737
|
const cmd = program.command("why").description("who introduced a dependency (and any open vulnerabilities), from git history").argument("<package>", "package name to explain").action(async function(pkg) {
|
|
4667
4738
|
const global = readGlobal(this);
|
|
4668
|
-
const
|
|
4669
|
-
if (!await gitHistoryAvailable(
|
|
4739
|
+
const root3 = rootOf(global);
|
|
4740
|
+
if (!await gitHistoryAvailable(root3)) {
|
|
4670
4741
|
throw new CliError(
|
|
4671
4742
|
"git history is required for `vg why` (not a git repository, or git is unavailable)",
|
|
4672
4743
|
ExitCode.USAGE_ERROR
|
|
4673
4744
|
);
|
|
4674
4745
|
}
|
|
4675
|
-
const timelines = await buildVersionTimelines(
|
|
4746
|
+
const timelines = await buildVersionTimelines(root3);
|
|
4676
4747
|
const pt = timelines ? findPackageAnyEcosystem(timelines, pkg) ?? null : null;
|
|
4677
|
-
const artifact = readScanArtifact(
|
|
4748
|
+
const artifact = readScanArtifact(root3);
|
|
4678
4749
|
const vulnPkg = artifact?.extended?.vulnerabilities?.packages.find((p) => p.package === pkg) ?? null;
|
|
4679
4750
|
if (global.json) {
|
|
4680
4751
|
json({ package: pkg, history: pt?.changes ?? [], vulnerabilities: vulnPkg?.advisories ?? [] });
|
|
@@ -4743,8 +4814,8 @@ function registerBisect(program) {
|
|
|
4743
4814
|
const cmd = program.command("bisect").description("pinpoint the commit where a dependency crossed a version line, from git history").argument("<package>", "package name to bisect").argument("<constraint>", 'target version or semver range (a bare version means ">=" that version)').option("--assert", "exit non-zero when the current version does not satisfy the constraint (CI gate)").action(async function(pkg, constraintArg) {
|
|
4744
4815
|
const global = readGlobal(this);
|
|
4745
4816
|
const opts = this.opts();
|
|
4746
|
-
const
|
|
4747
|
-
if (!await gitHistoryAvailable(
|
|
4817
|
+
const root3 = rootOf(global);
|
|
4818
|
+
if (!await gitHistoryAvailable(root3)) {
|
|
4748
4819
|
throw new CliError(
|
|
4749
4820
|
"git history is required for `vg bisect` (not a git repository, or git is unavailable)",
|
|
4750
4821
|
ExitCode.USAGE_ERROR
|
|
@@ -4757,7 +4828,7 @@ function registerBisect(program) {
|
|
|
4757
4828
|
ExitCode.USAGE_ERROR
|
|
4758
4829
|
);
|
|
4759
4830
|
}
|
|
4760
|
-
const timelines = await buildVersionTimelines(
|
|
4831
|
+
const timelines = await buildVersionTimelines(root3);
|
|
4761
4832
|
const pt = timelines ? findPackageAnyEcosystem(timelines, pkg) ?? null : null;
|
|
4762
4833
|
if (!pt || pt.changes.length === 0) {
|
|
4763
4834
|
if (global.json) {
|
|
@@ -4844,9 +4915,9 @@ function normalizeStandards(raw) {
|
|
|
4844
4915
|
}
|
|
4845
4916
|
return { banned };
|
|
4846
4917
|
}
|
|
4847
|
-
function loadStandards(
|
|
4918
|
+
function loadStandards(root3) {
|
|
4848
4919
|
for (const rel of STANDARDS_FILES) {
|
|
4849
|
-
const p =
|
|
4920
|
+
const p = path46__default.join(root3, rel);
|
|
4850
4921
|
let text;
|
|
4851
4922
|
try {
|
|
4852
4923
|
text = fs14__default.readFileSync(p, "utf8");
|
|
@@ -4874,18 +4945,18 @@ function checkStandards(policy, records) {
|
|
|
4874
4945
|
function registerDrift(program) {
|
|
4875
4946
|
const cmd = program.command("drift").description("what is outdated across dependencies (offline; --online for currency)").option("--online", "query the npm registry for latest versions (opt-in network)").option("--fail-on <level>", "exit 2 on violations \u2014 major|minor (version drift) or standards (enterprise policy)").action(async function(opts) {
|
|
4876
4947
|
const global = readGlobal(this);
|
|
4877
|
-
const
|
|
4948
|
+
const root3 = rootOf(global);
|
|
4878
4949
|
if (opts.online && global.local) {
|
|
4879
4950
|
throw new CliError("--online conflicts with --local (no network in local mode)", ExitCode.USAGE_ERROR);
|
|
4880
4951
|
}
|
|
4881
4952
|
if (opts.failOn && !["major", "minor", "standards"].includes(opts.failOn)) {
|
|
4882
4953
|
throw new CliError(`--fail-on must be one of: major, minor, standards (got "${opts.failOn}")`, ExitCode.USAGE_ERROR);
|
|
4883
4954
|
}
|
|
4884
|
-
const inv = inventory(
|
|
4955
|
+
const inv = inventory(root3);
|
|
4885
4956
|
if (opts.online && !global.local) await enrichOnline(inv.records);
|
|
4886
4957
|
let standards = null;
|
|
4887
4958
|
if (opts.failOn === "standards") {
|
|
4888
|
-
const loaded = loadStandards(
|
|
4959
|
+
const loaded = loadStandards(root3);
|
|
4889
4960
|
if (loaded.path && !loaded.policy) {
|
|
4890
4961
|
throw new CliError(`standards policy at ${loaded.path} is not valid JSON`, ExitCode.USAGE_ERROR);
|
|
4891
4962
|
}
|
|
@@ -5567,8 +5638,8 @@ async function runHostBench(options = {}) {
|
|
|
5567
5638
|
}
|
|
5568
5639
|
async function runArmSimulate(input) {
|
|
5569
5640
|
const { arm, lib, modelPath, turns, trie, env, live } = input;
|
|
5570
|
-
const
|
|
5571
|
-
const session = await acquireHostSession(lib,
|
|
5641
|
+
const path48 = `${modelPath}#${arm.id}`;
|
|
5642
|
+
const session = await acquireHostSession(lib, path48);
|
|
5572
5643
|
const system = "You are a coding agent with a Task Capsule.";
|
|
5573
5644
|
const capsule = "function readConfig(): Config { return load(); }";
|
|
5574
5645
|
let last = null;
|
|
@@ -6426,8 +6497,8 @@ function registerModels(program) {
|
|
|
6426
6497
|
printRawFleet(report);
|
|
6427
6498
|
return;
|
|
6428
6499
|
}
|
|
6429
|
-
const
|
|
6430
|
-
const orch = buildFullStatus(
|
|
6500
|
+
const root3 = rootOf(global);
|
|
6501
|
+
const orch = buildFullStatus(root3, models, sys);
|
|
6431
6502
|
const backends = probeInferenceBackends({ hasBinary });
|
|
6432
6503
|
if (global.json) {
|
|
6433
6504
|
json({ ...orch, fleet: report, backends });
|
|
@@ -6511,9 +6582,9 @@ function registerModels(program) {
|
|
|
6511
6582
|
);
|
|
6512
6583
|
});
|
|
6513
6584
|
applyGlobalOptions(modeCmd);
|
|
6514
|
-
const
|
|
6585
|
+
const resolve23 = cmd.command("resolve").description("resolve a Code Mode to pack + underlying model + fit (no download)").argument("[mode]", "spark | flow | forge (default: auto-fit or configured default)").action(function(modeArg) {
|
|
6515
6586
|
const global = readGlobal(this);
|
|
6516
|
-
const
|
|
6587
|
+
const root3 = rootOf(global);
|
|
6517
6588
|
const sys = gatherSystemMemory();
|
|
6518
6589
|
const state = readOrchestratorState();
|
|
6519
6590
|
const mode = parseMode(modeArg) ?? state.defaultMode;
|
|
@@ -6522,7 +6593,7 @@ function registerModels(program) {
|
|
|
6522
6593
|
mode: mode ?? void 0,
|
|
6523
6594
|
autoFit: !mode,
|
|
6524
6595
|
system: sys,
|
|
6525
|
-
repo: repoSignals(
|
|
6596
|
+
repo: repoSignals(root3),
|
|
6526
6597
|
pin: pin2,
|
|
6527
6598
|
defaultMode: state.defaultMode
|
|
6528
6599
|
});
|
|
@@ -6543,10 +6614,10 @@ function registerModels(program) {
|
|
|
6543
6614
|
info(c.yellow(" will not fit \u2014 choose a lower mode or free memory before loading"));
|
|
6544
6615
|
}
|
|
6545
6616
|
});
|
|
6546
|
-
applyGlobalOptions(
|
|
6617
|
+
applyGlobalOptions(resolve23);
|
|
6547
6618
|
const installCmd = cmd.command("install").description("resolve the best Code Mode for this machine and install the pack").argument("[mode]", "spark | flow | forge (default: auto-fit)").option("--dry-run", "print the install plan only \u2014 do not download").option("--yes", "skip prompts (install already runs by default)").action(async function(modeArg, opts) {
|
|
6548
6619
|
const global = readGlobal(this);
|
|
6549
|
-
const
|
|
6620
|
+
const root3 = rootOf(global);
|
|
6550
6621
|
const models = discoverModels();
|
|
6551
6622
|
const sys = gatherSystemMemory();
|
|
6552
6623
|
const state = readOrchestratorState();
|
|
@@ -6556,7 +6627,7 @@ function registerModels(program) {
|
|
|
6556
6627
|
mode: mode ?? void 0,
|
|
6557
6628
|
autoFit: !mode,
|
|
6558
6629
|
system: sys,
|
|
6559
|
-
repo: repoSignals(
|
|
6630
|
+
repo: repoSignals(root3),
|
|
6560
6631
|
pin: pin2,
|
|
6561
6632
|
installedNames: models.map((m) => m.name),
|
|
6562
6633
|
defaultMode: state.defaultMode
|
|
@@ -6932,8 +7003,8 @@ function registerModels(program) {
|
|
|
6932
7003
|
}
|
|
6933
7004
|
});
|
|
6934
7005
|
if (opts.out) {
|
|
6935
|
-
const outPath =
|
|
6936
|
-
fs14.mkdirSync(
|
|
7006
|
+
const outPath = path46.resolve(opts.out);
|
|
7007
|
+
fs14.mkdirSync(path46.dirname(outPath), { recursive: true });
|
|
6937
7008
|
fs14.writeFileSync(outPath, `${JSON.stringify(report, null, 2)}
|
|
6938
7009
|
`);
|
|
6939
7010
|
if (!global.json) info(c.dim(` wrote ${outPath}`));
|
|
@@ -6978,9 +7049,9 @@ function hasBinary(bin) {
|
|
|
6978
7049
|
const probe = spawnSync(process.platform === "win32" ? "where" : "which", [bin], { stdio: "ignore" });
|
|
6979
7050
|
return probe.status === 0;
|
|
6980
7051
|
}
|
|
6981
|
-
function repoSignals(
|
|
7052
|
+
function repoSignals(root3) {
|
|
6982
7053
|
try {
|
|
6983
|
-
const g = loadGraph(
|
|
7054
|
+
const g = loadGraph(root3);
|
|
6984
7055
|
if (!g) return {};
|
|
6985
7056
|
const fileCount = g.nodes.filter((n) => n.kind === "file").length;
|
|
6986
7057
|
return { fileCount, graphNodeCount: g.nodes.length };
|
|
@@ -6988,12 +7059,12 @@ function repoSignals(root2) {
|
|
|
6988
7059
|
return {};
|
|
6989
7060
|
}
|
|
6990
7061
|
}
|
|
6991
|
-
function buildFullStatus(
|
|
7062
|
+
function buildFullStatus(root3, models, sys) {
|
|
6992
7063
|
const state = readOrchestratorState();
|
|
6993
7064
|
const freeDiskBytes = freeBytesOnVolume(weightStoreDir());
|
|
6994
7065
|
return buildOrchestratorStatus({
|
|
6995
7066
|
system: sys,
|
|
6996
|
-
repo: repoSignals(
|
|
7067
|
+
repo: repoSignals(root3),
|
|
6997
7068
|
installedNames: models.map((m) => m.name),
|
|
6998
7069
|
freeDiskBytes,
|
|
6999
7070
|
pins: state.pins,
|
|
@@ -7048,11 +7119,11 @@ function fitColor(label) {
|
|
|
7048
7119
|
return label;
|
|
7049
7120
|
}
|
|
7050
7121
|
function confirmYesNo(question) {
|
|
7051
|
-
return new Promise((
|
|
7122
|
+
return new Promise((resolve23) => {
|
|
7052
7123
|
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
7053
7124
|
rl.question(`${question} [y/N] `, (answer) => {
|
|
7054
7125
|
rl.close();
|
|
7055
|
-
|
|
7126
|
+
resolve23(/^y(es)?$/i.test(answer.trim()));
|
|
7056
7127
|
});
|
|
7057
7128
|
});
|
|
7058
7129
|
}
|
|
@@ -7083,14 +7154,14 @@ function resolveUninstallTarget(name, runtimeOpt) {
|
|
|
7083
7154
|
}
|
|
7084
7155
|
return exact[0];
|
|
7085
7156
|
}
|
|
7086
|
-
const base =
|
|
7157
|
+
const base = path46.basename(wantLc);
|
|
7087
7158
|
const soft = fleet.filter((m) => {
|
|
7088
7159
|
const n = m.name.toLowerCase();
|
|
7089
|
-
return n === base || n.endsWith("/" + base) || n.endsWith(wantLc) ||
|
|
7160
|
+
return n === base || n.endsWith("/" + base) || n.endsWith(wantLc) || path46.basename(n) === base;
|
|
7090
7161
|
});
|
|
7091
7162
|
if (soft.length === 1) return soft[0];
|
|
7092
7163
|
if (soft.length > 1) {
|
|
7093
|
-
const baseHits = soft.filter((m) =>
|
|
7164
|
+
const baseHits = soft.filter((m) => path46.basename(m.name).toLowerCase() === base);
|
|
7094
7165
|
const pool = baseHits.length ? baseHits : soft;
|
|
7095
7166
|
const order = ["ollama", "gguf", "lm-studio"];
|
|
7096
7167
|
for (const rt of order) {
|
|
@@ -7127,12 +7198,12 @@ function printBackends(backends) {
|
|
|
7127
7198
|
info(c.dim(` [${mark}] ${b.id} \xB7 ${b.detail ?? b.label}`));
|
|
7128
7199
|
}
|
|
7129
7200
|
}
|
|
7130
|
-
function codeConfigPath(
|
|
7131
|
-
return
|
|
7201
|
+
function codeConfigPath(root3) {
|
|
7202
|
+
return path46.join(root3, ".vibgrate", "code.json");
|
|
7132
7203
|
}
|
|
7133
|
-
function loadCodeConfig(
|
|
7204
|
+
function loadCodeConfig(root3) {
|
|
7134
7205
|
try {
|
|
7135
|
-
const raw = JSON.parse(fs14.readFileSync(codeConfigPath(
|
|
7206
|
+
const raw = JSON.parse(fs14.readFileSync(codeConfigPath(root3), "utf8"));
|
|
7136
7207
|
return sanitize(raw);
|
|
7137
7208
|
} catch {
|
|
7138
7209
|
return {};
|
|
@@ -7206,11 +7277,11 @@ function readMcpServersFile(file) {
|
|
|
7206
7277
|
return {};
|
|
7207
7278
|
}
|
|
7208
7279
|
}
|
|
7209
|
-
function discoverMcpServers(
|
|
7280
|
+
function discoverMcpServers(root3, ownServers) {
|
|
7210
7281
|
const servers = {};
|
|
7211
7282
|
const sources = [];
|
|
7212
7283
|
for (const rel of MCP_CONFIG_FILES) {
|
|
7213
|
-
const found = readMcpServersFile(
|
|
7284
|
+
const found = readMcpServersFile(path46.join(root3, rel));
|
|
7214
7285
|
const names = Object.keys(found);
|
|
7215
7286
|
if (names.length) {
|
|
7216
7287
|
sources.push(rel);
|
|
@@ -7245,11 +7316,11 @@ function registerCode(program) {
|
|
|
7245
7316
|
if (!files.length) {
|
|
7246
7317
|
throw usageError("--restore-checkpoint needs the files to restore, e.g. -f src/a.ts -f src/b.ts");
|
|
7247
7318
|
}
|
|
7248
|
-
const
|
|
7319
|
+
const root4 = rootOf(global);
|
|
7249
7320
|
const child = await import('child_process');
|
|
7250
7321
|
const gitRunner = (gitArgs, gitEnv) => {
|
|
7251
7322
|
const res = child.spawnSync("git", gitArgs, {
|
|
7252
|
-
cwd:
|
|
7323
|
+
cwd: root4,
|
|
7253
7324
|
encoding: "utf8",
|
|
7254
7325
|
env: { ...process.env, ...gitEnv ?? {} },
|
|
7255
7326
|
timeout: 3e4,
|
|
@@ -7277,10 +7348,10 @@ function registerCode(program) {
|
|
|
7277
7348
|
const child = await import('child_process');
|
|
7278
7349
|
const os7 = await import('os');
|
|
7279
7350
|
const pathMod = await import('path');
|
|
7280
|
-
const
|
|
7351
|
+
const root4 = rootOf(global);
|
|
7281
7352
|
const gitRunner = (gitArgs, gitEnv) => {
|
|
7282
7353
|
const res = child.spawnSync("git", gitArgs, {
|
|
7283
|
-
cwd:
|
|
7354
|
+
cwd: root4,
|
|
7284
7355
|
encoding: "utf8",
|
|
7285
7356
|
env: { ...process.env, ...gitEnv ?? {} },
|
|
7286
7357
|
timeout: 6e4,
|
|
@@ -7292,7 +7363,7 @@ function registerCode(program) {
|
|
|
7292
7363
|
if (opts.streamJson) process.stdout.write(JSON.stringify(line) + "\n");
|
|
7293
7364
|
else if (global.json) json(line);
|
|
7294
7365
|
};
|
|
7295
|
-
const findWt = (id) => wt.listSessionWorktrees(gitRunner,
|
|
7366
|
+
const findWt = (id) => wt.listSessionWorktrees(gitRunner, root4).find((w) => w.id === id);
|
|
7296
7367
|
if (opts.worktreeDiff) {
|
|
7297
7368
|
const target = findWt(opts.worktreeDiff);
|
|
7298
7369
|
if (!target) throw new CliError(`no worktree ${opts.worktreeDiff} under .vibgrate/worktrees`, ExitCode.NOT_FOUND);
|
|
@@ -7328,7 +7399,7 @@ function registerCode(program) {
|
|
|
7328
7399
|
"delta.patch"
|
|
7329
7400
|
);
|
|
7330
7401
|
fs14.writeFileSync(patchFile, diff2.patch);
|
|
7331
|
-
const outcome = wt.applyWorktreePatch(gitRunner,
|
|
7402
|
+
const outcome = wt.applyWorktreePatch(gitRunner, root4, patchFile, diff2.files);
|
|
7332
7403
|
emitLine({ event: "worktree-applied", id: target.id, ...outcome });
|
|
7333
7404
|
if (!opts.streamJson && !global.json) {
|
|
7334
7405
|
out(
|
|
@@ -7339,7 +7410,7 @@ function registerCode(program) {
|
|
|
7339
7410
|
return;
|
|
7340
7411
|
}
|
|
7341
7412
|
if (opts.worktreeRemove) {
|
|
7342
|
-
const outcome = wt.removeSessionWorktree(gitRunner,
|
|
7413
|
+
const outcome = wt.removeSessionWorktree(gitRunner, root4, opts.worktreeRemove);
|
|
7343
7414
|
emitLine({ event: "worktree-removed", id: opts.worktreeRemove, ...outcome });
|
|
7344
7415
|
if (!opts.streamJson && !global.json) {
|
|
7345
7416
|
out(outcome.removed ? `removed worktree ${opts.worktreeRemove}` : `remove failed: ${outcome.error}`);
|
|
@@ -7393,7 +7464,7 @@ function registerCode(program) {
|
|
|
7393
7464
|
try {
|
|
7394
7465
|
const { parseMode: parseMode2, resolveMode: resolveMode2, readOrchestratorState: readOrchestratorState2, activePin: activePin2 } = await import('./model-orchestrator-LHYPJBFH.js');
|
|
7395
7466
|
const { gatherSystemMemory: gatherSystemMemory2 } = await import('./local-runtime-6SNWKSA7.js');
|
|
7396
|
-
const { loadGraph: loadGraph2 } = await import('./load-
|
|
7467
|
+
const { loadGraph: loadGraph2 } = await import('./load-AYFECIQB.js');
|
|
7397
7468
|
const state = readOrchestratorState2();
|
|
7398
7469
|
const mode = parseMode2(modeFlag) ?? state.defaultMode ?? void 0;
|
|
7399
7470
|
const pin = mode ? activePin2(mode, state) : null;
|
|
@@ -7478,7 +7549,7 @@ function registerCode(program) {
|
|
|
7478
7549
|
if (!(process.stdin.isTTY && process.stdout.isTTY)) {
|
|
7479
7550
|
throw usageError('say what to change, e.g. `vg code "add a --timeout flag to the scan command"` (guided mode needs an interactive terminal)');
|
|
7480
7551
|
}
|
|
7481
|
-
const { runInteractive } = await import('./interactive-
|
|
7552
|
+
const { runInteractive } = await import('./interactive-SR7TMF6M.js');
|
|
7482
7553
|
await runInteractive(
|
|
7483
7554
|
rootOf(global),
|
|
7484
7555
|
global,
|
|
@@ -7508,7 +7579,7 @@ function registerCode(program) {
|
|
|
7508
7579
|
}
|
|
7509
7580
|
if (!instruction && !stdinFirstTurn) throw usageError('say what to change, e.g. `vg code "add a --timeout flag to the scan command"`');
|
|
7510
7581
|
{
|
|
7511
|
-
const { ensureCodeMap } = await import('./ensure-map-
|
|
7582
|
+
const { ensureCodeMap } = await import('./ensure-map-342G2IV3.js');
|
|
7512
7583
|
const mapRoot = rootOf(global);
|
|
7513
7584
|
try {
|
|
7514
7585
|
await ensureCodeMap(mapRoot, global, {
|
|
@@ -7541,7 +7612,7 @@ function registerCode(program) {
|
|
|
7541
7612
|
throw e;
|
|
7542
7613
|
}
|
|
7543
7614
|
}
|
|
7544
|
-
const { root:
|
|
7615
|
+
const { root: root3, graph } = requireGraph(global);
|
|
7545
7616
|
let mockReply;
|
|
7546
7617
|
if (opts.mock) {
|
|
7547
7618
|
try {
|
|
@@ -7593,21 +7664,21 @@ function registerCode(program) {
|
|
|
7593
7664
|
process.stdout.write(JSON.stringify(line) + "\n");
|
|
7594
7665
|
};
|
|
7595
7666
|
try {
|
|
7596
|
-
const { RunOutcomeRecorder, newOneShotRunId, turnRunId } = await import('./run-outcome-
|
|
7667
|
+
const { RunOutcomeRecorder, newOneShotRunId, turnRunId } = await import('./run-outcome-YQQJG3LW.js');
|
|
7597
7668
|
const primarySlug = route.providers[0]?.model.includes("/") ? route.providers[0].model.split("/")[0] : route.providers[0]?.id;
|
|
7598
|
-
const { runCodeStreamJson, parseHostMessage } = await import('./stream-json-
|
|
7599
|
-
const { nodeCodeFs } = await import('./session-
|
|
7669
|
+
const { runCodeStreamJson, parseHostMessage } = await import('./stream-json-55YDNAFX.js');
|
|
7670
|
+
const { nodeCodeFs } = await import('./session-H34HUV2Y.js');
|
|
7600
7671
|
const readline6 = await import('readline');
|
|
7601
7672
|
const child = await import('child_process');
|
|
7602
7673
|
const { loadOrDiscoverFederation } = await import('./federation-MDQPO3PW.js');
|
|
7603
7674
|
const { resolveExecutionEnv } = await import('./execution-env-4MS5AYAN.js');
|
|
7604
7675
|
const { resolveModelExecutionProfile, mergeModelExecutionProfile } = await import('./model-execution-profile-ELWF3Z6H.js');
|
|
7605
|
-
const { startCodeRuntimeSession } = await import('./runtime-session-
|
|
7606
|
-
const { resolveGraphBackend } = await import('./graph-backend-
|
|
7676
|
+
const { startCodeRuntimeSession } = await import('./runtime-session-CNJ4DAV5.js');
|
|
7677
|
+
const { resolveGraphBackend } = await import('./graph-backend-RNWQBT3P.js');
|
|
7607
7678
|
const { detectGitRef } = await import('./git-ref-RSRAN4OH.js');
|
|
7608
7679
|
const { repositoryIdFromRoot } = await import('./paths-62AKTFSR.js');
|
|
7609
|
-
const { vgdRequest } = await import('./vgd-
|
|
7610
|
-
const federation = loadOrDiscoverFederation(
|
|
7680
|
+
const { vgdRequest } = await import('./vgd-URUZQORN.js');
|
|
7681
|
+
const federation = loadOrDiscoverFederation(root3);
|
|
7611
7682
|
const modelProfile = mergeModelExecutionProfile(
|
|
7612
7683
|
resolveModelExecutionProfile({
|
|
7613
7684
|
providerId: primarySlug,
|
|
@@ -7618,13 +7689,13 @@ function registerCode(program) {
|
|
|
7618
7689
|
config.modelProfile
|
|
7619
7690
|
);
|
|
7620
7691
|
const tier = securityTier ?? modelProfile.securityTier ?? (auto ? "L1" : "L0");
|
|
7621
|
-
const writableRoots = federation?.members.map((m) => m.root) ?? [
|
|
7692
|
+
const writableRoots = federation?.members.map((m) => m.root) ?? [root3];
|
|
7622
7693
|
const executionEnv = resolveExecutionEnv(tier, { writableRoots });
|
|
7623
|
-
const runtime = await startCodeRuntimeSession({ root:
|
|
7624
|
-
const git2 = detectGitRef(
|
|
7625
|
-
const repositoryId = repositoryIdFromRoot(
|
|
7694
|
+
const runtime = await startCodeRuntimeSession({ root: root3 });
|
|
7695
|
+
const git2 = detectGitRef(root3);
|
|
7696
|
+
const repositoryId = repositoryIdFromRoot(root3);
|
|
7626
7697
|
const runOutcome = new RunOutcomeRecorder(
|
|
7627
|
-
|
|
7698
|
+
root3,
|
|
7628
7699
|
repositoryId,
|
|
7629
7700
|
git2.ref || null,
|
|
7630
7701
|
{ mode: codeModeActive ? modeFlag : void 0, provider: primarySlug, model: route.providers[0]?.model },
|
|
@@ -7652,7 +7723,7 @@ function registerCode(program) {
|
|
|
7652
7723
|
});
|
|
7653
7724
|
const run = (command) => {
|
|
7654
7725
|
const res = child.spawnSync(command, {
|
|
7655
|
-
cwd:
|
|
7726
|
+
cwd: root3,
|
|
7656
7727
|
shell: true,
|
|
7657
7728
|
encoding: "utf8",
|
|
7658
7729
|
timeout: 12e4,
|
|
@@ -7664,10 +7735,10 @@ ${res.stderr}` : ""),
|
|
|
7664
7735
|
exitCode: res.status ?? 1
|
|
7665
7736
|
};
|
|
7666
7737
|
};
|
|
7667
|
-
const { createVgBuiltinMcpTools, mergeExternalToolsets } = await import('./vg-mcp-bridge-
|
|
7738
|
+
const { createVgBuiltinMcpTools, mergeExternalToolsets } = await import('./vg-mcp-bridge-2CNLHNEL.js');
|
|
7668
7739
|
const vgBuiltin = createVgBuiltinMcpTools({
|
|
7669
7740
|
getGraph: () => graph,
|
|
7670
|
-
root:
|
|
7741
|
+
root: root3,
|
|
7671
7742
|
local: global.local === true
|
|
7672
7743
|
});
|
|
7673
7744
|
let disposeMcpStream;
|
|
@@ -7693,7 +7764,7 @@ ${res.stderr}` : ""),
|
|
|
7693
7764
|
);
|
|
7694
7765
|
const gitRunner = (gitArgs, gitEnv) => {
|
|
7695
7766
|
const res = child.spawnSync("git", gitArgs, {
|
|
7696
|
-
cwd:
|
|
7767
|
+
cwd: root3,
|
|
7697
7768
|
encoding: "utf8",
|
|
7698
7769
|
env: { ...process.env, ...gitEnv ?? {} },
|
|
7699
7770
|
timeout: 3e4,
|
|
@@ -7709,9 +7780,9 @@ ${res.stderr}` : ""),
|
|
|
7709
7780
|
files,
|
|
7710
7781
|
indexFile: checkpointIndex
|
|
7711
7782
|
});
|
|
7712
|
-
const { loadApprovalRules, addApprovalRule, ruleForAction, ruleLabel } = await import('./approvals-
|
|
7783
|
+
const { loadApprovalRules, addApprovalRule, ruleForAction, ruleLabel } = await import('./approvals-ASPJQUZK.js');
|
|
7713
7784
|
const { loadProjectRules, nodeRulesReader } = await import('./rules-ASYEUNB5.js');
|
|
7714
|
-
const projectRules = loadProjectRules(nodeRulesReader(
|
|
7785
|
+
const projectRules = loadProjectRules(nodeRulesReader(root3));
|
|
7715
7786
|
if (projectRules.sources.length && !global.quiet) {
|
|
7716
7787
|
emitStream({
|
|
7717
7788
|
event: "event",
|
|
@@ -7721,9 +7792,9 @@ ${res.stderr}` : ""),
|
|
|
7721
7792
|
}
|
|
7722
7793
|
const baseAgentOptions = {
|
|
7723
7794
|
graph,
|
|
7724
|
-
root:
|
|
7795
|
+
root: root3,
|
|
7725
7796
|
providers: route.providers,
|
|
7726
|
-
fsImpl: nodeCodeFs(
|
|
7797
|
+
fsImpl: nodeCodeFs(root3),
|
|
7727
7798
|
run,
|
|
7728
7799
|
// Live command-output events for the VG Code panel (async shell).
|
|
7729
7800
|
// Sandboxed executionEnv still uses its own runner when set.
|
|
@@ -7767,8 +7838,8 @@ ${res.stderr}` : ""),
|
|
|
7767
7838
|
};
|
|
7768
7839
|
try {
|
|
7769
7840
|
if (opts.session) {
|
|
7770
|
-
const { runCodeStreamJsonSession, STREAM_JSON_PROTOCOL_VERSION } = await import('./stream-json-
|
|
7771
|
-
const { runAgent: runAgent2 } = await import('./agent-
|
|
7841
|
+
const { runCodeStreamJsonSession, STREAM_JSON_PROTOCOL_VERSION } = await import('./stream-json-55YDNAFX.js');
|
|
7842
|
+
const { runAgent: runAgent2 } = await import('./agent-UQC7QN77.js');
|
|
7772
7843
|
const {
|
|
7773
7844
|
condenseSession,
|
|
7774
7845
|
loadLatestSession,
|
|
@@ -7779,7 +7850,7 @@ ${res.stderr}` : ""),
|
|
|
7779
7850
|
sessionsDir,
|
|
7780
7851
|
summarizeSession
|
|
7781
7852
|
} = await import('./session-store-AQUORKWB.js');
|
|
7782
|
-
const { VERSION: VERSION2 } = await import('./version-
|
|
7853
|
+
const { VERSION: VERSION2 } = await import('./version-WSE3QOPK.js');
|
|
7783
7854
|
const resumedRecord = typeof opts.continue === "string" ? loadSession(mainRoot, opts.continue) : opts.continue ? loadLatestSession(mainRoot) : void 0;
|
|
7784
7855
|
if (typeof opts.continue === "string" && !resumedRecord) {
|
|
7785
7856
|
emitStream({
|
|
@@ -7811,8 +7882,8 @@ ${res.stderr}` : ""),
|
|
|
7811
7882
|
if (ended) return Promise.resolve(null);
|
|
7812
7883
|
const queued = pendingTurns.shift();
|
|
7813
7884
|
if (queued !== void 0) return Promise.resolve(queued);
|
|
7814
|
-
return new Promise((
|
|
7815
|
-
wakeTurn =
|
|
7885
|
+
return new Promise((resolve23) => {
|
|
7886
|
+
wakeTurn = resolve23;
|
|
7816
7887
|
});
|
|
7817
7888
|
};
|
|
7818
7889
|
let turnIndex = 0;
|
|
@@ -7991,8 +8062,8 @@ ${res.stderr}` : ""),
|
|
|
7991
8062
|
if (!autonomous && !tty) {
|
|
7992
8063
|
throw usageError("the agent approves each edit/command \u2014 run in a terminal to approve interactively, or pass --auto to run autonomously (or --single for a one-shot diff).");
|
|
7993
8064
|
}
|
|
7994
|
-
const { agentTask } = await import('./interactive-
|
|
7995
|
-
const { nodeCodeFs } = await import('./session-
|
|
8065
|
+
const { agentTask } = await import('./interactive-SR7TMF6M.js');
|
|
8066
|
+
const { nodeCodeFs } = await import('./session-H34HUV2Y.js');
|
|
7996
8067
|
const { TtyPrompter } = await import('./ui-K7D3BCHD.js');
|
|
7997
8068
|
let priorSummary;
|
|
7998
8069
|
let priorInstruction;
|
|
@@ -8007,10 +8078,10 @@ ${res.stderr}` : ""),
|
|
|
8007
8078
|
}
|
|
8008
8079
|
}
|
|
8009
8080
|
const prompter = tty ? new TtyPrompter() : void 0;
|
|
8010
|
-
const { createVgBuiltinMcpTools, mergeExternalToolsets } = await import('./vg-mcp-bridge-
|
|
8081
|
+
const { createVgBuiltinMcpTools, mergeExternalToolsets } = await import('./vg-mcp-bridge-2CNLHNEL.js');
|
|
8011
8082
|
const vgBuiltin = createVgBuiltinMcpTools({
|
|
8012
8083
|
getGraph: () => graph,
|
|
8013
|
-
root:
|
|
8084
|
+
root: root3,
|
|
8014
8085
|
local: global.local === true
|
|
8015
8086
|
});
|
|
8016
8087
|
let projectMcp;
|
|
@@ -8031,12 +8102,12 @@ ${res.stderr}` : ""),
|
|
|
8031
8102
|
const { loadFederation } = await import('./federation-MDQPO3PW.js');
|
|
8032
8103
|
const { resolveExecutionEnv } = await import('./execution-env-4MS5AYAN.js');
|
|
8033
8104
|
const { resolveModelExecutionProfile, mergeModelExecutionProfile } = await import('./model-execution-profile-ELWF3Z6H.js');
|
|
8034
|
-
const { startCodeRuntimeSession } = await import('./runtime-session-
|
|
8035
|
-
const { resolveGraphBackend } = await import('./graph-backend-
|
|
8105
|
+
const { startCodeRuntimeSession } = await import('./runtime-session-CNJ4DAV5.js');
|
|
8106
|
+
const { resolveGraphBackend } = await import('./graph-backend-RNWQBT3P.js');
|
|
8036
8107
|
const { detectGitRef } = await import('./git-ref-RSRAN4OH.js');
|
|
8037
8108
|
const { repositoryIdFromRoot } = await import('./paths-62AKTFSR.js');
|
|
8038
|
-
const { vgdRequest } = await import('./vgd-
|
|
8039
|
-
const federation = loadFederation(
|
|
8109
|
+
const { vgdRequest } = await import('./vgd-URUZQORN.js');
|
|
8110
|
+
const federation = loadFederation(root3);
|
|
8040
8111
|
const modelProfile = mergeModelExecutionProfile(
|
|
8041
8112
|
resolveModelExecutionProfile({
|
|
8042
8113
|
providerId: primarySlug,
|
|
@@ -8048,11 +8119,11 @@ ${res.stderr}` : ""),
|
|
|
8048
8119
|
);
|
|
8049
8120
|
const tier = securityTier ?? modelProfile.securityTier ?? (autonomous ? "L1" : "L0");
|
|
8050
8121
|
const executionEnv = resolveExecutionEnv(tier, {
|
|
8051
|
-
writableRoots: federation?.members.map((m) => m.root) ?? [
|
|
8122
|
+
writableRoots: federation?.members.map((m) => m.root) ?? [root3]
|
|
8052
8123
|
});
|
|
8053
|
-
const runtime = await startCodeRuntimeSession({ root:
|
|
8054
|
-
const git2 = detectGitRef(
|
|
8055
|
-
const repositoryId = repositoryIdFromRoot(
|
|
8124
|
+
const runtime = await startCodeRuntimeSession({ root: root3 });
|
|
8125
|
+
const git2 = detectGitRef(root3);
|
|
8126
|
+
const repositoryId = repositoryIdFromRoot(root3);
|
|
8056
8127
|
if (runtime.socketPath && git2.ref) {
|
|
8057
8128
|
try {
|
|
8058
8129
|
await vgdRequest(
|
|
@@ -8070,11 +8141,11 @@ ${res.stderr}` : ""),
|
|
|
8070
8141
|
});
|
|
8071
8142
|
try {
|
|
8072
8143
|
const agentResult = await agentTask({
|
|
8073
|
-
root:
|
|
8144
|
+
root: root3,
|
|
8074
8145
|
graph,
|
|
8075
8146
|
instruction,
|
|
8076
8147
|
providers: route.providers,
|
|
8077
|
-
fsImpl: nodeCodeFs(
|
|
8148
|
+
fsImpl: nodeCodeFs(root3),
|
|
8078
8149
|
attribution: { client: "vg-code", provider: primarySlug, model: route.providers[0]?.model },
|
|
8079
8150
|
auto: autonomous,
|
|
8080
8151
|
maxSteps,
|
|
@@ -8107,7 +8178,7 @@ ${res.stderr}` : ""),
|
|
|
8107
8178
|
}
|
|
8108
8179
|
let consent = !!opts.yes;
|
|
8109
8180
|
if (opts.apply && !consent && process.stdin.isTTY && process.stdout.isTTY) {
|
|
8110
|
-
consent = await confirm2(`Apply the proposed change to ${
|
|
8181
|
+
consent = await confirm2(`Apply the proposed change to ${root3}?`);
|
|
8111
8182
|
}
|
|
8112
8183
|
const onPhase = (phase, detail) => {
|
|
8113
8184
|
if (!global.json && !global.quiet) info(c.dim(` ${phase}: ${detail}`));
|
|
@@ -8116,7 +8187,7 @@ ${res.stderr}` : ""),
|
|
|
8116
8187
|
const providerSlug = primary?.model.includes("/") ? primary.model.split("/")[0] : primary?.id;
|
|
8117
8188
|
const result = await runCodeSession({
|
|
8118
8189
|
graph,
|
|
8119
|
-
root:
|
|
8190
|
+
root: root3,
|
|
8120
8191
|
instruction,
|
|
8121
8192
|
providers: route.providers,
|
|
8122
8193
|
apply: !!opts.apply,
|
|
@@ -8181,11 +8252,11 @@ function resolveSecurityTierFlag(flag, fromConfig, auto) {
|
|
|
8181
8252
|
return auto ? "L1" : "L0";
|
|
8182
8253
|
}
|
|
8183
8254
|
function confirm2(question) {
|
|
8184
|
-
return new Promise((
|
|
8255
|
+
return new Promise((resolve23) => {
|
|
8185
8256
|
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
8186
8257
|
rl.question(`${question} [y/N] `, (answer) => {
|
|
8187
8258
|
rl.close();
|
|
8188
|
-
|
|
8259
|
+
resolve23(/^y(es)?$/i.test(answer.trim()));
|
|
8189
8260
|
});
|
|
8190
8261
|
});
|
|
8191
8262
|
}
|
|
@@ -8195,9 +8266,9 @@ function registerSavings(program) {
|
|
|
8195
8266
|
const cmd = program.command("savings").description("local, privacy-safe report of tokens/$ saved vs a grep baseline (estimates)").option("--days <n>", "window in days", "30").option("--clear", "delete the recorded usage data for this repo").action(function() {
|
|
8196
8267
|
const global = readGlobal(this);
|
|
8197
8268
|
const days = Number(this.opts().days) || 30;
|
|
8198
|
-
const
|
|
8269
|
+
const root3 = rootOf(global);
|
|
8199
8270
|
if (this.opts().clear) {
|
|
8200
|
-
const existed = clearSavings(
|
|
8271
|
+
const existed = clearSavings(root3);
|
|
8201
8272
|
if (global.json) {
|
|
8202
8273
|
json({ ok: true, cleared: existed });
|
|
8203
8274
|
return;
|
|
@@ -8208,9 +8279,9 @@ function registerSavings(program) {
|
|
|
8208
8279
|
return;
|
|
8209
8280
|
}
|
|
8210
8281
|
const now = Date.now();
|
|
8211
|
-
const report = readSavings(
|
|
8212
|
-
const usage = readUsage(
|
|
8213
|
-
const models = readModelSavings(
|
|
8282
|
+
const report = readSavings(root3, days, now);
|
|
8283
|
+
const usage = readUsage(root3, days, now);
|
|
8284
|
+
const models = readModelSavings(root3, days, now);
|
|
8214
8285
|
if (global.json) {
|
|
8215
8286
|
json({ ...report, usage, models });
|
|
8216
8287
|
return;
|
|
@@ -8292,16 +8363,16 @@ function printSplit(usage) {
|
|
|
8292
8363
|
function registerLib(program) {
|
|
8293
8364
|
const cmd = program.command("lib").description("library currency: version-correct, drift-annotated usage docs").argument("[args...]", "a library name, or: add <source> | publish <name> | resolve <name> | refresh").option("--name <name>", "library name for `add`").option("--version <v>", "pin the doc version for `add`/`publish`").option("--online", "(deprecated; network is on by default) allow network for add/refresh URL sources").option("-b, --budget <n>", "trim docs to ~N tokens").option("--readme <path>", "README path for `publish` (default ./README.md)").option("--dts <path>", "TypeScript declaration (.d.ts) path for `publish`").option("--language <lang>", "primary language for `publish`").option("--region <region>", "data-residency region for the hosted catalog (same as scans; default us)").option("--ingest <url>", "hosted catalog/ingest URL override (host extracted; wins over --region)").action(async function(args, opts) {
|
|
8294
8365
|
const global = readGlobal(this);
|
|
8295
|
-
const
|
|
8366
|
+
const root3 = rootOf(global);
|
|
8296
8367
|
const [verb, ...rest] = args;
|
|
8297
8368
|
const network = !global.local;
|
|
8298
|
-
if (!verb) return listCatalog(
|
|
8369
|
+
if (!verb) return listCatalog(root3, global.json);
|
|
8299
8370
|
const online = (Boolean(opts.online) || network) && !global.local;
|
|
8300
|
-
if (verb === "add") return addCmd(
|
|
8301
|
-
if (verb === "publish") return publishCmd(
|
|
8302
|
-
if (verb === "resolve") return resolveCmd(
|
|
8303
|
-
if (verb === "refresh") return refreshCmd(
|
|
8304
|
-
return showCmd(
|
|
8371
|
+
if (verb === "add") return addCmd(root3, rest, { ...opts, online }, global.json);
|
|
8372
|
+
if (verb === "publish") return publishCmd(root3, rest, opts, global, global.json);
|
|
8373
|
+
if (verb === "resolve") return resolveCmd(root3, rest, global.json);
|
|
8374
|
+
if (verb === "refresh") return refreshCmd(root3, { online }, global.json);
|
|
8375
|
+
return showCmd(root3, args.join(" "), opts.budget ? Number(opts.budget) : void 0, global.json, {
|
|
8305
8376
|
online: network,
|
|
8306
8377
|
// hosted-on-miss allowed unless --local
|
|
8307
8378
|
region: opts.region,
|
|
@@ -8310,8 +8381,8 @@ function registerLib(program) {
|
|
|
8310
8381
|
});
|
|
8311
8382
|
applyGlobalOptions(cmd);
|
|
8312
8383
|
}
|
|
8313
|
-
function listCatalog(
|
|
8314
|
-
const catalog = loadCatalog(
|
|
8384
|
+
function listCatalog(root3, asJson) {
|
|
8385
|
+
const catalog = loadCatalog(root3);
|
|
8315
8386
|
const libs = Object.values(catalog.libraries).sort((a, b) => a.name.localeCompare(b.name));
|
|
8316
8387
|
if (asJson) {
|
|
8317
8388
|
json(libs.map((l) => ({ id: l.id, name: l.name, version: l.version, source: l.source.type })));
|
|
@@ -8319,18 +8390,18 @@ function listCatalog(root2, asJson) {
|
|
|
8319
8390
|
}
|
|
8320
8391
|
info(`${c.cyan("vg lib")} \xB7 ${libs.length} library(ies) in the catalog`);
|
|
8321
8392
|
for (const l of libs) {
|
|
8322
|
-
const d = driftFor(
|
|
8393
|
+
const d = driftFor(root3, l);
|
|
8323
8394
|
const tag = d.drift === "behind" ? c.yellow(" (docs behind your version)") : d.drift === "ahead" ? c.dim(" (docs ahead)") : "";
|
|
8324
8395
|
info(` ${c.bold(l.name)} ${c.dim(`@${l.version}`)} ${c.dim(`[${l.source.type}]`)}${tag}`);
|
|
8325
8396
|
}
|
|
8326
8397
|
if (!libs.length) info(c.dim(" empty \u2014 add docs with `vg lib add <path|url>`"));
|
|
8327
8398
|
}
|
|
8328
|
-
async function addCmd(
|
|
8399
|
+
async function addCmd(root3, rest, opts, asJson) {
|
|
8329
8400
|
const source = rest[0];
|
|
8330
8401
|
if (!source) throw usageError("usage: vg lib add <path|url|git> [--name <name>] [--version <v>] [--online]");
|
|
8331
8402
|
let entry;
|
|
8332
8403
|
try {
|
|
8333
|
-
entry = await addLibrary(source, { root:
|
|
8404
|
+
entry = await addLibrary(source, { root: root3, name: opts.name, version: opts.version, allowNetwork: opts.online });
|
|
8334
8405
|
} catch (err) {
|
|
8335
8406
|
throw new CliError(err.message, ExitCode.ERROR);
|
|
8336
8407
|
}
|
|
@@ -8340,9 +8411,9 @@ async function addCmd(root2, rest, opts, asJson) {
|
|
|
8340
8411
|
}
|
|
8341
8412
|
info(`${c.green("\u2714")} added ${c.bold(entry.name)} ${c.dim(`@${entry.version}`)} (${entry.bytes} bytes) \u2192 ${entry.docFile}`);
|
|
8342
8413
|
}
|
|
8343
|
-
function readPackageVersion(
|
|
8414
|
+
function readPackageVersion(root3) {
|
|
8344
8415
|
try {
|
|
8345
|
-
const pkg = JSON.parse(fs14__default.readFileSync(
|
|
8416
|
+
const pkg = JSON.parse(fs14__default.readFileSync(path46__default.join(root3, "package.json"), "utf8"));
|
|
8346
8417
|
return typeof pkg.version === "string" ? pkg.version : void 0;
|
|
8347
8418
|
} catch {
|
|
8348
8419
|
return void 0;
|
|
@@ -8356,17 +8427,17 @@ function readOptionalFile(p) {
|
|
|
8356
8427
|
return void 0;
|
|
8357
8428
|
}
|
|
8358
8429
|
}
|
|
8359
|
-
async function publishCmd(
|
|
8430
|
+
async function publishCmd(root3, rest, opts, global, asJson) {
|
|
8360
8431
|
if (global.local) throw usageError("vg lib publish uploads to the hosted catalog \u2014 it needs network (remove --local)");
|
|
8361
8432
|
const name = (rest.join(" ") || opts.name || "").trim();
|
|
8362
8433
|
if (!name) throw usageError("usage: vg lib publish <name> [--version <v>] [--readme <path>] [--dts <path>] [--language <lang>]");
|
|
8363
8434
|
const dsn = resolveDsn();
|
|
8364
8435
|
const parsed = dsn ? parseDsn$1(dsn) : null;
|
|
8365
8436
|
if (!parsed) throw new CliError("publishing a private library requires a DSN \u2014 run `vibgrate login` or set VIBGRATE_DSN", ExitCode.USAGE_ERROR);
|
|
8366
|
-
const version = (opts.version || readPackageVersion(
|
|
8437
|
+
const version = (opts.version || readPackageVersion(root3) || "").trim();
|
|
8367
8438
|
if (!version) throw usageError("a --version is required (or run from a package whose package.json has a version)");
|
|
8368
|
-
const readme = readOptionalFile(opts.readme ?
|
|
8369
|
-
const dts = opts.dts ? readOptionalFile(
|
|
8439
|
+
const readme = readOptionalFile(opts.readme ? path46__default.resolve(opts.readme) : path46__default.join(root3, "README.md"));
|
|
8440
|
+
const dts = opts.dts ? readOptionalFile(path46__default.resolve(opts.dts)) : void 0;
|
|
8370
8441
|
if (!readme && !dts) throw usageError("nothing to publish \u2014 no README.md found and no --dts given");
|
|
8371
8442
|
const result = await publishPrivateLibrary(
|
|
8372
8443
|
{ name, version, readme, dts, language: opts.language },
|
|
@@ -8384,20 +8455,20 @@ async function publishCmd(root2, rest, opts, global, asJson) {
|
|
|
8384
8455
|
info(`${c.green("\u2714")} published ${c.bold(name)} ${c.dim(`@${version}`)} \xB7 ${result.entities} entities \xB7 ${result.indexedTokens} tokens indexed (MCP-token meter)`);
|
|
8385
8456
|
info(c.dim(` private to your workspace \xB7 targetId ${result.targetId}`));
|
|
8386
8457
|
}
|
|
8387
|
-
function resolveCmd(
|
|
8458
|
+
function resolveCmd(root3, rest, asJson) {
|
|
8388
8459
|
const name = rest.join(" ");
|
|
8389
8460
|
if (!name) throw usageError("usage: vg lib resolve <name>");
|
|
8390
|
-
const entry = resolveLib(loadCatalog(
|
|
8461
|
+
const entry = resolveLib(loadCatalog(root3), name);
|
|
8391
8462
|
if (!entry) throw new CliError(`no library matches "${name}" (id ${libId(name)})`, ExitCode.NOT_FOUND);
|
|
8392
8463
|
if (asJson) json({ id: entry.id, name: entry.name, version: entry.version });
|
|
8393
8464
|
else info(`${entry.id} ${c.dim(`(${entry.name}@${entry.version})`)}`);
|
|
8394
8465
|
}
|
|
8395
|
-
async function refreshCmd(
|
|
8396
|
-
const catalog = loadCatalog(
|
|
8466
|
+
async function refreshCmd(root3, opts, asJson) {
|
|
8467
|
+
const catalog = loadCatalog(root3);
|
|
8397
8468
|
const results = [];
|
|
8398
8469
|
for (const entry of Object.values(catalog.libraries)) {
|
|
8399
8470
|
try {
|
|
8400
|
-
await addLibrary(entry.source.location, { root:
|
|
8471
|
+
await addLibrary(entry.source.location, { root: root3, name: entry.name, version: entry.version, allowNetwork: opts.online });
|
|
8401
8472
|
results.push({ name: entry.name, ok: true });
|
|
8402
8473
|
} catch {
|
|
8403
8474
|
results.push({ name: entry.name, ok: false });
|
|
@@ -8406,36 +8477,36 @@ async function refreshCmd(root2, opts, asJson) {
|
|
|
8406
8477
|
if (asJson) json({ refreshed: results });
|
|
8407
8478
|
else info(`${c.cyan("vg lib refresh")} \xB7 ${results.filter((r) => r.ok).length}/${results.length} re-ingested`);
|
|
8408
8479
|
}
|
|
8409
|
-
async function showCmd(
|
|
8480
|
+
async function showCmd(root3, name, budget, asJson, opts = {}) {
|
|
8410
8481
|
const online = opts.online;
|
|
8411
|
-
const catalog = loadCatalog(
|
|
8482
|
+
const catalog = loadCatalog(root3);
|
|
8412
8483
|
const entry = resolveLib(catalog, name);
|
|
8413
|
-
const ver = resolveVersion(
|
|
8484
|
+
const ver = resolveVersion(root3, name);
|
|
8414
8485
|
const displayName = entry?.name ?? name;
|
|
8415
8486
|
let localDoc;
|
|
8416
8487
|
let docVersion = ver.served ?? "*";
|
|
8417
8488
|
let docSource;
|
|
8418
8489
|
let drift;
|
|
8419
8490
|
if (entry) {
|
|
8420
|
-
localDoc = readDoc(
|
|
8491
|
+
localDoc = readDoc(root3, entry);
|
|
8421
8492
|
docVersion = entry.version;
|
|
8422
|
-
drift = driftFor(
|
|
8493
|
+
drift = driftFor(root3, entry);
|
|
8423
8494
|
} else {
|
|
8424
|
-
const local = localPackageDocs(
|
|
8495
|
+
const local = localPackageDocs(root3, name);
|
|
8425
8496
|
if (local) {
|
|
8426
8497
|
localDoc = local.docs;
|
|
8427
8498
|
docVersion = local.version ?? ver.served ?? "*";
|
|
8428
8499
|
docSource = local.source;
|
|
8429
8500
|
}
|
|
8430
8501
|
}
|
|
8431
|
-
const apiSurface = localApiSurface(
|
|
8502
|
+
const apiSurface = localApiSurface(root3, displayName);
|
|
8432
8503
|
const quality = localDoc !== void 0 ? assessDocQuality([localDoc, apiSurface].filter(Boolean).join("\n\n"), { name: displayName, symbols: symbolsFromApi(apiSurface) }) : { sufficient: false, score: 0, reasons: ["no local docs"] };
|
|
8433
8504
|
let hosted = null;
|
|
8434
8505
|
if (online && !quality.sufficient) {
|
|
8435
8506
|
const dsn = resolveDsn();
|
|
8436
8507
|
const parsed = dsn ? parseDsn$1(dsn) : null;
|
|
8437
8508
|
hosted = await fetchHostedDocsCached(
|
|
8438
|
-
|
|
8509
|
+
root3,
|
|
8439
8510
|
{ name: displayName, query: name, maxTokens: budget },
|
|
8440
8511
|
{ region: opts.region, ingest: opts.ingest, auth: parsed ? { keyId: parsed.keyId, secret: parsed.secret } : void 0 }
|
|
8441
8512
|
);
|
|
@@ -8482,13 +8553,13 @@ function registerExport(program) {
|
|
|
8482
8553
|
const cmd = program.command("export").description("export the map (format inferred: json|ndjson|graphml|dot|cypher|sql|md|html|cdx.json|spdx.json)").argument("[file]", "target file (or - for stdout)", "map.json").option("--compact", "compact JSON (no pretty-print; auto for large maps)").option("--slim", "drop heavy fields (area member lists, grounding) for smaller exports").action(function(file, opts) {
|
|
8483
8554
|
const global = readGlobal(this);
|
|
8484
8555
|
const { graph } = requireGraph(global);
|
|
8485
|
-
const
|
|
8556
|
+
const root3 = rootOf(global);
|
|
8486
8557
|
const format = detectFormat(file);
|
|
8487
8558
|
if (!format) throw usageError(`unknown export format for "${file}"`);
|
|
8488
8559
|
const needsDeps = format === "cyclonedx" || format === "spdx";
|
|
8489
8560
|
const content = exportGraph(format, {
|
|
8490
8561
|
graph,
|
|
8491
|
-
deps: needsDeps ? inventory(
|
|
8562
|
+
deps: needsDeps ? inventory(root3).records : void 0,
|
|
8492
8563
|
models: needsDeps && !global.local ? discoverModels() : void 0,
|
|
8493
8564
|
generatedAt: graph.generatedAt,
|
|
8494
8565
|
compact: opts.compact === true ? true : opts.compact === false ? false : void 0,
|
|
@@ -8498,7 +8569,7 @@ function registerExport(program) {
|
|
|
8498
8569
|
out(content.trimEnd());
|
|
8499
8570
|
return;
|
|
8500
8571
|
}
|
|
8501
|
-
fs14.mkdirSync(
|
|
8572
|
+
fs14.mkdirSync(path46.dirname(path46.resolve(file)), { recursive: true });
|
|
8502
8573
|
fs14.writeFileSync(file, content);
|
|
8503
8574
|
info(`${c.green("\u2714")} wrote ${file} ${c.dim(`(${format})`)}`);
|
|
8504
8575
|
});
|
|
@@ -8509,49 +8580,49 @@ function detectFormat(file) {
|
|
|
8509
8580
|
if (lower.endsWith(".cdx.json")) return "cyclonedx";
|
|
8510
8581
|
if (lower.endsWith(".spdx.json")) return "spdx";
|
|
8511
8582
|
if (file === "-") return "json";
|
|
8512
|
-
return formatForExt(
|
|
8583
|
+
return formatForExt(path46.extname(file));
|
|
8513
8584
|
}
|
|
8514
8585
|
function registerBundle(program) {
|
|
8515
8586
|
const cmd = program.command("bundle").description("build an air-gapped bundle (grammars + graph + library catalog)").option("--offline", "offline bundle (default; present for clarity)").option("-o, --out <dir>", "output directory", "vg-bundle").action(function(opts) {
|
|
8516
8587
|
const global = readGlobal(this);
|
|
8517
|
-
const
|
|
8518
|
-
const outDir =
|
|
8588
|
+
const root3 = rootOf(global);
|
|
8589
|
+
const outDir = path46.resolve(root3, opts.out ?? "vg-bundle");
|
|
8519
8590
|
let grammarFiles;
|
|
8520
8591
|
try {
|
|
8521
8592
|
grammarFiles = resolvedGrammarFiles();
|
|
8522
8593
|
} catch {
|
|
8523
8594
|
throw new CliError("no grammars found to bundle (run a build first)", ExitCode.ERROR);
|
|
8524
8595
|
}
|
|
8525
|
-
fs14.mkdirSync(
|
|
8596
|
+
fs14.mkdirSync(path46.join(outDir, "grammars"), { recursive: true });
|
|
8526
8597
|
let grammarCount = 0;
|
|
8527
8598
|
for (const { fileName, absPath } of grammarFiles) {
|
|
8528
|
-
fs14.copyFileSync(absPath,
|
|
8599
|
+
fs14.copyFileSync(absPath, path46.join(outDir, "grammars", fileName));
|
|
8529
8600
|
grammarCount++;
|
|
8530
8601
|
}
|
|
8531
8602
|
for (const id of Object.keys(TOOLCHAIN_GRAMMARS).sort()) {
|
|
8532
8603
|
const absPath = resolveToolchainGrammar(id);
|
|
8533
8604
|
if (!absPath) continue;
|
|
8534
|
-
fs14.copyFileSync(absPath,
|
|
8605
|
+
fs14.copyFileSync(absPath, path46.join(outDir, "grammars", path46.basename(absPath)));
|
|
8535
8606
|
grammarCount++;
|
|
8536
8607
|
}
|
|
8537
8608
|
const included = [`grammars/ (${grammarCount} .wasm)`];
|
|
8538
|
-
const graphSrc = resolveGraphPath(
|
|
8609
|
+
const graphSrc = resolveGraphPath(root3, global.graph);
|
|
8539
8610
|
if (fs14.existsSync(graphSrc)) {
|
|
8540
|
-
fs14.copyFileSync(graphSrc,
|
|
8611
|
+
fs14.copyFileSync(graphSrc, path46.join(outDir, "graph.json"));
|
|
8541
8612
|
included.push("graph.json");
|
|
8542
8613
|
} else {
|
|
8543
8614
|
const graph = loadGraphFileWithSnapshot(graphSrc);
|
|
8544
8615
|
if (graph) {
|
|
8545
|
-
fs14.writeFileSync(
|
|
8616
|
+
fs14.writeFileSync(path46.join(outDir, "graph.json"), serializeGraph(graph));
|
|
8546
8617
|
included.push("graph.json");
|
|
8547
8618
|
}
|
|
8548
8619
|
}
|
|
8549
|
-
const cat = catalogPath(
|
|
8620
|
+
const cat = catalogPath(root3);
|
|
8550
8621
|
if (fs14.existsSync(cat)) {
|
|
8551
|
-
fs14.copyFileSync(cat,
|
|
8622
|
+
fs14.copyFileSync(cat, path46.join(outDir, "vibgrate.lib.json"));
|
|
8552
8623
|
included.push("vibgrate.lib.json");
|
|
8553
|
-
if (fs14.existsSync(libDir(
|
|
8554
|
-
fs14.cpSync(libDir(
|
|
8624
|
+
if (fs14.existsSync(libDir(root3))) {
|
|
8625
|
+
fs14.cpSync(libDir(root3), path46.join(outDir, "lib"), { recursive: true });
|
|
8555
8626
|
included.push("lib/");
|
|
8556
8627
|
}
|
|
8557
8628
|
}
|
|
@@ -8563,15 +8634,15 @@ function registerBundle(program) {
|
|
|
8563
8634
|
included,
|
|
8564
8635
|
usage: "Run `vg build --grammars <this dir>/grammars` to build fully offline."
|
|
8565
8636
|
};
|
|
8566
|
-
fs14.writeFileSync(
|
|
8637
|
+
fs14.writeFileSync(path46.join(outDir, "MANIFEST.json"), `${stableStringify(manifest, 2)}
|
|
8567
8638
|
`);
|
|
8568
8639
|
if (global.json) {
|
|
8569
|
-
json({ out:
|
|
8640
|
+
json({ out: path46.relative(root3, outDir), ...manifest });
|
|
8570
8641
|
return;
|
|
8571
8642
|
}
|
|
8572
|
-
info(`${c.cyan("vg bundle")} \xB7 wrote ${
|
|
8643
|
+
info(`${c.cyan("vg bundle")} \xB7 wrote ${path46.relative(root3, outDir)}/`);
|
|
8573
8644
|
for (const i of included) info(` ${c.green("\u2714")} ${i}`);
|
|
8574
|
-
info(c.dim(` offline: vg build --grammars "${
|
|
8645
|
+
info(c.dim(` offline: vg build --grammars "${path46.relative(root3, outDir)}/grammars"`));
|
|
8575
8646
|
});
|
|
8576
8647
|
applyGlobalOptions(cmd);
|
|
8577
8648
|
}
|
|
@@ -8586,19 +8657,19 @@ function registerDoctor(program) {
|
|
|
8586
8657
|
var REACH_TIMEOUT_MS = 3e3;
|
|
8587
8658
|
var CONFIG_BASENAMES = ["vibgrate.config.ts", "vibgrate.config.js", "vibgrate.config.json"];
|
|
8588
8659
|
async function runDoctor(global) {
|
|
8589
|
-
const
|
|
8660
|
+
const root3 = rootOf(global);
|
|
8590
8661
|
const local = global.local === true;
|
|
8591
|
-
const graphPath = resolveGraphPath(
|
|
8592
|
-
const configFile = CONFIG_BASENAMES.find((f) => fs14.existsSync(
|
|
8593
|
-
const graph = loadGraph(
|
|
8662
|
+
const graphPath = resolveGraphPath(root3, global.graph);
|
|
8663
|
+
const configFile = CONFIG_BASENAMES.find((f) => fs14.existsSync(path46.join(root3, f))) ?? null;
|
|
8664
|
+
const graph = loadGraph(root3, graphPath);
|
|
8594
8665
|
let staleFiles = null;
|
|
8595
8666
|
if (graph) {
|
|
8596
|
-
const probe = probeFreshness(
|
|
8667
|
+
const probe = probeFreshness(root3);
|
|
8597
8668
|
if (probe) staleFiles = driftCount(probe.drift);
|
|
8598
8669
|
}
|
|
8599
|
-
const catalog = loadCatalog(
|
|
8670
|
+
const catalog = loadCatalog(root3);
|
|
8600
8671
|
const libCount = Object.keys(catalog.libraries).length;
|
|
8601
|
-
const creds = diagnoseCredentials(
|
|
8672
|
+
const creds = diagnoseCredentials(root3);
|
|
8602
8673
|
const base = hostedBase();
|
|
8603
8674
|
const reachable = local ? null : await probeReachable(base);
|
|
8604
8675
|
const launch = detectServeLaunch();
|
|
@@ -8612,17 +8683,17 @@ async function runDoctor(global) {
|
|
|
8612
8683
|
version: VERSION,
|
|
8613
8684
|
node: process.version,
|
|
8614
8685
|
platform: `${process.platform}/${process.arch}`,
|
|
8615
|
-
root:
|
|
8686
|
+
root: root3,
|
|
8616
8687
|
configFile,
|
|
8617
8688
|
map: {
|
|
8618
|
-
path:
|
|
8689
|
+
path: path46.relative(root3, graphPath),
|
|
8619
8690
|
built: graph !== null,
|
|
8620
8691
|
generatedAt: graph?.generatedAt ?? null,
|
|
8621
8692
|
staleFiles
|
|
8622
8693
|
},
|
|
8623
8694
|
libCatalog: {
|
|
8624
|
-
path:
|
|
8625
|
-
present: fs14.existsSync(catalogPath(
|
|
8695
|
+
path: path46.relative(root3, catalogPath(root3)),
|
|
8696
|
+
present: fs14.existsSync(catalogPath(root3)),
|
|
8626
8697
|
libraries: libCount
|
|
8627
8698
|
},
|
|
8628
8699
|
credentials: creds,
|
|
@@ -8679,7 +8750,7 @@ async function runDoctor(global) {
|
|
|
8679
8750
|
);
|
|
8680
8751
|
info(
|
|
8681
8752
|
` host pool ${li.hostPool.size} warm session(s)` + (li.hostPool.sessions[0] ? c.dim(
|
|
8682
|
-
` \xB7 last ${
|
|
8753
|
+
` \xB7 last ${path46.basename(li.hostPool.sessions[0].modelPath)} kv=${li.hostPool.sessions[0].kvBlocks}`
|
|
8683
8754
|
) : c.dim(" \xB7 none (load via vg code / vgd host-load)"))
|
|
8684
8755
|
);
|
|
8685
8756
|
if (li.dynamicOnToken || li.dynamicCustomSampler) {
|
|
@@ -8692,16 +8763,16 @@ async function runDoctor(global) {
|
|
|
8692
8763
|
}
|
|
8693
8764
|
info(c.dim(" tip `vg models mode --apply-recommend` pins the recommended Code Mode"));
|
|
8694
8765
|
}
|
|
8695
|
-
function diagnoseCredentials(
|
|
8766
|
+
function diagnoseCredentials(root3) {
|
|
8696
8767
|
const none = { source: "none", path: null, host: null, workspaceId: null };
|
|
8697
8768
|
if (process.env.VIBGRATE_DSN) {
|
|
8698
8769
|
const parsed2 = parseDsn$1(process.env.VIBGRATE_DSN);
|
|
8699
8770
|
return { source: "env", path: null, host: parsed2?.host ?? null, workspaceId: parsed2?.workspaceId ?? null };
|
|
8700
8771
|
}
|
|
8701
|
-
const stored = readStoredCredentials({ cwd:
|
|
8772
|
+
const stored = readStoredCredentials({ cwd: root3 });
|
|
8702
8773
|
if (!stored) return none;
|
|
8703
|
-
const file = credentialsPath({ cwd:
|
|
8704
|
-
const source = process.env.VIBGRATE_CREDENTIALS ? "env" : file === projectCredentialsPath(
|
|
8774
|
+
const file = credentialsPath({ cwd: root3 });
|
|
8775
|
+
const source = process.env.VIBGRATE_CREDENTIALS ? "env" : file === projectCredentialsPath(root3) ? "project" : "home";
|
|
8705
8776
|
const parsed = parseDsn$1(stored.dsn);
|
|
8706
8777
|
return {
|
|
8707
8778
|
source,
|
|
@@ -8986,7 +9057,7 @@ async function readStdin() {
|
|
|
8986
9057
|
return Buffer.concat(chunks).toString("utf-8");
|
|
8987
9058
|
}
|
|
8988
9059
|
function readFileOrThrow(file, label) {
|
|
8989
|
-
const p =
|
|
9060
|
+
const p = path46.resolve(file);
|
|
8990
9061
|
if (!fs14.existsSync(p)) throw usageError(`${label} file not found: ${p}`);
|
|
8991
9062
|
return fs14.readFileSync(p, "utf-8");
|
|
8992
9063
|
}
|
|
@@ -8998,8 +9069,8 @@ function writeOutput(outFile, content) {
|
|
|
8998
9069
|
process.stdout.write(content.endsWith("\n") ? content : content + "\n");
|
|
8999
9070
|
return;
|
|
9000
9071
|
}
|
|
9001
|
-
const outPath =
|
|
9002
|
-
const dir =
|
|
9072
|
+
const outPath = path46.resolve(outFile);
|
|
9073
|
+
const dir = path46.dirname(outPath);
|
|
9003
9074
|
if (!fs14.existsSync(dir)) throw usageError(`output directory does not exist: ${dir}`);
|
|
9004
9075
|
fs14.writeFileSync(outPath, content.endsWith("\n") ? content : content + "\n", "utf-8");
|
|
9005
9076
|
info(c.dim(`wrote ${outPath}`));
|
|
@@ -9034,9 +9105,9 @@ function registerHcs(program) {
|
|
|
9034
9105
|
}
|
|
9035
9106
|
const statePath = opts.previous ?? opts.out;
|
|
9036
9107
|
const useIncremental = canIncremental && statePath !== void 0;
|
|
9037
|
-
const
|
|
9038
|
-
if (!fs14.existsSync(
|
|
9039
|
-
throw usageError(`not a directory: ${
|
|
9108
|
+
const root3 = path46.resolve(dir);
|
|
9109
|
+
if (!fs14.existsSync(root3) || !fs14.statSync(root3).isDirectory()) {
|
|
9110
|
+
throw usageError(`not a directory: ${root3}`);
|
|
9040
9111
|
}
|
|
9041
9112
|
const supported = new Set(
|
|
9042
9113
|
engine.supportedLanguages().split(",").map((l) => l.trim()).filter(Boolean)
|
|
@@ -9059,7 +9130,7 @@ function registerHcs(program) {
|
|
|
9059
9130
|
}
|
|
9060
9131
|
for (const entry of entries.sort((a, b) => a.name < b.name ? -1 : 1)) {
|
|
9061
9132
|
if (entry.name.startsWith(".")) continue;
|
|
9062
|
-
const absChild =
|
|
9133
|
+
const absChild = path46.join(abs, entry.name);
|
|
9063
9134
|
const relChild = rel ? `${rel}/${entry.name}` : entry.name;
|
|
9064
9135
|
if (entry.isDirectory()) {
|
|
9065
9136
|
if (!SKIP_DIRS2.has(entry.name)) walk2(absChild, relChild);
|
|
@@ -9067,7 +9138,7 @@ function registerHcs(program) {
|
|
|
9067
9138
|
}
|
|
9068
9139
|
if (!entry.isFile()) continue;
|
|
9069
9140
|
if (!opts.includeTests && TEST_PATTERNS.some((p) => p.test(relChild))) continue;
|
|
9070
|
-
const lang = extToLang.get(
|
|
9141
|
+
const lang = extToLang.get(path46.extname(entry.name).toLowerCase());
|
|
9071
9142
|
if (!lang) continue;
|
|
9072
9143
|
try {
|
|
9073
9144
|
if (fs14.statSync(absChild).size > MAX_EXTRACT_FILE_BYTES) continue;
|
|
@@ -9079,9 +9150,9 @@ function registerHcs(program) {
|
|
|
9079
9150
|
}
|
|
9080
9151
|
}
|
|
9081
9152
|
};
|
|
9082
|
-
walk2(
|
|
9153
|
+
walk2(root3, "");
|
|
9083
9154
|
if (byLanguage.size === 0 && !useIncremental) {
|
|
9084
|
-
info(`no extractable sources found under ${
|
|
9155
|
+
info(`no extractable sources found under ${root3} (languages: ${[...wanted].sort().join(", ")})`);
|
|
9085
9156
|
writeOutput(opts.out, "");
|
|
9086
9157
|
return;
|
|
9087
9158
|
}
|
|
@@ -9101,7 +9172,7 @@ function registerHcs(program) {
|
|
|
9101
9172
|
writeOutput(opts.out, parts2.join("\n"));
|
|
9102
9173
|
return;
|
|
9103
9174
|
}
|
|
9104
|
-
const previousPath =
|
|
9175
|
+
const previousPath = path46.resolve(statePath);
|
|
9105
9176
|
const previousNdjson = !opts.full && fs14.existsSync(previousPath) ? fs14.readFileSync(previousPath, "utf-8") : "";
|
|
9106
9177
|
if (opts.full) info(c.dim("running a full re-extraction (--full)"));
|
|
9107
9178
|
else if (!previousNdjson) info(c.dim(`previous stream not found at ${previousPath} \u2014 running a full extraction`));
|
|
@@ -9206,11 +9277,11 @@ function registerHcs(program) {
|
|
|
9206
9277
|
applyGlobalOptions(cmd);
|
|
9207
9278
|
}
|
|
9208
9279
|
var initCommand = new Command("init").description("Initialize vibgrate in a project").argument("[path]", "Path to initialize", ".").option("--baseline", "Create initial baseline after init").option("--yes", "Skip confirmation prompts").action(async (targetPath, opts) => {
|
|
9209
|
-
const rootDir =
|
|
9210
|
-
const vibgrateDir2 =
|
|
9280
|
+
const rootDir = path46.resolve(targetPath);
|
|
9281
|
+
const vibgrateDir2 = path46.join(rootDir, ".vibgrate");
|
|
9211
9282
|
await ensureDir(vibgrateDir2);
|
|
9212
9283
|
console.log(chalk7.green("\u2714") + ` Created ${chalk7.bold(".vibgrate/")} directory`);
|
|
9213
|
-
const configPath =
|
|
9284
|
+
const configPath = path46.join(rootDir, "vibgrate.config.ts");
|
|
9214
9285
|
if (await pathExists(configPath)) {
|
|
9215
9286
|
console.log(chalk7.dim(" vibgrate.config.ts already exists, skipping"));
|
|
9216
9287
|
} else {
|
|
@@ -9218,7 +9289,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
|
|
|
9218
9289
|
console.log(chalk7.green("\u2714") + ` Created ${chalk7.bold("vibgrate.config.ts")}`);
|
|
9219
9290
|
}
|
|
9220
9291
|
if (opts.baseline) {
|
|
9221
|
-
const { runBaseline } = await import('./baseline-
|
|
9292
|
+
const { runBaseline } = await import('./baseline-FIV6PR2M.js');
|
|
9222
9293
|
await runBaseline(rootDir);
|
|
9223
9294
|
}
|
|
9224
9295
|
console.log("");
|
|
@@ -9321,7 +9392,7 @@ function collectPackageImports(graph, ecosystem, pkg) {
|
|
|
9321
9392
|
};
|
|
9322
9393
|
}
|
|
9323
9394
|
async function firstReferenceLine(rootDir, relFile, identifier, readFile3) {
|
|
9324
|
-
const content = await readFile3(
|
|
9395
|
+
const content = await readFile3(path46.join(rootDir, relFile));
|
|
9325
9396
|
if (!content) return null;
|
|
9326
9397
|
const escaped = identifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9327
9398
|
const re = new RegExp(`\\b${escaped}\\b`);
|
|
@@ -9557,7 +9628,7 @@ var AI_ASSISTANT_DETECTORS = [
|
|
|
9557
9628
|
async function detectAiAssistant(rootDir) {
|
|
9558
9629
|
for (const detector of AI_ASSISTANT_DETECTORS) {
|
|
9559
9630
|
for (const file of detector.files) {
|
|
9560
|
-
if (await pathExists$1(
|
|
9631
|
+
if (await pathExists$1(path46.join(rootDir, file))) {
|
|
9561
9632
|
return detector.name;
|
|
9562
9633
|
}
|
|
9563
9634
|
}
|
|
@@ -9589,6 +9660,18 @@ init_cli_invocation();
|
|
|
9589
9660
|
function shouldBuildCodeMap(opts) {
|
|
9590
9661
|
return opts.graph !== false && !opts.maxPrivacy && !opts.noLocalArtifacts;
|
|
9591
9662
|
}
|
|
9663
|
+
function architectureGraphView2(graph) {
|
|
9664
|
+
return {
|
|
9665
|
+
nodes: graph.nodes.map((n) => ({ id: n.id, file: n.file, kind: n.kind })),
|
|
9666
|
+
edges: graph.edges.map((e) => ({
|
|
9667
|
+
src: e.src,
|
|
9668
|
+
dst: e.dst,
|
|
9669
|
+
kind: e.kind,
|
|
9670
|
+
confidence: e.confidence,
|
|
9671
|
+
resolution: e.resolution
|
|
9672
|
+
}))
|
|
9673
|
+
};
|
|
9674
|
+
}
|
|
9592
9675
|
async function attachReachability(artifact, rootDir, opts, graph) {
|
|
9593
9676
|
try {
|
|
9594
9677
|
const dsn = resolveDsn(opts.dsn);
|
|
@@ -9750,8 +9833,8 @@ var scanCommand = new Command("scan").description("Scan a project for upgrade dr
|
|
|
9750
9833
|
'Exclude paths matching a glob pattern. Repeatable, and a single value may list several patterns separated by commas or semicolons (e.g. --exclude "legacy/**,vendor/**"). Merged with excludes from the config file.',
|
|
9751
9834
|
collectExcludes,
|
|
9752
9835
|
[]
|
|
9753
|
-
).option("--concurrency <n>", "Max concurrent
|
|
9754
|
-
const rootDir =
|
|
9836
|
+
).option("--concurrency <n>", "Max concurrent registry lookups", "8").option("--push", "Auto-push results to Vibgrate API after scan").option("--dsn <dsn>", "DSN token for push (or use VIBGRATE_DSN env)").option("--region <region>", "Override data residency region for push (us, eu)").option("--strict", "Fail on push errors").option("--ui-purpose", "Enable optional UI purpose evidence extraction (slower)").option("--no-local-artifacts", "Do not write .vibgrate JSON artifacts to disk").option("--max-privacy", "Enable strongest privacy mode (minimal scanners, no local artifacts)").option("--offline", "Run without network calls; do not upload results").option("--full", "Comprehensive scan: turns on known-vulnerability detection (= --vulns) and, when a standards policy exists, a banned-dependency report \u2014 on top of drift scoring and the code map").option("--vulns", "Also scan installed dependencies for known vulnerabilities (OSV online, or advisories from --package-manifest when offline)").option("--package-manifest <file>", "Use local package-version manifest JSON/ZIP (for offline mode)").option("--project-scan-timeout <seconds>", "Per-project scan timeout in seconds (default: 180)").option("--drift-budget <score>", "Fail if DriftScore is above budget (0-100)").option("--drift-worsening <percent>", "Fail if drift worsens by more than % since baseline").option("--repository-name <name>", "Override the repository name recorded for this scan (defaults to the directory or package.json name)").option("--force", "Always create a fresh scan ingest, even if the repository is unchanged since the last scan (skips the unchanged/reuse optimization). Used by scheduled and dashboard-triggered scans.").option("--no-graph", "Skip building the local code map (the AI/docs index) that scan produces after scoring drift").option("--quiet", "Suppress promotional output (the free-plan tracking panel and the AI Context install prompt); scan results are unaffected").action(async (targetPath, opts) => {
|
|
9837
|
+
const rootDir = path46.resolve(targetPath);
|
|
9755
9838
|
if (!await pathExists$1(rootDir)) {
|
|
9756
9839
|
console.error(chalk7.red(`Path does not exist: ${rootDir}`));
|
|
9757
9840
|
process.exit(1);
|
|
@@ -9759,7 +9842,7 @@ var scanCommand = new Command("scan").description("Scan a project for upgrade dr
|
|
|
9759
9842
|
const hasDsn = !!resolveDsn(opts.dsn);
|
|
9760
9843
|
let willPush = !opts.offline && (opts.push || hasDsn);
|
|
9761
9844
|
const noLocalArtifacts = opts.localArtifacts === false;
|
|
9762
|
-
const isFirstRun = !await pathExists$1(
|
|
9845
|
+
const isFirstRun = !await pathExists$1(path46.join(rootDir, ".vibgrate", "scan_result.json"));
|
|
9763
9846
|
let pinnedRegion;
|
|
9764
9847
|
let planTier;
|
|
9765
9848
|
let upgradeUrl;
|
|
@@ -9891,7 +9974,7 @@ var scanCommand = new Command("scan").description("Scan a project for upgrade dr
|
|
|
9891
9974
|
const wantGraph = shouldBuildCodeMap({ graph: opts.graph, maxPrivacy: opts.maxPrivacy, noLocalArtifacts });
|
|
9892
9975
|
let builtGraph = null;
|
|
9893
9976
|
if (wantGraph) {
|
|
9894
|
-
scanOpts.postScan = async (report) => {
|
|
9977
|
+
scanOpts.postScan = async (report, ctx) => {
|
|
9895
9978
|
const result = await buildGraph({
|
|
9896
9979
|
root: rootDir,
|
|
9897
9980
|
exclude: opts.exclude,
|
|
@@ -9902,6 +9985,14 @@ var scanCommand = new Command("scan").description("Scan a project for upgrade dr
|
|
|
9902
9985
|
writeSnapshot(rootDir, result.graph.provenance.corpusHash, result.fileStats, {
|
|
9903
9986
|
exclude: opts.exclude
|
|
9904
9987
|
});
|
|
9988
|
+
refineArchitectureWithGraph(
|
|
9989
|
+
{ projects: ctx.projects, solutions: ctx.solutions, extended: ctx.extended },
|
|
9990
|
+
architectureGraphView2(result.graph)
|
|
9991
|
+
);
|
|
9992
|
+
refineArchitectureWithAstRoles(
|
|
9993
|
+
{ projects: ctx.projects, solutions: ctx.solutions, extended: ctx.extended },
|
|
9994
|
+
result.fileRoles
|
|
9995
|
+
);
|
|
9905
9996
|
const { counts } = result.graph.meta;
|
|
9906
9997
|
return `${counts.nodes.toLocaleString()} nodes \xB7 ${counts.edges.toLocaleString()} edges`;
|
|
9907
9998
|
};
|
|
@@ -10048,11 +10139,11 @@ function scanStaleness(rootDir, artifactPath) {
|
|
|
10048
10139
|
scanned++;
|
|
10049
10140
|
if (scanned > MAX_ENTRIES2) break;
|
|
10050
10141
|
if (e.isDirectory()) {
|
|
10051
|
-
if (!SKIP_DIRS3.has(e.name) && !e.name.startsWith(".")) walk2(
|
|
10142
|
+
if (!SKIP_DIRS3.has(e.name) && !e.name.startsWith(".")) walk2(path46.join(dir, e.name), depth + 1);
|
|
10052
10143
|
continue;
|
|
10053
10144
|
}
|
|
10054
10145
|
if (!WATCHED_FILES.has(e.name) && !WATCHED_EXT.test(e.name)) continue;
|
|
10055
|
-
const abs =
|
|
10146
|
+
const abs = path46.join(dir, e.name);
|
|
10056
10147
|
let mtimeMs;
|
|
10057
10148
|
try {
|
|
10058
10149
|
mtimeMs = fs14.statSync(abs).mtimeMs;
|
|
@@ -10061,7 +10152,7 @@ function scanStaleness(rootDir, artifactPath) {
|
|
|
10061
10152
|
}
|
|
10062
10153
|
if (mtimeMs > newest) {
|
|
10063
10154
|
newest = mtimeMs;
|
|
10064
|
-
newestChanged =
|
|
10155
|
+
newestChanged = path46.relative(rootDir, abs) || e.name;
|
|
10065
10156
|
}
|
|
10066
10157
|
}
|
|
10067
10158
|
};
|
|
@@ -10169,7 +10260,7 @@ function importPattern(ecosystem, pkg) {
|
|
|
10169
10260
|
}
|
|
10170
10261
|
return new RegExp(`^\\s*(?:import\\s+${e}(?:[.\\s]|$)|from\\s+${e}(?:[.\\s]))`, "gm");
|
|
10171
10262
|
}
|
|
10172
|
-
function analyzeTree(
|
|
10263
|
+
function analyzeTree(root3, packages) {
|
|
10173
10264
|
const result = /* @__PURE__ */ new Map();
|
|
10174
10265
|
const tracked = [];
|
|
10175
10266
|
for (const p of packages) {
|
|
@@ -10201,13 +10292,13 @@ function analyzeTree(root2, packages) {
|
|
|
10201
10292
|
if (filesSeen >= MAX_FILES) return;
|
|
10202
10293
|
if (entry.isDirectory()) {
|
|
10203
10294
|
if (SKIP_DIRS4.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
10204
|
-
walk2(
|
|
10295
|
+
walk2(path46.join(dir, entry.name), depth + 1);
|
|
10205
10296
|
continue;
|
|
10206
10297
|
}
|
|
10207
|
-
const fileEco = EXT_ECOSYSTEM[
|
|
10298
|
+
const fileEco = EXT_ECOSYSTEM[path46.extname(entry.name)];
|
|
10208
10299
|
if (!fileEco || fileEco === "unknown") continue;
|
|
10209
10300
|
filesSeen++;
|
|
10210
|
-
const full =
|
|
10301
|
+
const full = path46.join(dir, entry.name);
|
|
10211
10302
|
let content;
|
|
10212
10303
|
try {
|
|
10213
10304
|
if (fs14.statSync(full).size > MAX_FILE_BYTES2) continue;
|
|
@@ -10229,7 +10320,7 @@ function analyzeTree(root2, packages) {
|
|
|
10229
10320
|
}
|
|
10230
10321
|
}
|
|
10231
10322
|
};
|
|
10232
|
-
walk2(
|
|
10323
|
+
walk2(root3, 0);
|
|
10233
10324
|
for (const t of tracked) {
|
|
10234
10325
|
result.set(t.name, {
|
|
10235
10326
|
importSites: t.importSites,
|
|
@@ -10522,7 +10613,7 @@ function applyPlan(rootDir, upgrades, opts = {}) {
|
|
|
10522
10613
|
}
|
|
10523
10614
|
const outcomes = effective.map((t) => {
|
|
10524
10615
|
const c2 = pmCommandFor(u.ecosystem, u.package, u.to, opts.packageManager, { workspaceRoot: t.isWorkspaceRoot });
|
|
10525
|
-
return { t, res: run(c2,
|
|
10616
|
+
return { t, res: run(c2, path46.join(rootDir, t.dir)) };
|
|
10526
10617
|
});
|
|
10527
10618
|
const failures = outcomes.filter((o) => !o.res.ok);
|
|
10528
10619
|
if (failures.length === 0) {
|
|
@@ -10551,8 +10642,8 @@ function applyPlan(rootDir, upgrades, opts = {}) {
|
|
|
10551
10642
|
return results;
|
|
10552
10643
|
}
|
|
10553
10644
|
var REGISTRY_URL = "https://registry.npmjs.org/@vibgrate%2fcli/latest";
|
|
10554
|
-
var CACHE_DIR =
|
|
10555
|
-
var CACHE_FILE =
|
|
10645
|
+
var CACHE_DIR = path46.join(os.homedir(), ".vibgrate");
|
|
10646
|
+
var CACHE_FILE = path46.join(CACHE_DIR, "update-check.json");
|
|
10556
10647
|
async function fetchLatestVersion() {
|
|
10557
10648
|
try {
|
|
10558
10649
|
const controller = new AbortController();
|
|
@@ -10615,8 +10706,8 @@ function buildDeferredUpdateScript(opts) {
|
|
|
10615
10706
|
}
|
|
10616
10707
|
function scheduleDeferredUpdate(command, pid = process.pid, dir = os.tmpdir()) {
|
|
10617
10708
|
const stamp = `${pid}-${process.hrtime.bigint().toString(36)}`;
|
|
10618
|
-
const scriptPath =
|
|
10619
|
-
const logPath =
|
|
10709
|
+
const scriptPath = path46.join(dir, `vg-update-${stamp}.cmd`);
|
|
10710
|
+
const logPath = path46.join(dir, `vg-update-${stamp}.log`);
|
|
10620
10711
|
try {
|
|
10621
10712
|
fs14.writeFileSync(scriptPath, buildDeferredUpdateScript({ pid, command, logPath }), "utf8");
|
|
10622
10713
|
const child = spawn(process.env.ComSpec || "cmd.exe", ["/d", "/s", "/c", scriptPath], {
|
|
@@ -10672,14 +10763,14 @@ async function findGlobalInstall(pkg = PACKAGE_NAME, run = defaultRunner) {
|
|
|
10672
10763
|
pm: "yarn",
|
|
10673
10764
|
root: () => {
|
|
10674
10765
|
const dir = tryRun("yarn global dir");
|
|
10675
|
-
return dir ?
|
|
10766
|
+
return dir ? path46.join(dir, "node_modules") : null;
|
|
10676
10767
|
}
|
|
10677
10768
|
},
|
|
10678
|
-
{ pm: "bun", root: () =>
|
|
10769
|
+
{ pm: "bun", root: () => path46.join(os.homedir(), ".bun", "install", "global", "node_modules") }
|
|
10679
10770
|
];
|
|
10680
10771
|
for (const probe of probes) {
|
|
10681
|
-
const
|
|
10682
|
-
if (
|
|
10772
|
+
const root3 = probe.root();
|
|
10773
|
+
if (root3 && await pathExists(path46.join(root3, pkg))) return probe.pm;
|
|
10683
10774
|
}
|
|
10684
10775
|
return null;
|
|
10685
10776
|
}
|
|
@@ -10698,15 +10789,15 @@ function getGlobalUpdateCommand(pm, pkg, version) {
|
|
|
10698
10789
|
}
|
|
10699
10790
|
}
|
|
10700
10791
|
async function detectPackageManager(cwd) {
|
|
10701
|
-
if (await pathExists(
|
|
10702
|
-
if (await pathExists(
|
|
10703
|
-
if (await pathExists(
|
|
10792
|
+
if (await pathExists(path46.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
10793
|
+
if (await pathExists(path46.join(cwd, "bun.lockb"))) return "bun";
|
|
10794
|
+
if (await pathExists(path46.join(cwd, "yarn.lock"))) return "yarn";
|
|
10704
10795
|
return "npm";
|
|
10705
10796
|
}
|
|
10706
10797
|
async function detectWorkspaceRoot(cwd) {
|
|
10707
|
-
if (await pathExists(
|
|
10798
|
+
if (await pathExists(path46.join(cwd, "pnpm-workspace.yaml"))) return true;
|
|
10708
10799
|
try {
|
|
10709
|
-
const pkgPath =
|
|
10800
|
+
const pkgPath = path46.join(cwd, "package.json");
|
|
10710
10801
|
const raw = await (await import('fs/promises')).readFile(pkgPath, "utf-8");
|
|
10711
10802
|
const pkg = JSON.parse(raw);
|
|
10712
10803
|
return pkg.workspaces != null;
|
|
@@ -10733,7 +10824,7 @@ function getInstallCommand(pm, pkg, version, isDev, opts = {}) {
|
|
|
10733
10824
|
}
|
|
10734
10825
|
async function getLocalDependencyState(cwd, pkg = PACKAGE_NAME) {
|
|
10735
10826
|
try {
|
|
10736
|
-
const pkgPath =
|
|
10827
|
+
const pkgPath = path46.join(cwd, "package.json");
|
|
10737
10828
|
const raw = await (await import('fs/promises')).readFile(pkgPath, "utf-8");
|
|
10738
10829
|
const parsed = JSON.parse(raw);
|
|
10739
10830
|
if (parsed.devDependencies?.[pkg]) return "dev";
|
|
@@ -10748,18 +10839,18 @@ async function confirmWorkspaceRoot(pm) {
|
|
|
10748
10839
|
async function confirm3(question) {
|
|
10749
10840
|
if (!(process.stdin.isTTY && process.stdout.isTTY)) return false;
|
|
10750
10841
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
10751
|
-
return new Promise((
|
|
10842
|
+
return new Promise((resolve23) => {
|
|
10752
10843
|
rl.question(chalk7.yellow(question), (ans) => {
|
|
10753
10844
|
rl.close();
|
|
10754
10845
|
const t = ans.trim().toLowerCase();
|
|
10755
|
-
|
|
10846
|
+
resolve23(t === "y" || t === "yes");
|
|
10756
10847
|
});
|
|
10757
10848
|
});
|
|
10758
10849
|
}
|
|
10759
10850
|
var CAPTURED_OUTPUT_LIMIT = 64e3;
|
|
10760
10851
|
async function runInstall(cmd, cwd) {
|
|
10761
10852
|
const { spawn: spawn4 } = await import('child_process');
|
|
10762
|
-
return new Promise((
|
|
10853
|
+
return new Promise((resolve23) => {
|
|
10763
10854
|
const child = spawn4(cmd, {
|
|
10764
10855
|
cwd,
|
|
10765
10856
|
shell: true,
|
|
@@ -10778,15 +10869,15 @@ async function runInstall(cmd, cwd) {
|
|
|
10778
10869
|
};
|
|
10779
10870
|
tee(child.stdout, process.stdout);
|
|
10780
10871
|
tee(child.stderr, process.stderr);
|
|
10781
|
-
child.on("error", (err) =>
|
|
10872
|
+
child.on("error", (err) => resolve23({ ok: false, output: `${output}
|
|
10782
10873
|
${String(err)}` }));
|
|
10783
|
-
child.on("close", (code) =>
|
|
10874
|
+
child.on("close", (code) => resolve23({ ok: code === 0, output }));
|
|
10784
10875
|
});
|
|
10785
10876
|
}
|
|
10786
10877
|
async function releaseWindowsFileLocks(cwd, keepVersion) {
|
|
10787
10878
|
reapAndReport(cwd, keepVersion);
|
|
10788
10879
|
try {
|
|
10789
|
-
const { vgdSocketPath } = await import('./vgd-
|
|
10880
|
+
const { vgdSocketPath } = await import('./vgd-URUZQORN.js');
|
|
10790
10881
|
const result = await stopVgd(vgdSocketPath());
|
|
10791
10882
|
if (result === "stopped") {
|
|
10792
10883
|
console.log(chalk7.dim("Stopped the vgd daemon so its files can be replaced \u2014 it restarts on the next command."));
|
|
@@ -10911,7 +11002,7 @@ var updateCommand = new Command("update").description("Update vibgrate to the la
|
|
|
10911
11002
|
);
|
|
10912
11003
|
async function rewarmVgdAndReport(cwd) {
|
|
10913
11004
|
try {
|
|
10914
|
-
const { vgdIsRunning, vgdRequest } = await import('./vgd-
|
|
11005
|
+
const { vgdIsRunning, vgdRequest } = await import('./vgd-URUZQORN.js');
|
|
10915
11006
|
if (!await vgdIsRunning()) return;
|
|
10916
11007
|
const loaded = await vgdRequest({ op: "load-graph", root: cwd });
|
|
10917
11008
|
if (!loaded.ok || !("repositoryId" in loaded)) return;
|
|
@@ -11000,13 +11091,13 @@ function collectCandidates(artifact) {
|
|
|
11000
11091
|
return out2;
|
|
11001
11092
|
}
|
|
11002
11093
|
var defaultFixScanner = async (rootDir) => {
|
|
11003
|
-
const scratch =
|
|
11094
|
+
const scratch = path46.join(os.tmpdir(), `vg-fix-scan-${process.pid}.json`);
|
|
11004
11095
|
const scanOpts = { vibgrateVersion: VERSION, format: "json", out: scratch, quiet: true, concurrency: 8 };
|
|
11005
11096
|
const advanced = await loadAdvancedScanHook();
|
|
11006
11097
|
return runCoreScan(rootDir, scanOpts, advanced);
|
|
11007
11098
|
};
|
|
11008
11099
|
async function loadArtifact(rootDir, inFile, scan = defaultFixScanner) {
|
|
11009
|
-
const artifactPath =
|
|
11100
|
+
const artifactPath = path46.isAbsolute(inFile) ? inFile : path46.join(rootDir, inFile);
|
|
11010
11101
|
if (await pathExists(artifactPath)) {
|
|
11011
11102
|
const staleness = scanStaleness(rootDir, artifactPath);
|
|
11012
11103
|
if (!staleness.stale) {
|
|
@@ -11033,7 +11124,7 @@ async function repositoryIdentity(rootDir, override) {
|
|
|
11033
11124
|
}
|
|
11034
11125
|
}
|
|
11035
11126
|
var fixCommand = new Command("fix").description("Get ranked, risk-tiered upgrade plans from the hosted planner and optionally apply them (prompts before changing files; --yes applies non-interactively, --dry-run previews, --no-apply only prints). Requires login/DSN.").argument("[path]", "Path to analyse", ".").option("--in <file>", "Scan artifact to read", ".vibgrate/scan_result.json").option("--format <format>", "Output format (text|json|md)", "text").option("--dsn <dsn>", 'DSN token (or use VIBGRATE_DSN env / "vg login")').option("--region <region>", "Override data residency region (us, eu)").option("--repository-name <name>", "Override the repository name recorded for this plan").option("--plan <tier>", "Apply a specific plan non-interactively (safe|balanced|aggressive)").option("--yes", "Apply the recommended plan without prompting").option("--dry-run", "Preview the recommended plan (or --plan <tier>) without applying; never prompts").option("--no-apply", "Only print the plans; never modify the project").option("--fail-on-vulns <severity>", "Exit non-zero if the recommended plan leaves an advisory at/above this severity unresolved (low|moderate|high|critical)").action(async (targetPath, opts) => {
|
|
11036
|
-
const rootDir =
|
|
11127
|
+
const rootDir = path46.resolve(targetPath);
|
|
11037
11128
|
if (!await pathExists(rootDir)) {
|
|
11038
11129
|
console.error(chalk7.red(`Path does not exist: ${rootDir}`));
|
|
11039
11130
|
process.exit(1);
|
|
@@ -11194,7 +11285,7 @@ function emit(report, format) {
|
|
|
11194
11285
|
}
|
|
11195
11286
|
}
|
|
11196
11287
|
function relProjectDir(rootDir, projectPath) {
|
|
11197
|
-
const rel =
|
|
11288
|
+
const rel = path46.isAbsolute(projectPath) ? path46.relative(rootDir, projectPath) : projectPath;
|
|
11198
11289
|
return rel === "" ? "." : rel;
|
|
11199
11290
|
}
|
|
11200
11291
|
async function buildTargetResolver(rootDir, artifact) {
|
|
@@ -11235,14 +11326,14 @@ function promptPlanSelection(response) {
|
|
|
11235
11326
|
const defaultIdx = plans.findIndex((p) => p.tier === response.recommended);
|
|
11236
11327
|
const def = defaultIdx >= 0 ? defaultIdx + 1 : 1;
|
|
11237
11328
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
11238
|
-
return new Promise((
|
|
11329
|
+
return new Promise((resolve23) => {
|
|
11239
11330
|
rl.question(`Plan [1-${plans.length}, default ${def}, q to cancel]: `, (ans) => {
|
|
11240
11331
|
rl.close();
|
|
11241
11332
|
const t = ans.trim().toLowerCase();
|
|
11242
|
-
if (t === "q" || t === "quit") return
|
|
11333
|
+
if (t === "q" || t === "quit") return resolve23(null);
|
|
11243
11334
|
const n = t === "" ? def : Number.parseInt(t, 10);
|
|
11244
|
-
if (!Number.isInteger(n) || n < 1 || n > plans.length) return
|
|
11245
|
-
|
|
11335
|
+
if (!Number.isInteger(n) || n < 1 || n > plans.length) return resolve23(null);
|
|
11336
|
+
resolve23(plans[n - 1].tier);
|
|
11246
11337
|
});
|
|
11247
11338
|
});
|
|
11248
11339
|
}
|
|
@@ -11664,6 +11755,19 @@ function formatArchitectureDiagram(arch) {
|
|
|
11664
11755
|
lines.push("");
|
|
11665
11756
|
lines.push(chalk7.bold(" Archetype: ") + `${arch.archetype}` + chalk7.dim(` (${Math.round(arch.archetypeConfidence * 100)}% confidence)`));
|
|
11666
11757
|
lines.push(` Files classified: ${arch.totalClassified}` + (arch.unclassified > 0 ? chalk7.dim(` (${arch.unclassified} unclassified)`) : ""));
|
|
11758
|
+
if (arch.folders && arch.folders.length > 0) {
|
|
11759
|
+
lines.push(` Folders classified: ${arch.folders.length}`);
|
|
11760
|
+
for (const folder of arch.folders.slice(0, 12)) {
|
|
11761
|
+
lines.push(` ${folder.path} ${folder.layer} ${Math.round(folder.confidence * 100)}% ${folder.fileCount} file${folder.fileCount !== 1 ? "s" : ""}`);
|
|
11762
|
+
}
|
|
11763
|
+
if (arch.folders.length > 12) {
|
|
11764
|
+
lines.push(chalk7.dim(` + ${arch.folders.length - 12} more`));
|
|
11765
|
+
}
|
|
11766
|
+
}
|
|
11767
|
+
if (arch.unclassifiedFiles && arch.unclassifiedFiles.length > 0) {
|
|
11768
|
+
const extra = arch.unclassified > arch.unclassifiedFiles.length ? ` of ${arch.unclassified}` : "";
|
|
11769
|
+
lines.push(` Unclassified source (sample): ${arch.unclassifiedFiles.length}${extra}`);
|
|
11770
|
+
}
|
|
11667
11771
|
lines.push("");
|
|
11668
11772
|
if (arch.layers.length > 0) {
|
|
11669
11773
|
for (const layer of arch.layers) {
|
|
@@ -11672,6 +11776,17 @@ function formatArchitectureDiagram(arch) {
|
|
|
11672
11776
|
}
|
|
11673
11777
|
lines.push("");
|
|
11674
11778
|
}
|
|
11779
|
+
if (arch.violations && arch.violations.length > 0) {
|
|
11780
|
+
const extra = arch.violationsCapped ? "+" : "";
|
|
11781
|
+
lines.push(` Boundary violations (${arch.violations.length}${extra})`);
|
|
11782
|
+
for (const v of arch.violations.slice(0, 8)) {
|
|
11783
|
+
lines.push(` ${v.fromFile} \u2192 ${v.toFile} ${v.rule}`);
|
|
11784
|
+
}
|
|
11785
|
+
if (arch.violations.length > 8) {
|
|
11786
|
+
lines.push(chalk7.dim(` + ${arch.violations.length - 8} more`));
|
|
11787
|
+
}
|
|
11788
|
+
lines.push("");
|
|
11789
|
+
}
|
|
11675
11790
|
return lines;
|
|
11676
11791
|
}
|
|
11677
11792
|
function generatePriorityActions(artifact) {
|
|
@@ -12037,7 +12152,7 @@ function formatMarkdown(artifact) {
|
|
|
12037
12152
|
|
|
12038
12153
|
// src/reporting/commands/report.ts
|
|
12039
12154
|
var reportCommand = new Command("report").description("Generate a drift report from a scan artifact").option("--in <file>", "Input artifact file", ".vibgrate/scan_result.json").option("--format <format>", "Output format (md|text|json)", "text").action(async (opts) => {
|
|
12040
|
-
const artifactPath =
|
|
12155
|
+
const artifactPath = path46.resolve(opts.in);
|
|
12041
12156
|
if (!await pathExists(artifactPath)) {
|
|
12042
12157
|
console.error(chalk7.red(`Artifact not found: ${artifactPath}`));
|
|
12043
12158
|
console.error(chalk7.dim('Run "vibgrate scan" first to generate a scan artifact.'));
|
|
@@ -12084,10 +12199,10 @@ function openUrl(url) {
|
|
|
12084
12199
|
}
|
|
12085
12200
|
|
|
12086
12201
|
// src/reporting/commands/login.ts
|
|
12087
|
-
var delay = (ms) => new Promise((
|
|
12202
|
+
var delay = (ms) => new Promise((resolve23) => setTimeout(resolve23, ms));
|
|
12088
12203
|
function isInside(dir, file) {
|
|
12089
|
-
const rel =
|
|
12090
|
-
return !!rel && !rel.startsWith("..") && !
|
|
12204
|
+
const rel = path46.relative(dir, file);
|
|
12205
|
+
return !!rel && !rel.startsWith("..") && !path46.isAbsolute(rel);
|
|
12091
12206
|
}
|
|
12092
12207
|
var loginCommand = new Command("login").description("Authenticate the CLI with your Vibgrate workspace via the browser").option("--ingest <url>", "Ingest API URL (overrides --region)").option("--region <region>", `Data residency region (${availableRegionIds().join(", ")})`, "us").option("--no-browser", "Do not attempt to open a browser automatically").option(
|
|
12093
12208
|
"--local",
|
|
@@ -12191,9 +12306,9 @@ var loginCommand = new Command("login").description("Authenticate the CLI with y
|
|
|
12191
12306
|
}
|
|
12192
12307
|
console.log(chalk7.dim(` Credentials saved to ${credsFile}`));
|
|
12193
12308
|
try {
|
|
12194
|
-
const
|
|
12195
|
-
if (
|
|
12196
|
-
const res = ensureGitignored(gitignoreEntryForCredentials(
|
|
12309
|
+
const root3 = findGitRoot();
|
|
12310
|
+
if (root3 && isInside(root3, credsFile)) {
|
|
12311
|
+
const res = ensureGitignored(gitignoreEntryForCredentials(root3, credsFile), root3);
|
|
12197
12312
|
if (res.status === "created") {
|
|
12198
12313
|
console.log(chalk7.dim(` Created .gitignore and ignored ${res.entry}`));
|
|
12199
12314
|
} else if (res.status === "added") {
|
|
@@ -12253,6 +12368,789 @@ var logoutCommand = new Command("logout").description("Clear stored Vibgrate log
|
|
|
12253
12368
|
);
|
|
12254
12369
|
}
|
|
12255
12370
|
});
|
|
12371
|
+
|
|
12372
|
+
// src/reporting/commands/locale/client.ts
|
|
12373
|
+
function buildUrl(scheme, host, path48, query) {
|
|
12374
|
+
const url = new URL(`${scheme}://${host}/v1/locale${path48}`);
|
|
12375
|
+
for (const [key2, value] of Object.entries(query ?? {})) {
|
|
12376
|
+
if (value !== void 0 && value !== "") url.searchParams.set(key2, String(value));
|
|
12377
|
+
}
|
|
12378
|
+
return url.toString();
|
|
12379
|
+
}
|
|
12380
|
+
async function toEnvelope(response, url) {
|
|
12381
|
+
let envelope = null;
|
|
12382
|
+
const text = await response.text();
|
|
12383
|
+
try {
|
|
12384
|
+
envelope = text ? JSON.parse(text) : null;
|
|
12385
|
+
} catch {
|
|
12386
|
+
envelope = null;
|
|
12387
|
+
}
|
|
12388
|
+
if (response.ok && envelope) return envelope;
|
|
12389
|
+
const serverMessage = envelope?.errors?.map((e) => e.message).join(" ") ?? "";
|
|
12390
|
+
const requestId = envelope?.meta?.requestId;
|
|
12391
|
+
const suffix = requestId ? ` (request ${requestId})` : "";
|
|
12392
|
+
if (response.status === 401) {
|
|
12393
|
+
throw new CliError(
|
|
12394
|
+
`${serverMessage || "Authentication failed."}${suffix}
|
|
12395
|
+
Run "vg login", set VIBGRATE_DSN, or pass --dsn.`,
|
|
12396
|
+
ExitCode.ERROR
|
|
12397
|
+
);
|
|
12398
|
+
}
|
|
12399
|
+
if (response.status === 402) {
|
|
12400
|
+
throw new CliError(`${serverMessage || "This feature requires a paid plan."}${suffix}`, ExitCode.ERROR);
|
|
12401
|
+
}
|
|
12402
|
+
if (response.status === 403) {
|
|
12403
|
+
throw new CliError(`${serverMessage || "Not permitted."}${suffix}`, ExitCode.ERROR);
|
|
12404
|
+
}
|
|
12405
|
+
if (response.status === 404) {
|
|
12406
|
+
throw new CliError(`${serverMessage || "Not found."}${suffix}`, ExitCode.ERROR);
|
|
12407
|
+
}
|
|
12408
|
+
throw new CliError(
|
|
12409
|
+
`Vibgrate Cloud returned ${response.status} for ${new URL(url).pathname}.${suffix}` + (serverMessage ? `
|
|
12410
|
+
${serverMessage}` : ""),
|
|
12411
|
+
ExitCode.ERROR
|
|
12412
|
+
);
|
|
12413
|
+
}
|
|
12414
|
+
function createLocaleClient(dsn) {
|
|
12415
|
+
const parsed = parseDsn$1(dsn);
|
|
12416
|
+
if (!parsed) {
|
|
12417
|
+
throw new CliError(
|
|
12418
|
+
"Invalid DSN format.\nExpected: vibgrate+https://<key_id>:<secret>@<host>/<workspace_id>",
|
|
12419
|
+
ExitCode.ERROR
|
|
12420
|
+
);
|
|
12421
|
+
}
|
|
12422
|
+
const request = async (method, path48, opts = {}) => {
|
|
12423
|
+
const url = buildUrl(parsed.scheme, parsed.host, path48, opts.query);
|
|
12424
|
+
const body = opts.body === void 0 ? void 0 : JSON.stringify(opts.body);
|
|
12425
|
+
const response = await fetch(url, {
|
|
12426
|
+
method,
|
|
12427
|
+
headers: {
|
|
12428
|
+
// Same presentation as the ingest path — see the module header.
|
|
12429
|
+
Authorization: `VibgrateDSN ${parsed.keyId}:${parsed.secret}`,
|
|
12430
|
+
"X-Vibgrate-Timestamp": String(Date.now()),
|
|
12431
|
+
...body ? { "Content-Type": "application/json" } : {}
|
|
12432
|
+
},
|
|
12433
|
+
body
|
|
12434
|
+
});
|
|
12435
|
+
return toEnvelope(response, url);
|
|
12436
|
+
};
|
|
12437
|
+
return {
|
|
12438
|
+
workspaceId: parsed.workspaceId,
|
|
12439
|
+
get: (path48, query) => request("GET", path48, { query }),
|
|
12440
|
+
post: (path48, body) => request("POST", path48, { body }),
|
|
12441
|
+
patch: (path48, body) => request("PATCH", path48, { body }),
|
|
12442
|
+
del: (path48, body) => request("DELETE", path48, { body })
|
|
12443
|
+
};
|
|
12444
|
+
}
|
|
12445
|
+
var LOCALE_CONFIG_FILENAMES = ["vibgrate.locale.yaml", ".vibgrate/locale.yaml"];
|
|
12446
|
+
function findLocaleConfig(cwd) {
|
|
12447
|
+
for (const name of LOCALE_CONFIG_FILENAMES) {
|
|
12448
|
+
const candidate = path46.join(cwd, name);
|
|
12449
|
+
if (fs14.existsSync(candidate)) return candidate;
|
|
12450
|
+
}
|
|
12451
|
+
return null;
|
|
12452
|
+
}
|
|
12453
|
+
function parseLocaleConfig(text) {
|
|
12454
|
+
const raw = parse(text);
|
|
12455
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
12456
|
+
const obj = raw;
|
|
12457
|
+
const stringArray = (value) => {
|
|
12458
|
+
if (!Array.isArray(value)) return void 0;
|
|
12459
|
+
const items = value.filter((v) => typeof v === "string" && v.trim().length > 0);
|
|
12460
|
+
return items.length ? items : void 0;
|
|
12461
|
+
};
|
|
12462
|
+
const str = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
12463
|
+
return {
|
|
12464
|
+
project: str(obj.project),
|
|
12465
|
+
sourceLanguage: str(obj.sourceLanguage),
|
|
12466
|
+
targetLanguages: stringArray(obj.targetLanguages),
|
|
12467
|
+
format: str(obj.format),
|
|
12468
|
+
path: str(obj.path),
|
|
12469
|
+
namespaces: stringArray(obj.namespaces),
|
|
12470
|
+
version: str(obj.version)
|
|
12471
|
+
};
|
|
12472
|
+
}
|
|
12473
|
+
function loadLocaleConfig(cwd) {
|
|
12474
|
+
const file = findLocaleConfig(cwd);
|
|
12475
|
+
if (!file) return { config: {}, file: null };
|
|
12476
|
+
try {
|
|
12477
|
+
return { config: parseLocaleConfig(fs14.readFileSync(file, "utf8")), file };
|
|
12478
|
+
} catch (error) {
|
|
12479
|
+
throw new Error(
|
|
12480
|
+
`Could not parse ${path46.relative(cwd, file)}: ${error instanceof Error ? error.message : String(error)}`
|
|
12481
|
+
);
|
|
12482
|
+
}
|
|
12483
|
+
}
|
|
12484
|
+
function serializeLocaleConfig(config) {
|
|
12485
|
+
const out2 = {};
|
|
12486
|
+
for (const [key2, value] of Object.entries(config)) {
|
|
12487
|
+
if (value === void 0 || value === null) continue;
|
|
12488
|
+
if (Array.isArray(value) && value.length === 0) continue;
|
|
12489
|
+
if (typeof value === "string" && !value.trim()) continue;
|
|
12490
|
+
out2[key2] = value;
|
|
12491
|
+
}
|
|
12492
|
+
return stringify(out2);
|
|
12493
|
+
}
|
|
12494
|
+
function writeLocaleConfig(cwd, config, filename) {
|
|
12495
|
+
const target = filename ?? path46.join(cwd, LOCALE_CONFIG_FILENAMES[0]);
|
|
12496
|
+
fs14.mkdirSync(path46.dirname(target), { recursive: true });
|
|
12497
|
+
fs14.writeFileSync(target, serializeLocaleConfig(config), "utf8");
|
|
12498
|
+
return target;
|
|
12499
|
+
}
|
|
12500
|
+
function resolveLocaleSettings(config, flags) {
|
|
12501
|
+
return {
|
|
12502
|
+
project: flags.project ?? config.project,
|
|
12503
|
+
sourceLanguage: flags.sourceLanguage ?? config.sourceLanguage ?? "en-US",
|
|
12504
|
+
targetLanguages: flags.targetLanguages ?? config.targetLanguages ?? [],
|
|
12505
|
+
format: flags.format ?? config.format ?? "nested",
|
|
12506
|
+
path: flags.path ?? config.path ?? "src/locales/{language}/{namespace}.json",
|
|
12507
|
+
namespaces: flags.namespaces ?? config.namespaces ?? ["common"],
|
|
12508
|
+
version: flags.version ?? config.version ?? "latest"
|
|
12509
|
+
};
|
|
12510
|
+
}
|
|
12511
|
+
var SUPPORTED_FORMATS = ["json", "nested", "flat", "yaml", "yml"];
|
|
12512
|
+
var PLANNED_FORMATS = [
|
|
12513
|
+
"po",
|
|
12514
|
+
"xliff",
|
|
12515
|
+
"xliff2",
|
|
12516
|
+
"android",
|
|
12517
|
+
"strings",
|
|
12518
|
+
"resx",
|
|
12519
|
+
"csv",
|
|
12520
|
+
"arb",
|
|
12521
|
+
"properties"
|
|
12522
|
+
];
|
|
12523
|
+
var LocaleNestConflictError = class extends Error {
|
|
12524
|
+
constructor(leafKey, branchKey) {
|
|
12525
|
+
super(
|
|
12526
|
+
`Cannot write nested output: "${leafKey}" is a value and "${branchKey}" needs it to be a group. Rename one of them, or switch this project to the "flat" format, which stores keys verbatim.`
|
|
12527
|
+
);
|
|
12528
|
+
this.leafKey = leafKey;
|
|
12529
|
+
this.branchKey = branchKey;
|
|
12530
|
+
this.name = "LocaleNestConflictError";
|
|
12531
|
+
}
|
|
12532
|
+
leafKey;
|
|
12533
|
+
branchKey;
|
|
12534
|
+
};
|
|
12535
|
+
function nest(catalog) {
|
|
12536
|
+
const out2 = {};
|
|
12537
|
+
for (const [key2, value] of Object.entries(catalog)) {
|
|
12538
|
+
const parts = key2.split(".");
|
|
12539
|
+
let cursor = out2;
|
|
12540
|
+
for (let i = 0; i < parts.length - 1; i += 1) {
|
|
12541
|
+
const part = parts[i];
|
|
12542
|
+
const existing2 = cursor[part];
|
|
12543
|
+
if (existing2 === void 0) {
|
|
12544
|
+
cursor[part] = {};
|
|
12545
|
+
} else if (typeof existing2 !== "object" || existing2 === null || Array.isArray(existing2)) {
|
|
12546
|
+
throw new LocaleNestConflictError(parts.slice(0, i + 1).join("."), key2);
|
|
12547
|
+
}
|
|
12548
|
+
cursor = cursor[part];
|
|
12549
|
+
}
|
|
12550
|
+
const leaf = parts[parts.length - 1];
|
|
12551
|
+
const existing = cursor[leaf];
|
|
12552
|
+
if (typeof existing === "object" && existing !== null && !Array.isArray(existing)) {
|
|
12553
|
+
throw new LocaleNestConflictError(key2, `${key2}.\u2026`);
|
|
12554
|
+
}
|
|
12555
|
+
cursor[leaf] = value;
|
|
12556
|
+
}
|
|
12557
|
+
return out2;
|
|
12558
|
+
}
|
|
12559
|
+
function flatten(input, prefix = "") {
|
|
12560
|
+
const out2 = {};
|
|
12561
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
12562
|
+
if (prefix) out2[prefix] = input === null || input === void 0 ? "" : String(input);
|
|
12563
|
+
return out2;
|
|
12564
|
+
}
|
|
12565
|
+
for (const [key2, value] of Object.entries(input)) {
|
|
12566
|
+
const next2 = prefix ? `${prefix}.${key2}` : key2;
|
|
12567
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
12568
|
+
Object.assign(out2, flatten(value, next2));
|
|
12569
|
+
} else if (Array.isArray(value)) {
|
|
12570
|
+
out2[next2] = JSON.stringify(value);
|
|
12571
|
+
} else {
|
|
12572
|
+
out2[next2] = value === null || value === void 0 ? "" : String(value);
|
|
12573
|
+
}
|
|
12574
|
+
}
|
|
12575
|
+
return out2;
|
|
12576
|
+
}
|
|
12577
|
+
var nestedJson = {
|
|
12578
|
+
id: "nested",
|
|
12579
|
+
extension: ".json",
|
|
12580
|
+
encode: (catalog) => `${JSON.stringify(nest(sortKeys(catalog)), null, 2)}
|
|
12581
|
+
`,
|
|
12582
|
+
decode: (text) => text.trim() ? flatten(JSON.parse(text)) : {}
|
|
12583
|
+
};
|
|
12584
|
+
var flatJson = {
|
|
12585
|
+
id: "flat",
|
|
12586
|
+
extension: ".json",
|
|
12587
|
+
encode: (catalog) => `${JSON.stringify(sortKeys(catalog), null, 2)}
|
|
12588
|
+
`,
|
|
12589
|
+
decode: (text) => {
|
|
12590
|
+
if (!text.trim()) return {};
|
|
12591
|
+
const parsed = JSON.parse(text);
|
|
12592
|
+
const out2 = {};
|
|
12593
|
+
for (const [key2, value] of Object.entries(parsed)) {
|
|
12594
|
+
out2[key2] = value === null || value === void 0 ? "" : String(value);
|
|
12595
|
+
}
|
|
12596
|
+
return out2;
|
|
12597
|
+
}
|
|
12598
|
+
};
|
|
12599
|
+
var yamlCodec = {
|
|
12600
|
+
id: "yaml",
|
|
12601
|
+
extension: ".yaml",
|
|
12602
|
+
encode: (catalog) => stringify(nest(sortKeys(catalog))),
|
|
12603
|
+
decode: (text) => text.trim() ? flatten(parse(text)) : {}
|
|
12604
|
+
};
|
|
12605
|
+
function sortKeys(catalog) {
|
|
12606
|
+
return Object.fromEntries(Object.entries(catalog).sort(([a], [b]) => a.localeCompare(b)));
|
|
12607
|
+
}
|
|
12608
|
+
var CODECS = {
|
|
12609
|
+
json: nestedJson,
|
|
12610
|
+
nested: nestedJson,
|
|
12611
|
+
flat: flatJson,
|
|
12612
|
+
yaml: yamlCodec,
|
|
12613
|
+
yml: { ...yamlCodec, id: "yml", extension: ".yml" }
|
|
12614
|
+
};
|
|
12615
|
+
function isSupportedFormat(format) {
|
|
12616
|
+
return format in CODECS;
|
|
12617
|
+
}
|
|
12618
|
+
function isPlannedFormat(format) {
|
|
12619
|
+
return PLANNED_FORMATS.includes(format);
|
|
12620
|
+
}
|
|
12621
|
+
function codecFor(format) {
|
|
12622
|
+
const codec = CODECS[format];
|
|
12623
|
+
if (codec) return codec;
|
|
12624
|
+
if (isPlannedFormat(format)) {
|
|
12625
|
+
throw new Error(
|
|
12626
|
+
`The "${format}" format is not available in this release yet. Supported now: ${SUPPORTED_FORMATS.join(", ")}.`
|
|
12627
|
+
);
|
|
12628
|
+
}
|
|
12629
|
+
throw new Error(
|
|
12630
|
+
`Unknown format "${format}". Supported: ${SUPPORTED_FORMATS.join(", ")}. Planned: ${PLANNED_FORMATS.join(", ")}.`
|
|
12631
|
+
);
|
|
12632
|
+
}
|
|
12633
|
+
function validatePathTemplate(template) {
|
|
12634
|
+
if (!template.trim()) return "path template is required.";
|
|
12635
|
+
if (!template.includes("{language}")) {
|
|
12636
|
+
return 'path template must contain {language} (e.g. "src/locales/{language}/{namespace}.json").';
|
|
12637
|
+
}
|
|
12638
|
+
if (path46.isAbsolute(template)) {
|
|
12639
|
+
return "path template must be relative to the project directory.";
|
|
12640
|
+
}
|
|
12641
|
+
if (template.split(/[\\/]/).includes("..")) {
|
|
12642
|
+
return 'path template must not contain "..".';
|
|
12643
|
+
}
|
|
12644
|
+
return null;
|
|
12645
|
+
}
|
|
12646
|
+
function renderPath(template, language, namespace) {
|
|
12647
|
+
return template.replaceAll("{language}", language).replaceAll("{namespace}", namespace);
|
|
12648
|
+
}
|
|
12649
|
+
function resolveLocaleFiles(cwd, template, languages, namespaces) {
|
|
12650
|
+
const out2 = [];
|
|
12651
|
+
for (const language of languages) {
|
|
12652
|
+
for (const namespace of namespaces) {
|
|
12653
|
+
const relative13 = renderPath(template, language, namespace);
|
|
12654
|
+
const file = path46.resolve(cwd, relative13);
|
|
12655
|
+
out2.push({ language, namespace, file, relative: relative13, exists: fs14.existsSync(file) });
|
|
12656
|
+
}
|
|
12657
|
+
}
|
|
12658
|
+
return out2;
|
|
12659
|
+
}
|
|
12660
|
+
function readCatalog(file, format) {
|
|
12661
|
+
if (!fs14.existsSync(file)) return {};
|
|
12662
|
+
const text = fs14.readFileSync(file, "utf8");
|
|
12663
|
+
try {
|
|
12664
|
+
return codecFor(format).decode(text);
|
|
12665
|
+
} catch (error) {
|
|
12666
|
+
throw new Error(
|
|
12667
|
+
`Could not parse ${file}: ${error instanceof Error ? error.message : String(error)}`
|
|
12668
|
+
);
|
|
12669
|
+
}
|
|
12670
|
+
}
|
|
12671
|
+
function writeCatalog(file, format, catalog, opts = {}) {
|
|
12672
|
+
const encoded = codecFor(format).encode(sortKeys(catalog));
|
|
12673
|
+
const existing = fs14.existsSync(file) ? fs14.readFileSync(file, "utf8") : null;
|
|
12674
|
+
const changed = existing !== encoded;
|
|
12675
|
+
if (changed && !opts.dryRun) {
|
|
12676
|
+
fs14.mkdirSync(path46.dirname(file), { recursive: true });
|
|
12677
|
+
fs14.writeFileSync(file, encoded, "utf8");
|
|
12678
|
+
}
|
|
12679
|
+
return { file, changed, bytes: Buffer.byteLength(encoded, "utf8") };
|
|
12680
|
+
}
|
|
12681
|
+
function diffCatalogs(before, after) {
|
|
12682
|
+
const diff2 = { added: [], changed: [], removed: [], unchanged: [] };
|
|
12683
|
+
for (const [key2, value] of Object.entries(after)) {
|
|
12684
|
+
if (!(key2 in before)) diff2.added.push(key2);
|
|
12685
|
+
else if (before[key2] !== value) diff2.changed.push(key2);
|
|
12686
|
+
else diff2.unchanged.push(key2);
|
|
12687
|
+
}
|
|
12688
|
+
for (const key2 of Object.keys(before)) {
|
|
12689
|
+
if (!(key2 in after)) diff2.removed.push(key2);
|
|
12690
|
+
}
|
|
12691
|
+
return diff2;
|
|
12692
|
+
}
|
|
12693
|
+
|
|
12694
|
+
// src/reporting/commands/locale/index.ts
|
|
12695
|
+
var cwdOption = ["-C, --cwd <dir>", "Run against this directory (default: current)"];
|
|
12696
|
+
function root(opts) {
|
|
12697
|
+
return path46.resolve(opts.cwd ?? process.cwd());
|
|
12698
|
+
}
|
|
12699
|
+
function withCommonOptions(cmd) {
|
|
12700
|
+
return cmd.option(...cwdOption).option("--dsn <dsn>", 'DSN token (or use VIBGRATE_DSN, or run "vg login")').option("--project <slug>", "Locale project slug or id").option("--format <format>", `Locale file format (${SUPPORTED_FORMATS.join(", ")})`).option("--path <template>", "Path template, e.g. src/locales/{language}/{namespace}.json").option("--namespace <name>", "Restrict to one namespace").option("--language <code>", "Restrict to one BCP-47 language").option("--ver <version>", "Target version (default: latest)").option("--json", "Machine-readable output").option("-q, --quiet", "Suppress non-essential output");
|
|
12701
|
+
}
|
|
12702
|
+
function settingsFor(opts) {
|
|
12703
|
+
const dir = root(opts);
|
|
12704
|
+
const { config } = loadLocaleConfig(dir);
|
|
12705
|
+
const merged = resolveLocaleSettings(config, {
|
|
12706
|
+
project: opts.project,
|
|
12707
|
+
format: opts.format,
|
|
12708
|
+
path: opts.path,
|
|
12709
|
+
version: opts.ver,
|
|
12710
|
+
namespaces: opts.namespace ? [opts.namespace] : void 0,
|
|
12711
|
+
targetLanguages: opts.language ? [opts.language] : void 0
|
|
12712
|
+
});
|
|
12713
|
+
if (!merged.project) {
|
|
12714
|
+
throw new CliError(
|
|
12715
|
+
'No locale project configured.\nRun "vg locale config --project <slug>", or pass --project <slug>.',
|
|
12716
|
+
ExitCode.ERROR
|
|
12717
|
+
);
|
|
12718
|
+
}
|
|
12719
|
+
if (!isSupportedFormat(merged.format)) {
|
|
12720
|
+
codecFor(merged.format);
|
|
12721
|
+
}
|
|
12722
|
+
const pathError = validatePathTemplate(merged.path);
|
|
12723
|
+
if (pathError) throw new CliError(pathError, ExitCode.ERROR);
|
|
12724
|
+
return {
|
|
12725
|
+
dir,
|
|
12726
|
+
settings: merged
|
|
12727
|
+
};
|
|
12728
|
+
}
|
|
12729
|
+
function clientFor(opts) {
|
|
12730
|
+
const dsn = resolveDsn(opts.dsn);
|
|
12731
|
+
if (!dsn) {
|
|
12732
|
+
throw new CliError(
|
|
12733
|
+
'No DSN available.\nRun "vg login", set VIBGRATE_DSN, or pass --dsn.',
|
|
12734
|
+
ExitCode.ERROR
|
|
12735
|
+
);
|
|
12736
|
+
}
|
|
12737
|
+
return createLocaleClient(dsn);
|
|
12738
|
+
}
|
|
12739
|
+
function emit2(opts, payload, human) {
|
|
12740
|
+
if (opts.json) {
|
|
12741
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
12742
|
+
return;
|
|
12743
|
+
}
|
|
12744
|
+
if (!opts.quiet) human();
|
|
12745
|
+
}
|
|
12746
|
+
var configCmd = withCommonOptions(
|
|
12747
|
+
new Command("config").description("Create or update vibgrate.locale.yaml (no credentials are stored)")
|
|
12748
|
+
).option("--source-language <code>", "Source language (default: en-US)").option("--target-languages <codes>", "Comma-separated target languages").option("--namespaces <names>", "Comma-separated namespaces").action(
|
|
12749
|
+
async (opts) => {
|
|
12750
|
+
const dir = root(opts);
|
|
12751
|
+
const { config, file } = loadLocaleConfig(dir);
|
|
12752
|
+
const csv2 = (v) => v ? v.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
12753
|
+
const next2 = {
|
|
12754
|
+
project: opts.project ?? config.project,
|
|
12755
|
+
sourceLanguage: opts.sourceLanguage ?? config.sourceLanguage ?? "en-US",
|
|
12756
|
+
targetLanguages: csv2(opts.targetLanguages) ?? config.targetLanguages,
|
|
12757
|
+
format: opts.format ?? config.format ?? "nested",
|
|
12758
|
+
path: opts.path ?? config.path ?? "src/locales/{language}/{namespace}.json",
|
|
12759
|
+
namespaces: csv2(opts.namespaces) ?? config.namespaces ?? ["common"],
|
|
12760
|
+
version: opts.ver ?? config.version ?? "latest"
|
|
12761
|
+
};
|
|
12762
|
+
const pathError = validatePathTemplate(next2.path);
|
|
12763
|
+
if (pathError) throw new CliError(pathError, ExitCode.ERROR);
|
|
12764
|
+
if (!isSupportedFormat(next2.format)) codecFor(next2.format);
|
|
12765
|
+
const written = writeLocaleConfig(dir, next2, file ?? void 0);
|
|
12766
|
+
emit2(opts, { file: written, config: next2 }, () => {
|
|
12767
|
+
console.log(chalk7.green(`Wrote ${path46.relative(dir, written) || path46.basename(written)}`));
|
|
12768
|
+
console.log(
|
|
12769
|
+
chalk7.dim('Credentials are not stored here \u2014 run "vg login" or set VIBGRATE_DSN.')
|
|
12770
|
+
);
|
|
12771
|
+
});
|
|
12772
|
+
}
|
|
12773
|
+
);
|
|
12774
|
+
var statusCmd = withCommonOptions(
|
|
12775
|
+
new Command("status").description("Show translation coverage and health for the locale project")
|
|
12776
|
+
).action(async (opts) => {
|
|
12777
|
+
const { settings } = settingsFor(opts);
|
|
12778
|
+
const client = clientFor(opts);
|
|
12779
|
+
const res = await client.get(
|
|
12780
|
+
`/projects/${encodeURIComponent(settings.project)}/status`
|
|
12781
|
+
);
|
|
12782
|
+
const data = res.data;
|
|
12783
|
+
if (!data) throw new CliError("Vibgrate Cloud returned no status data.", ExitCode.ERROR);
|
|
12784
|
+
emit2(opts, data, () => {
|
|
12785
|
+
console.log(chalk7.bold(`${data.project.name} (${data.project.slug})`));
|
|
12786
|
+
console.log(chalk7.dim(`Source: ${data.project.sourceLanguage} \xB7 ${data.coverage.totalKeys} keys`));
|
|
12787
|
+
console.log("");
|
|
12788
|
+
for (const lang of data.coverage.languages) {
|
|
12789
|
+
const pct = lang.coveragePercent === null ? "\u2014" : `${lang.coveragePercent}%`;
|
|
12790
|
+
const parts = [`${lang.translated} translated`];
|
|
12791
|
+
if (lang.machine) parts.push(chalk7.yellow(`${lang.machine} machine`));
|
|
12792
|
+
if (lang.needsReview) parts.push(chalk7.yellow(`${lang.needsReview} to review`));
|
|
12793
|
+
if (lang.missing) parts.push(chalk7.dim(`${lang.missing} missing`));
|
|
12794
|
+
console.log(` ${lang.language.padEnd(10)} ${pct.padStart(6)} ${parts.join(" \xB7 ")}`);
|
|
12795
|
+
}
|
|
12796
|
+
if (data.namespaces.length) {
|
|
12797
|
+
console.log("");
|
|
12798
|
+
console.log(chalk7.dim(`Namespaces: ${data.namespaces.map((n) => `${n.name} (${n.keys})`).join(", ")}`));
|
|
12799
|
+
}
|
|
12800
|
+
console.log(
|
|
12801
|
+
chalk7.dim(
|
|
12802
|
+
data.lastTranslationAt ? `Last translation: ${data.lastTranslationAt}` : "No translations recorded yet."
|
|
12803
|
+
)
|
|
12804
|
+
);
|
|
12805
|
+
});
|
|
12806
|
+
});
|
|
12807
|
+
function collectLocalKeys(dir, settings, languages) {
|
|
12808
|
+
const namespaces = settings.namespaces ?? ["common"];
|
|
12809
|
+
const sourceLanguage = settings.sourceLanguage ?? "en-US";
|
|
12810
|
+
const format = settings.format;
|
|
12811
|
+
const template = settings.path;
|
|
12812
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
12813
|
+
const filesRead = [];
|
|
12814
|
+
const allLanguages = [sourceLanguage, ...languages.filter((l) => l !== sourceLanguage)];
|
|
12815
|
+
for (const file of resolveLocaleFiles(dir, template, allLanguages, namespaces)) {
|
|
12816
|
+
if (!file.exists) continue;
|
|
12817
|
+
filesRead.push(file.relative);
|
|
12818
|
+
const catalog = readCatalog(file.file, format);
|
|
12819
|
+
for (const [key2, value] of Object.entries(catalog)) {
|
|
12820
|
+
const id = `${file.namespace}\0${key2}`;
|
|
12821
|
+
const entry = byKey.get(id) ?? {
|
|
12822
|
+
namespace: file.namespace,
|
|
12823
|
+
key: key2,
|
|
12824
|
+
sourceValue: null,
|
|
12825
|
+
translations: []
|
|
12826
|
+
};
|
|
12827
|
+
if (file.language === sourceLanguage) entry.sourceValue = value;
|
|
12828
|
+
else entry.translations.push({ language: file.language, value });
|
|
12829
|
+
byKey.set(id, entry);
|
|
12830
|
+
}
|
|
12831
|
+
}
|
|
12832
|
+
return { keys: [...byKey.values()], filesRead };
|
|
12833
|
+
}
|
|
12834
|
+
var PUSH_BATCH_SIZE = 400;
|
|
12835
|
+
function makePushCommand(name, description) {
|
|
12836
|
+
return withCommonOptions(new Command(name).description(description)).option("--dry-run", "Report what would be pushed without sending anything").option(
|
|
12837
|
+
"--source-only",
|
|
12838
|
+
"Push only source-language keys, leaving existing translations untouched"
|
|
12839
|
+
).action(async (opts) => {
|
|
12840
|
+
const { dir, settings } = settingsFor(opts);
|
|
12841
|
+
const languages = opts.sourceOnly ? [] : settings.targetLanguages ?? [];
|
|
12842
|
+
const { keys, filesRead } = collectLocalKeys(dir, settings, languages);
|
|
12843
|
+
if (!keys.length) {
|
|
12844
|
+
throw new CliError(
|
|
12845
|
+
`No locale files found for template "${settings.path}".
|
|
12846
|
+
Check the template with "vg locale config --path <template>", or create the source files first.`,
|
|
12847
|
+
ExitCode.ERROR
|
|
12848
|
+
);
|
|
12849
|
+
}
|
|
12850
|
+
if (opts.dryRun) {
|
|
12851
|
+
const summary = {
|
|
12852
|
+
dryRun: true,
|
|
12853
|
+
filesRead,
|
|
12854
|
+
keys: keys.length,
|
|
12855
|
+
translations: keys.reduce((n, k) => n + k.translations.length, 0),
|
|
12856
|
+
version: settings.version
|
|
12857
|
+
};
|
|
12858
|
+
emit2(opts, summary, () => {
|
|
12859
|
+
console.log(chalk7.bold("Dry run \u2014 nothing was sent."));
|
|
12860
|
+
console.log(` ${summary.keys} keys from ${filesRead.length} file(s)`);
|
|
12861
|
+
console.log(` ${summary.translations} translations`);
|
|
12862
|
+
console.log(chalk7.dim(` version: ${summary.version}`));
|
|
12863
|
+
});
|
|
12864
|
+
return;
|
|
12865
|
+
}
|
|
12866
|
+
const client = clientFor(opts);
|
|
12867
|
+
let created = 0;
|
|
12868
|
+
let updated = 0;
|
|
12869
|
+
let translationsWritten = 0;
|
|
12870
|
+
for (let i = 0; i < keys.length; i += PUSH_BATCH_SIZE) {
|
|
12871
|
+
const batch = keys.slice(i, i + PUSH_BATCH_SIZE);
|
|
12872
|
+
const res = await client.post(
|
|
12873
|
+
`/projects/${encodeURIComponent(settings.project)}/keys`,
|
|
12874
|
+
{
|
|
12875
|
+
version: settings.version,
|
|
12876
|
+
keys: batch.map((k) => ({
|
|
12877
|
+
namespace: k.namespace,
|
|
12878
|
+
key: k.key,
|
|
12879
|
+
sourceValue: k.sourceValue,
|
|
12880
|
+
translations: k.translations.map((t) => ({
|
|
12881
|
+
language: t.language,
|
|
12882
|
+
value: t.value,
|
|
12883
|
+
origin: "import"
|
|
12884
|
+
}))
|
|
12885
|
+
}))
|
|
12886
|
+
}
|
|
12887
|
+
);
|
|
12888
|
+
created += res.data?.created ?? 0;
|
|
12889
|
+
updated += res.data?.updated ?? 0;
|
|
12890
|
+
translationsWritten += res.data?.translationsWritten ?? 0;
|
|
12891
|
+
}
|
|
12892
|
+
emit2(opts, { created, updated, translationsWritten, version: settings.version }, () => {
|
|
12893
|
+
console.log(
|
|
12894
|
+
chalk7.green(
|
|
12895
|
+
`Pushed ${keys.length} keys \u2014 ${created} created, ${updated} updated, ${translationsWritten} translations.`
|
|
12896
|
+
)
|
|
12897
|
+
);
|
|
12898
|
+
});
|
|
12899
|
+
});
|
|
12900
|
+
}
|
|
12901
|
+
var PULL_PAGE_SIZE = 200;
|
|
12902
|
+
async function fetchAllKeys(client, project, language, namespace) {
|
|
12903
|
+
const out2 = [];
|
|
12904
|
+
let offset = 0;
|
|
12905
|
+
for (; ; ) {
|
|
12906
|
+
const res = await client.get(`/projects/${encodeURIComponent(project)}/keys`, {
|
|
12907
|
+
language,
|
|
12908
|
+
namespace,
|
|
12909
|
+
limit: PULL_PAGE_SIZE,
|
|
12910
|
+
offset
|
|
12911
|
+
});
|
|
12912
|
+
const page = res.data?.keys ?? [];
|
|
12913
|
+
out2.push(...page);
|
|
12914
|
+
const total = res.meta?.pagination?.total ?? out2.length;
|
|
12915
|
+
offset += page.length;
|
|
12916
|
+
if (!page.length || out2.length >= total) break;
|
|
12917
|
+
}
|
|
12918
|
+
return out2;
|
|
12919
|
+
}
|
|
12920
|
+
function makePullCommand(name, description) {
|
|
12921
|
+
return withCommonOptions(new Command(name).description(description)).option("--dry-run", "Report what would change without writing any file").option(
|
|
12922
|
+
"--include-machine",
|
|
12923
|
+
"Also write machine and needs-review translations (default: accepted translations only)"
|
|
12924
|
+
).action(async (opts) => {
|
|
12925
|
+
const { dir, settings } = settingsFor(opts);
|
|
12926
|
+
const client = clientFor(opts);
|
|
12927
|
+
const languages = opts.language ? [opts.language] : settings.targetLanguages ?? [];
|
|
12928
|
+
if (!languages.length) {
|
|
12929
|
+
throw new CliError(
|
|
12930
|
+
'No target languages configured.\nRun "vg locale config --target-languages fr-FR,de-DE", or pass --language <code>.',
|
|
12931
|
+
ExitCode.ERROR
|
|
12932
|
+
);
|
|
12933
|
+
}
|
|
12934
|
+
const namespaces = settings.namespaces ?? ["common"];
|
|
12935
|
+
const results = [];
|
|
12936
|
+
for (const language of languages) {
|
|
12937
|
+
for (const namespace of namespaces) {
|
|
12938
|
+
const keys = await fetchAllKeys(client, settings.project, language, namespace);
|
|
12939
|
+
const catalog = {};
|
|
12940
|
+
for (const entry of keys) {
|
|
12941
|
+
const t = entry.translation;
|
|
12942
|
+
if (!t || t.value === null) continue;
|
|
12943
|
+
const accepted = t.status === "translated";
|
|
12944
|
+
if (!accepted && !opts.includeMachine) continue;
|
|
12945
|
+
catalog[entry.key] = t.value;
|
|
12946
|
+
}
|
|
12947
|
+
const target = resolveLocaleFiles(dir, settings.path, [language], [namespace])[0];
|
|
12948
|
+
const before = readCatalog(target.file, settings.format);
|
|
12949
|
+
const diff2 = diffCatalogs(before, catalog);
|
|
12950
|
+
const write = writeCatalog(target.file, settings.format, catalog, {
|
|
12951
|
+
dryRun: opts.dryRun
|
|
12952
|
+
});
|
|
12953
|
+
results.push({
|
|
12954
|
+
file: target.relative,
|
|
12955
|
+
changed: write.changed,
|
|
12956
|
+
added: diff2.added.length,
|
|
12957
|
+
updated: diff2.changed.length
|
|
12958
|
+
});
|
|
12959
|
+
}
|
|
12960
|
+
}
|
|
12961
|
+
const changed = results.filter((r) => r.changed);
|
|
12962
|
+
emit2(opts, { dryRun: Boolean(opts.dryRun), files: results }, () => {
|
|
12963
|
+
if (opts.dryRun) console.log(chalk7.bold("Dry run \u2014 no files were written."));
|
|
12964
|
+
for (const r of changed) {
|
|
12965
|
+
console.log(` ${chalk7.green(r.changed ? "update" : "ok")} ${r.file} (+${r.added} ~${r.updated})`);
|
|
12966
|
+
}
|
|
12967
|
+
const unchanged = results.length - changed.length;
|
|
12968
|
+
console.log(
|
|
12969
|
+
chalk7.dim(
|
|
12970
|
+
`${changed.length} file(s) ${opts.dryRun ? "would change" : "written"}, ${unchanged} unchanged.`
|
|
12971
|
+
)
|
|
12972
|
+
);
|
|
12973
|
+
});
|
|
12974
|
+
});
|
|
12975
|
+
}
|
|
12976
|
+
var saveMissingCmd = withCommonOptions(
|
|
12977
|
+
new Command("save-missing").description(
|
|
12978
|
+
"Push only keys that do not exist in Vibgrate Cloud yet (never overwrites a translation)"
|
|
12979
|
+
)
|
|
12980
|
+
).option("--dry-run", "Report what would be sent without sending anything").action(async (opts) => {
|
|
12981
|
+
const { dir, settings } = settingsFor(opts);
|
|
12982
|
+
const client = clientFor(opts);
|
|
12983
|
+
const sourceLanguage = settings.sourceLanguage ?? "en-US";
|
|
12984
|
+
const { keys } = collectLocalKeys(dir, settings, []);
|
|
12985
|
+
const namespaces = settings.namespaces ?? ["common"];
|
|
12986
|
+
const existing = /* @__PURE__ */ new Set();
|
|
12987
|
+
for (const namespace of namespaces) {
|
|
12988
|
+
const remote = await fetchAllKeys(client, settings.project, sourceLanguage, namespace);
|
|
12989
|
+
for (const entry of remote) existing.add(`${entry.namespace}\0${entry.key}`);
|
|
12990
|
+
}
|
|
12991
|
+
const missing = keys.filter((k) => !existing.has(`${k.namespace}\0${k.key}`));
|
|
12992
|
+
if (!missing.length) {
|
|
12993
|
+
emit2(opts, { missing: 0 }, () => console.log(chalk7.dim("No missing keys \u2014 nothing to send.")));
|
|
12994
|
+
return;
|
|
12995
|
+
}
|
|
12996
|
+
if (opts.dryRun) {
|
|
12997
|
+
emit2(opts, { dryRun: true, missing: missing.length, keys: missing.map((k) => `${k.namespace}:${k.key}`) }, () => {
|
|
12998
|
+
console.log(chalk7.bold(`Dry run \u2014 ${missing.length} missing key(s) would be created.`));
|
|
12999
|
+
for (const k of missing.slice(0, 20)) console.log(` ${chalk7.green("+")} ${k.namespace}:${k.key}`);
|
|
13000
|
+
if (missing.length > 20) console.log(chalk7.dim(` \u2026and ${missing.length - 20} more`));
|
|
13001
|
+
});
|
|
13002
|
+
return;
|
|
13003
|
+
}
|
|
13004
|
+
let created = 0;
|
|
13005
|
+
for (let i = 0; i < missing.length; i += PUSH_BATCH_SIZE) {
|
|
13006
|
+
const res = await client.post(
|
|
13007
|
+
`/projects/${encodeURIComponent(settings.project)}/keys`,
|
|
13008
|
+
{
|
|
13009
|
+
version: settings.version,
|
|
13010
|
+
keys: missing.slice(i, i + PUSH_BATCH_SIZE).map((k) => ({
|
|
13011
|
+
namespace: k.namespace,
|
|
13012
|
+
key: k.key,
|
|
13013
|
+
sourceValue: k.sourceValue
|
|
13014
|
+
}))
|
|
13015
|
+
}
|
|
13016
|
+
);
|
|
13017
|
+
created += res.data?.created ?? 0;
|
|
13018
|
+
}
|
|
13019
|
+
emit2(opts, { created }, () => console.log(chalk7.green(`Created ${created} missing key(s).`)));
|
|
13020
|
+
});
|
|
13021
|
+
var addCmd2 = withCommonOptions(
|
|
13022
|
+
new Command("add").description("Add or update a single key").argument("<key>", "Key name").argument(
|
|
13023
|
+
"[value]",
|
|
13024
|
+
"Source value"
|
|
13025
|
+
)
|
|
13026
|
+
).action(async (key2, value, opts) => {
|
|
13027
|
+
const { settings } = settingsFor(opts);
|
|
13028
|
+
const client = clientFor(opts);
|
|
13029
|
+
const namespace = opts.namespace ?? settings.namespaces?.[0] ?? "common";
|
|
13030
|
+
const res = await client.post(
|
|
13031
|
+
`/projects/${encodeURIComponent(settings.project)}/keys`,
|
|
13032
|
+
{
|
|
13033
|
+
version: settings.version,
|
|
13034
|
+
keys: [
|
|
13035
|
+
{
|
|
13036
|
+
namespace,
|
|
13037
|
+
key: key2,
|
|
13038
|
+
sourceValue: value ?? null,
|
|
13039
|
+
translations: opts.language && value !== void 0 ? [{ language: opts.language, value, origin: "manual" }] : []
|
|
13040
|
+
}
|
|
13041
|
+
]
|
|
13042
|
+
}
|
|
13043
|
+
);
|
|
13044
|
+
emit2(
|
|
13045
|
+
opts,
|
|
13046
|
+
res.data,
|
|
13047
|
+
() => console.log(chalk7.green(`${res.data?.created ? "Created" : "Updated"} ${namespace}:${key2}`))
|
|
13048
|
+
);
|
|
13049
|
+
});
|
|
13050
|
+
var removeCmd = withCommonOptions(
|
|
13051
|
+
new Command("remove").description("Remove one or more keys").argument("<keys...>", "Key names")
|
|
13052
|
+
).action(async (keys, opts) => {
|
|
13053
|
+
const { settings } = settingsFor(opts);
|
|
13054
|
+
const client = clientFor(opts);
|
|
13055
|
+
const namespace = opts.namespace ?? settings.namespaces?.[0] ?? "common";
|
|
13056
|
+
const res = await client.del(
|
|
13057
|
+
`/projects/${encodeURIComponent(settings.project)}/keys`,
|
|
13058
|
+
{ namespace, keys }
|
|
13059
|
+
);
|
|
13060
|
+
emit2(
|
|
13061
|
+
opts,
|
|
13062
|
+
res.data,
|
|
13063
|
+
() => console.log(
|
|
13064
|
+
chalk7.green(
|
|
13065
|
+
`Removed ${res.data?.removed ?? 0} key(s) and ${res.data?.translationsRemoved ?? 0} translation(s).`
|
|
13066
|
+
)
|
|
13067
|
+
)
|
|
13068
|
+
);
|
|
13069
|
+
});
|
|
13070
|
+
var getCmd = withCommonOptions(
|
|
13071
|
+
new Command("get").description("Show one key and its translations").argument("<key>", "Key name")
|
|
13072
|
+
).action(async (key2, opts) => {
|
|
13073
|
+
const { settings } = settingsFor(opts);
|
|
13074
|
+
const client = clientFor(opts);
|
|
13075
|
+
const namespace = opts.namespace ?? settings.namespaces?.[0] ?? "common";
|
|
13076
|
+
const res = await client.get(
|
|
13077
|
+
`/projects/${encodeURIComponent(settings.project)}/keys`,
|
|
13078
|
+
{ namespace, q: key2, language: opts.language, limit: 50 }
|
|
13079
|
+
);
|
|
13080
|
+
const match = (res.data?.keys ?? []).find((k) => k.key === key2);
|
|
13081
|
+
if (!match) {
|
|
13082
|
+
throw new CliError(`Key "${key2}" not found in namespace "${namespace}".`, ExitCode.ERROR);
|
|
13083
|
+
}
|
|
13084
|
+
emit2(opts, match, () => {
|
|
13085
|
+
console.log(chalk7.bold(`${match.namespace}:${match.key}`));
|
|
13086
|
+
if (match.sourceValue !== null) console.log(` source: ${match.sourceValue}`);
|
|
13087
|
+
if (match.translation) {
|
|
13088
|
+
const value = match.translation.value ?? chalk7.dim("(not translated)");
|
|
13089
|
+
console.log(` ${opts.language}: ${value} ${chalk7.dim(`[${match.translation.status}]`)}`);
|
|
13090
|
+
}
|
|
13091
|
+
});
|
|
13092
|
+
});
|
|
13093
|
+
var formatCmd = withCommonOptions(
|
|
13094
|
+
new Command("format").description(
|
|
13095
|
+
"Re-format local locale files in place (local-only \u2014 no network, works on any plan)"
|
|
13096
|
+
)
|
|
13097
|
+
).option("--dry-run", "Report which files would change without writing").action(async (opts) => {
|
|
13098
|
+
const dir = root(opts);
|
|
13099
|
+
const { config } = loadLocaleConfig(dir);
|
|
13100
|
+
const settings = resolveLocaleSettings(config, {
|
|
13101
|
+
format: opts.format,
|
|
13102
|
+
path: opts.path,
|
|
13103
|
+
namespaces: opts.namespace ? [opts.namespace] : void 0,
|
|
13104
|
+
targetLanguages: opts.language ? [opts.language] : void 0
|
|
13105
|
+
});
|
|
13106
|
+
const pathError = validatePathTemplate(settings.path);
|
|
13107
|
+
if (pathError) throw new CliError(pathError, ExitCode.ERROR);
|
|
13108
|
+
const languages = [
|
|
13109
|
+
settings.sourceLanguage ?? "en-US",
|
|
13110
|
+
...settings.targetLanguages ?? []
|
|
13111
|
+
];
|
|
13112
|
+
const files = resolveLocaleFiles(dir, settings.path, languages, settings.namespaces ?? ["common"]);
|
|
13113
|
+
const changed = [];
|
|
13114
|
+
for (const file of files) {
|
|
13115
|
+
if (!file.exists) continue;
|
|
13116
|
+
const catalog = readCatalog(file.file, settings.format);
|
|
13117
|
+
const result = writeCatalog(file.file, settings.format, catalog, { dryRun: opts.dryRun });
|
|
13118
|
+
if (result.changed) changed.push(file.relative);
|
|
13119
|
+
}
|
|
13120
|
+
emit2(opts, { dryRun: Boolean(opts.dryRun), changed }, () => {
|
|
13121
|
+
for (const f of changed) console.log(` ${chalk7.green("format")} ${f}`);
|
|
13122
|
+
console.log(
|
|
13123
|
+
chalk7.dim(`${changed.length} file(s) ${opts.dryRun ? "would be reformatted" : "reformatted"}.`)
|
|
13124
|
+
);
|
|
13125
|
+
});
|
|
13126
|
+
});
|
|
13127
|
+
function buildLocaleCommand(name) {
|
|
13128
|
+
const cmd = new Command(name).description(
|
|
13129
|
+
"Manage application translations \u2014 locale projects, keys, and translations in Vibgrate Cloud"
|
|
13130
|
+
);
|
|
13131
|
+
cmd.addCommand(configCmd);
|
|
13132
|
+
cmd.addCommand(makePushCommand("push", "Upload local locale files to Vibgrate Cloud"));
|
|
13133
|
+
cmd.addCommand(makePushCommand("sync", "Alias for push"));
|
|
13134
|
+
cmd.addCommand(makePullCommand("pull", "Write translations from Vibgrate Cloud into local files"));
|
|
13135
|
+
cmd.addCommand(makePullCommand("download", "Alias for pull"));
|
|
13136
|
+
cmd.addCommand(saveMissingCmd);
|
|
13137
|
+
cmd.addCommand(addCmd2);
|
|
13138
|
+
cmd.addCommand(removeCmd);
|
|
13139
|
+
cmd.addCommand(getCmd);
|
|
13140
|
+
cmd.addCommand(statusCmd);
|
|
13141
|
+
cmd.addCommand(formatCmd);
|
|
13142
|
+
cmd.addHelpText(
|
|
13143
|
+
"after",
|
|
13144
|
+
"\nLocal-only commands (no network, any plan): config, format\nCloud commands (Team plan or above): push, pull, save-missing, add, remove, get, status\n"
|
|
13145
|
+
);
|
|
13146
|
+
return cmd;
|
|
13147
|
+
}
|
|
13148
|
+
function registerLocale(program) {
|
|
13149
|
+
program.addCommand(buildLocaleCommand("locale"));
|
|
13150
|
+
const alias = buildLocaleCommand("localize");
|
|
13151
|
+
alias.description('Alias for "vg locale"');
|
|
13152
|
+
program.addCommand(alias, { hidden: true });
|
|
13153
|
+
}
|
|
12256
13154
|
var OPENVEX_CONTEXT = "https://openvex.dev/ns/v0.2.0";
|
|
12257
13155
|
var VEX_STATUSES = ["not_affected", "affected", "fixed", "under_investigation"];
|
|
12258
13156
|
var VEX_JUSTIFICATIONS = [
|
|
@@ -12342,7 +13240,7 @@ var vexCommand = new Command("vex").description("Generate an OpenVEX document (e
|
|
|
12342
13240
|
try {
|
|
12343
13241
|
const statements = [];
|
|
12344
13242
|
if (opts.from) {
|
|
12345
|
-
const fromPath =
|
|
13243
|
+
const fromPath = path46.resolve(opts.from);
|
|
12346
13244
|
if (!await pathExists(fromPath)) {
|
|
12347
13245
|
process.stderr.write(chalk7.red(`Statements file not found: ${fromPath}
|
|
12348
13246
|
`));
|
|
@@ -12360,9 +13258,9 @@ var vexCommand = new Command("vex").description("Generate an OpenVEX document (e
|
|
|
12360
13258
|
});
|
|
12361
13259
|
const json2 = JSON.stringify(doc, null, 2);
|
|
12362
13260
|
if (opts.out) {
|
|
12363
|
-
await writeTextFile(
|
|
13261
|
+
await writeTextFile(path46.resolve(opts.out), json2 + "\n");
|
|
12364
13262
|
process.stderr.write(
|
|
12365
|
-
chalk7.green("\u2714") + ` Wrote OpenVEX document (${doc.statements.length} statement(s)) to ${
|
|
13263
|
+
chalk7.green("\u2714") + ` Wrote OpenVEX document (${doc.statements.length} statement(s)) to ${path46.resolve(opts.out)}
|
|
12366
13264
|
`
|
|
12367
13265
|
);
|
|
12368
13266
|
} else {
|
|
@@ -12545,7 +13443,7 @@ function formatDeltaText(base, current) {
|
|
|
12545
13443
|
return lines.join("\n");
|
|
12546
13444
|
}
|
|
12547
13445
|
async function readArtifactOrExit(filePath) {
|
|
12548
|
-
const absolutePath =
|
|
13446
|
+
const absolutePath = path46.resolve(filePath);
|
|
12549
13447
|
if (!await pathExists(absolutePath)) {
|
|
12550
13448
|
console.error(chalk7.red(`Artifact not found: ${absolutePath}`));
|
|
12551
13449
|
process.exit(1);
|
|
@@ -12562,7 +13460,7 @@ var exportCommand = new Command("export").description("Export scan artifact as S
|
|
|
12562
13460
|
const sbom = format === "cyclonedx" ? toCycloneDx(artifact) : toSpdx(artifact);
|
|
12563
13461
|
const body = JSON.stringify(sbom, null, 2);
|
|
12564
13462
|
if (opts.out) {
|
|
12565
|
-
await writeTextFile(
|
|
13463
|
+
await writeTextFile(path46.resolve(opts.out), body);
|
|
12566
13464
|
console.log(chalk7.green("\u2714") + ` SBOM written to ${opts.out}`);
|
|
12567
13465
|
} else {
|
|
12568
13466
|
console.log(body);
|
|
@@ -12573,7 +13471,7 @@ var deltaCommand = new Command("delta").description("Show SBOM delta between two
|
|
|
12573
13471
|
const current = await readArtifactOrExit(opts.to);
|
|
12574
13472
|
const report = formatDeltaText(base, current);
|
|
12575
13473
|
if (opts.out) {
|
|
12576
|
-
await writeTextFile(
|
|
13474
|
+
await writeTextFile(path46.resolve(opts.out), report);
|
|
12577
13475
|
console.log(chalk7.green("\u2714") + ` SBOM delta report written to ${opts.out}`);
|
|
12578
13476
|
} else {
|
|
12579
13477
|
console.log(report);
|
|
@@ -12703,66 +13601,66 @@ function resolveRegime(id) {
|
|
|
12703
13601
|
}
|
|
12704
13602
|
return regime;
|
|
12705
13603
|
}
|
|
12706
|
-
function evidenceDir(
|
|
12707
|
-
return
|
|
13604
|
+
function evidenceDir(root3) {
|
|
13605
|
+
return path46.join(root3, ".vibgrate", "evidence");
|
|
12708
13606
|
}
|
|
12709
|
-
function orgPath(
|
|
12710
|
-
return
|
|
13607
|
+
function orgPath(root3) {
|
|
13608
|
+
return path46.join(evidenceDir(root3), "org.json");
|
|
12711
13609
|
}
|
|
12712
|
-
function productsPath(
|
|
12713
|
-
return
|
|
13610
|
+
function productsPath(root3) {
|
|
13611
|
+
return path46.join(evidenceDir(root3), "products.json");
|
|
12714
13612
|
}
|
|
12715
|
-
function releasesDir(
|
|
12716
|
-
return
|
|
13613
|
+
function releasesDir(root3) {
|
|
13614
|
+
return path46.join(evidenceDir(root3), "releases");
|
|
12717
13615
|
}
|
|
12718
|
-
function releasePath(
|
|
13616
|
+
function releasePath(root3, productId, version) {
|
|
12719
13617
|
const safe = `${productId}@${version}`.replace(/[^A-Za-z0-9._@-]/g, "_");
|
|
12720
|
-
return
|
|
13618
|
+
return path46.join(releasesDir(root3), `${safe}.json`);
|
|
12721
13619
|
}
|
|
12722
13620
|
var DEFAULT_ORG = {
|
|
12723
13621
|
responsiblePersons: [],
|
|
12724
13622
|
defaultRegime: DEFAULT_REGIME
|
|
12725
13623
|
};
|
|
12726
|
-
async function loadOrg(
|
|
12727
|
-
const p = orgPath(
|
|
13624
|
+
async function loadOrg(root3) {
|
|
13625
|
+
const p = orgPath(root3);
|
|
12728
13626
|
if (!await pathExists(p)) return { ...DEFAULT_ORG };
|
|
12729
13627
|
const org = await readJsonFile(p);
|
|
12730
13628
|
return { ...DEFAULT_ORG, ...org, responsiblePersons: org.responsiblePersons ?? [] };
|
|
12731
13629
|
}
|
|
12732
|
-
async function saveOrg(
|
|
12733
|
-
await writeJsonFile(orgPath(
|
|
13630
|
+
async function saveOrg(root3, org) {
|
|
13631
|
+
await writeJsonFile(orgPath(root3), org);
|
|
12734
13632
|
}
|
|
12735
|
-
async function loadProducts(
|
|
12736
|
-
const p = productsPath(
|
|
13633
|
+
async function loadProducts(root3) {
|
|
13634
|
+
const p = productsPath(root3);
|
|
12737
13635
|
if (!await pathExists(p)) return [];
|
|
12738
13636
|
return readJsonFile(p);
|
|
12739
13637
|
}
|
|
12740
|
-
async function saveProducts(
|
|
12741
|
-
await writeJsonFile(productsPath(
|
|
13638
|
+
async function saveProducts(root3, products) {
|
|
13639
|
+
await writeJsonFile(productsPath(root3), products);
|
|
12742
13640
|
}
|
|
12743
|
-
async function getProduct(
|
|
12744
|
-
const products = await loadProducts(
|
|
13641
|
+
async function getProduct(root3, id) {
|
|
13642
|
+
const products = await loadProducts(root3);
|
|
12745
13643
|
return products.find((p) => p.id === id || p.name === id);
|
|
12746
13644
|
}
|
|
12747
|
-
async function freezeRelease(
|
|
12748
|
-
const p = releasePath(
|
|
13645
|
+
async function freezeRelease(root3, release) {
|
|
13646
|
+
const p = releasePath(root3, release.productId, release.version);
|
|
12749
13647
|
if (await pathExists(p)) {
|
|
12750
13648
|
throw new CliError(
|
|
12751
|
-
`release ${release.productId}@${release.version} is already frozen and manifests are immutable \u2014 delete ${
|
|
13649
|
+
`release ${release.productId}@${release.version} is already frozen and manifests are immutable \u2014 delete ${path46.relative(root3, p)} only if it was frozen in error`,
|
|
12752
13650
|
ExitCode.USAGE_ERROR
|
|
12753
13651
|
);
|
|
12754
13652
|
}
|
|
12755
13653
|
await writeJsonFile(p, release);
|
|
12756
13654
|
return p;
|
|
12757
13655
|
}
|
|
12758
|
-
async function loadReleases(
|
|
12759
|
-
const dir = releasesDir(
|
|
13656
|
+
async function loadReleases(root3, productId) {
|
|
13657
|
+
const dir = releasesDir(root3);
|
|
12760
13658
|
if (!await pathExists(dir)) return [];
|
|
12761
13659
|
const { readdir } = await import('fs/promises');
|
|
12762
13660
|
const files = (await readdir(dir)).filter((f) => f.endsWith(".json")).sort();
|
|
12763
13661
|
const releases = [];
|
|
12764
13662
|
for (const f of files) {
|
|
12765
|
-
const rel = await readJsonFile(
|
|
13663
|
+
const rel = await readJsonFile(path46.join(dir, f));
|
|
12766
13664
|
if (!productId || rel.productId === productId) releases.push(rel);
|
|
12767
13665
|
}
|
|
12768
13666
|
return releases;
|
|
@@ -13455,14 +14353,14 @@ function verifyEvidenceEnvelope(env, opts = {}) {
|
|
|
13455
14353
|
};
|
|
13456
14354
|
}
|
|
13457
14355
|
var DEFAULT_KEY = "attest-key.pem";
|
|
13458
|
-
function resolveSigningKey(
|
|
14356
|
+
function resolveSigningKey(root3, explicit) {
|
|
13459
14357
|
const chosen = process.env.VG_ATTEST_KEY;
|
|
13460
|
-
const keyPath = chosen ?
|
|
14358
|
+
const keyPath = chosen ? path46.resolve(chosen) : path46.join(root3, ".vibgrate", DEFAULT_KEY);
|
|
13461
14359
|
let minted = false;
|
|
13462
14360
|
if (!fs14.existsSync(keyPath)) {
|
|
13463
14361
|
if (chosen) throw new CliError(`signing key not found: ${chosen}`, ExitCode.USAGE_ERROR);
|
|
13464
14362
|
const kp = generateKeypair();
|
|
13465
|
-
fs14.mkdirSync(
|
|
14363
|
+
fs14.mkdirSync(path46.dirname(keyPath), { recursive: true });
|
|
13466
14364
|
fs14.writeFileSync(keyPath, kp.privatePem, { mode: 384 });
|
|
13467
14365
|
fs14.writeFileSync(`${keyPath}.pub`, kp.publicPem);
|
|
13468
14366
|
minted = true;
|
|
@@ -13480,16 +14378,16 @@ function resolveSigningKey(root2, explicit) {
|
|
|
13480
14378
|
}
|
|
13481
14379
|
function writeBundle(input) {
|
|
13482
14380
|
const dir = input.outDir;
|
|
13483
|
-
fs14.mkdirSync(
|
|
13484
|
-
const write = (rel, data) => fs14.writeFileSync(
|
|
14381
|
+
fs14.mkdirSync(path46.join(dir, "inputs", "releases"), { recursive: true });
|
|
14382
|
+
const write = (rel, data) => fs14.writeFileSync(path46.join(dir, rel), `${JSON.stringify(data, null, 2)}
|
|
13485
14383
|
`);
|
|
13486
14384
|
write("result.json", input.result);
|
|
13487
|
-
write(
|
|
14385
|
+
write(path46.join("inputs", "advisory.json"), input.advisory);
|
|
13488
14386
|
for (const rel of input.releases) {
|
|
13489
14387
|
const safe = `${rel.productId}@${rel.version}`.replace(/[^A-Za-z0-9._@-]/g, "_");
|
|
13490
|
-
write(
|
|
14388
|
+
write(path46.join("inputs", "releases", `${safe}.json`), rel);
|
|
13491
14389
|
}
|
|
13492
|
-
write(
|
|
14390
|
+
write(path46.join("inputs", "datapack.lock"), { dataPackVersion: input.result.meta.dataPackVersion, kernelVersion: input.result.meta.kernelVersion });
|
|
13493
14391
|
write("provenance.json", {
|
|
13494
14392
|
method: "deterministic",
|
|
13495
14393
|
modelDrafted: [],
|
|
@@ -13505,13 +14403,13 @@ function writeBundle(input) {
|
|
|
13505
14403
|
disclaimer: input.regime.disclaimer
|
|
13506
14404
|
});
|
|
13507
14405
|
if (input.envelope) {
|
|
13508
|
-
fs14.writeFileSync(
|
|
14406
|
+
fs14.writeFileSync(path46.join(dir, "evidence.intoto.jsonl"), `${JSON.stringify(input.envelope)}
|
|
13509
14407
|
`);
|
|
13510
14408
|
}
|
|
13511
14409
|
if (input.timestampToken) {
|
|
13512
|
-
fs14.writeFileSync(
|
|
14410
|
+
fs14.writeFileSync(path46.join(dir, "timestamp.tsr"), input.timestampToken);
|
|
13513
14411
|
}
|
|
13514
|
-
fs14.writeFileSync(
|
|
14412
|
+
fs14.writeFileSync(path46.join(dir, "VERIFY.md"), verifyDoc(input));
|
|
13515
14413
|
return dir;
|
|
13516
14414
|
}
|
|
13517
14415
|
function verifyDoc(input) {
|
|
@@ -13571,14 +14469,14 @@ function undeterminedFields(result, regime) {
|
|
|
13571
14469
|
if (result.products.some((p) => p.status === "affected" && p.memberStates.length === 0)) gaps.push("member_states");
|
|
13572
14470
|
return gaps.filter((g) => regime.submission.requires.some((r) => g.startsWith(r)) || g.startsWith("exposure") || g.startsWith("member"));
|
|
13573
14471
|
}
|
|
13574
|
-
async function recordDrill(
|
|
13575
|
-
const dir =
|
|
13576
|
-
const p =
|
|
14472
|
+
async function recordDrill(root3, record) {
|
|
14473
|
+
const dir = path46.join(evidenceDir(root3), "drills");
|
|
14474
|
+
const p = path46.join(dir, `${record.drillId}.json`);
|
|
13577
14475
|
await writeJsonFile(p, record);
|
|
13578
14476
|
return p;
|
|
13579
14477
|
}
|
|
13580
|
-
async function hasRecentDrill(
|
|
13581
|
-
const dir =
|
|
14478
|
+
async function hasRecentDrill(root3, asOf) {
|
|
14479
|
+
const dir = path46.join(evidenceDir(root3), "drills");
|
|
13582
14480
|
if (!await pathExists(dir)) return void 0;
|
|
13583
14481
|
const { readdir } = await import('fs/promises');
|
|
13584
14482
|
const { readJsonFile: readJsonFile2 } = await import('./fs-4G5A65KS.js');
|
|
@@ -13586,7 +14484,7 @@ async function hasRecentDrill(root2, asOf) {
|
|
|
13586
14484
|
if (files.length === 0) return void 0;
|
|
13587
14485
|
const cutoff = new Date(new Date(asOf).getTime() - 90 * 24 * 3600 * 1e3).toISOString();
|
|
13588
14486
|
for (const f of files) {
|
|
13589
|
-
const rec = await readJsonFile2(
|
|
14487
|
+
const rec = await readJsonFile2(path46.join(dir, f));
|
|
13590
14488
|
if (rec.ranAt >= cutoff) return true;
|
|
13591
14489
|
}
|
|
13592
14490
|
return false;
|
|
@@ -13665,8 +14563,8 @@ function formatRegimeList() {
|
|
|
13665
14563
|
}
|
|
13666
14564
|
|
|
13667
14565
|
// src/reporting/commands/evidence/index.ts
|
|
13668
|
-
function
|
|
13669
|
-
return
|
|
14566
|
+
function root2(opts) {
|
|
14567
|
+
return path46.resolve(opts.cwd ?? process.cwd());
|
|
13670
14568
|
}
|
|
13671
14569
|
function slug(name) {
|
|
13672
14570
|
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "product";
|
|
@@ -13680,14 +14578,14 @@ function nowIso() {
|
|
|
13680
14578
|
function csv(v) {
|
|
13681
14579
|
return v ? v.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
13682
14580
|
}
|
|
13683
|
-
var
|
|
14581
|
+
var cwdOption2 = ["-C, --cwd <dir>", "Run against this directory (default: current)"];
|
|
13684
14582
|
async function releasesByProductMap(rootDir, products) {
|
|
13685
14583
|
const map = /* @__PURE__ */ new Map();
|
|
13686
14584
|
for (const p of products) map.set(p.id, await loadReleases(rootDir, p.id));
|
|
13687
14585
|
return map;
|
|
13688
14586
|
}
|
|
13689
|
-
var initCmd = new Command("init").description("Set org, coordinator, and the person with filing authority (per regime)").option(...
|
|
13690
|
-
const rootDir =
|
|
14587
|
+
var initCmd = new Command("init").description("Set org, coordinator, and the person with filing authority (per regime)").option(...cwdOption2).option("--regime <id>", "Default regime for this repo", DEFAULT_REGIME).option("--legal-entity <name>", "Legal entity name").option("--establishment <cc>", "Main establishment (country code)").option("--eu-rep <name>", "EU authorised representative").option("--coordinator <csirt>", "Coordinator CSIRT / competent authority").option("--responsible <name>", "Add a responsible person").option("--role <role>", "Role of the responsible person").option("--filing-authority", "The responsible person has filing authority").option("--ooo <contact>", "Out-of-hours contact for the responsible person").action(async (opts) => {
|
|
14588
|
+
const rootDir = root2(opts);
|
|
13691
14589
|
resolveRegime(opts.regime);
|
|
13692
14590
|
const org = await loadOrg(rootDir);
|
|
13693
14591
|
org.defaultRegime = opts.regime;
|
|
@@ -13709,8 +14607,8 @@ var initCmd = new Command("init").description("Set org, coordinator, and the per
|
|
|
13709
14607
|
var regimesCmd = new Command("regimes").description("List available reporting regimes").action(() => {
|
|
13710
14608
|
console.log(formatRegimeList());
|
|
13711
14609
|
});
|
|
13712
|
-
var productAdd = new Command("add").description("Register a product with digital elements (PDE)").argument("<name>", "Product name").option(...
|
|
13713
|
-
const rootDir =
|
|
14610
|
+
var productAdd = new Command("add").description("Register a product with digital elements (PDE)").argument("<name>", "Product name").option(...cwdOption2).option("--regime <id>", "Regime whose classification vocabulary applies").option("--classification <id>", "Regime classification id (see `vg evidence regimes`)", "default").option("--markets <cc,cc>", "Member states / markets, comma-separated").option("--bind <ref>", "Bind to a repo/image/registry (repeatable)", (v, acc) => [...acc, v], []).option("--in-scope", "Record an in-scope determination").option("--out-of-scope", "Record an out-of-scope determination").option("--rationale <text>", "Scope determination rationale").option("--until <date>", "Declared support period end (YYYY-MM-DD)").action(async (name, opts) => {
|
|
14611
|
+
const rootDir = root2(opts);
|
|
13714
14612
|
const regime = resolveRegime(opts.regime ?? (await loadOrg(rootDir)).defaultRegime);
|
|
13715
14613
|
if (opts.classification !== "default" && regime.classifications.length && !regime.classifications.some((c2) => c2.id === opts.classification)) {
|
|
13716
14614
|
throw new CliError(`classification "${opts.classification}" is not valid for regime ${regime.id} \u2014 options: ${regime.classifications.map((c2) => c2.id).join(", ")}`, ExitCode.USAGE_ERROR);
|
|
@@ -13732,29 +14630,29 @@ var productAdd = new Command("add").description("Register a product with digital
|
|
|
13732
14630
|
await saveProducts(rootDir, products);
|
|
13733
14631
|
console.log(chalk7.green("\u2714") + ` product ${chalk7.bold(id)} registered (${regime.name})`);
|
|
13734
14632
|
});
|
|
13735
|
-
var productList = new Command("list").description("List registered products").option(...
|
|
13736
|
-
const products = await loadProducts(
|
|
14633
|
+
var productList = new Command("list").description("List registered products").option(...cwdOption2).action(async (opts) => {
|
|
14634
|
+
const products = await loadProducts(root2(opts));
|
|
13737
14635
|
if (!products.length) return console.log(chalk7.dim("no products registered \u2014 vg evidence product add <name>"));
|
|
13738
14636
|
for (const p of products) {
|
|
13739
14637
|
console.log(` ${chalk7.bold(p.id)} ${p.name} ${chalk7.dim(`[${p.classification}]`)} ${chalk7.dim(p.memberStates.join(" ") || "no markets")}`);
|
|
13740
14638
|
}
|
|
13741
14639
|
});
|
|
13742
|
-
var productShow = new Command("show").description("Show a product and its frozen releases").argument("<id>").option(...
|
|
13743
|
-
const rootDir =
|
|
14640
|
+
var productShow = new Command("show").description("Show a product and its frozen releases").argument("<id>").option(...cwdOption2).action(async (id, opts) => {
|
|
14641
|
+
const rootDir = root2(opts);
|
|
13744
14642
|
const product = await getProduct(rootDir, id);
|
|
13745
14643
|
if (!product) throw new CliError(`no such product: ${id}`, ExitCode.NOT_FOUND);
|
|
13746
14644
|
const releases = await loadReleases(rootDir, product.id);
|
|
13747
14645
|
console.log(JSON.stringify({ product, releases: releases.map((r) => ({ version: r.version, shipDate: r.shipDate, components: r.components.length })) }, null, 2));
|
|
13748
14646
|
});
|
|
13749
14647
|
var productCmd = new Command("product").description("Register and inspect products with digital elements").addCommand(productAdd).addCommand(productList).addCommand(productShow);
|
|
13750
|
-
var releaseCmd = new Command("release").description("Freeze a shipped release into an immutable component manifest").argument("<product>", "Product id").argument("<version>", "Shipped version").option(...
|
|
13751
|
-
const rootDir =
|
|
14648
|
+
var releaseCmd = new Command("release").description("Freeze a shipped release into an immutable component manifest").argument("<product>", "Product id").argument("<version>", "Shipped version").option(...cwdOption2).option("--from <file>", "Scan artifact or SBOM captured at release", ".vibgrate/scan_result.json").option("--ship-date <date>", "Ship date (YYYY-MM-DD)").option("--build-id <id>", "Build id").option("--digest <sha256>", "Artefact digest").option("--markets <cc,cc>", "Distribution markets/channels, comma-separated").action(async (productId, version, opts) => {
|
|
14649
|
+
const rootDir = root2(opts);
|
|
13752
14650
|
const product = await getProduct(rootDir, productId);
|
|
13753
14651
|
if (!product) throw new CliError(`no such product: ${productId} \u2014 register it with \`vg evidence product add\``, ExitCode.NOT_FOUND);
|
|
13754
14652
|
const release = await buildRelease({
|
|
13755
14653
|
productId: product.id,
|
|
13756
14654
|
version,
|
|
13757
|
-
from:
|
|
14655
|
+
from: path46.resolve(rootDir, opts.from),
|
|
13758
14656
|
shipDate: opts.shipDate,
|
|
13759
14657
|
buildId: opts.buildId,
|
|
13760
14658
|
artefactDigest: opts.digest,
|
|
@@ -13762,7 +14660,7 @@ var releaseCmd = new Command("release").description("Freeze a shipped release in
|
|
|
13762
14660
|
frozenAt: nowIso()
|
|
13763
14661
|
});
|
|
13764
14662
|
const p = await freezeRelease(rootDir, release);
|
|
13765
|
-
console.log(chalk7.green("\u2714") + ` froze ${chalk7.bold(`${product.id}@${version}`)} \u2014 ${release.components.length} components, immutable at ${
|
|
14663
|
+
console.log(chalk7.green("\u2714") + ` froze ${chalk7.bold(`${product.id}@${version}`)} \u2014 ${release.components.length} components, immutable at ${path46.relative(rootDir, p)}`);
|
|
13766
14664
|
});
|
|
13767
14665
|
async function runExposure(rootDir, vuln, opts) {
|
|
13768
14666
|
const org = await loadOrg(rootDir);
|
|
@@ -13794,8 +14692,8 @@ function exposureExit(status) {
|
|
|
13794
14692
|
if (status === "undetermined") return ExitCode.NOT_FOUND;
|
|
13795
14693
|
return ExitCode.OK;
|
|
13796
14694
|
}
|
|
13797
|
-
var exposureCmd = new Command("exposure").description("Which shipped products contain this vulnerability \u2014 with signed evidence").argument("<vuln>", "Vulnerability id (CVE / GHSA / OSV / EUVD)").option(...
|
|
13798
|
-
const rootDir =
|
|
14695
|
+
var exposureCmd = new Command("exposure").description("Which shipped products contain this vulnerability \u2014 with signed evidence").argument("<vuln>", "Vulnerability id (CVE / GHSA / OSV / EUVD)").option(...cwdOption2).option("--regime <id>", "Reporting regime").option("--advisory <file>", "Advisory file (OSV or Vibgrate-shaped) instead of fetching").option("--offline", "No network; requires --advisory").option("--as-of <date>", "Determine as of this date (drives support status)").option("--products <substr>", "Restrict to products matching this substring").option("--include-eol", "Include releases whose support period has expired").option("--format <fmt>", "Output format (table|json)", "table").option("--generated-at <iso>", "Override the recorded timestamp (reproducibility)").option("--data-pack-version <v>", "Record the data pack version used").option("--pack", "Also print the submission pack").option("--stage <stage>", "Pack stage (regime-specific)").option("--bundle <dir>", "Write a signed evidence bundle to this directory").option("--no-sign", "Do not sign the evidence bundle").option("--tsa <url>", "RFC 3161 Time-Stamping Authority URL to timestamp the result").option("--pub <file>", "Trust root to pin the signer when re-verifying").action(async (vuln, opts) => {
|
|
14696
|
+
const rootDir = root2(opts);
|
|
13799
14697
|
const { result, regime, releases, advisory } = await runExposure(rootDir, vuln, opts);
|
|
13800
14698
|
let timestampToken;
|
|
13801
14699
|
if (opts.tsa) {
|
|
@@ -13814,7 +14712,7 @@ var exposureCmd = new Command("exposure").description("Which shipped products co
|
|
|
13814
14712
|
console.log("\n" + buildPack(result, regime, org, stage));
|
|
13815
14713
|
}
|
|
13816
14714
|
if (opts.bundle) {
|
|
13817
|
-
const dir =
|
|
14715
|
+
const dir = path46.resolve(rootDir, opts.bundle, `evidence-${result.meta.evidenceId}`);
|
|
13818
14716
|
let envelope;
|
|
13819
14717
|
if (opts.sign !== false) {
|
|
13820
14718
|
const { key: key2, minted } = resolveSigningKey(rootDir);
|
|
@@ -13822,12 +14720,12 @@ var exposureCmd = new Command("exposure").description("Which shipped products co
|
|
|
13822
14720
|
envelope = signEvidenceStatement(buildEvidenceStatement(result, VERSION), key2);
|
|
13823
14721
|
}
|
|
13824
14722
|
writeBundle({ outDir: dir, result, advisory, releases, regime, envelope, timestampToken, cliVersion: VERSION });
|
|
13825
|
-
console.error(chalk7.green("\u2714") + ` evidence bundle written to ${
|
|
14723
|
+
console.error(chalk7.green("\u2714") + ` evidence bundle written to ${path46.relative(rootDir, dir)}${envelope ? " (signed)" : " (unsigned)"}${timestampToken ? " \xB7 RFC 3161" : ""}`);
|
|
13826
14724
|
}
|
|
13827
14725
|
process.exitCode = exposureExit(result.overallStatus);
|
|
13828
14726
|
});
|
|
13829
|
-
var readinessCmd = new Command("readiness").description("Deterministic gap report against the regime obligations").option(...
|
|
13830
|
-
const rootDir =
|
|
14727
|
+
var readinessCmd = new Command("readiness").description("Deterministic gap report against the regime obligations").option(...cwdOption2).option("--regime <id>", "Reporting regime").option("--format <fmt>", "Output format (table|json)", "table").action(async (opts) => {
|
|
14728
|
+
const rootDir = root2(opts);
|
|
13831
14729
|
const org = await loadOrg(rootDir);
|
|
13832
14730
|
const regime = resolveRegime(opts.regime ?? org.defaultRegime);
|
|
13833
14731
|
const products = await loadProducts(rootDir);
|
|
@@ -13836,8 +14734,8 @@ var readinessCmd = new Command("readiness").description("Deterministic gap repor
|
|
|
13836
14734
|
const report = computeReadiness({ regime, org, products, releasesByProduct, recentDrill });
|
|
13837
14735
|
console.log(opts.format === "json" ? JSON.stringify(report, null, 2) : formatReadiness(report, regime));
|
|
13838
14736
|
});
|
|
13839
|
-
var supportCmd = new Command("support-period").description("Declare or show a product support period").argument("<product>", "Product id").option(...
|
|
13840
|
-
const rootDir =
|
|
14737
|
+
var supportCmd = new Command("support-period").description("Declare or show a product support period").argument("<product>", "Product id").option(...cwdOption2).option("--from <date>", "Declared support start (YYYY-MM-DD)").option("--until <date>", "Declared support end (YYYY-MM-DD)").action(async (productId, opts) => {
|
|
14738
|
+
const rootDir = root2(opts);
|
|
13841
14739
|
const products = await loadProducts(rootDir);
|
|
13842
14740
|
const product = products.find((p) => p.id === productId || p.name === productId);
|
|
13843
14741
|
if (!product) throw new CliError(`no such product: ${productId}`, ExitCode.NOT_FOUND);
|
|
@@ -13847,22 +14745,22 @@ var supportCmd = new Command("support-period").description("Declare or show a pr
|
|
|
13847
14745
|
}
|
|
13848
14746
|
console.log(` ${chalk7.bold(product.id)} support period: ${product.supportPeriod?.declaredFrom ?? "\u2014"} \u2192 ${chalk7.bold(product.supportPeriod?.declaredUntil ?? "not declared")}`);
|
|
13849
14747
|
});
|
|
13850
|
-
var packCmd = new Command("pack").description("Build the submission pack a human pastes into the reporting platform").argument("<vuln>", "Vulnerability id").option(...
|
|
13851
|
-
const rootDir =
|
|
14748
|
+
var packCmd = new Command("pack").description("Build the submission pack a human pastes into the reporting platform").argument("<vuln>", "Vulnerability id").option(...cwdOption2).option("--regime <id>", "Reporting regime").option("--stage <stage>", "Reporting stage").option("--advisory <file>", "Advisory file (OSV or Vibgrate-shaped)").option("--offline", "No network; requires --advisory").option("--out <file>", "Write the pack to this file").action(async (vuln, opts) => {
|
|
14749
|
+
const rootDir = root2(opts);
|
|
13852
14750
|
const { result, regime } = await runExposure(rootDir, vuln, opts);
|
|
13853
14751
|
const org = await loadOrg(rootDir);
|
|
13854
14752
|
const stage = opts.stage ?? regime.clocks[0]?.stage ?? "early-warning";
|
|
13855
14753
|
const md = buildPack(result, regime, org, stage);
|
|
13856
14754
|
if (opts.out) {
|
|
13857
|
-
fs14.writeFileSync(
|
|
14755
|
+
fs14.writeFileSync(path46.resolve(rootDir, opts.out), md);
|
|
13858
14756
|
console.error(chalk7.green("\u2714") + ` pack written to ${opts.out}`);
|
|
13859
14757
|
} else {
|
|
13860
14758
|
console.log(md);
|
|
13861
14759
|
}
|
|
13862
14760
|
process.exitCode = exposureExit(result.overallStatus);
|
|
13863
14761
|
});
|
|
13864
|
-
var drillCmd = new Command("drill").description("Timed dry-run of the determination step against a simulated advisory").option(...
|
|
13865
|
-
const rootDir =
|
|
14762
|
+
var drillCmd = new Command("drill").description("Timed dry-run of the determination step against a simulated advisory").option(...cwdOption2).option("--regime <id>", "Reporting regime").option("--scenario <name>", "Scenario seed (deterministic pick)", "kev-recent").option("--elapsed <seconds>", "Record the wall-clock your team actually took").option("--format <fmt>", "Output format (table|json)", "table").action(async (opts) => {
|
|
14763
|
+
const rootDir = root2(opts);
|
|
13866
14764
|
const org = await loadOrg(rootDir);
|
|
13867
14765
|
const regime = resolveRegime(opts.regime ?? org.defaultRegime);
|
|
13868
14766
|
const products = await loadProducts(rootDir);
|
|
@@ -13896,18 +14794,18 @@ var drillCmd = new Command("drill").description("Timed dry-run of the determinat
|
|
|
13896
14794
|
}
|
|
13897
14795
|
});
|
|
13898
14796
|
var verifyCmd = new Command("verify").description("Verify an evidence bundle offline (no Vibgrate needed)").argument("<bundle>", "Path to an evidence bundle directory or evidence.intoto.jsonl").option("--pub <file>", "Public key PEM to pin the signer (establish trust)").action((bundlePath, opts) => {
|
|
13899
|
-
const abs =
|
|
13900
|
-
const envPath = fs14.statSync(abs).isDirectory() ?
|
|
14797
|
+
const abs = path46.resolve(bundlePath);
|
|
14798
|
+
const envPath = fs14.statSync(abs).isDirectory() ? path46.join(abs, "evidence.intoto.jsonl") : abs;
|
|
13901
14799
|
if (!fs14.existsSync(envPath)) throw new CliError(`no evidence.intoto.jsonl at ${bundlePath}`, ExitCode.NOT_FOUND);
|
|
13902
14800
|
const envelope = JSON.parse(fs14.readFileSync(envPath, "utf8").trim().split("\n")[0]);
|
|
13903
|
-
const resultPath =
|
|
14801
|
+
const resultPath = path46.join(path46.dirname(envPath), "result.json");
|
|
13904
14802
|
const result = fs14.existsSync(resultPath) ? JSON.parse(fs14.readFileSync(resultPath, "utf8")) : void 0;
|
|
13905
|
-
const publicKeyPem = opts.pub ? fs14.readFileSync(
|
|
14803
|
+
const publicKeyPem = opts.pub ? fs14.readFileSync(path46.resolve(opts.pub), "utf8") : void 0;
|
|
13906
14804
|
const v = verifyEvidenceEnvelope(envelope, { publicKeyPem, result });
|
|
13907
14805
|
const color = v.status === "verified" ? chalk7.green : v.status === "failed" ? chalk7.red : chalk7.yellow;
|
|
13908
14806
|
console.log(" " + color(v.status.toUpperCase()) + ` ${v.reason}`);
|
|
13909
14807
|
if (v.evidenceId) console.log(" " + chalk7.dim(`evidence ${v.evidenceId} \xB7 regime ${v.regime} \xB7 advisory ${v.advisoryId} \xB7 ${v.overallStatus}`));
|
|
13910
|
-
const tsrPath =
|
|
14808
|
+
const tsrPath = path46.join(path46.dirname(envPath), "timestamp.tsr");
|
|
13911
14809
|
if (fs14.existsSync(tsrPath) && result) {
|
|
13912
14810
|
const t = verifyTimestamp(fs14.readFileSync(tsrPath), exposureSubjectDigest(result));
|
|
13913
14811
|
const tcolor = t.imprintMatches ? chalk7.green : chalk7.red;
|
|
@@ -13915,8 +14813,8 @@ var verifyCmd = new Command("verify").description("Verify an evidence bundle off
|
|
|
13915
14813
|
}
|
|
13916
14814
|
process.exitCode = v.status === "verified" ? ExitCode.OK : v.status === "unverified" ? ExitCode.GATE_FAILED : ExitCode.ERROR;
|
|
13917
14815
|
});
|
|
13918
|
-
var watchCmd = new Command("watch").description('Check CISA KEV for new exposure against your shipped components (surfaces the listing; does not decide "actively exploited")').option(...
|
|
13919
|
-
const rootDir =
|
|
14816
|
+
var watchCmd = new Command("watch").description('Check CISA KEV for new exposure against your shipped components (surfaces the listing; does not decide "actively exploited")').option(...cwdOption2).option("--regime <id>", "Reporting regime").option("--since <date>", "Only KEV entries added on/after this date (YYYY-MM-DD)").option("--webhook <url>", "POST the alert payload to this URL when there are hits").option("--format <fmt>", "Output format (table|json)", "table").action(async (opts) => {
|
|
14817
|
+
const rootDir = root2(opts);
|
|
13920
14818
|
const org = await loadOrg(rootDir);
|
|
13921
14819
|
const regime = resolveRegime(opts.regime ?? org.defaultRegime);
|
|
13922
14820
|
const products = await loadProducts(rootDir);
|
|
@@ -13967,8 +14865,8 @@ var watchCmd = new Command("watch").description('Check CISA KEV for new exposure
|
|
|
13967
14865
|
console.log(" " + chalk7.dim('watch surfaces the CISA KEV listing. Whether a vulnerability is "actively exploited" for a filing is your determination, not ours.'));
|
|
13968
14866
|
process.exitCode = hits.some((h) => h.result.overallStatus === "affected") ? ExitCode.GATE_FAILED : hits.some((h) => h.result.overallStatus === "undetermined") ? ExitCode.NOT_FOUND : ExitCode.OK;
|
|
13969
14867
|
});
|
|
13970
|
-
var exportCmd = new Command("export").description("Air-gap bundle of all evidence state (org, products, frozen releases, readiness)").option(...
|
|
13971
|
-
const rootDir =
|
|
14868
|
+
var exportCmd = new Command("export").description("Air-gap bundle of all evidence state (org, products, frozen releases, readiness)").option(...cwdOption2).option("--out <dir>", "Output directory", "evidence-export").option("--regime <id>", "Reporting regime for the readiness snapshot").action(async (opts) => {
|
|
14869
|
+
const rootDir = root2(opts);
|
|
13972
14870
|
const org = await loadOrg(rootDir);
|
|
13973
14871
|
const regime = resolveRegime(opts.regime ?? org.defaultRegime);
|
|
13974
14872
|
const products = await loadProducts(rootDir);
|
|
@@ -13976,15 +14874,15 @@ var exportCmd = new Command("export").description("Air-gap bundle of all evidenc
|
|
|
13976
14874
|
const releases = [];
|
|
13977
14875
|
for (const list of releasesByProduct.values()) releases.push(...list);
|
|
13978
14876
|
const report = computeReadiness({ regime, org, products, releasesByProduct, recentDrill: await hasRecentDrill(rootDir, today()) });
|
|
13979
|
-
const dir =
|
|
13980
|
-
await writeJsonFile(
|
|
13981
|
-
await writeJsonFile(
|
|
13982
|
-
for (const r of releases) await writeJsonFile(
|
|
13983
|
-
await writeJsonFile(
|
|
13984
|
-
console.error(chalk7.green("\u2714") + ` exported evidence state to ${
|
|
14877
|
+
const dir = path46.resolve(rootDir, opts.out);
|
|
14878
|
+
await writeJsonFile(path46.join(dir, "org.json"), org);
|
|
14879
|
+
await writeJsonFile(path46.join(dir, "products.json"), products);
|
|
14880
|
+
for (const r of releases) await writeJsonFile(path46.join(dir, "releases", `${r.productId}@${r.version}.json`.replace(/[^A-Za-z0-9._@-]/g, "_")), r);
|
|
14881
|
+
await writeJsonFile(path46.join(dir, "readiness.json"), report);
|
|
14882
|
+
console.error(chalk7.green("\u2714") + ` exported evidence state to ${path46.relative(rootDir, dir)} (${products.length} products, ${releases.length} frozen releases)`);
|
|
13985
14883
|
});
|
|
13986
|
-
var pushCmd = new Command("push").description("Push the product registry (and an optional exposure result) to Vibgrate Cloud").option(...
|
|
13987
|
-
const rootDir =
|
|
14884
|
+
var pushCmd = new Command("push").description("Push the product registry (and an optional exposure result) to Vibgrate Cloud").option(...cwdOption2).option("--dsn <dsn>", "DSN token (or use VIBGRATE_DSN env / `vg login`)").option("--regime <id>", "Reporting regime").option("--result <file>", "An exposure result.json (or a bundle dir) to include").option("--signed", "Mark the included exposure result as signed").option("--strict", "Fail on push errors").action(async (opts) => {
|
|
14885
|
+
const rootDir = root2(opts);
|
|
13988
14886
|
const dsn = resolveDsn(opts.dsn);
|
|
13989
14887
|
if (!dsn) throw new CliError("no DSN \u2014 run `vg login`, set VIBGRATE_DSN, or pass --dsn", ExitCode.USAGE_ERROR);
|
|
13990
14888
|
const parsed = parseDsn$1(dsn);
|
|
@@ -14007,8 +14905,8 @@ var pushCmd = new Command("push").description("Push the product registry (and an
|
|
|
14007
14905
|
}));
|
|
14008
14906
|
let exposure;
|
|
14009
14907
|
if (opts.result) {
|
|
14010
|
-
const raw =
|
|
14011
|
-
const resultPath = fs14.existsSync(raw) && fs14.statSync(raw).isDirectory() ?
|
|
14908
|
+
const raw = path46.resolve(rootDir, opts.result);
|
|
14909
|
+
const resultPath = fs14.existsSync(raw) && fs14.statSync(raw).isDirectory() ? path46.join(raw, "result.json") : raw;
|
|
14012
14910
|
if (!fs14.existsSync(resultPath)) throw new CliError(`no result.json at ${opts.result}`, ExitCode.NOT_FOUND);
|
|
14013
14911
|
exposure = JSON.parse(fs14.readFileSync(resultPath, "utf8"));
|
|
14014
14912
|
}
|
|
@@ -14090,7 +14988,9 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
14090
14988
|
"dsn",
|
|
14091
14989
|
"update",
|
|
14092
14990
|
"sbom",
|
|
14093
|
-
"evidence"
|
|
14991
|
+
"evidence",
|
|
14992
|
+
"locale",
|
|
14993
|
+
"localize"
|
|
14094
14994
|
]);
|
|
14095
14995
|
function buildProgram() {
|
|
14096
14996
|
const program = new Command();
|
|
@@ -14150,6 +15050,7 @@ ${aliasNote}Docs: https://vibgrate.com/help`;
|
|
|
14150
15050
|
program.addCommand(updateCommand);
|
|
14151
15051
|
program.addCommand(sbomCommand);
|
|
14152
15052
|
program.addCommand(evidenceCommand);
|
|
15053
|
+
registerLocale(program);
|
|
14153
15054
|
return program;
|
|
14154
15055
|
}
|
|
14155
15056
|
var VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
@@ -14206,7 +15107,7 @@ function dispatch(argv, cwd) {
|
|
|
14206
15107
|
}
|
|
14207
15108
|
const looksLikeQuestion = /\s/.test(first) || first.endsWith("?");
|
|
14208
15109
|
if (looksLikeQuestion) return ["ask", ...args];
|
|
14209
|
-
if (fs14.existsSync(
|
|
15110
|
+
if (fs14.existsSync(path46.resolve(cwd, first))) {
|
|
14210
15111
|
return [hasBuildOnlyFlag(args) ? "build" : "scan", ...args];
|
|
14211
15112
|
}
|
|
14212
15113
|
const moved = MOVED_COMMANDS[first];
|
|
@@ -14251,9 +15152,9 @@ async function main(argv = process.argv) {
|
|
|
14251
15152
|
}
|
|
14252
15153
|
function readCwd(raw) {
|
|
14253
15154
|
const i = raw.findIndex((a) => a === "-C" || a === "--cwd");
|
|
14254
|
-
if (i >= 0 && raw[i + 1]) return
|
|
15155
|
+
if (i >= 0 && raw[i + 1]) return path46.resolve(raw[i + 1]);
|
|
14255
15156
|
const eq = raw.find((a) => a.startsWith("--cwd="));
|
|
14256
|
-
if (eq) return
|
|
15157
|
+
if (eq) return path46.resolve(eq.slice("--cwd=".length));
|
|
14257
15158
|
return process.cwd();
|
|
14258
15159
|
}
|
|
14259
15160
|
function handleError(err) {
|