cachelint 0.1.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.
@@ -0,0 +1,197 @@
1
+ // src/exit-codes.ts
2
+ var EXIT_OK = 0;
3
+ var EXIT_CHECK_FAILED = 1;
4
+ var EXIT_PRO_REQUIRED = 2;
5
+ var EXIT_LOCK_PROBLEM = 3;
6
+ var EXIT_CONFIG_OR_SOURCE_ERROR = 4;
7
+
8
+ // src/errors.ts
9
+ var CachelintError = class extends Error {
10
+ /** Stable string code (e.g. `"CFG_INVALID"`) — safe to log and to grep. */
11
+ code;
12
+ /** Suggested exit code if this error propagates to the CLI's top-level. */
13
+ exitCode;
14
+ /** Optional one-line hint to print after the message. */
15
+ hint;
16
+ constructor(args) {
17
+ super(args.message);
18
+ this.name = this.constructor.name;
19
+ this.code = args.code;
20
+ this.exitCode = args.exitCode;
21
+ if (args.hint !== void 0) this.hint = args.hint;
22
+ if (args.cause !== void 0) this.cause = args.cause;
23
+ }
24
+ };
25
+ var ConfigError = class extends CachelintError {
26
+ /** Optional file path the error was found in. */
27
+ file;
28
+ /** Optional "key.path" inside the config that failed. */
29
+ keyPath;
30
+ constructor(args) {
31
+ super({
32
+ code: args.code ?? "CFG_INVALID",
33
+ message: args.message,
34
+ exitCode: EXIT_CONFIG_OR_SOURCE_ERROR,
35
+ ...args.hint !== void 0 ? { hint: args.hint } : {},
36
+ ...args.cause !== void 0 ? { cause: args.cause } : {}
37
+ });
38
+ if (args.file !== void 0) this.file = args.file;
39
+ if (args.keyPath !== void 0) this.keyPath = args.keyPath;
40
+ }
41
+ };
42
+ var SourceError = class extends CachelintError {
43
+ path;
44
+ constructor(args) {
45
+ super({
46
+ code: args.code ?? "SRC_INVALID",
47
+ message: args.message,
48
+ exitCode: EXIT_CONFIG_OR_SOURCE_ERROR,
49
+ ...args.hint !== void 0 ? { hint: args.hint } : {},
50
+ ...args.cause !== void 0 ? { cause: args.cause } : {}
51
+ });
52
+ if (args.path !== void 0) this.path = args.path;
53
+ }
54
+ };
55
+ var RuleError = class extends CachelintError {
56
+ ruleId;
57
+ constructor(args) {
58
+ super({
59
+ code: args.code ?? "RULE_INVALID",
60
+ message: args.message,
61
+ exitCode: EXIT_CONFIG_OR_SOURCE_ERROR,
62
+ ...args.hint !== void 0 ? { hint: args.hint } : {},
63
+ ...args.cause !== void 0 ? { cause: args.cause } : {}
64
+ });
65
+ if (args.ruleId !== void 0) this.ruleId = args.ruleId;
66
+ }
67
+ };
68
+ var LockError = class extends CachelintError {
69
+ constructor(args) {
70
+ super({
71
+ code: args.code ?? "LOCK_INVALID",
72
+ message: args.message,
73
+ exitCode: EXIT_LOCK_PROBLEM,
74
+ ...args.hint !== void 0 ? { hint: args.hint } : {},
75
+ ...args.cause !== void 0 ? { cause: args.cause } : {}
76
+ });
77
+ }
78
+ };
79
+ var LockMissingError = class extends LockError {
80
+ constructor(path) {
81
+ super({
82
+ code: "LOCK_MISSING",
83
+ message: `cachelint.lock not found at ${path}`,
84
+ hint: "Run `cachelint hash <paths>` once and commit the resulting cachelint.lock."
85
+ });
86
+ }
87
+ };
88
+ var LockCorruptError = class extends LockError {
89
+ constructor(message, hint) {
90
+ super({ code: "LOCK_CORRUPT", message, ...hint !== void 0 ? { hint } : {} });
91
+ }
92
+ };
93
+ var LicenseError = class extends CachelintError {
94
+ constructor(args) {
95
+ super({
96
+ code: args.code ?? "PRO_REQUIRED",
97
+ message: args.message,
98
+ exitCode: EXIT_PRO_REQUIRED,
99
+ ...args.hint !== void 0 ? { hint: args.hint } : {}
100
+ });
101
+ }
102
+ };
103
+ var ExactCountUnavailableError = class extends CachelintError {
104
+ constructor(message, hint) {
105
+ super({
106
+ code: "EXACT_UNAVAILABLE",
107
+ message,
108
+ exitCode: EXIT_CONFIG_OR_SOURCE_ERROR,
109
+ ...hint !== void 0 ? { hint } : {}
110
+ });
111
+ }
112
+ };
113
+ var AdapterError = class extends CachelintError {
114
+ constructor(args) {
115
+ super({
116
+ code: args.code ?? "ADAPTER_INVALID",
117
+ message: args.message,
118
+ exitCode: EXIT_CONFIG_OR_SOURCE_ERROR,
119
+ ...args.hint !== void 0 ? { hint: args.hint } : {},
120
+ ...args.cause !== void 0 ? { cause: args.cause } : {}
121
+ });
122
+ }
123
+ };
124
+ var COST_UNAVAILABLE_CODE = "COST_UNAVAILABLE";
125
+ function costUnavailableWarning(err) {
126
+ return {
127
+ code: COST_UNAVAILABLE_CODE,
128
+ message: `cost estimation failed (${err instanceof Error ? err.message : String(err)}) \u2014 continuing without cost data`
129
+ };
130
+ }
131
+ function diagnosticsExitCode(diagnostics, opts = {}) {
132
+ const strict = opts.strict === true;
133
+ for (const d of diagnostics) {
134
+ if (d.severity === "error") return EXIT_CHECK_FAILED;
135
+ if (strict && d.severity === "warn") return EXIT_CHECK_FAILED;
136
+ }
137
+ return 0;
138
+ }
139
+ function redactSecrets(text) {
140
+ if (typeof text !== "string") return text;
141
+ let out = text;
142
+ out = out.replace(/sk-ant-[A-Za-z0-9_-]{10,}/g, "<redacted>");
143
+ out = out.replace(/sk-(?:proj-)?[A-Za-z0-9_-]{10,}/g, "<redacted>");
144
+ out = out.replace(/Bearer\s+[A-Za-z0-9._~+/=-]{8,}/gi, "Bearer <redacted>");
145
+ out = out.replace(/(x-api-key[\s:=]+)["']?[A-Za-z0-9._~+/=-]{8,}["']?/gi, "$1<redacted>");
146
+ out = out.replace(/(Authorization[\s:=]+)["']?[^"'\s,;]{8,}["']?/gi, "$1<redacted>");
147
+ out = out.replace(/\b[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\b/g, "<redacted-token>");
148
+ out = out.replace(
149
+ /\b(ANTHROPIC_API_KEY|OPENAI_API_KEY|CACHELINT_LICENSE_KEY)\b\s*[=:]\s*["']?[^"'\s,;]+["']?/g,
150
+ "$1=<redacted>"
151
+ );
152
+ return out;
153
+ }
154
+ function renderError(err, opts = {}) {
155
+ const verbose = opts.verbose === true;
156
+ if (err instanceof CachelintError) {
157
+ const lines = [];
158
+ lines.push(`cachelint: ${err.message} [${err.code}]`);
159
+ if (err.hint) lines.push(` hint: ${err.hint}`);
160
+ if (verbose && err.stack) lines.push(err.stack);
161
+ return redactSecrets(lines.join("\n"));
162
+ }
163
+ if (err instanceof Error) {
164
+ if (verbose && err.stack) return redactSecrets(`cachelint: unexpected error
165
+ ${err.stack}`);
166
+ return redactSecrets(`cachelint: unexpected error: ${err.message}`);
167
+ }
168
+ return redactSecrets(`cachelint: unexpected error: ${String(err)}`);
169
+ }
170
+ function exitCodeFor(err) {
171
+ if (err instanceof CachelintError) return err.exitCode;
172
+ return EXIT_CONFIG_OR_SOURCE_ERROR;
173
+ }
174
+
175
+ export {
176
+ EXIT_OK,
177
+ EXIT_CHECK_FAILED,
178
+ EXIT_PRO_REQUIRED,
179
+ EXIT_LOCK_PROBLEM,
180
+ EXIT_CONFIG_OR_SOURCE_ERROR,
181
+ CachelintError,
182
+ ConfigError,
183
+ SourceError,
184
+ RuleError,
185
+ LockError,
186
+ LockMissingError,
187
+ LockCorruptError,
188
+ LicenseError,
189
+ ExactCountUnavailableError,
190
+ AdapterError,
191
+ COST_UNAVAILABLE_CODE,
192
+ costUnavailableWarning,
193
+ diagnosticsExitCode,
194
+ redactSecrets,
195
+ renderError,
196
+ exitCodeFor
197
+ };
@@ -0,0 +1,286 @@
1
+ import {
2
+ DEFAULT_EXACT_LIMIT,
3
+ DEFAULT_EXACT_MODEL,
4
+ countTokensExact
5
+ } from "./chunk-UUVSBJ62.js";
6
+ import {
7
+ heuristicTokens,
8
+ lint,
9
+ usdPer1kCalls
10
+ } from "./chunk-2JSY5BQA.js";
11
+ import {
12
+ getModelPricing,
13
+ referencePricing
14
+ } from "./chunk-O7ZE6CFI.js";
15
+ import {
16
+ requirePro
17
+ } from "./chunk-GXASKZ4Z.js";
18
+ import {
19
+ diffLockfiles,
20
+ isRegression,
21
+ renderDiffHuman
22
+ } from "./chunk-DYQPXD3G.js";
23
+ import {
24
+ COST_LINE_OVERLAPPED,
25
+ RECEIPT_EXACT_FALLBACK_LINE,
26
+ buildReceiptLines,
27
+ costLabel,
28
+ costLineAtRisk,
29
+ costLineInvalidates,
30
+ costLineWasted,
31
+ costSummary,
32
+ formatUsdPer1k
33
+ } from "./chunk-VWE5TDL5.js";
34
+ import {
35
+ DEFAULT_LOCKFILE_NAME,
36
+ computeBundles,
37
+ makeLockfile,
38
+ readLockfileIfExists
39
+ } from "./chunk-BUZCVKMZ.js";
40
+ import {
41
+ resolveConfigArg,
42
+ resolveSources,
43
+ toPosixRelative
44
+ } from "./chunk-MW7BXKPR.js";
45
+ import {
46
+ CACHELINT_VERSION
47
+ } from "./chunk-WPJYUQMU.js";
48
+ import {
49
+ COST_UNAVAILABLE_CODE,
50
+ LockMissingError,
51
+ costUnavailableWarning,
52
+ redactSecrets
53
+ } from "./chunk-XKRPGMZT.js";
54
+
55
+ // src/commands/check.ts
56
+ import { resolve as pathResolve } from "path";
57
+ async function check(args = {}) {
58
+ const root = pathResolve(args.projectRoot ?? process.cwd());
59
+ const { config, configPath } = resolveConfigArg(args.config, root);
60
+ const { bundles, warnings } = computeBundles({
61
+ ...args.paths !== void 0 ? { paths: args.paths } : {},
62
+ config,
63
+ projectRoot: root
64
+ });
65
+ const lockPath = pathResolve(root, args.lockfile ?? DEFAULT_LOCKFILE_NAME);
66
+ const committed = readLockfileIfExists(lockPath);
67
+ if (committed === null) throw new LockMissingError(args.lockfile ?? DEFAULT_LOCKFILE_NAME);
68
+ const current = makeLockfile(bundles);
69
+ const diff = diffLockfiles(committed, current);
70
+ const regression = isRegression(diff);
71
+ const allFiles = [...new Set(bundles.flatMap((b) => b.files.map((f) => f.path)))].sort();
72
+ const lintResult = lint({
73
+ paths: allFiles,
74
+ config,
75
+ projectRoot: root,
76
+ ...args.strict !== void 0 ? { strict: args.strict } : {},
77
+ ...args.pack !== void 0 ? { pack: args.pack } : {},
78
+ // R7: cost is forwarded but never recomputed — check reuses the embedded
79
+ // lint call's resolution/cost data (no second resolution pass).
80
+ ...args.cost !== void 0 ? { cost: args.cost } : {}
81
+ });
82
+ const diagnostics = lintResult.diagnostics;
83
+ let exactCounts = null;
84
+ if (args.exact === true) {
85
+ requirePro("exact", "`--exact` token counting");
86
+ const model = args.exactModel ?? DEFAULT_EXACT_MODEL;
87
+ const resolved = resolveSources({
88
+ paths: allFiles,
89
+ projectRoot: root,
90
+ required: false,
91
+ ...config.exclude !== void 0 ? { exclude: config.exclude } : {},
92
+ ...config.max_file_bytes !== void 0 ? { maxFileBytes: config.max_file_bytes } : {},
93
+ ...config.max_total_bytes !== void 0 ? { maxTotalBytes: config.max_total_bytes } : {}
94
+ });
95
+ const textBySha = new Map(resolved.sources.map((s) => [s.sha256, s.normalized]));
96
+ const seen = /* @__PURE__ */ new Set();
97
+ const requests = [];
98
+ for (const b of current.bundles) {
99
+ for (const f of b.files) {
100
+ if (seen.has(f.sha256)) continue;
101
+ seen.add(f.sha256);
102
+ const text = textBySha.get(f.sha256);
103
+ if (text === void 0) {
104
+ warnings.push({ code: "EXACT_SHA_MISMATCH", message: `--exact: skipping (content changed during the run)`, path: f.path });
105
+ continue;
106
+ }
107
+ requests.push({ sha256: f.sha256, model, text });
108
+ }
109
+ }
110
+ exactCounts = await countTokensExact(requests, args.exactLimit ?? DEFAULT_EXACT_LIMIT);
111
+ }
112
+ const exitCode = regression || lintResult.exitCode === 1 ? 1 : 0;
113
+ let cost;
114
+ if (args.cost !== false) {
115
+ if (lintResult.cost === void 0) {
116
+ for (const w of lintResult.warnings) {
117
+ if (w.code === COST_UNAVAILABLE_CODE) warnings.push(w);
118
+ }
119
+ } else {
120
+ try {
121
+ cost = {
122
+ ...lintResult.cost,
123
+ receipt: computeReceipt(bundles, lintResult.cost.normalizedCharsByFile, exactCounts, args.exactModel)
124
+ };
125
+ } catch (err) {
126
+ warnings.push(costUnavailableWarning(err));
127
+ }
128
+ }
129
+ }
130
+ return {
131
+ version: CACHELINT_VERSION,
132
+ configPath,
133
+ packLoaded: lintResult.packLoaded,
134
+ lockfilePath: lockPath,
135
+ diff,
136
+ regression,
137
+ diagnostics,
138
+ exactCounts,
139
+ warnings,
140
+ exitCode,
141
+ ...cost !== void 0 ? { cost } : {}
142
+ };
143
+ }
144
+ function computeReceipt(bundles, normalizedCharsByFile, exactCounts, exactModelArg) {
145
+ const ref = referencePricing();
146
+ let engagementMinimumTokens = ref.minCacheablePrefixTokens;
147
+ let engagementMinimumSource = ref.model;
148
+ if (exactModelArg !== void 0) {
149
+ const named = getModelPricing(exactModelArg);
150
+ if (named !== null && named.minCacheablePrefixTokens > engagementMinimumTokens) {
151
+ engagementMinimumTokens = named.minCacheablePrefixTokens;
152
+ engagementMinimumSource = named.model;
153
+ }
154
+ }
155
+ const exactBySha = exactCounts === null ? null : new Map(exactCounts.map((e) => [e.sha256, e.tokens]));
156
+ const entries = [];
157
+ let exactRefined = false;
158
+ let anyFallback = false;
159
+ for (const b of bundles) {
160
+ let heuristicSum = 0;
161
+ let exactSum = 0;
162
+ let fullyExact = exactBySha !== null;
163
+ for (const f of b.files) {
164
+ const chars = normalizedCharsByFile[f.path];
165
+ if (chars === void 0) {
166
+ throw new Error(
167
+ `cost estimate unavailable: ${f.path} is in the bundle but was not part of source resolution (binary, oversize, or excluded)`
168
+ );
169
+ }
170
+ heuristicSum += heuristicTokens(chars);
171
+ const exact = exactBySha?.get(f.sha256);
172
+ if (exact === void 0) fullyExact = false;
173
+ else exactSum += exact;
174
+ }
175
+ const useExact = exactBySha !== null && fullyExact;
176
+ if (exactBySha !== null) {
177
+ if (useExact) exactRefined = true;
178
+ else anyFallback = true;
179
+ }
180
+ const protectedTokens = useExact ? exactSum : heuristicSum;
181
+ const engaged = protectedTokens >= engagementMinimumTokens;
182
+ entries.push({
183
+ name: b.name,
184
+ protectedTokens,
185
+ tokenSource: useExact ? "exact" : "heuristic",
186
+ engaged,
187
+ ...engaged ? { protectedUsdPer1kCalls: usdPer1kCalls(protectedTokens, ref) } : {}
188
+ });
189
+ }
190
+ let engagedTokens = 0;
191
+ for (const e of entries) {
192
+ if (e.engaged) engagedTokens += e.protectedTokens;
193
+ }
194
+ return {
195
+ bundles: entries,
196
+ protectedTokens: engagedTokens,
197
+ protectedUsdPer1kCalls: usdPer1kCalls(engagedTokens, ref),
198
+ engagementMinimumTokens,
199
+ engagementMinimumSource,
200
+ exactRefined,
201
+ ...anyFallback ? { exactFallbackReason: RECEIPT_EXACT_FALLBACK_LINE } : {}
202
+ };
203
+ }
204
+ function renderCheckHuman(r) {
205
+ const lines = [];
206
+ if (r.packLoaded !== null) lines.push(`note pack "${r.packLoaded}" loaded (no additional rules in this build yet)`);
207
+ for (const w of r.warnings) lines.push(`note ${w.path ? w.path + ": " : ""}${w.message}`);
208
+ if (r.regression) {
209
+ lines.push("cachelint: cachelint.lock is out of date \u2014");
210
+ lines.push(renderDiffHuman(r.diff));
211
+ }
212
+ const label = r.cost !== void 0 ? costLabel({ model: r.cost.pricing.referenceModel }, r.cost.pricing.asOf) : "";
213
+ let errors = 0;
214
+ let warns = 0;
215
+ for (let i = 0; i < r.diagnostics.length; i++) {
216
+ const d = r.diagnostics[i];
217
+ if (d.severity === "error") errors++;
218
+ else warns++;
219
+ const level = d.severity === "error" ? "error " : "warning";
220
+ lines.push(`${d.file}:${d.line}:${d.column} ${level} ${d.rule} ${d.message}`);
221
+ if (d.whyUrl) lines.push(` why: ${d.whyUrl}`);
222
+ const dc = r.cost?.perDiagnostic[i] ?? null;
223
+ if (dc !== null) {
224
+ if (!dc.countsTowardTotal) {
225
+ lines.push(` ${COST_LINE_OVERLAPPED}`);
226
+ } else if (dc.volatility === "per-call") {
227
+ lines.push(` ${costLineInvalidates(dc.fromHereTokens)}`);
228
+ const usd = dc.wastedUsdPer1kCalls !== void 0 ? formatUsdPer1k(dc.wastedUsdPer1kCalls) : null;
229
+ if (usd !== null) lines.push(` ${costLineWasted(usd, label)}`);
230
+ } else {
231
+ lines.push(` ${costLineAtRisk(dc.fromHereTokens)}`);
232
+ }
233
+ }
234
+ }
235
+ if (r.exactCounts) {
236
+ for (const e of r.exactCounts) lines.push(`tokens ${e.sha256.slice(0, 12)} ${e.tokens} (${e.model})`);
237
+ }
238
+ if (r.exitCode === 0) {
239
+ lines.push(errors + warns > 0 ? `cachelint: lock OK; ${warns} warning${warns === 1 ? "" : "s"} (non-blocking)` : "cachelint: lock OK, no problems found.");
240
+ if (r.cost !== void 0) pushReceiptLines(lines, r.cost, label);
241
+ } else {
242
+ if (r.cost !== void 0) {
243
+ const summary = costSummary(r.cost.totals, label);
244
+ if (summary !== null) lines.push(`cachelint: ${summary}`);
245
+ }
246
+ const bits = [];
247
+ if (r.regression) bits.push("stable-prefix hash moved");
248
+ if (errors > 0) bits.push(`${errors} error-severity finding${errors === 1 ? "" : "s"}`);
249
+ if (!r.regression && errors === 0 && warns > 0) bits.push(`${warns} warning${warns === 1 ? "" : "s"} (elevated by --strict)`);
250
+ lines.push(`cachelint: FAILED \u2014 ${bits.length > 0 ? bits.join("; ") : "see above"}`);
251
+ }
252
+ return redactSecrets(lines.join("\n"));
253
+ }
254
+ function pushReceiptLines(lines, cost, label) {
255
+ for (const l of buildReceiptLines(cost, label)) lines.push(`cachelint: ${l}`);
256
+ }
257
+ function renderCheckJson(r, projectRoot = process.cwd()) {
258
+ return redactSecrets(
259
+ JSON.stringify(
260
+ {
261
+ version: r.version,
262
+ configPath: r.configPath,
263
+ packLoaded: r.packLoaded,
264
+ lockfilePath: toPosixRelative(r.lockfilePath, pathResolve(projectRoot)),
265
+ exitCode: r.exitCode,
266
+ regression: r.regression,
267
+ diff: r.diff,
268
+ diagnostics: r.diagnostics,
269
+ ...r.exactCounts ? { exactCounts: r.exactCounts } : {},
270
+ warnings: r.warnings,
271
+ // R4: present only when cost was computed — `--no-cost` (and
272
+ // degradation) omit the block by construction. The shared shape
273
+ // mirrors lint's cost block exactly; `receipt` is check-specific.
274
+ ...r.cost !== void 0 ? { cost: r.cost } : {}
275
+ },
276
+ null,
277
+ 2
278
+ )
279
+ ) + "\n";
280
+ }
281
+
282
+ export {
283
+ check,
284
+ renderCheckHuman,
285
+ renderCheckJson
286
+ };
package/dist/cli.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * v1.0 stability contract for cachelint's process exit codes.
4
+ *
5
+ * Imported by both the CLI (src/cli.ts) and the error taxonomy (src/errors.ts),
6
+ * which is why it lives in its own module — importing cli.ts has side effects
7
+ * (it calls `program.parseAsync`), and we want errors.ts to be safe to import
8
+ * anywhere.
9
+ *
10
+ * The table:
11
+ * 0 ok
12
+ * 1 hash moved / error-severity rule fired (real regression)
13
+ * 2 Pro feature invoked without a license (entitlement)
14
+ * 3 cachelint.lock missing or corrupt
15
+ * 4 config / source error (incl. missing ANTHROPIC_API_KEY when --exact IS licensed)
16
+ */
17
+ declare const EXIT_OK = 0;
18
+ declare const EXIT_CHECK_FAILED = 1;
19
+ declare const EXIT_PRO_REQUIRED = 2;
20
+ declare const EXIT_LOCK_PROBLEM = 3;
21
+ declare const EXIT_CONFIG_OR_SOURCE_ERROR = 4;
22
+
23
+ /**
24
+ * cachelint — CLI entry point.
25
+ *
26
+ * Unit 1 ships stub subcommands so the bin is real and `--help` / `--version`
27
+ * are instant; Units 2–4 fill in the actual behavior.
28
+ *
29
+ * Design notes:
30
+ * - Heavy modules (rules engine, lockfile, license verify) are lazy-imported
31
+ * INSIDE each action so cold-start for `--help` / `--version` stays fast.
32
+ * - Exit codes are pinned (see Requirements R4 in the plan):
33
+ * 0 ok or only warns without --strict
34
+ * 1 hash moved / error-severity rule fired (real regression)
35
+ * 2 Pro feature invoked without a license (entitlement)
36
+ * 3 cachelint.lock missing or corrupt
37
+ * 4 config or source error (incl. missing ANTHROPIC_API_KEY when --exact IS licensed)
38
+ */
39
+
40
+ /**
41
+ * Run the CLI. Extracted so the module can be imported in tests without
42
+ * arg parsing as a side effect.
43
+ */
44
+ declare function runCli(argv?: readonly string[]): Promise<void>;
45
+
46
+ export { EXIT_CHECK_FAILED, EXIT_CONFIG_OR_SOURCE_ERROR, EXIT_LOCK_PROBLEM, EXIT_OK, EXIT_PRO_REQUIRED, runCli };