@mesh-tech/mesh-cli 0.28.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
1
  {
2
- "commit": "dd1a85628",
3
- "builtAt": "2026-09-17T22:16:24.040Z"
2
+ "commit": "a5fdbfed6",
3
+ "builtAt": "2026-09-18T19:56:52.850Z"
4
4
  }
@@ -0,0 +1,28 @@
1
+ export interface ProbeResult {
2
+ findings: UnresolvedImport[];
3
+ unchecked: string | null;
4
+ }
5
+ export interface UnresolvedImport {
6
+ pkg: string;
7
+ specifier: string;
8
+ file: string;
9
+ importer: string;
10
+ via: string[];
11
+ fix: string;
12
+ }
13
+ export declare function stripComments(source: string): string;
14
+ export declare function moduleSpecifiers(source: string): string[];
15
+ export declare function splitSpecifier(specifier: string): {
16
+ pkg: string;
17
+ sub: string;
18
+ } | null;
19
+ export declare function findPackageDir(fromDir: string, pkg: string): string | null;
20
+ export declare function resolveSubpath(pkgDir: string, sub: string): string | null;
21
+ export declare function programEntry(appDir: string): string | null;
22
+ export declare function dependenciesInstalled(dir: string, stopAt?: string): boolean;
23
+ export interface ProbeOptions {
24
+ entry?: string;
25
+ label?: string;
26
+ root?: string;
27
+ }
28
+ export declare function resolveProgramImports(appDir: string, options?: ProbeOptions): ProbeResult;
@@ -0,0 +1,276 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { builtinModules } from "node:module";
4
+ import { parse as parseYaml } from "yaml";
5
+ const CONDITIONS = new Set(["source", "node", "import", "require", "module", "module-sync", "default"]);
6
+ const EXTENSIONS = ["", ".ts", ".tsx", ".js", ".mjs", ".cjs", "/index.ts", "/index.js", "/index.mjs"];
7
+ const SCOPE = "@mesh-tech/";
8
+ const FILE_BUDGET = 1500;
9
+ const BUILTINS = new Set(builtinModules);
10
+ export function stripComments(source) {
11
+ return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:"'`\\])\/\/[^\n]*/g, "$1");
12
+ }
13
+ export function moduleSpecifiers(source) {
14
+ const text = stripComments(source);
15
+ const out = [];
16
+ for (const m of text.matchAll(/(?<![.\w$])(?:import|export)\b([^;]*?)\bfrom\s*["']([^"']+)["']/g)) {
17
+ if (/^\s*type\s/.test(m[1] ?? ""))
18
+ continue;
19
+ out.push(m[2] ?? "");
20
+ }
21
+ for (const m of text.matchAll(/(?<![.\w$])import\s*["']([^"']+)["']/g))
22
+ out.push(m[1] ?? "");
23
+ for (const m of text.matchAll(/(?<![.\w$])import\s*\(\s*["']([^"']+)["']\s*\)/g))
24
+ out.push(m[1] ?? "");
25
+ for (const m of text.matchAll(/(?<![.\w$])require\s*\(\s*["']([^"']+)["']\s*\)/g))
26
+ out.push(m[1] ?? "");
27
+ return [...new Set(out)].filter(Boolean);
28
+ }
29
+ export function splitSpecifier(specifier) {
30
+ if (!specifier || specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("#"))
31
+ return null;
32
+ if (specifier.includes(":"))
33
+ return null;
34
+ if (BUILTINS.has(specifier))
35
+ return null;
36
+ const parts = specifier.split("/");
37
+ const depth = specifier.startsWith("@") ? 2 : 1;
38
+ if (parts.length < depth || parts.some((p) => p === ""))
39
+ return null;
40
+ const pkg = parts.slice(0, depth).join("/");
41
+ const rest = parts.slice(depth);
42
+ return { pkg, sub: rest.length > 0 ? `./${rest.join("/")}` : "." };
43
+ }
44
+ export function findPackageDir(fromDir, pkg) {
45
+ let dir = path.resolve(fromDir);
46
+ for (;;) {
47
+ const candidate = path.join(dir, "node_modules", ...pkg.split("/"));
48
+ if (fs.existsSync(path.join(candidate, "package.json"))) {
49
+ try {
50
+ return fs.realpathSync(candidate);
51
+ }
52
+ catch {
53
+ return candidate;
54
+ }
55
+ }
56
+ const parent = path.dirname(dir);
57
+ if (parent === dir)
58
+ return null;
59
+ dir = parent;
60
+ }
61
+ }
62
+ function conditionTargets(node, acc = []) {
63
+ if (typeof node === "string") {
64
+ acc.push(node);
65
+ return acc;
66
+ }
67
+ if (Array.isArray(node)) {
68
+ for (const entry of node)
69
+ conditionTargets(entry, acc);
70
+ return acc;
71
+ }
72
+ if (!node || typeof node !== "object")
73
+ return acc;
74
+ for (const [key, value] of Object.entries(node)) {
75
+ if (!CONDITIONS.has(key))
76
+ continue;
77
+ conditionTargets(value, acc);
78
+ }
79
+ return acc;
80
+ }
81
+ function exportTargets(exportsField, sub) {
82
+ if (typeof exportsField === "string")
83
+ return sub === "." ? [exportsField] : [];
84
+ if (Array.isArray(exportsField))
85
+ return exportsField.flatMap((entry) => exportTargets(entry, sub));
86
+ if (!exportsField || typeof exportsField !== "object")
87
+ return [];
88
+ const map = exportsField;
89
+ const keys = Object.keys(map);
90
+ if (keys.length > 0 && !keys.some((k) => k.startsWith(".")))
91
+ return sub === "." ? conditionTargets(map) : [];
92
+ if (sub in map)
93
+ return conditionTargets(map[sub]);
94
+ let best = [];
95
+ let bestPrefix = -1;
96
+ for (const key of keys) {
97
+ const star = key.indexOf("*");
98
+ if (star === -1)
99
+ continue;
100
+ const prefix = key.slice(0, star);
101
+ const suffix = key.slice(star + 1);
102
+ if (sub.length < prefix.length + suffix.length)
103
+ continue;
104
+ if (!sub.startsWith(prefix) || !sub.endsWith(suffix))
105
+ continue;
106
+ if (prefix.length <= bestPrefix)
107
+ continue;
108
+ const segment = sub.slice(prefix.length, sub.length - suffix.length);
109
+ bestPrefix = prefix.length;
110
+ best = conditionTargets(map[key]).map((target) => target.replaceAll("*", segment));
111
+ }
112
+ return best;
113
+ }
114
+ function isDirectory(p) {
115
+ try {
116
+ return fs.statSync(p).isDirectory();
117
+ }
118
+ catch {
119
+ return false;
120
+ }
121
+ }
122
+ function firstFile(base) {
123
+ for (const ext of EXTENSIONS) {
124
+ const candidate = base + ext;
125
+ try {
126
+ if (fs.statSync(candidate).isFile())
127
+ return candidate;
128
+ }
129
+ catch {
130
+ }
131
+ }
132
+ return null;
133
+ }
134
+ export function resolveSubpath(pkgDir, sub) {
135
+ let manifest = {};
136
+ try {
137
+ manifest = JSON.parse(fs.readFileSync(path.join(pkgDir, "package.json"), "utf8"));
138
+ }
139
+ catch {
140
+ return null;
141
+ }
142
+ if (manifest.exports !== undefined) {
143
+ for (const target of exportTargets(manifest.exports, sub)) {
144
+ const file = firstFile(path.resolve(pkgDir, target));
145
+ if (file)
146
+ return file;
147
+ }
148
+ return null;
149
+ }
150
+ if (sub === ".")
151
+ return firstFile(path.resolve(pkgDir, manifest.main ?? "index.js"));
152
+ return firstFile(path.resolve(pkgDir, sub));
153
+ }
154
+ export function programEntry(appDir) {
155
+ const app = path.resolve(appDir);
156
+ const index = path.join(app, "index.ts");
157
+ if (fs.existsSync(index))
158
+ return index;
159
+ let main;
160
+ try {
161
+ main = parseYaml(fs.readFileSync(path.join(app, "Pulumi.yaml"), "utf8"))?.main;
162
+ }
163
+ catch {
164
+ return null;
165
+ }
166
+ if (typeof main !== "string" || !main)
167
+ return null;
168
+ const target = path.resolve(app, main);
169
+ return isDirectory(target) ? firstFile(path.join(target, "index")) : firstFile(target);
170
+ }
171
+ export function dependenciesInstalled(dir, stopAt) {
172
+ const root = stopAt === undefined ? null : path.resolve(stopAt);
173
+ let at = path.resolve(dir);
174
+ for (;;) {
175
+ if (fs.existsSync(path.join(at, "node_modules")))
176
+ return true;
177
+ if (at === root)
178
+ return false;
179
+ const parent = path.dirname(at);
180
+ if (parent === at)
181
+ return false;
182
+ at = parent;
183
+ }
184
+ }
185
+ function suggestedRange(appDir, parentDir) {
186
+ try {
187
+ const manifest = JSON.parse(fs.readFileSync(path.join(appDir, "package.json"), "utf8"));
188
+ for (const section of [manifest.dependencies, manifest.devDependencies]) {
189
+ for (const [name, range] of Object.entries(section ?? {})) {
190
+ if (name.startsWith(SCOPE) && range)
191
+ return range;
192
+ }
193
+ }
194
+ }
195
+ catch {
196
+ }
197
+ if (!parentDir)
198
+ return null;
199
+ try {
200
+ const version = JSON.parse(fs.readFileSync(path.join(parentDir, "package.json"), "utf8")).version;
201
+ return version ? `^${version}` : null;
202
+ }
203
+ catch {
204
+ return null;
205
+ }
206
+ }
207
+ function displayPath(app, visit) {
208
+ const within = path.relative(visit.pkg === null ? app : visit.pkgDir, visit.file).split(path.sep).join("/");
209
+ return visit.pkg === null ? within : `${visit.pkg}/${within}`;
210
+ }
211
+ export function resolveProgramImports(appDir, options = {}) {
212
+ const app = path.resolve(appDir);
213
+ const label = options.label ?? path.basename(app);
214
+ const entryFile = options.entry ? firstFile(path.resolve(app, options.entry)) : programEntry(app);
215
+ if (!dependenciesInstalled(app, options.root)) {
216
+ return { findings: [], unchecked: "dependencies are not installed (run the install, then re-run)" };
217
+ }
218
+ if (!entryFile) {
219
+ return { findings: [], unchecked: "no Pulumi program found (index.ts, or the file Pulumi.yaml's `main` names)" };
220
+ }
221
+ const findings = [];
222
+ const reported = new Set();
223
+ const seen = new Set([entryFile]);
224
+ const queue = [{ file: entryFile, pkgDir: app, pkg: null, via: [] }];
225
+ let budget = FILE_BUDGET;
226
+ while (queue.length > 0 && budget-- > 0) {
227
+ const here = queue.shift();
228
+ let source;
229
+ try {
230
+ source = fs.readFileSync(here.file, "utf8");
231
+ }
232
+ catch {
233
+ continue;
234
+ }
235
+ const fromDir = path.dirname(here.file);
236
+ for (const specifier of moduleSpecifiers(source)) {
237
+ if (specifier.startsWith(".")) {
238
+ if (here.pkg === null)
239
+ continue;
240
+ const next = firstFile(path.resolve(fromDir, specifier));
241
+ if (next && !seen.has(next) && next.startsWith(here.pkgDir)) {
242
+ seen.add(next);
243
+ queue.push({ ...here, file: next });
244
+ }
245
+ continue;
246
+ }
247
+ const split = splitSpecifier(specifier);
248
+ if (!split)
249
+ continue;
250
+ const inScope = split.pkg.startsWith(SCOPE);
251
+ if (here.pkg !== null && !inScope)
252
+ continue;
253
+ const pkgDir = findPackageDir(fromDir, split.pkg);
254
+ const target = pkgDir ? resolveSubpath(pkgDir, split.sub) : null;
255
+ if (!pkgDir || !target) {
256
+ if (reported.has(specifier))
257
+ continue;
258
+ reported.add(specifier);
259
+ const range = inScope ? suggestedRange(app, here.pkg === null ? null : here.pkgDir) : null;
260
+ const line = `"${split.pkg}": "${range ?? (inScope ? "<the version your other @mesh-tech packages use>" : "<the version you need>")}"`;
261
+ const fix = pkgDir
262
+ ? `${split.pkg} is installed but ships no ${split.sub} — the installed version predates the subpath; raise ${line} in ${label}/package.json`
263
+ : `add ${line} to ${label}/package.json dependencies`;
264
+ findings.push({ pkg: split.pkg, specifier, file: here.file, importer: displayPath(app, here), via: here.via, fix });
265
+ continue;
266
+ }
267
+ if (!inScope || seen.has(target))
268
+ continue;
269
+ seen.add(target);
270
+ queue.push({ file: target, pkgDir, pkg: split.pkg, via: [...here.via, specifier] });
271
+ }
272
+ }
273
+ findings.sort((a, b) => a.pkg.localeCompare(b.pkg) || a.specifier.localeCompare(b.specifier));
274
+ const unchecked = queue.length > 0 ? `the import graph exceeded ${FILE_BUDGET} files` : null;
275
+ return { findings, unchecked };
276
+ }
@@ -1,6 +1,7 @@
1
1
  import { Command } from "commander";
2
+ import { type ProbeResult } from "./app-check-resolve.js";
2
3
  import { jsonReport, type Check, type CheckResult, type CheckStatus } from "./dev-doctor.js";
3
- export type AppCheckCode = "UI_IN_API" | "NO_UI_SERVICE" | "NO_REGISTER" | "CUSTOM_SESSION_AUTH" | "NON_PLATFORM_LOGGER" | "LOCAL_AUTH_LIB" | "NO_AUTHZ_POINTER" | "METADATA_UNPUBLISHED" | "IAC_GRANTS" | "POINTER_PROJECT_OVERRIDE" | "PASSWORD_STORE" | "EMAIL_ALLOWLIST" | "USERS_TABLE" | "DOCS_SITE_PUBLIC" | "DOCS_AUDIENCE_IMPLICIT" | "DOCS_AUDIENCE_UNGRANTABLE" | "DOCS_VENDOR_AUDIENCE_PARTNER";
4
+ export type AppCheckCode = "UNRESOLVABLE_IMPORT" | "UI_IN_API" | "NO_UI_SERVICE" | "NO_REGISTER" | "CUSTOM_SESSION_AUTH" | "NON_PLATFORM_LOGGER" | "LOCAL_AUTH_LIB" | "NO_AUTHZ_POINTER" | "METADATA_UNPUBLISHED" | "IAC_GRANTS" | "POINTER_PROJECT_OVERRIDE" | "PASSWORD_STORE" | "EMAIL_ALLOWLIST" | "USERS_TABLE" | "DOCS_SITE_PUBLIC" | "DOCS_AUDIENCE_IMPLICIT" | "DOCS_AUDIENCE_UNGRANTABLE" | "DOCS_VENDOR_AUDIENCE_PARTNER";
4
5
  export interface AppCheckRule {
5
6
  gate: string;
6
7
  title: string;
@@ -35,14 +36,16 @@ export declare function docsSiteFindings(text: string): Array<{
35
36
  }>;
36
37
  export declare function statedKind(decl: Pick<SurfaceDeclaration, "ctor">, args: Map<string, string>): string | undefined;
37
38
  export declare function listApps(root: string): string[];
38
- export declare function scanApp(root: string, appRel: string): Finding[];
39
+ export declare function probeResolution(root: string, appRel: string): ProbeResult;
40
+ export declare function scanApp(root: string, appRel: string, resolution?: ProbeResult): Finding[];
39
41
  export declare function scanRepo(root: string): Finding[];
40
42
  export interface AppCheckContext {
41
43
  root: string;
42
44
  app: string | null;
43
45
  findings: Finding[];
46
+ unchecked: string | null;
44
47
  }
45
- export declare function resultFor(code: AppCheckCode, findings: Finding[]): CheckResult;
48
+ export declare function resultFor(code: AppCheckCode, findings: Finding[], unchecked?: string | null): CheckResult;
46
49
  export declare function appContractChecks(scope: "app" | "repo"): Check<AppCheckContext>[];
47
50
  export interface AppCheckReport {
48
51
  root: string;
@@ -3,8 +3,17 @@ import * as path from "node:path";
3
3
  import { logError, logInfo } from "../utils/index.js";
4
4
  import { MeshCliError, emitJsonPayload } from "../utils/errors.js";
5
5
  import { resolveTargetRoot } from "./skills.js";
6
+ import { programEntry, resolveProgramImports } from "./app-check-resolve.js";
6
7
  import { ICONS, aggregateStatus, jsonReport, renderHuman, runChecks, } from "./dev-doctor.js";
7
8
  export const APP_CHECK_RULES = {
9
+ UNRESOLVABLE_IMPORT: {
10
+ gate: "0.6",
11
+ title: "The Pulumi program's imports resolve",
12
+ severity: "block",
13
+ remediation: "declare the package in apps/<app>/package.json dependencies, at the range the app's other @mesh-tech/* packages use, and re-install — " +
14
+ "an optional peer that nothing names installs silently and fails at `mesh deploy`",
15
+ scope: "app",
16
+ },
8
17
  UI_IN_API: {
9
18
  gate: "0.1",
10
19
  title: "UI is its own service",
@@ -610,7 +619,10 @@ export function listApps(root) {
610
619
  apps.push(".");
611
620
  return apps;
612
621
  }
613
- export function scanApp(root, appRel) {
622
+ export function probeResolution(root, appRel) {
623
+ return resolveProgramImports(path.resolve(root, appRel), { root, label: appRel === "." ? "this app" : appRel });
624
+ }
625
+ export function scanApp(root, appRel, resolution = probeResolution(root, appRel)) {
614
626
  const app = path.resolve(root, appRel);
615
627
  const index = path.join(app, "index.ts");
616
628
  const findings = [];
@@ -620,6 +632,12 @@ export function scanApp(root, appRel) {
620
632
  const hasProgram = fs.existsSync(index) || fs.existsSync(path.join(app, "Pulumi.yaml"));
621
633
  if (!hasProgram)
622
634
  return findings;
635
+ const entry = programEntry(app);
636
+ for (const bad of resolution.findings) {
637
+ const chain = [...bad.via, bad.specifier].join(" → ");
638
+ const at = rel(root, entry ?? index);
639
+ hit("UNRESOLVABLE_IMPORT", at, `${chain} does not resolve from this app (imported by ${bad.importer}) — ${bad.fix}`);
640
+ }
623
641
  if (fs.existsSync(index)) {
624
642
  const indexRel = rel(root, index);
625
643
  let servesHtml = false;
@@ -728,9 +746,12 @@ export function scanRepo(root) {
728
746
  }
729
747
  return findings;
730
748
  }
731
- export function resultFor(code, findings) {
749
+ export function resultFor(code, findings, unchecked = null) {
732
750
  const rule = APP_CHECK_RULES[code];
733
751
  const mine = findings.filter((f) => f.code === code);
752
+ if (code === "UNRESOLVABLE_IMPORT" && unchecked) {
753
+ return { status: "ok", summary: `gate ${rule.gate}: not checked — ${unchecked}` };
754
+ }
734
755
  if (mine.length === 0)
735
756
  return { status: "ok", summary: `gate ${rule.gate}: no ${code} finding` };
736
757
  return {
@@ -745,7 +766,7 @@ export function appContractChecks(scope) {
745
766
  id: code,
746
767
  title: `${APP_CHECK_RULES[code].title} (${APP_CHECK_RULES[code].gate})`,
747
768
  phases: ["ondemand", "preflight"],
748
- run: async (ctx) => resultFor(code, ctx.findings),
769
+ run: async (ctx) => resultFor(code, ctx.findings, ctx.unchecked),
749
770
  }));
750
771
  }
751
772
  export async function runAppCheck(root, apps) {
@@ -758,10 +779,12 @@ export async function runAppCheck(root, apps) {
758
779
  }
759
780
  const scopes = [];
760
781
  for (const app of targets) {
761
- const results = await runChecks({ root, app, findings: scanApp(root, app) }, "ondemand", appContractChecks("app"));
782
+ const resolution = probeResolution(root, app);
783
+ const ctx = { root, app, findings: scanApp(root, app, resolution), unchecked: resolution.unchecked };
784
+ const results = await runChecks(ctx, "ondemand", appContractChecks("app"));
762
785
  scopes.push({ scope: app, ...jsonReport(results) });
763
786
  }
764
- const repoResults = await runChecks({ root, app: null, findings: scanRepo(root) }, "ondemand", appContractChecks("repo"));
787
+ const repoResults = await runChecks({ root, app: null, findings: scanRepo(root), unchecked: null }, "ondemand", appContractChecks("repo"));
765
788
  scopes.push({ scope: "(repo)", ...jsonReport(repoResults) });
766
789
  return { root, status: aggregateStatus(scopes.map((s) => ({ status: s.status, summary: "" }))), scopes };
767
790
  }
@@ -787,7 +810,7 @@ export function registerAppCommands(program) {
787
810
  const app = program.command("app").description("Inspect a tenant app against the Mesh app contract");
788
811
  app
789
812
  .command("check")
790
- .description("Check tenant apps against the Mesh app contract (@mesh-tech/app-kit skills/apps/references/app-contract.md): a UI is its own service, the app registers with the Hub, sign-in goes through the platform proxy, logs go through @mesh-tech/logger, and people/roles/keys are surfaced through the Hub. Exit 1 on any blocking finding — the same gate mesh create-app, tenant CI and the platform reviewer run.")
813
+ .description("Check tenant apps against the Mesh app contract (@mesh-tech/app-kit skills/apps/references/app-contract.md): a UI is its own service, the app registers with the Hub, sign-in goes through the platform proxy, logs go through @mesh-tech/logger, people/roles/keys are surfaced through the Hub, and the Pulumi program's imports resolve from the app directory. Exit 1 on any blocking finding — the same gate mesh create-app, tenant CI and the platform reviewer run. The import gate reads the installed dependency tree, so run it after the install; with no node_modules it reports itself not checked rather than met.")
791
814
  .argument("[apps...]", "repo-relative app dirs to check (default: every apps/* with a Pulumi program)")
792
815
  .option("--root <dir>", "checkout to scan (default: the enclosing git root)")
793
816
  .option("--json", "machine-readable output (one report per app plus the repo-scoped checks)", false)
@@ -3,6 +3,7 @@ import { type AwsIdentity } from "../utils/aws-auth.js";
3
3
  declare const CONFIG_DIR: string;
4
4
  declare const CONFIG_FILE: string;
5
5
  declare const CREDENTIALS_FILE: string;
6
+ export declare function loginScopes(config: Pick<ContextConfig, "hubProjectId">): string;
6
7
  interface ContextConfig {
7
8
  issuer: string;
8
9
  clientId: string;
@@ -12,7 +13,9 @@ interface ContextConfig {
12
13
  vpn?: string;
13
14
  vpnJoinBroker?: string;
14
15
  registryBroker?: string;
16
+ hubProjectId?: string;
15
17
  }
18
+ export declare function withDiscoveredFields(config: ContextConfig, data: Record<string, unknown>): ContextConfig;
16
19
  interface CachedCredentials {
17
20
  idToken: string;
18
21
  accessToken: string;
@@ -46,6 +49,7 @@ export declare function renderAwsFallbackStatus(context: string, reason: string,
46
49
  lines: StatusLine[];
47
50
  exitCode: 0 | 1;
48
51
  };
52
+ export declare function tokenAudience(payload: Record<string, unknown>): string[];
49
53
  export declare function registerLoginCommand(program: Command): void;
50
54
  export declare const RESERVED_REGISTRY_CONTEXT = "registry";
51
55
  export declare const NO_REGISTRY_DEPRECATED = "--no-registry is deprecated and ignored: mesh login no longer touches the registry.";
@@ -107,6 +111,10 @@ export declare function ensureLogin(context: string, opts?: {
107
111
  device?: boolean;
108
112
  interactive?: boolean;
109
113
  }): Promise<CachedCredentials | null>;
114
+ export declare function adoptPublishedHubProject(context: string, config: ContextConfig, discover?: (context: string, opts: {
115
+ quiet: true;
116
+ silent: true;
117
+ }) => Promise<ContextConfig | null>, write?: (context: string, config: ContextConfig) => void): Promise<ContextConfig>;
110
118
  export { CONFIG_DIR, CONFIG_FILE, CREDENTIALS_FILE };
111
119
  export type { ContextConfig, CachedCredentials, CredentialsFile };
112
120
  export declare function readAllContextConfigs(): ConfigFile;