@clef-sh/cli 0.1.12 → 0.1.13-beta.92
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/dist/index.cjs +613 -145
- package/dist/index.cjs.map +4 -4
- package/dist/index.mjs +613 -145
- package/dist/index.mjs.map +4 -4
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -21959,7 +21959,12 @@ var VALID_KMS_PROVIDERS;
|
|
|
21959
21959
|
var init_types2 = __esm({
|
|
21960
21960
|
"../core/src/kms/types.ts"() {
|
|
21961
21961
|
"use strict";
|
|
21962
|
-
VALID_KMS_PROVIDERS = [
|
|
21962
|
+
VALID_KMS_PROVIDERS = [
|
|
21963
|
+
"aws",
|
|
21964
|
+
"gcp",
|
|
21965
|
+
"azure",
|
|
21966
|
+
"cloud"
|
|
21967
|
+
];
|
|
21963
21968
|
}
|
|
21964
21969
|
});
|
|
21965
21970
|
|
|
@@ -94826,10 +94831,177 @@ var require_azure = __commonJS({
|
|
|
94826
94831
|
}
|
|
94827
94832
|
});
|
|
94828
94833
|
|
|
94829
|
-
// ../
|
|
94834
|
+
// ../client/dist/kms.js
|
|
94830
94835
|
var require_kms = __commonJS({
|
|
94836
|
+
"../client/dist/kms.js"(exports2, module2) {
|
|
94837
|
+
"use strict";
|
|
94838
|
+
var __defProp2 = Object.defineProperty;
|
|
94839
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
94840
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
94841
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
94842
|
+
var __export2 = (target, all) => {
|
|
94843
|
+
for (var name in all)
|
|
94844
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
94845
|
+
};
|
|
94846
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
94847
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
94848
|
+
for (let key of __getOwnPropNames2(from))
|
|
94849
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
94850
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
94851
|
+
}
|
|
94852
|
+
return to;
|
|
94853
|
+
};
|
|
94854
|
+
var __toCommonJS = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
94855
|
+
var kms_exports = {};
|
|
94856
|
+
__export2(kms_exports, {
|
|
94857
|
+
ClefClientError: () => ClefClientError,
|
|
94858
|
+
CloudKmsProvider: () => CloudKmsProvider
|
|
94859
|
+
});
|
|
94860
|
+
module2.exports = __toCommonJS(kms_exports);
|
|
94861
|
+
var ClefClientError = class extends Error {
|
|
94862
|
+
constructor(message, statusCode, fix) {
|
|
94863
|
+
super(message);
|
|
94864
|
+
this.statusCode = statusCode;
|
|
94865
|
+
this.fix = fix;
|
|
94866
|
+
this.name = "ClefClientError";
|
|
94867
|
+
}
|
|
94868
|
+
statusCode;
|
|
94869
|
+
fix;
|
|
94870
|
+
};
|
|
94871
|
+
function resolveToken(explicit) {
|
|
94872
|
+
if (explicit) return explicit;
|
|
94873
|
+
if (typeof process !== "undefined" && process.env?.CLEF_SERVICE_TOKEN) {
|
|
94874
|
+
return process.env.CLEF_SERVICE_TOKEN;
|
|
94875
|
+
}
|
|
94876
|
+
throw new ClefClientError(
|
|
94877
|
+
"No service token configured",
|
|
94878
|
+
void 0,
|
|
94879
|
+
"Set CLEF_SERVICE_TOKEN or pass token in options."
|
|
94880
|
+
);
|
|
94881
|
+
}
|
|
94882
|
+
async function request(baseUrl, opts) {
|
|
94883
|
+
const url = `${baseUrl}${opts.path}`;
|
|
94884
|
+
const headers = {
|
|
94885
|
+
Authorization: `Bearer ${opts.token}`,
|
|
94886
|
+
Accept: "application/json"
|
|
94887
|
+
};
|
|
94888
|
+
if (opts.body !== void 0) {
|
|
94889
|
+
headers["Content-Type"] = "application/json";
|
|
94890
|
+
}
|
|
94891
|
+
const init = {
|
|
94892
|
+
method: opts.method,
|
|
94893
|
+
headers,
|
|
94894
|
+
body: opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
|
|
94895
|
+
};
|
|
94896
|
+
let response;
|
|
94897
|
+
try {
|
|
94898
|
+
response = await opts.fetchFn(url, init);
|
|
94899
|
+
} catch (err) {
|
|
94900
|
+
try {
|
|
94901
|
+
response = await opts.fetchFn(url, init);
|
|
94902
|
+
} catch {
|
|
94903
|
+
throw new ClefClientError(
|
|
94904
|
+
`Connection failed: ${err.message}`,
|
|
94905
|
+
void 0,
|
|
94906
|
+
"Is the endpoint reachable? Check your CLEF_ENDPOINT setting."
|
|
94907
|
+
);
|
|
94908
|
+
}
|
|
94909
|
+
}
|
|
94910
|
+
if (response.status >= 500) {
|
|
94911
|
+
response = await opts.fetchFn(url, init);
|
|
94912
|
+
}
|
|
94913
|
+
if (response.status === 401) {
|
|
94914
|
+
throw new ClefClientError("Authentication failed", 401, "Check your CLEF_SERVICE_TOKEN.");
|
|
94915
|
+
}
|
|
94916
|
+
if (response.status === 503) {
|
|
94917
|
+
throw new ClefClientError("Secrets expired or not loaded", 503, "Check the agent logs.");
|
|
94918
|
+
}
|
|
94919
|
+
if (!response.ok) {
|
|
94920
|
+
const text = await response.text().catch(() => "");
|
|
94921
|
+
throw new ClefClientError(
|
|
94922
|
+
`HTTP ${response.status}: ${text || response.statusText}`,
|
|
94923
|
+
response.status
|
|
94924
|
+
);
|
|
94925
|
+
}
|
|
94926
|
+
const json = await response.json();
|
|
94927
|
+
if (json && typeof json === "object" && "success" in json) {
|
|
94928
|
+
if (!json.success) {
|
|
94929
|
+
throw new ClefClientError(json.message || "Request failed", response.status);
|
|
94930
|
+
}
|
|
94931
|
+
return json.data;
|
|
94932
|
+
}
|
|
94933
|
+
return json;
|
|
94934
|
+
}
|
|
94935
|
+
var CloudKmsProvider = class {
|
|
94936
|
+
endpoint;
|
|
94937
|
+
token;
|
|
94938
|
+
constructor(options) {
|
|
94939
|
+
this.endpoint = options.endpoint;
|
|
94940
|
+
this.token = resolveToken(options.token);
|
|
94941
|
+
}
|
|
94942
|
+
async wrap(_keyId, _plaintext) {
|
|
94943
|
+
throw new ClefClientError(
|
|
94944
|
+
"CloudKmsProvider.wrap() is not supported. Use the keyservice sidecar for encryption."
|
|
94945
|
+
);
|
|
94946
|
+
}
|
|
94947
|
+
async unwrap(keyId, wrappedKey, _algorithm) {
|
|
94948
|
+
const result = await request(this.endpoint, {
|
|
94949
|
+
method: "POST",
|
|
94950
|
+
path: "/api/v1/cloud/kms/decrypt",
|
|
94951
|
+
body: {
|
|
94952
|
+
keyArn: keyId,
|
|
94953
|
+
ciphertext: wrappedKey.toString("base64")
|
|
94954
|
+
},
|
|
94955
|
+
token: this.token,
|
|
94956
|
+
fetchFn: globalThis.fetch
|
|
94957
|
+
});
|
|
94958
|
+
return Buffer.from(result.plaintext, "base64");
|
|
94959
|
+
}
|
|
94960
|
+
};
|
|
94961
|
+
}
|
|
94962
|
+
});
|
|
94963
|
+
|
|
94964
|
+
// ../runtime/dist/kms/index.js
|
|
94965
|
+
var require_kms2 = __commonJS({
|
|
94831
94966
|
"../runtime/dist/kms/index.js"(exports2) {
|
|
94832
94967
|
"use strict";
|
|
94968
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
94969
|
+
if (k2 === void 0) k2 = k;
|
|
94970
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
94971
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
94972
|
+
desc = { enumerable: true, get: function() {
|
|
94973
|
+
return m[k];
|
|
94974
|
+
} };
|
|
94975
|
+
}
|
|
94976
|
+
Object.defineProperty(o, k2, desc);
|
|
94977
|
+
}) : (function(o, m, k, k2) {
|
|
94978
|
+
if (k2 === void 0) k2 = k;
|
|
94979
|
+
o[k2] = m[k];
|
|
94980
|
+
}));
|
|
94981
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
94982
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
94983
|
+
}) : function(o, v) {
|
|
94984
|
+
o["default"] = v;
|
|
94985
|
+
});
|
|
94986
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
94987
|
+
var ownKeys = function(o) {
|
|
94988
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
94989
|
+
var ar = [];
|
|
94990
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
94991
|
+
return ar;
|
|
94992
|
+
};
|
|
94993
|
+
return ownKeys(o);
|
|
94994
|
+
};
|
|
94995
|
+
return function(mod) {
|
|
94996
|
+
if (mod && mod.__esModule) return mod;
|
|
94997
|
+
var result = {};
|
|
94998
|
+
if (mod != null) {
|
|
94999
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
95000
|
+
}
|
|
95001
|
+
__setModuleDefault(result, mod);
|
|
95002
|
+
return result;
|
|
95003
|
+
};
|
|
95004
|
+
})();
|
|
94833
95005
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
94834
95006
|
exports2.AzureKmsProvider = exports2.GcpKmsProvider = exports2.AwsKmsProvider = void 0;
|
|
94835
95007
|
exports2.createKmsProvider = createKmsProvider;
|
|
@@ -94848,7 +95020,7 @@ var require_kms = __commonJS({
|
|
|
94848
95020
|
Object.defineProperty(exports2, "AzureKmsProvider", { enumerable: true, get: function() {
|
|
94849
95021
|
return azure_2.AzureKmsProvider;
|
|
94850
95022
|
} });
|
|
94851
|
-
function createKmsProvider(provider, options) {
|
|
95023
|
+
async function createKmsProvider(provider, options) {
|
|
94852
95024
|
switch (provider) {
|
|
94853
95025
|
case "aws":
|
|
94854
95026
|
return new aws_1.AwsKmsProvider(options?.region);
|
|
@@ -94856,6 +95028,17 @@ var require_kms = __commonJS({
|
|
|
94856
95028
|
return new gcp_1.GcpKmsProvider();
|
|
94857
95029
|
case "azure":
|
|
94858
95030
|
return new azure_1.AzureKmsProvider();
|
|
95031
|
+
case "cloud": {
|
|
95032
|
+
try {
|
|
95033
|
+
const { CloudKmsProvider } = await Promise.resolve().then(() => __importStar(require_kms()));
|
|
95034
|
+
return new CloudKmsProvider({
|
|
95035
|
+
endpoint: options?.endpoint ?? "",
|
|
95036
|
+
token: options?.token
|
|
95037
|
+
});
|
|
95038
|
+
} catch {
|
|
95039
|
+
throw new Error("Clef Cloud KMS requires @clef-sh/client. Install it with: npm install @clef-sh/client");
|
|
95040
|
+
}
|
|
95041
|
+
}
|
|
94859
95042
|
default:
|
|
94860
95043
|
throw new Error(`Unknown KMS provider: ${provider}`);
|
|
94861
95044
|
}
|
|
@@ -94908,7 +95091,7 @@ var require_artifact_decryptor = __commonJS({
|
|
|
94908
95091
|
exports2.ArtifactDecryptor = void 0;
|
|
94909
95092
|
var crypto6 = __importStar(require("crypto"));
|
|
94910
95093
|
var decrypt_1 = require_decrypt();
|
|
94911
|
-
var kms_1 =
|
|
95094
|
+
var kms_1 = require_kms2();
|
|
94912
95095
|
var ArtifactDecryptor = class {
|
|
94913
95096
|
ageDecryptor = new decrypt_1.AgeDecryptor();
|
|
94914
95097
|
privateKey;
|
|
@@ -94957,7 +95140,7 @@ var require_artifact_decryptor = __commonJS({
|
|
|
94957
95140
|
const envelope = artifact.envelope;
|
|
94958
95141
|
let dek;
|
|
94959
95142
|
try {
|
|
94960
|
-
const kms = (0, kms_1.createKmsProvider)(envelope.provider);
|
|
95143
|
+
const kms = await (0, kms_1.createKmsProvider)(envelope.provider);
|
|
94961
95144
|
const wrappedKey = Buffer.from(envelope.wrappedKey, "base64");
|
|
94962
95145
|
dek = await kms.unwrap(envelope.keyId, wrappedKey, envelope.algorithm);
|
|
94963
95146
|
} catch (err) {
|
|
@@ -96225,11 +96408,11 @@ var require_dist3 = __commonJS({
|
|
|
96225
96408
|
Object.defineProperty(exports2, "createVcsProvider", { enumerable: true, get: function() {
|
|
96226
96409
|
return index_1.createVcsProvider;
|
|
96227
96410
|
} });
|
|
96228
|
-
var kms_1 =
|
|
96411
|
+
var kms_1 = require_kms2();
|
|
96229
96412
|
Object.defineProperty(exports2, "AwsKmsProvider", { enumerable: true, get: function() {
|
|
96230
96413
|
return kms_1.AwsKmsProvider;
|
|
96231
96414
|
} });
|
|
96232
|
-
var kms_2 =
|
|
96415
|
+
var kms_2 = require_kms2();
|
|
96233
96416
|
Object.defineProperty(exports2, "createKmsProvider", { enumerable: true, get: function() {
|
|
96234
96417
|
return kms_2.createKmsProvider;
|
|
96235
96418
|
} });
|
|
@@ -97317,11 +97500,23 @@ function sym(key) {
|
|
|
97317
97500
|
}
|
|
97318
97501
|
|
|
97319
97502
|
// src/output/formatter.ts
|
|
97503
|
+
var _jsonMode = false;
|
|
97504
|
+
var _yesMode = false;
|
|
97505
|
+
function setJsonMode(json) {
|
|
97506
|
+
_jsonMode = json;
|
|
97507
|
+
}
|
|
97508
|
+
function isJsonMode() {
|
|
97509
|
+
return _jsonMode;
|
|
97510
|
+
}
|
|
97511
|
+
function setYesMode(yes) {
|
|
97512
|
+
_yesMode = yes;
|
|
97513
|
+
}
|
|
97320
97514
|
function color(fn, str2) {
|
|
97321
97515
|
return isPlainMode() ? str2 : fn(str2);
|
|
97322
97516
|
}
|
|
97323
97517
|
var formatter = {
|
|
97324
97518
|
success(message) {
|
|
97519
|
+
if (_jsonMode) return;
|
|
97325
97520
|
const icon = sym("success");
|
|
97326
97521
|
process.stdout.write(color(import_picocolors.default.green, `${icon} ${message}`) + "\n");
|
|
97327
97522
|
},
|
|
@@ -97338,15 +97533,18 @@ var formatter = {
|
|
|
97338
97533
|
process.stderr.write(color(import_picocolors.default.yellow, `${icon} ${message}`) + "\n");
|
|
97339
97534
|
},
|
|
97340
97535
|
info(message) {
|
|
97536
|
+
if (_jsonMode) return;
|
|
97341
97537
|
const icon = sym("info");
|
|
97342
97538
|
process.stdout.write(color(import_picocolors.default.blue, `${icon} ${message}`) + "\n");
|
|
97343
97539
|
},
|
|
97344
97540
|
hint(message) {
|
|
97541
|
+
if (_jsonMode) return;
|
|
97345
97542
|
const icon = sym("arrow");
|
|
97346
97543
|
process.stdout.write(`${icon} ${message}
|
|
97347
97544
|
`);
|
|
97348
97545
|
},
|
|
97349
97546
|
keyValue(key, value) {
|
|
97547
|
+
if (_jsonMode) return;
|
|
97350
97548
|
const icon = sym("key");
|
|
97351
97549
|
const arrow = sym("arrow");
|
|
97352
97550
|
const prefix = icon ? `${icon} ` : "";
|
|
@@ -97354,30 +97552,38 @@ var formatter = {
|
|
|
97354
97552
|
`);
|
|
97355
97553
|
},
|
|
97356
97554
|
pendingItem(key, days) {
|
|
97555
|
+
if (_jsonMode) return;
|
|
97357
97556
|
const icon = sym("pending");
|
|
97358
97557
|
const prefix = icon ? `${icon} ` : "[pending] ";
|
|
97359
97558
|
process.stdout.write(`${prefix}${key} \u2014 ${days} day${days !== 1 ? "s" : ""} pending
|
|
97360
97559
|
`);
|
|
97361
97560
|
},
|
|
97362
97561
|
recipientItem(label, keyPreview2) {
|
|
97562
|
+
if (_jsonMode) return;
|
|
97363
97563
|
const icon = sym("recipient");
|
|
97364
97564
|
const prefix = icon ? `${icon} ` : "";
|
|
97365
97565
|
process.stdout.write(`${prefix}${label.padEnd(15)}${keyPreview2}
|
|
97366
97566
|
`);
|
|
97367
97567
|
},
|
|
97368
97568
|
section(label) {
|
|
97569
|
+
if (_jsonMode) return;
|
|
97369
97570
|
process.stdout.write(`
|
|
97370
97571
|
${label}
|
|
97371
97572
|
|
|
97372
97573
|
`);
|
|
97373
97574
|
},
|
|
97374
97575
|
print(message) {
|
|
97576
|
+
if (_jsonMode) return;
|
|
97375
97577
|
process.stdout.write(message + "\n");
|
|
97376
97578
|
},
|
|
97377
97579
|
raw(message) {
|
|
97378
97580
|
process.stdout.write(message);
|
|
97379
97581
|
},
|
|
97582
|
+
json(data) {
|
|
97583
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
97584
|
+
},
|
|
97380
97585
|
table(rows, columns) {
|
|
97586
|
+
if (_jsonMode) return;
|
|
97381
97587
|
const widths = columns.map((col, i) => {
|
|
97382
97588
|
const maxDataWidth = rows.reduce(
|
|
97383
97589
|
(max, row) => Math.max(max, stripAnsi(row[i] ?? "").length),
|
|
@@ -97394,6 +97600,10 @@ ${label}
|
|
|
97394
97600
|
}
|
|
97395
97601
|
},
|
|
97396
97602
|
async confirm(prompt) {
|
|
97603
|
+
if (_yesMode) return true;
|
|
97604
|
+
if (_jsonMode) {
|
|
97605
|
+
throw new Error("--json requires --yes for destructive operations");
|
|
97606
|
+
}
|
|
97397
97607
|
const rl = readline.createInterface({
|
|
97398
97608
|
input: process.stdin,
|
|
97399
97609
|
output: process.stderr
|
|
@@ -97428,6 +97638,9 @@ ${label}
|
|
|
97428
97638
|
`);
|
|
97429
97639
|
},
|
|
97430
97640
|
async secretPrompt(prompt) {
|
|
97641
|
+
if (_jsonMode) {
|
|
97642
|
+
throw new Error("--json mode requires value on the command line (no interactive prompt)");
|
|
97643
|
+
}
|
|
97431
97644
|
return new Promise((resolve7, reject) => {
|
|
97432
97645
|
process.stderr.write(color(import_picocolors.default.cyan, `${prompt}: `));
|
|
97433
97646
|
if (process.stdin.isTTY) {
|
|
@@ -97470,7 +97683,14 @@ function pad(str2, width) {
|
|
|
97470
97683
|
}
|
|
97471
97684
|
|
|
97472
97685
|
// src/handle-error.ts
|
|
97686
|
+
function exitJsonError(message) {
|
|
97687
|
+
formatter.json({ error: true, message });
|
|
97688
|
+
process.exit(1);
|
|
97689
|
+
}
|
|
97473
97690
|
function handleCommandError(err) {
|
|
97691
|
+
if (isJsonMode()) {
|
|
97692
|
+
exitJsonError(err.message);
|
|
97693
|
+
}
|
|
97474
97694
|
if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
|
|
97475
97695
|
formatter.formatDependencyError(err);
|
|
97476
97696
|
} else {
|
|
@@ -97970,7 +98190,7 @@ async function handleSecondDevOnboarding(repoRoot, clefConfigPath, deps2, option
|
|
|
97970
98190
|
"OS keychain is not available on this system.\n The private key will be written to the filesystem instead.\n See https://docs.clef.sh/guide/key-storage for security implications."
|
|
97971
98191
|
);
|
|
97972
98192
|
let keyPath;
|
|
97973
|
-
if (options.nonInteractive || !process.stdin.isTTY) {
|
|
98193
|
+
if (options.nonInteractive || isJsonMode() || !process.stdin.isTTY) {
|
|
97974
98194
|
keyPath = process.env.CLEF_AGE_KEY_FILE || defaultAgeKeyPath(label);
|
|
97975
98195
|
keyPath = path23.resolve(keyPath);
|
|
97976
98196
|
} else {
|
|
@@ -98003,6 +98223,10 @@ async function handleSecondDevOnboarding(repoRoot, clefConfigPath, deps2, option
|
|
|
98003
98223
|
formatter.success("Created .clef/.gitignore");
|
|
98004
98224
|
}
|
|
98005
98225
|
formatter.success(`Key label: ${label}`);
|
|
98226
|
+
if (isJsonMode()) {
|
|
98227
|
+
formatter.json({ action: "onboarded", manifest: "clef.yaml", config: ".clef/config.yaml" });
|
|
98228
|
+
return;
|
|
98229
|
+
}
|
|
98006
98230
|
formatter.section("Next steps:");
|
|
98007
98231
|
formatter.hint("clef recipients request \u2014 request access to encrypted secrets");
|
|
98008
98232
|
formatter.hint("clef update \u2014 scaffold new environments");
|
|
@@ -98013,7 +98237,7 @@ async function handleFullSetup(repoRoot, manifestPath, clefConfigPath, deps2, op
|
|
|
98013
98237
|
let namespaces = options.namespaces ? options.namespaces.split(",").map((s) => s.trim()) : [];
|
|
98014
98238
|
const backend = options.backend ?? "age";
|
|
98015
98239
|
let secretsDir = options.secretsDir ?? "secrets";
|
|
98016
|
-
if (!options.nonInteractive && process.stdin.isTTY) {
|
|
98240
|
+
if (!options.nonInteractive && !isJsonMode() && process.stdin.isTTY) {
|
|
98017
98241
|
const envAnswer = await promptWithDefault(
|
|
98018
98242
|
"Environments (comma-separated)",
|
|
98019
98243
|
environments.join(",")
|
|
@@ -98068,7 +98292,7 @@ async function handleFullSetup(repoRoot, manifestPath, clefConfigPath, deps2, op
|
|
|
98068
98292
|
formatter.warn(
|
|
98069
98293
|
"OS keychain is not available on this system.\n The private key must be written to the filesystem instead.\n See https://docs.clef.sh/guide/key-storage for security implications."
|
|
98070
98294
|
);
|
|
98071
|
-
if (!options.nonInteractive && process.stdin.isTTY) {
|
|
98295
|
+
if (!options.nonInteractive && !isJsonMode() && process.stdin.isTTY) {
|
|
98072
98296
|
const confirmed = await formatter.confirm("Write the private key to the filesystem?");
|
|
98073
98297
|
if (!confirmed) {
|
|
98074
98298
|
formatter.error(
|
|
@@ -98079,7 +98303,7 @@ async function handleFullSetup(repoRoot, manifestPath, clefConfigPath, deps2, op
|
|
|
98079
98303
|
}
|
|
98080
98304
|
}
|
|
98081
98305
|
let keyPath;
|
|
98082
|
-
if (options.nonInteractive || !process.stdin.isTTY) {
|
|
98306
|
+
if (options.nonInteractive || isJsonMode() || !process.stdin.isTTY) {
|
|
98083
98307
|
keyPath = defaultAgeKeyPath(label);
|
|
98084
98308
|
if (await isInsideAnyGitRepo(path23.resolve(keyPath))) {
|
|
98085
98309
|
throw new Error(
|
|
@@ -98199,6 +98423,17 @@ async function handleFullSetup(repoRoot, manifestPath, clefConfigPath, deps2, op
|
|
|
98199
98423
|
formatter.print(" clef config set analytics false (permanent)\n");
|
|
98200
98424
|
} catch {
|
|
98201
98425
|
}
|
|
98426
|
+
if (isJsonMode()) {
|
|
98427
|
+
formatter.json({
|
|
98428
|
+
action: "initialized",
|
|
98429
|
+
manifest: "clef.yaml",
|
|
98430
|
+
environments: manifest.environments.map((e) => e.name),
|
|
98431
|
+
namespaces: manifest.namespaces.map((n) => n.name),
|
|
98432
|
+
backend,
|
|
98433
|
+
scaffolded: scaffoldedCount
|
|
98434
|
+
});
|
|
98435
|
+
return;
|
|
98436
|
+
}
|
|
98202
98437
|
formatter.section("Next steps:");
|
|
98203
98438
|
formatter.hint("clef set <namespace>/<env> <KEY> <value> \u2014 add a secret");
|
|
98204
98439
|
formatter.hint("clef scan \u2014 check for existing plaintext secrets");
|
|
@@ -98459,7 +98694,9 @@ function registerGetCommand(program3, deps2) {
|
|
|
98459
98694
|
return;
|
|
98460
98695
|
}
|
|
98461
98696
|
const val = decrypted.values[key];
|
|
98462
|
-
if (
|
|
98697
|
+
if (isJsonMode()) {
|
|
98698
|
+
formatter.json({ key, value: val, namespace, environment });
|
|
98699
|
+
} else if (opts.raw) {
|
|
98463
98700
|
formatter.raw(val);
|
|
98464
98701
|
} else {
|
|
98465
98702
|
const copied = copyToClipboard(val);
|
|
@@ -98564,6 +98801,16 @@ function registerSetCommand(program3, deps2) {
|
|
|
98564
98801
|
pendingErrors.push(env.name);
|
|
98565
98802
|
}
|
|
98566
98803
|
}
|
|
98804
|
+
if (isJsonMode()) {
|
|
98805
|
+
formatter.json({
|
|
98806
|
+
key,
|
|
98807
|
+
namespace: namespace2,
|
|
98808
|
+
environments: manifest.environments.map((e) => e.name),
|
|
98809
|
+
action: "created",
|
|
98810
|
+
pending: true
|
|
98811
|
+
});
|
|
98812
|
+
return;
|
|
98813
|
+
}
|
|
98567
98814
|
formatter.success(
|
|
98568
98815
|
`'${key}' set in ${namespace2} across all environments ${sym("locked")}`
|
|
98569
98816
|
);
|
|
@@ -98587,6 +98834,16 @@ function registerSetCommand(program3, deps2) {
|
|
|
98587
98834
|
} catch {
|
|
98588
98835
|
}
|
|
98589
98836
|
}
|
|
98837
|
+
if (isJsonMode()) {
|
|
98838
|
+
formatter.json({
|
|
98839
|
+
key,
|
|
98840
|
+
namespace: namespace2,
|
|
98841
|
+
environments: manifest.environments.map((e) => e.name),
|
|
98842
|
+
action: "created",
|
|
98843
|
+
pending: false
|
|
98844
|
+
});
|
|
98845
|
+
return;
|
|
98846
|
+
}
|
|
98590
98847
|
formatter.success(`'${key}' set in ${namespace2} across all environments`);
|
|
98591
98848
|
formatter.hint(`git add ${namespace2}/ # stage all updated files`);
|
|
98592
98849
|
}
|
|
@@ -98650,6 +98907,10 @@ function registerSetCommand(program3, deps2) {
|
|
|
98650
98907
|
process.exit(1);
|
|
98651
98908
|
return;
|
|
98652
98909
|
}
|
|
98910
|
+
if (isJsonMode()) {
|
|
98911
|
+
formatter.json({ key, namespace, environment, action: "created", pending: true });
|
|
98912
|
+
return;
|
|
98913
|
+
}
|
|
98653
98914
|
formatter.success(`${key} set in ${namespace}/${environment} ${sym("locked")}`);
|
|
98654
98915
|
formatter.print(
|
|
98655
98916
|
` ${sym("pending")} Marked as pending \u2014 replace with a real value before deploying`
|
|
@@ -98664,6 +98925,10 @@ function registerSetCommand(program3, deps2) {
|
|
|
98664
98925
|
The value is saved. Run clef lint to check for stale pending markers.`
|
|
98665
98926
|
);
|
|
98666
98927
|
}
|
|
98928
|
+
if (isJsonMode()) {
|
|
98929
|
+
formatter.json({ key, namespace, environment, action: "created", pending: false });
|
|
98930
|
+
return;
|
|
98931
|
+
}
|
|
98667
98932
|
formatter.success(`${key} set in ${namespace}/${environment}`);
|
|
98668
98933
|
formatter.hint(
|
|
98669
98934
|
`Commit: git add ${manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", environment)}`
|
|
@@ -98732,7 +98997,10 @@ function registerCompareCommand(program3, deps2) {
|
|
|
98732
98997
|
compareBuf.copy(paddedCompare);
|
|
98733
98998
|
const timingEqual = crypto5.timingSafeEqual(paddedStored, paddedCompare);
|
|
98734
98999
|
const match = storedBuf.length === compareBuf.length && timingEqual;
|
|
98735
|
-
if (
|
|
99000
|
+
if (isJsonMode()) {
|
|
99001
|
+
formatter.json({ match, key, namespace, environment });
|
|
99002
|
+
if (!match) process.exit(1);
|
|
99003
|
+
} else if (match) {
|
|
98736
99004
|
formatter.success(`${key} ${sym("arrow")} values match`);
|
|
98737
99005
|
} else {
|
|
98738
99006
|
formatter.failure(`${key} ${sym("arrow")} values do not match`);
|
|
@@ -98780,6 +99048,15 @@ Type the key name to confirm:`
|
|
|
98780
99048
|
}
|
|
98781
99049
|
const bulkOps = new BulkOps();
|
|
98782
99050
|
await bulkOps.deleteAcrossEnvironments(namespace, key, manifest, sopsClient, repoRoot);
|
|
99051
|
+
if (isJsonMode()) {
|
|
99052
|
+
formatter.json({
|
|
99053
|
+
key,
|
|
99054
|
+
namespace,
|
|
99055
|
+
environments: manifest.environments.map((e) => e.name),
|
|
99056
|
+
action: "deleted"
|
|
99057
|
+
});
|
|
99058
|
+
return;
|
|
99059
|
+
}
|
|
98783
99060
|
formatter.success(`Deleted '${key}' from ${namespace} in all environments`);
|
|
98784
99061
|
} else {
|
|
98785
99062
|
const [namespace, environment] = parseTarget(target);
|
|
@@ -98818,6 +99095,10 @@ Type the key name to confirm:`
|
|
|
98818
99095
|
`Key deleted but pending metadata could not be cleaned up. Run clef lint to verify.`
|
|
98819
99096
|
);
|
|
98820
99097
|
}
|
|
99098
|
+
if (isJsonMode()) {
|
|
99099
|
+
formatter.json({ key, namespace, environment, action: "deleted" });
|
|
99100
|
+
return;
|
|
99101
|
+
}
|
|
98821
99102
|
formatter.success(`Deleted '${key}' from ${namespace}/${environment}`);
|
|
98822
99103
|
formatter.hint(
|
|
98823
99104
|
`Commit: git add ${manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", environment)}`
|
|
@@ -98836,10 +99117,11 @@ Type the key name to confirm:`
|
|
|
98836
99117
|
var path33 = __toESM(require("path"));
|
|
98837
99118
|
var import_picocolors2 = __toESM(require_picocolors());
|
|
98838
99119
|
init_src();
|
|
99120
|
+
var MASKED = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
98839
99121
|
function registerDiffCommand(program3, deps2) {
|
|
98840
99122
|
program3.command("diff <namespace> <env-a> <env-b>").description(
|
|
98841
99123
|
"Compare secrets between two environments for a namespace.\n\nExit codes:\n 0 No differences\n 1 Differences found"
|
|
98842
|
-
).option("--show-identical", "Include identical keys in the output").option("--show-values", "Show plaintext values instead of masking them").
|
|
99124
|
+
).option("--show-identical", "Include identical keys in the output").option("--show-values", "Show plaintext values instead of masking them").action(
|
|
98843
99125
|
async (namespace, envA, envB, options) => {
|
|
98844
99126
|
try {
|
|
98845
99127
|
const repoRoot = program3.opts().dir || process.cwd();
|
|
@@ -98866,19 +99148,20 @@ function registerDiffCommand(program3, deps2) {
|
|
|
98866
99148
|
formatter.warn("Warning: printing plaintext values for protected environment.");
|
|
98867
99149
|
}
|
|
98868
99150
|
}
|
|
98869
|
-
if (
|
|
98870
|
-
const
|
|
99151
|
+
if (isJsonMode()) {
|
|
99152
|
+
const jsonData = options.showValues ? result : {
|
|
98871
99153
|
...result,
|
|
98872
99154
|
rows: result.rows.map((r) => ({
|
|
98873
99155
|
...r,
|
|
98874
|
-
valueA: r.valueA !== null ?
|
|
98875
|
-
valueB: r.valueB !== null ?
|
|
99156
|
+
valueA: r.valueA !== null ? MASKED : null,
|
|
99157
|
+
valueB: r.valueB !== null ? MASKED : null,
|
|
98876
99158
|
masked: true
|
|
98877
99159
|
}))
|
|
98878
99160
|
};
|
|
98879
|
-
formatter.
|
|
99161
|
+
formatter.json(jsonData);
|
|
98880
99162
|
const hasDiffs2 = result.rows.some((r) => r.status !== "identical");
|
|
98881
99163
|
process.exit(hasDiffs2 ? 1 : 0);
|
|
99164
|
+
return;
|
|
98882
99165
|
}
|
|
98883
99166
|
formatDiffOutput(
|
|
98884
99167
|
result,
|
|
@@ -98898,7 +99181,6 @@ function registerDiffCommand(program3, deps2) {
|
|
|
98898
99181
|
}
|
|
98899
99182
|
);
|
|
98900
99183
|
}
|
|
98901
|
-
var MASKED = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
98902
99184
|
function formatDiffOutput(result, envA, envB, showIdentical, showValues) {
|
|
98903
99185
|
const filteredRows = showIdentical ? result.rows : result.rows.filter((r) => r.status !== "identical");
|
|
98904
99186
|
if (filteredRows.length === 0) {
|
|
@@ -99180,7 +99462,7 @@ async function fetchCheckpoint(config) {
|
|
|
99180
99462
|
}
|
|
99181
99463
|
|
|
99182
99464
|
// package.json
|
|
99183
|
-
var version2 = "0.1.
|
|
99465
|
+
var version2 = "0.1.13-beta.92";
|
|
99184
99466
|
var package_default = {
|
|
99185
99467
|
name: "@clef-sh/cli",
|
|
99186
99468
|
version: version2,
|
|
@@ -99251,7 +99533,7 @@ var package_default = {
|
|
|
99251
99533
|
function registerLintCommand(program3, deps2) {
|
|
99252
99534
|
program3.command("lint").description(
|
|
99253
99535
|
"Full repo health check \u2014 matrix completeness, schema validation, SOPS integrity.\n\nExit codes:\n 0 No errors (warnings are allowed)\n 1 Errors found"
|
|
99254
|
-
).option("--fix", "Auto-fix safe issues (scaffold missing files)").option("--
|
|
99536
|
+
).option("--fix", "Auto-fix safe issues (scaffold missing files)").option("--push", "Push results as OTLP to CLEF_TELEMETRY_URL").action(async (options) => {
|
|
99255
99537
|
try {
|
|
99256
99538
|
const repoRoot = program3.opts().dir || process.cwd();
|
|
99257
99539
|
const parser = new ManifestParser();
|
|
@@ -99286,8 +99568,8 @@ function registerLintCommand(program3, deps2) {
|
|
|
99286
99568
|
await pushOtlp(payload, config);
|
|
99287
99569
|
formatter.success("Lint results pushed to telemetry endpoint.");
|
|
99288
99570
|
}
|
|
99289
|
-
if (
|
|
99290
|
-
formatter.
|
|
99571
|
+
if (isJsonMode()) {
|
|
99572
|
+
formatter.json(result);
|
|
99291
99573
|
const hasErrors2 = result.issues.some((i) => i.severity === "error");
|
|
99292
99574
|
process.exit(hasErrors2 ? 1 : 0);
|
|
99293
99575
|
return;
|
|
@@ -99396,6 +99678,10 @@ function registerRotateCommand(program3, deps2) {
|
|
|
99396
99678
|
const relativeFile = manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", environment);
|
|
99397
99679
|
formatter.print(`${sym("working")} Rotating ${namespace}/${environment}...`);
|
|
99398
99680
|
await sopsClient.reEncrypt(filePath, options.newKey);
|
|
99681
|
+
if (isJsonMode()) {
|
|
99682
|
+
formatter.json({ namespace, environment, file: relativeFile, action: "rotated" });
|
|
99683
|
+
return;
|
|
99684
|
+
}
|
|
99399
99685
|
formatter.success(`Rotated. New values encrypted. ${sym("locked")}`);
|
|
99400
99686
|
formatter.hint(
|
|
99401
99687
|
`git add ${relativeFile} && git commit -m "rotate: ${namespace}/${environment}"`
|
|
@@ -99441,15 +99727,28 @@ function registerHooksCommand(program3, deps2) {
|
|
|
99441
99727
|
}
|
|
99442
99728
|
const git = new GitIntegration(deps2.runner);
|
|
99443
99729
|
await git.installPreCommitHook(repoRoot);
|
|
99730
|
+
let mergeDriverOk = false;
|
|
99731
|
+
try {
|
|
99732
|
+
await git.installMergeDriver(repoRoot);
|
|
99733
|
+
mergeDriverOk = true;
|
|
99734
|
+
} catch {
|
|
99735
|
+
}
|
|
99736
|
+
if (isJsonMode()) {
|
|
99737
|
+
formatter.json({
|
|
99738
|
+
preCommitHook: true,
|
|
99739
|
+
mergeDriver: mergeDriverOk,
|
|
99740
|
+
hookPath
|
|
99741
|
+
});
|
|
99742
|
+
return;
|
|
99743
|
+
}
|
|
99444
99744
|
formatter.success("Pre-commit hook installed");
|
|
99445
99745
|
formatter.print(` ${sym("pending")} ${hookPath}`);
|
|
99446
99746
|
formatter.hint(
|
|
99447
99747
|
"Hook checks SOPS metadata on staged .enc files and runs: clef scan --staged"
|
|
99448
99748
|
);
|
|
99449
|
-
|
|
99450
|
-
await git.installMergeDriver(repoRoot);
|
|
99749
|
+
if (mergeDriverOk) {
|
|
99451
99750
|
formatter.success("SOPS merge driver configured");
|
|
99452
|
-
}
|
|
99751
|
+
} else {
|
|
99453
99752
|
formatter.warn("Could not configure SOPS merge driver. Run inside a git repository.");
|
|
99454
99753
|
}
|
|
99455
99754
|
} catch (err) {
|
|
@@ -99748,6 +100047,14 @@ Usage: clef export payments/production --format env`
|
|
|
99748
100047
|
);
|
|
99749
100048
|
try {
|
|
99750
100049
|
const decrypted = await sopsClient.decrypt(filePath);
|
|
100050
|
+
if (isJsonMode()) {
|
|
100051
|
+
const pairs = Object.entries(decrypted.values).map(([k, v]) => ({
|
|
100052
|
+
key: k,
|
|
100053
|
+
value: v
|
|
100054
|
+
}));
|
|
100055
|
+
formatter.json({ pairs, namespace, environment });
|
|
100056
|
+
return;
|
|
100057
|
+
}
|
|
99751
100058
|
const consumption = new ConsumptionClient();
|
|
99752
100059
|
const output = consumption.formatExport(decrypted, "env", !options.export);
|
|
99753
100060
|
if (options.raw) {
|
|
@@ -99788,7 +100095,7 @@ init_src();
|
|
|
99788
100095
|
function registerDoctorCommand(program3, deps2) {
|
|
99789
100096
|
program3.command("doctor").description(
|
|
99790
100097
|
"Check your environment for required dependencies and configuration.\n\nExit codes:\n 0 All checks pass\n 1 One or more checks failed"
|
|
99791
|
-
).option("--
|
|
100098
|
+
).option("--fix", "Attempt to auto-fix issues").action(async (options) => {
|
|
99792
100099
|
const repoRoot = program3.opts().dir || process.cwd();
|
|
99793
100100
|
const clefVersion = program3.version() ?? "unknown";
|
|
99794
100101
|
const checks = [];
|
|
@@ -99875,7 +100182,7 @@ function registerDoctorCommand(program3, deps2) {
|
|
|
99875
100182
|
formatter.warn("--fix cannot resolve these issues automatically.");
|
|
99876
100183
|
}
|
|
99877
100184
|
}
|
|
99878
|
-
if (
|
|
100185
|
+
if (isJsonMode()) {
|
|
99879
100186
|
const json = {
|
|
99880
100187
|
clef: { version: clefVersion, ok: true },
|
|
99881
100188
|
sops: {
|
|
@@ -99905,7 +100212,7 @@ function registerDoctorCommand(program3, deps2) {
|
|
|
99905
100212
|
ok: mergeDriverOk
|
|
99906
100213
|
}
|
|
99907
100214
|
};
|
|
99908
|
-
formatter.
|
|
100215
|
+
formatter.json(json);
|
|
99909
100216
|
const hasFailures = checks.some((c) => !c.ok);
|
|
99910
100217
|
process.exit(hasFailures ? 1 : 0);
|
|
99911
100218
|
return;
|
|
@@ -100039,6 +100346,11 @@ function registerUpdateCommand(program3, deps2) {
|
|
|
100039
100346
|
);
|
|
100040
100347
|
}
|
|
100041
100348
|
}
|
|
100349
|
+
if (isJsonMode()) {
|
|
100350
|
+
formatter.json({ scaffolded: scaffoldedCount, failed: failedCount });
|
|
100351
|
+
process.exit(failedCount > 0 ? 1 : 0);
|
|
100352
|
+
return;
|
|
100353
|
+
}
|
|
100042
100354
|
if (scaffoldedCount > 0) {
|
|
100043
100355
|
formatter.success(`Scaffolded ${scaffoldedCount} encrypted file(s)`);
|
|
100044
100356
|
}
|
|
@@ -100064,69 +100376,57 @@ function registerScanCommand(program3, deps2) {
|
|
|
100064
100376
|
"--severity <level>",
|
|
100065
100377
|
"Detection level: all (patterns+entropy) or high (patterns only)",
|
|
100066
100378
|
"all"
|
|
100067
|
-
).
|
|
100068
|
-
|
|
100069
|
-
|
|
100070
|
-
|
|
100071
|
-
|
|
100072
|
-
|
|
100073
|
-
|
|
100074
|
-
|
|
100075
|
-
|
|
100076
|
-
|
|
100077
|
-
|
|
100078
|
-
formatter.error(err.message);
|
|
100079
|
-
}
|
|
100080
|
-
process.exit(2);
|
|
100081
|
-
return;
|
|
100082
|
-
}
|
|
100083
|
-
if (options.severity && options.severity !== "all" && options.severity !== "high") {
|
|
100084
|
-
formatter.error(`Invalid severity '${options.severity}'. Must be 'all' or 'high'.`);
|
|
100085
|
-
process.exit(2);
|
|
100086
|
-
return;
|
|
100087
|
-
}
|
|
100088
|
-
const severity = options.severity === "high" ? "high" : "all";
|
|
100089
|
-
const scanRunner = new ScanRunner(deps2.runner);
|
|
100090
|
-
if (!options.json) {
|
|
100091
|
-
formatter.print(import_picocolors4.default.dim("Scanning repository for unencrypted secrets..."));
|
|
100092
|
-
}
|
|
100093
|
-
let result;
|
|
100094
|
-
try {
|
|
100095
|
-
result = await scanRunner.scan(repoRoot, manifest, {
|
|
100096
|
-
stagedOnly: options.staged,
|
|
100097
|
-
paths: paths.length > 0 ? paths : void 0,
|
|
100098
|
-
severity
|
|
100099
|
-
});
|
|
100100
|
-
} catch (err) {
|
|
100101
|
-
formatter.error(`Scan failed: ${err.message}`);
|
|
100102
|
-
process.exit(2);
|
|
100103
|
-
return;
|
|
100104
|
-
}
|
|
100105
|
-
if (options.json) {
|
|
100106
|
-
const totalIssues = result.matches.length + result.unencryptedMatrixFiles.length;
|
|
100107
|
-
formatter.raw(
|
|
100108
|
-
JSON.stringify(
|
|
100109
|
-
{
|
|
100110
|
-
matches: result.matches,
|
|
100111
|
-
unencryptedMatrixFiles: result.unencryptedMatrixFiles,
|
|
100112
|
-
filesScanned: result.filesScanned,
|
|
100113
|
-
filesSkipped: result.filesSkipped,
|
|
100114
|
-
durationMs: result.durationMs,
|
|
100115
|
-
summary: `${totalIssues} issue${totalIssues !== 1 ? "s" : ""} found`
|
|
100116
|
-
},
|
|
100117
|
-
null,
|
|
100118
|
-
2
|
|
100119
|
-
) + "\n"
|
|
100120
|
-
);
|
|
100121
|
-
const hasIssues2 = result.matches.length > 0 || result.unencryptedMatrixFiles.length > 0;
|
|
100122
|
-
process.exit(hasIssues2 ? 1 : 0);
|
|
100123
|
-
return;
|
|
100379
|
+
).action(async (paths, options) => {
|
|
100380
|
+
const repoRoot = program3.opts().dir || process.cwd();
|
|
100381
|
+
let manifest;
|
|
100382
|
+
try {
|
|
100383
|
+
const parser = new ManifestParser();
|
|
100384
|
+
manifest = parser.parse(path43.join(repoRoot, "clef.yaml"));
|
|
100385
|
+
} catch (err) {
|
|
100386
|
+
if (err instanceof ManifestValidationError || err.message?.includes("clef.yaml")) {
|
|
100387
|
+
formatter.error("No clef.yaml found. Run 'clef init' to set up this repository.");
|
|
100388
|
+
} else {
|
|
100389
|
+
formatter.error(err.message);
|
|
100124
100390
|
}
|
|
100125
|
-
|
|
100126
|
-
|
|
100127
|
-
process.exit(hasIssues ? 1 : 0);
|
|
100391
|
+
process.exit(2);
|
|
100392
|
+
return;
|
|
100128
100393
|
}
|
|
100129
|
-
|
|
100394
|
+
if (options.severity && options.severity !== "all" && options.severity !== "high") {
|
|
100395
|
+
formatter.error(`Invalid severity '${options.severity}'. Must be 'all' or 'high'.`);
|
|
100396
|
+
process.exit(2);
|
|
100397
|
+
return;
|
|
100398
|
+
}
|
|
100399
|
+
const severity = options.severity === "high" ? "high" : "all";
|
|
100400
|
+
const scanRunner = new ScanRunner(deps2.runner);
|
|
100401
|
+
formatter.print(import_picocolors4.default.dim("Scanning repository for unencrypted secrets..."));
|
|
100402
|
+
let result;
|
|
100403
|
+
try {
|
|
100404
|
+
result = await scanRunner.scan(repoRoot, manifest, {
|
|
100405
|
+
stagedOnly: options.staged,
|
|
100406
|
+
paths: paths.length > 0 ? paths : void 0,
|
|
100407
|
+
severity
|
|
100408
|
+
});
|
|
100409
|
+
} catch (err) {
|
|
100410
|
+
formatter.error(`Scan failed: ${err.message}`);
|
|
100411
|
+
process.exit(2);
|
|
100412
|
+
return;
|
|
100413
|
+
}
|
|
100414
|
+
if (isJsonMode()) {
|
|
100415
|
+
formatter.json({
|
|
100416
|
+
matches: result.matches,
|
|
100417
|
+
unencryptedMatrixFiles: result.unencryptedMatrixFiles,
|
|
100418
|
+
filesScanned: result.filesScanned,
|
|
100419
|
+
filesSkipped: result.filesSkipped,
|
|
100420
|
+
durationMs: result.durationMs
|
|
100421
|
+
});
|
|
100422
|
+
const hasIssues2 = result.matches.length > 0 || result.unencryptedMatrixFiles.length > 0;
|
|
100423
|
+
process.exit(hasIssues2 ? 1 : 0);
|
|
100424
|
+
return;
|
|
100425
|
+
}
|
|
100426
|
+
formatScanOutput(result);
|
|
100427
|
+
const hasIssues = result.matches.length > 0 || result.unencryptedMatrixFiles.length > 0;
|
|
100428
|
+
process.exit(hasIssues ? 1 : 0);
|
|
100429
|
+
});
|
|
100130
100430
|
}
|
|
100131
100431
|
function formatScanOutput(result) {
|
|
100132
100432
|
const totalIssues = result.matches.length + result.unencryptedMatrixFiles.length;
|
|
@@ -100291,6 +100591,17 @@ function registerImportCommand(program3, deps2) {
|
|
|
100291
100591
|
process.exit(2);
|
|
100292
100592
|
return;
|
|
100293
100593
|
}
|
|
100594
|
+
if (isJsonMode()) {
|
|
100595
|
+
formatter.json({
|
|
100596
|
+
imported: result.imported,
|
|
100597
|
+
skipped: result.skipped,
|
|
100598
|
+
failed: result.failed,
|
|
100599
|
+
warnings: result.warnings,
|
|
100600
|
+
dryRun: opts.dryRun
|
|
100601
|
+
});
|
|
100602
|
+
process.exit(result.failed.length > 0 ? 1 : 0);
|
|
100603
|
+
return;
|
|
100604
|
+
}
|
|
100294
100605
|
for (const warning of result.warnings) {
|
|
100295
100606
|
formatter.print(` ${sym("warning")} ${warning}`);
|
|
100296
100607
|
}
|
|
@@ -100350,6 +100661,7 @@ var path45 = __toESM(require("path"));
|
|
|
100350
100661
|
var readline4 = __toESM(require("readline"));
|
|
100351
100662
|
init_src();
|
|
100352
100663
|
function waitForEnter(message) {
|
|
100664
|
+
if (isJsonMode()) return Promise.resolve();
|
|
100353
100665
|
return new Promise((resolve7) => {
|
|
100354
100666
|
const rl = readline4.createInterface({
|
|
100355
100667
|
input: process.stdin,
|
|
@@ -100383,6 +100695,10 @@ function registerRecipientsCommand(program3, deps2) {
|
|
|
100383
100695
|
const sopsClient = await createSopsClient(repoRoot, deps2.runner);
|
|
100384
100696
|
const recipientManager = new RecipientManager(sopsClient, matrixManager);
|
|
100385
100697
|
const recipients = await recipientManager.list(manifest, repoRoot, opts.environment);
|
|
100698
|
+
if (isJsonMode()) {
|
|
100699
|
+
formatter.json(recipients);
|
|
100700
|
+
return;
|
|
100701
|
+
}
|
|
100386
100702
|
if (recipients.length === 0) {
|
|
100387
100703
|
const scope2 = opts.environment ? ` for environment '${opts.environment}'` : "";
|
|
100388
100704
|
formatter.info(`No recipients configured${scope2}.`);
|
|
@@ -100413,7 +100729,7 @@ function registerRecipientsCommand(program3, deps2) {
|
|
|
100413
100729
|
return;
|
|
100414
100730
|
}
|
|
100415
100731
|
const normalizedKey = validation.key;
|
|
100416
|
-
const
|
|
100732
|
+
const result = await executeRecipientAdd(
|
|
100417
100733
|
repoRoot,
|
|
100418
100734
|
program3,
|
|
100419
100735
|
deps2,
|
|
@@ -100421,11 +100737,21 @@ function registerRecipientsCommand(program3, deps2) {
|
|
|
100421
100737
|
opts.label,
|
|
100422
100738
|
opts.environment
|
|
100423
100739
|
);
|
|
100424
|
-
if (
|
|
100425
|
-
|
|
100426
|
-
|
|
100427
|
-
|
|
100428
|
-
|
|
100740
|
+
if (result) {
|
|
100741
|
+
if (isJsonMode()) {
|
|
100742
|
+
formatter.json({
|
|
100743
|
+
action: "added",
|
|
100744
|
+
key: normalizedKey,
|
|
100745
|
+
label: opts.label || keyPreview(normalizedKey),
|
|
100746
|
+
environment: opts.environment,
|
|
100747
|
+
reEncryptedFiles: result.reEncryptedFiles.length
|
|
100748
|
+
});
|
|
100749
|
+
} else {
|
|
100750
|
+
const label = opts.label || keyPreview(normalizedKey);
|
|
100751
|
+
formatter.hint(
|
|
100752
|
+
`git add clef.yaml && git add -A && git commit -m "add recipient: ${label} [${opts.environment}]"`
|
|
100753
|
+
);
|
|
100754
|
+
}
|
|
100429
100755
|
}
|
|
100430
100756
|
} catch (err) {
|
|
100431
100757
|
handleCommandError(err);
|
|
@@ -100521,6 +100847,16 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`
|
|
|
100521
100847
|
const relative7 = path45.relative(repoRoot, file);
|
|
100522
100848
|
formatter.print(` ${sym("success")} ${relative7}`);
|
|
100523
100849
|
}
|
|
100850
|
+
if (isJsonMode()) {
|
|
100851
|
+
formatter.json({
|
|
100852
|
+
action: "removed",
|
|
100853
|
+
key: trimmedKey,
|
|
100854
|
+
label,
|
|
100855
|
+
environment: opts.environment ?? null,
|
|
100856
|
+
reEncryptedFiles: result.reEncryptedFiles.length
|
|
100857
|
+
});
|
|
100858
|
+
return;
|
|
100859
|
+
}
|
|
100524
100860
|
formatter.success(
|
|
100525
100861
|
`${label} removed. ${result.reEncryptedFiles.length} files re-encrypted. ${sym("locked")}`
|
|
100526
100862
|
);
|
|
@@ -100585,6 +100921,15 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`
|
|
|
100585
100921
|
return;
|
|
100586
100922
|
}
|
|
100587
100923
|
upsertRequest(repoRoot, publicKey, label, opts.environment);
|
|
100924
|
+
if (isJsonMode()) {
|
|
100925
|
+
formatter.json({
|
|
100926
|
+
action: "requested",
|
|
100927
|
+
label,
|
|
100928
|
+
key: publicKey,
|
|
100929
|
+
environment: opts.environment ?? null
|
|
100930
|
+
});
|
|
100931
|
+
return;
|
|
100932
|
+
}
|
|
100588
100933
|
const scope = opts.environment ? ` for environment '${opts.environment}'` : "";
|
|
100589
100934
|
formatter.success(`Access requested as '${label}'${scope}`);
|
|
100590
100935
|
formatter.print(` Key: ${keyPreview(publicKey)}`);
|
|
@@ -100600,6 +100945,15 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`
|
|
|
100600
100945
|
try {
|
|
100601
100946
|
const repoRoot = program3.opts().dir || process.cwd();
|
|
100602
100947
|
const requests = loadRequests(repoRoot);
|
|
100948
|
+
if (isJsonMode()) {
|
|
100949
|
+
formatter.json(
|
|
100950
|
+
requests.map((r) => ({
|
|
100951
|
+
...r,
|
|
100952
|
+
requestedAt: r.requestedAt.toISOString()
|
|
100953
|
+
}))
|
|
100954
|
+
);
|
|
100955
|
+
return;
|
|
100956
|
+
}
|
|
100603
100957
|
if (requests.length === 0) {
|
|
100604
100958
|
formatter.info("No pending access requests.");
|
|
100605
100959
|
return;
|
|
@@ -100639,7 +100993,7 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`
|
|
|
100639
100993
|
process.exit(2);
|
|
100640
100994
|
return;
|
|
100641
100995
|
}
|
|
100642
|
-
const
|
|
100996
|
+
const result = await executeRecipientAdd(
|
|
100643
100997
|
repoRoot,
|
|
100644
100998
|
program3,
|
|
100645
100999
|
deps2,
|
|
@@ -100647,11 +101001,21 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`
|
|
|
100647
101001
|
request.label,
|
|
100648
101002
|
environment
|
|
100649
101003
|
);
|
|
100650
|
-
if (
|
|
101004
|
+
if (result) {
|
|
100651
101005
|
removeRequest(repoRoot, identifier);
|
|
100652
|
-
|
|
100653
|
-
|
|
100654
|
-
|
|
101006
|
+
if (isJsonMode()) {
|
|
101007
|
+
formatter.json({
|
|
101008
|
+
action: "approved",
|
|
101009
|
+
identifier,
|
|
101010
|
+
label: request.label,
|
|
101011
|
+
environment,
|
|
101012
|
+
reEncryptedFiles: result.reEncryptedFiles.length
|
|
101013
|
+
});
|
|
101014
|
+
} else {
|
|
101015
|
+
formatter.hint(
|
|
101016
|
+
`git add clef.yaml ${REQUESTS_FILENAME} && git add -A && git commit -m "approve recipient: ${request.label} [${environment}]"`
|
|
101017
|
+
);
|
|
101018
|
+
}
|
|
100655
101019
|
}
|
|
100656
101020
|
} catch (err) {
|
|
100657
101021
|
handleCommandError(err);
|
|
@@ -100667,7 +101031,7 @@ async function executeRecipientAdd(repoRoot, _program, deps2, key, label, enviro
|
|
|
100667
101031
|
`Environment '${environment}' not found. Available: ${manifest.environments.map((e) => e.name).join(", ")}`
|
|
100668
101032
|
);
|
|
100669
101033
|
process.exit(2);
|
|
100670
|
-
return
|
|
101034
|
+
return null;
|
|
100671
101035
|
}
|
|
100672
101036
|
const matrixManager = new MatrixManager();
|
|
100673
101037
|
const sopsClient = await createSopsClient(repoRoot, deps2.runner);
|
|
@@ -100676,7 +101040,7 @@ async function executeRecipientAdd(repoRoot, _program, deps2, key, label, enviro
|
|
|
100676
101040
|
if (existing.some((r) => r.key === key)) {
|
|
100677
101041
|
formatter.error(`Recipient '${keyPreview(key)}' is already present.`);
|
|
100678
101042
|
process.exit(2);
|
|
100679
|
-
return
|
|
101043
|
+
return null;
|
|
100680
101044
|
}
|
|
100681
101045
|
const allCells = matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists && c.environment === environment);
|
|
100682
101046
|
const fileCount = allCells.length;
|
|
@@ -100693,7 +101057,7 @@ This will re-encrypt ${fileCount} files in the matrix.`);
|
|
|
100693
101057
|
const confirmed = await formatter.confirm("Proceed?");
|
|
100694
101058
|
if (!confirmed) {
|
|
100695
101059
|
formatter.info("Aborted.");
|
|
100696
|
-
return
|
|
101060
|
+
return null;
|
|
100697
101061
|
}
|
|
100698
101062
|
formatter.print(`
|
|
100699
101063
|
${sym("working")} Re-encrypting matrix...`);
|
|
@@ -100710,7 +101074,7 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`);
|
|
|
100710
101074
|
);
|
|
100711
101075
|
formatter.print("\nNo changes were applied. Investigate the error above and retry.");
|
|
100712
101076
|
process.exit(1);
|
|
100713
|
-
return
|
|
101077
|
+
return null;
|
|
100714
101078
|
}
|
|
100715
101079
|
for (const file of result.reEncryptedFiles) {
|
|
100716
101080
|
const relative7 = path45.relative(repoRoot, file);
|
|
@@ -100720,7 +101084,7 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`);
|
|
|
100720
101084
|
formatter.success(
|
|
100721
101085
|
`${displayLabel} added. ${result.reEncryptedFiles.length} files re-encrypted. ${sym("locked")}`
|
|
100722
101086
|
);
|
|
100723
|
-
return
|
|
101087
|
+
return { reEncryptedFiles: result.reEncryptedFiles };
|
|
100724
101088
|
}
|
|
100725
101089
|
|
|
100726
101090
|
// src/commands/merge-driver.ts
|
|
@@ -100883,6 +101247,16 @@ function registerServiceCommand(program3, deps2) {
|
|
|
100883
101247
|
repoRoot,
|
|
100884
101248
|
kmsEnvConfigs
|
|
100885
101249
|
);
|
|
101250
|
+
if (isJsonMode()) {
|
|
101251
|
+
formatter.json({
|
|
101252
|
+
action: "created",
|
|
101253
|
+
identity: result.identity.name,
|
|
101254
|
+
namespaces: result.identity.namespaces,
|
|
101255
|
+
environments: Object.keys(result.identity.environments),
|
|
101256
|
+
privateKeys: result.privateKeys
|
|
101257
|
+
});
|
|
101258
|
+
return;
|
|
101259
|
+
}
|
|
100886
101260
|
formatter.success(`Service identity '${name}' created.`);
|
|
100887
101261
|
formatter.print(`
|
|
100888
101262
|
Namespaces: ${result.identity.namespaces.join(", ")}`);
|
|
@@ -100936,6 +101310,10 @@ function registerServiceCommand(program3, deps2) {
|
|
|
100936
101310
|
const sopsClient = await createSopsClient(repoRoot, deps2.runner);
|
|
100937
101311
|
const manager = new ServiceIdentityManager(sopsClient, matrixManager);
|
|
100938
101312
|
const identities = manager.list(manifest);
|
|
101313
|
+
if (isJsonMode()) {
|
|
101314
|
+
formatter.json(identities);
|
|
101315
|
+
return;
|
|
101316
|
+
}
|
|
100939
101317
|
if (identities.length === 0) {
|
|
100940
101318
|
formatter.info("No service identities configured.");
|
|
100941
101319
|
return;
|
|
@@ -100965,6 +101343,10 @@ function registerServiceCommand(program3, deps2) {
|
|
|
100965
101343
|
process.exit(1);
|
|
100966
101344
|
return;
|
|
100967
101345
|
}
|
|
101346
|
+
if (isJsonMode()) {
|
|
101347
|
+
formatter.json(identity);
|
|
101348
|
+
return;
|
|
101349
|
+
}
|
|
100968
101350
|
formatter.print(`
|
|
100969
101351
|
Service Identity: ${identity.name}`);
|
|
100970
101352
|
formatter.print(`Description: ${identity.description}`);
|
|
@@ -100993,6 +101375,11 @@ Service Identity: ${identity.name}`);
|
|
|
100993
101375
|
const sopsClient = await createSopsClient(repoRoot, deps2.runner);
|
|
100994
101376
|
const manager = new ServiceIdentityManager(sopsClient, matrixManager);
|
|
100995
101377
|
const issues = await manager.validate(manifest, repoRoot);
|
|
101378
|
+
if (isJsonMode()) {
|
|
101379
|
+
formatter.json({ issues });
|
|
101380
|
+
process.exit(issues.length > 0 ? 1 : 0);
|
|
101381
|
+
return;
|
|
101382
|
+
}
|
|
100996
101383
|
if (issues.length === 0) {
|
|
100997
101384
|
formatter.success("All service identities are valid.");
|
|
100998
101385
|
return;
|
|
@@ -101043,6 +101430,17 @@ Service Identity: ${identity.name}`);
|
|
|
101043
101430
|
const manager = new ServiceIdentityManager(sopsClient, matrixManager);
|
|
101044
101431
|
formatter.print(`${sym("working")} Updating service identity '${name}'...`);
|
|
101045
101432
|
await manager.updateEnvironments(name, kmsEnvConfigs, manifest, repoRoot);
|
|
101433
|
+
if (isJsonMode()) {
|
|
101434
|
+
formatter.json({
|
|
101435
|
+
action: "updated",
|
|
101436
|
+
identity: name,
|
|
101437
|
+
changed: Object.entries(kmsEnvConfigs).map(([env, cfg]) => ({
|
|
101438
|
+
environment: env,
|
|
101439
|
+
provider: cfg.provider
|
|
101440
|
+
}))
|
|
101441
|
+
});
|
|
101442
|
+
return;
|
|
101443
|
+
}
|
|
101046
101444
|
formatter.success(`Service identity '${name}' updated.`);
|
|
101047
101445
|
for (const [envName, kmsConfig] of Object.entries(kmsEnvConfigs)) {
|
|
101048
101446
|
formatter.print(` ${envName}: switched to KMS envelope (${kmsConfig.provider})`);
|
|
@@ -101078,6 +101476,10 @@ Service Identity: ${identity.name}`);
|
|
|
101078
101476
|
const manager = new ServiceIdentityManager(sopsClient, matrixManager);
|
|
101079
101477
|
formatter.print(`${sym("working")} Deleting service identity '${name}'...`);
|
|
101080
101478
|
await manager.delete(name, manifest, repoRoot);
|
|
101479
|
+
if (isJsonMode()) {
|
|
101480
|
+
formatter.json({ action: "deleted", identity: name });
|
|
101481
|
+
return;
|
|
101482
|
+
}
|
|
101081
101483
|
formatter.success(`Service identity '${name}' deleted.`);
|
|
101082
101484
|
formatter.hint(
|
|
101083
101485
|
`git add clef.yaml && git commit -m "chore: delete service identity '${name}'"`
|
|
@@ -101114,6 +101516,15 @@ Service Identity: ${identity.name}`);
|
|
|
101114
101516
|
}
|
|
101115
101517
|
formatter.print(`${sym("working")} Rotating key for '${name}'...`);
|
|
101116
101518
|
const newKeys = await manager.rotateKey(name, manifest, repoRoot, opts.environment);
|
|
101519
|
+
if (isJsonMode()) {
|
|
101520
|
+
formatter.json({
|
|
101521
|
+
action: "rotated",
|
|
101522
|
+
identity: name,
|
|
101523
|
+
environments: Object.keys(newKeys),
|
|
101524
|
+
privateKeys: newKeys
|
|
101525
|
+
});
|
|
101526
|
+
return;
|
|
101527
|
+
}
|
|
101117
101528
|
formatter.success(`Key rotated for '${name}'.`);
|
|
101118
101529
|
const entries = Object.entries(newKeys);
|
|
101119
101530
|
const block = entries.map(([env, key]) => `${env}: ${key}`).join("\n");
|
|
@@ -101254,7 +101665,7 @@ function registerPackCommand(program3, deps2) {
|
|
|
101254
101665
|
const envConfig = si?.environments[environment];
|
|
101255
101666
|
if (envConfig && isKmsEnvelope(envConfig)) {
|
|
101256
101667
|
const { createKmsProvider } = await Promise.resolve().then(() => __toESM(require_dist3()));
|
|
101257
|
-
kmsProvider = createKmsProvider(envConfig.kms.provider, {
|
|
101668
|
+
kmsProvider = await createKmsProvider(envConfig.kms.provider, {
|
|
101258
101669
|
region: envConfig.kms.region
|
|
101259
101670
|
});
|
|
101260
101671
|
}
|
|
@@ -101289,6 +101700,18 @@ function registerPackCommand(program3, deps2) {
|
|
|
101289
101700
|
const fileOut = new FilePackOutput(outputPath);
|
|
101290
101701
|
await fileOut.write(memOutput.artifact, memOutput.json);
|
|
101291
101702
|
}
|
|
101703
|
+
if (isJsonMode()) {
|
|
101704
|
+
formatter.json({
|
|
101705
|
+
identity,
|
|
101706
|
+
environment,
|
|
101707
|
+
keyCount: result.keyCount,
|
|
101708
|
+
namespaceCount: result.namespaceCount,
|
|
101709
|
+
artifactSize: result.artifactSize,
|
|
101710
|
+
revision: result.revision,
|
|
101711
|
+
output: outputPath ?? null
|
|
101712
|
+
});
|
|
101713
|
+
return;
|
|
101714
|
+
}
|
|
101292
101715
|
formatter.success(
|
|
101293
101716
|
`Artifact packed: ${result.keyCount} keys from ${result.namespaceCount} namespace(s).`
|
|
101294
101717
|
);
|
|
@@ -101366,6 +101789,15 @@ function registerRevokeCommand(program3, _deps) {
|
|
|
101366
101789
|
fs32.mkdirSync(artifactDir, { recursive: true });
|
|
101367
101790
|
fs32.writeFileSync(artifactPath, JSON.stringify(revoked, null, 2) + "\n", "utf-8");
|
|
101368
101791
|
const relPath = path49.relative(repoRoot, artifactPath);
|
|
101792
|
+
if (isJsonMode()) {
|
|
101793
|
+
formatter.json({
|
|
101794
|
+
identity,
|
|
101795
|
+
environment,
|
|
101796
|
+
revokedAt: revoked.revokedAt,
|
|
101797
|
+
markerPath: relPath
|
|
101798
|
+
});
|
|
101799
|
+
return;
|
|
101800
|
+
}
|
|
101369
101801
|
formatter.success(`Artifact revoked: ${relPath}`);
|
|
101370
101802
|
formatter.print("");
|
|
101371
101803
|
formatter.print(`${sym("arrow")} If your agent fetches artifacts from git (VCS source):`);
|
|
@@ -101394,37 +101826,35 @@ init_src();
|
|
|
101394
101826
|
function registerDriftCommand(program3, _deps) {
|
|
101395
101827
|
program3.command("drift <path>").description(
|
|
101396
101828
|
"Compare key sets across two local Clef repos without decryption.\n\nReads encrypted YAML files as plaintext (key names are not encrypted)\nand reports keys that exist in some environments but not others.\n\nDoes not require sops to be installed.\n\nExit codes:\n 0 No drift\n 1 Drift found"
|
|
101397
|
-
).option("--
|
|
101398
|
-
|
|
101399
|
-
|
|
101400
|
-
|
|
101401
|
-
|
|
101402
|
-
|
|
101403
|
-
|
|
101404
|
-
|
|
101405
|
-
|
|
101406
|
-
|
|
101407
|
-
|
|
101408
|
-
process.exit(1);
|
|
101409
|
-
return;
|
|
101410
|
-
}
|
|
101411
|
-
const payload = driftResultToOtlp(result, version2);
|
|
101412
|
-
await pushOtlp(payload, config);
|
|
101413
|
-
formatter.success("Drift results pushed to telemetry endpoint.");
|
|
101414
|
-
}
|
|
101415
|
-
if (options.json) {
|
|
101416
|
-
formatter.raw(JSON.stringify(result, null, 2) + "\n");
|
|
101417
|
-
process.exit(result.issues.length > 0 ? 1 : 0);
|
|
101829
|
+
).option("--push", "Push results as OTLP to CLEF_TELEMETRY_URL").option("--namespace <name...>", "Scope to specific namespace(s)").action(async (remotePath, options) => {
|
|
101830
|
+
try {
|
|
101831
|
+
const localRoot = program3.opts().dir || process.cwd();
|
|
101832
|
+
const remoteRoot = path50.resolve(localRoot, remotePath);
|
|
101833
|
+
const detector = new DriftDetector();
|
|
101834
|
+
const result = detector.detect(localRoot, remoteRoot, options.namespace);
|
|
101835
|
+
if (options.push) {
|
|
101836
|
+
const config = resolveTelemetryConfig();
|
|
101837
|
+
if (!config) {
|
|
101838
|
+
formatter.error("--push requires CLEF_TELEMETRY_URL to be set.");
|
|
101839
|
+
process.exit(1);
|
|
101418
101840
|
return;
|
|
101419
101841
|
}
|
|
101420
|
-
|
|
101842
|
+
const payload = driftResultToOtlp(result, version2);
|
|
101843
|
+
await pushOtlp(payload, config);
|
|
101844
|
+
formatter.success("Drift results pushed to telemetry endpoint.");
|
|
101845
|
+
}
|
|
101846
|
+
if (isJsonMode()) {
|
|
101847
|
+
formatter.json(result);
|
|
101421
101848
|
process.exit(result.issues.length > 0 ? 1 : 0);
|
|
101422
|
-
|
|
101423
|
-
formatter.error(err.message);
|
|
101424
|
-
process.exit(1);
|
|
101849
|
+
return;
|
|
101425
101850
|
}
|
|
101851
|
+
formatDriftOutput(result);
|
|
101852
|
+
process.exit(result.issues.length > 0 ? 1 : 0);
|
|
101853
|
+
} catch (err) {
|
|
101854
|
+
formatter.error(err.message);
|
|
101855
|
+
process.exit(1);
|
|
101426
101856
|
}
|
|
101427
|
-
);
|
|
101857
|
+
});
|
|
101428
101858
|
}
|
|
101429
101859
|
function formatDriftOutput(result) {
|
|
101430
101860
|
if (result.namespacesCompared === 0) {
|
|
@@ -101517,7 +101947,7 @@ async function getHeadSha(repoRoot, runner2) {
|
|
|
101517
101947
|
function registerReportCommand(program3, deps2) {
|
|
101518
101948
|
program3.command("report").description(
|
|
101519
101949
|
"Generate a metadata report for this Clef repository.\n\nIncludes repo identity, matrix status, policy issues, and recipient\nsummaries. Never exposes ciphertext, key names, or decrypted values.\n\nExit codes:\n 0 No errors\n 1 Errors found"
|
|
101520
|
-
).option("--
|
|
101950
|
+
).option("--push", "Push report as OTLP to CLEF_TELEMETRY_URL (with automatic gap-fill)").option("--at <sha>", "Generate report at a specific commit").option("--since <sha>", "Generate reports for all commits since <sha>").option("--namespace <name...>", "Filter to namespace(s)").option("--environment <name...>", "Filter to environment(s)").action(
|
|
101521
101951
|
async (options) => {
|
|
101522
101952
|
try {
|
|
101523
101953
|
const repoRoot = program3.opts().dir || process.cwd();
|
|
@@ -101529,7 +101959,7 @@ function registerReportCommand(program3, deps2) {
|
|
|
101529
101959
|
deps2.runner
|
|
101530
101960
|
);
|
|
101531
101961
|
await maybePush(report, options.push);
|
|
101532
|
-
outputReport(report
|
|
101962
|
+
outputReport(report);
|
|
101533
101963
|
return;
|
|
101534
101964
|
}
|
|
101535
101965
|
const sopsClient = await createSopsClient(repoRoot, deps2.runner);
|
|
@@ -101547,7 +101977,7 @@ function registerReportCommand(program3, deps2) {
|
|
|
101547
101977
|
});
|
|
101548
101978
|
if (options.push) {
|
|
101549
101979
|
await pushWithGapFill(repoRoot, headReport, deps2.runner);
|
|
101550
|
-
outputReport(headReport
|
|
101980
|
+
outputReport(headReport);
|
|
101551
101981
|
return;
|
|
101552
101982
|
}
|
|
101553
101983
|
if (options.since) {
|
|
@@ -101561,8 +101991,8 @@ function registerReportCommand(program3, deps2) {
|
|
|
101561
101991
|
reports.push(await generateReportAtCommit(repoRoot, sha, version2, deps2.runner));
|
|
101562
101992
|
}
|
|
101563
101993
|
}
|
|
101564
|
-
if (
|
|
101565
|
-
formatter.
|
|
101994
|
+
if (isJsonMode()) {
|
|
101995
|
+
formatter.json(reports);
|
|
101566
101996
|
} else {
|
|
101567
101997
|
formatter.print(
|
|
101568
101998
|
`Generated ${reports.length} report(s) for commits since ${options.since.slice(0, 8)}`
|
|
@@ -101577,7 +102007,7 @@ function registerReportCommand(program3, deps2) {
|
|
|
101577
102007
|
process.exit(reports.some((r) => r.policy.issueCount.error > 0) ? 1 : 0);
|
|
101578
102008
|
return;
|
|
101579
102009
|
}
|
|
101580
|
-
outputReport(headReport
|
|
102010
|
+
outputReport(headReport);
|
|
101581
102011
|
} catch (err) {
|
|
101582
102012
|
handleCommandError(err);
|
|
101583
102013
|
}
|
|
@@ -101628,9 +102058,9 @@ async function maybePush(report, push) {
|
|
|
101628
102058
|
await pushOtlp(payload, config);
|
|
101629
102059
|
formatter.success("Report pushed to telemetry endpoint.");
|
|
101630
102060
|
}
|
|
101631
|
-
function outputReport(report
|
|
101632
|
-
if (
|
|
101633
|
-
formatter.
|
|
102061
|
+
function outputReport(report) {
|
|
102062
|
+
if (isJsonMode()) {
|
|
102063
|
+
formatter.json(report);
|
|
101634
102064
|
process.exit(report.policy.issueCount.error > 0 ? 1 : 0);
|
|
101635
102065
|
return;
|
|
101636
102066
|
}
|
|
@@ -101799,6 +102229,16 @@ function registerInstallCommand(program3, _deps) {
|
|
|
101799
102229
|
}
|
|
101800
102230
|
const manifestFile = files.find((f2) => f2.name === "broker.yaml");
|
|
101801
102231
|
const manifest = manifestFile ? (0, import_yaml.parse)(manifestFile.content) : {};
|
|
102232
|
+
if (isJsonMode()) {
|
|
102233
|
+
formatter.json({
|
|
102234
|
+
broker: entry.name,
|
|
102235
|
+
provider: entry.provider,
|
|
102236
|
+
tier: entry.tier,
|
|
102237
|
+
files: files.map((f2) => `brokers/${entry.name}/${f2.name}`)
|
|
102238
|
+
});
|
|
102239
|
+
process.exit(0);
|
|
102240
|
+
return;
|
|
102241
|
+
}
|
|
101802
102242
|
formatter.print("");
|
|
101803
102243
|
formatter.print(` ${sym("success")} ${entry.name}`);
|
|
101804
102244
|
formatter.print("");
|
|
@@ -101862,6 +102302,11 @@ function registerSearchCommand(program3, _deps) {
|
|
|
101862
102302
|
if (options.tier) {
|
|
101863
102303
|
results = results.filter((b) => b.tier === Number(options.tier));
|
|
101864
102304
|
}
|
|
102305
|
+
if (isJsonMode()) {
|
|
102306
|
+
formatter.json(results);
|
|
102307
|
+
process.exit(0);
|
|
102308
|
+
return;
|
|
102309
|
+
}
|
|
101865
102310
|
if (results.length === 0) {
|
|
101866
102311
|
formatter.info("No brokers found matching your query.");
|
|
101867
102312
|
process.exit(0);
|
|
@@ -101992,6 +102437,20 @@ ${sym("working")} Backend migration summary:`);
|
|
|
101992
102437
|
}
|
|
101993
102438
|
}
|
|
101994
102439
|
);
|
|
102440
|
+
if (isJsonMode()) {
|
|
102441
|
+
formatter.json({
|
|
102442
|
+
backend: target.backend,
|
|
102443
|
+
migratedFiles: result.migratedFiles,
|
|
102444
|
+
skippedFiles: result.skippedFiles,
|
|
102445
|
+
verifiedFiles: result.verifiedFiles,
|
|
102446
|
+
warnings: result.warnings,
|
|
102447
|
+
rolledBack: result.rolledBack,
|
|
102448
|
+
error: result.error ?? null,
|
|
102449
|
+
dryRun: opts.dryRun ?? false
|
|
102450
|
+
});
|
|
102451
|
+
process.exit(result.rolledBack ? 1 : 0);
|
|
102452
|
+
return;
|
|
102453
|
+
}
|
|
101995
102454
|
if (result.rolledBack) {
|
|
101996
102455
|
formatter.error(`Migration failed: ${result.error}`);
|
|
101997
102456
|
formatter.info("All changes have been rolled back.");
|
|
@@ -102080,7 +102539,7 @@ function registerServeCommand(program3, deps2) {
|
|
|
102080
102539
|
const envConfig = si.environments[opts.env];
|
|
102081
102540
|
if (envConfig && isKmsEnvelope(envConfig)) {
|
|
102082
102541
|
const { createKmsProvider } = await Promise.resolve().then(() => __toESM(require_dist3()));
|
|
102083
|
-
kmsProvider = createKmsProvider(envConfig.kms.provider, {
|
|
102542
|
+
kmsProvider = await createKmsProvider(envConfig.kms.provider, {
|
|
102084
102543
|
region: envConfig.kms.region
|
|
102085
102544
|
});
|
|
102086
102545
|
}
|
|
@@ -102142,12 +102601,18 @@ var VERSION = package_default.version;
|
|
|
102142
102601
|
var program2 = new Command();
|
|
102143
102602
|
var runner = new NodeSubprocessRunner();
|
|
102144
102603
|
var deps = { runner };
|
|
102145
|
-
program2.name("clef").option("--dir <path>", "Path to a local Clef repository root (default: current directory)").option("--plain", "Plain output, no emoji or colour");
|
|
102604
|
+
program2.name("clef").option("--dir <path>", "Path to a local Clef repository root (default: current directory)").option("--plain", "Plain output, no emoji or colour").option("--json", "Output machine-readable JSON (suppresses human output)").option("--yes", "Auto-confirm destructive operations (required with --json for writes)");
|
|
102146
102605
|
program2.hook("preAction", async () => {
|
|
102147
102606
|
const opts = program2.opts();
|
|
102148
102607
|
if (opts.plain) {
|
|
102149
102608
|
setPlainMode(true);
|
|
102150
102609
|
}
|
|
102610
|
+
if (opts.json) {
|
|
102611
|
+
setJsonMode(true);
|
|
102612
|
+
}
|
|
102613
|
+
if (opts.yes) {
|
|
102614
|
+
setYesMode(true);
|
|
102615
|
+
}
|
|
102151
102616
|
});
|
|
102152
102617
|
program2.addHelpText("beforeAll", () => {
|
|
102153
102618
|
const clef = isPlainMode() ? "clef" : symbols.clef;
|
|
@@ -102246,6 +102711,9 @@ async function main() {
|
|
|
102246
102711
|
}
|
|
102247
102712
|
}
|
|
102248
102713
|
main().catch((err) => {
|
|
102714
|
+
if (isJsonMode()) {
|
|
102715
|
+
exitJsonError(err.message);
|
|
102716
|
+
}
|
|
102249
102717
|
formatter.error(err.message);
|
|
102250
102718
|
process.exit(1);
|
|
102251
102719
|
});
|