@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.mjs
CHANGED
|
@@ -22358,7 +22358,12 @@ var VALID_KMS_PROVIDERS;
|
|
|
22358
22358
|
var init_types2 = __esm({
|
|
22359
22359
|
"../core/src/kms/types.ts"() {
|
|
22360
22360
|
"use strict";
|
|
22361
|
-
VALID_KMS_PROVIDERS = [
|
|
22361
|
+
VALID_KMS_PROVIDERS = [
|
|
22362
|
+
"aws",
|
|
22363
|
+
"gcp",
|
|
22364
|
+
"azure",
|
|
22365
|
+
"cloud"
|
|
22366
|
+
];
|
|
22362
22367
|
}
|
|
22363
22368
|
});
|
|
22364
22369
|
|
|
@@ -95224,10 +95229,177 @@ var require_azure = __commonJS({
|
|
|
95224
95229
|
}
|
|
95225
95230
|
});
|
|
95226
95231
|
|
|
95227
|
-
// ../
|
|
95232
|
+
// ../client/dist/kms.js
|
|
95228
95233
|
var require_kms = __commonJS({
|
|
95234
|
+
"../client/dist/kms.js"(exports, module) {
|
|
95235
|
+
"use strict";
|
|
95236
|
+
var __defProp2 = Object.defineProperty;
|
|
95237
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
95238
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
95239
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
95240
|
+
var __export2 = (target, all) => {
|
|
95241
|
+
for (var name in all)
|
|
95242
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
95243
|
+
};
|
|
95244
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
95245
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
95246
|
+
for (let key of __getOwnPropNames2(from))
|
|
95247
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
95248
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
95249
|
+
}
|
|
95250
|
+
return to;
|
|
95251
|
+
};
|
|
95252
|
+
var __toCommonJS = (mod3) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod3);
|
|
95253
|
+
var kms_exports = {};
|
|
95254
|
+
__export2(kms_exports, {
|
|
95255
|
+
ClefClientError: () => ClefClientError,
|
|
95256
|
+
CloudKmsProvider: () => CloudKmsProvider
|
|
95257
|
+
});
|
|
95258
|
+
module.exports = __toCommonJS(kms_exports);
|
|
95259
|
+
var ClefClientError = class extends Error {
|
|
95260
|
+
constructor(message, statusCode, fix) {
|
|
95261
|
+
super(message);
|
|
95262
|
+
this.statusCode = statusCode;
|
|
95263
|
+
this.fix = fix;
|
|
95264
|
+
this.name = "ClefClientError";
|
|
95265
|
+
}
|
|
95266
|
+
statusCode;
|
|
95267
|
+
fix;
|
|
95268
|
+
};
|
|
95269
|
+
function resolveToken(explicit) {
|
|
95270
|
+
if (explicit) return explicit;
|
|
95271
|
+
if (typeof process !== "undefined" && process.env?.CLEF_SERVICE_TOKEN) {
|
|
95272
|
+
return process.env.CLEF_SERVICE_TOKEN;
|
|
95273
|
+
}
|
|
95274
|
+
throw new ClefClientError(
|
|
95275
|
+
"No service token configured",
|
|
95276
|
+
void 0,
|
|
95277
|
+
"Set CLEF_SERVICE_TOKEN or pass token in options."
|
|
95278
|
+
);
|
|
95279
|
+
}
|
|
95280
|
+
async function request(baseUrl, opts2) {
|
|
95281
|
+
const url = `${baseUrl}${opts2.path}`;
|
|
95282
|
+
const headers = {
|
|
95283
|
+
Authorization: `Bearer ${opts2.token}`,
|
|
95284
|
+
Accept: "application/json"
|
|
95285
|
+
};
|
|
95286
|
+
if (opts2.body !== void 0) {
|
|
95287
|
+
headers["Content-Type"] = "application/json";
|
|
95288
|
+
}
|
|
95289
|
+
const init = {
|
|
95290
|
+
method: opts2.method,
|
|
95291
|
+
headers,
|
|
95292
|
+
body: opts2.body !== void 0 ? JSON.stringify(opts2.body) : void 0
|
|
95293
|
+
};
|
|
95294
|
+
let response;
|
|
95295
|
+
try {
|
|
95296
|
+
response = await opts2.fetchFn(url, init);
|
|
95297
|
+
} catch (err) {
|
|
95298
|
+
try {
|
|
95299
|
+
response = await opts2.fetchFn(url, init);
|
|
95300
|
+
} catch {
|
|
95301
|
+
throw new ClefClientError(
|
|
95302
|
+
`Connection failed: ${err.message}`,
|
|
95303
|
+
void 0,
|
|
95304
|
+
"Is the endpoint reachable? Check your CLEF_ENDPOINT setting."
|
|
95305
|
+
);
|
|
95306
|
+
}
|
|
95307
|
+
}
|
|
95308
|
+
if (response.status >= 500) {
|
|
95309
|
+
response = await opts2.fetchFn(url, init);
|
|
95310
|
+
}
|
|
95311
|
+
if (response.status === 401) {
|
|
95312
|
+
throw new ClefClientError("Authentication failed", 401, "Check your CLEF_SERVICE_TOKEN.");
|
|
95313
|
+
}
|
|
95314
|
+
if (response.status === 503) {
|
|
95315
|
+
throw new ClefClientError("Secrets expired or not loaded", 503, "Check the agent logs.");
|
|
95316
|
+
}
|
|
95317
|
+
if (!response.ok) {
|
|
95318
|
+
const text = await response.text().catch(() => "");
|
|
95319
|
+
throw new ClefClientError(
|
|
95320
|
+
`HTTP ${response.status}: ${text || response.statusText}`,
|
|
95321
|
+
response.status
|
|
95322
|
+
);
|
|
95323
|
+
}
|
|
95324
|
+
const json = await response.json();
|
|
95325
|
+
if (json && typeof json === "object" && "success" in json) {
|
|
95326
|
+
if (!json.success) {
|
|
95327
|
+
throw new ClefClientError(json.message || "Request failed", response.status);
|
|
95328
|
+
}
|
|
95329
|
+
return json.data;
|
|
95330
|
+
}
|
|
95331
|
+
return json;
|
|
95332
|
+
}
|
|
95333
|
+
var CloudKmsProvider = class {
|
|
95334
|
+
endpoint;
|
|
95335
|
+
token;
|
|
95336
|
+
constructor(options) {
|
|
95337
|
+
this.endpoint = options.endpoint;
|
|
95338
|
+
this.token = resolveToken(options.token);
|
|
95339
|
+
}
|
|
95340
|
+
async wrap(_keyId, _plaintext) {
|
|
95341
|
+
throw new ClefClientError(
|
|
95342
|
+
"CloudKmsProvider.wrap() is not supported. Use the keyservice sidecar for encryption."
|
|
95343
|
+
);
|
|
95344
|
+
}
|
|
95345
|
+
async unwrap(keyId, wrappedKey, _algorithm) {
|
|
95346
|
+
const result = await request(this.endpoint, {
|
|
95347
|
+
method: "POST",
|
|
95348
|
+
path: "/api/v1/cloud/kms/decrypt",
|
|
95349
|
+
body: {
|
|
95350
|
+
keyArn: keyId,
|
|
95351
|
+
ciphertext: wrappedKey.toString("base64")
|
|
95352
|
+
},
|
|
95353
|
+
token: this.token,
|
|
95354
|
+
fetchFn: globalThis.fetch
|
|
95355
|
+
});
|
|
95356
|
+
return Buffer.from(result.plaintext, "base64");
|
|
95357
|
+
}
|
|
95358
|
+
};
|
|
95359
|
+
}
|
|
95360
|
+
});
|
|
95361
|
+
|
|
95362
|
+
// ../runtime/dist/kms/index.js
|
|
95363
|
+
var require_kms2 = __commonJS({
|
|
95229
95364
|
"../runtime/dist/kms/index.js"(exports) {
|
|
95230
95365
|
"use strict";
|
|
95366
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
95367
|
+
if (k2 === void 0) k2 = k;
|
|
95368
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
95369
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
95370
|
+
desc = { enumerable: true, get: function() {
|
|
95371
|
+
return m[k];
|
|
95372
|
+
} };
|
|
95373
|
+
}
|
|
95374
|
+
Object.defineProperty(o, k2, desc);
|
|
95375
|
+
}) : (function(o, m, k, k2) {
|
|
95376
|
+
if (k2 === void 0) k2 = k;
|
|
95377
|
+
o[k2] = m[k];
|
|
95378
|
+
}));
|
|
95379
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
95380
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
95381
|
+
}) : function(o, v) {
|
|
95382
|
+
o["default"] = v;
|
|
95383
|
+
});
|
|
95384
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() {
|
|
95385
|
+
var ownKeys = function(o) {
|
|
95386
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
95387
|
+
var ar = [];
|
|
95388
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
95389
|
+
return ar;
|
|
95390
|
+
};
|
|
95391
|
+
return ownKeys(o);
|
|
95392
|
+
};
|
|
95393
|
+
return function(mod3) {
|
|
95394
|
+
if (mod3 && mod3.__esModule) return mod3;
|
|
95395
|
+
var result = {};
|
|
95396
|
+
if (mod3 != null) {
|
|
95397
|
+
for (var k = ownKeys(mod3), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod3, k[i]);
|
|
95398
|
+
}
|
|
95399
|
+
__setModuleDefault(result, mod3);
|
|
95400
|
+
return result;
|
|
95401
|
+
};
|
|
95402
|
+
})();
|
|
95231
95403
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
95232
95404
|
exports.AzureKmsProvider = exports.GcpKmsProvider = exports.AwsKmsProvider = void 0;
|
|
95233
95405
|
exports.createKmsProvider = createKmsProvider;
|
|
@@ -95246,7 +95418,7 @@ var require_kms = __commonJS({
|
|
|
95246
95418
|
Object.defineProperty(exports, "AzureKmsProvider", { enumerable: true, get: function() {
|
|
95247
95419
|
return azure_2.AzureKmsProvider;
|
|
95248
95420
|
} });
|
|
95249
|
-
function createKmsProvider(provider, options) {
|
|
95421
|
+
async function createKmsProvider(provider, options) {
|
|
95250
95422
|
switch (provider) {
|
|
95251
95423
|
case "aws":
|
|
95252
95424
|
return new aws_1.AwsKmsProvider(options?.region);
|
|
@@ -95254,6 +95426,17 @@ var require_kms = __commonJS({
|
|
|
95254
95426
|
return new gcp_1.GcpKmsProvider();
|
|
95255
95427
|
case "azure":
|
|
95256
95428
|
return new azure_1.AzureKmsProvider();
|
|
95429
|
+
case "cloud": {
|
|
95430
|
+
try {
|
|
95431
|
+
const { CloudKmsProvider } = await Promise.resolve().then(() => __importStar(require_kms()));
|
|
95432
|
+
return new CloudKmsProvider({
|
|
95433
|
+
endpoint: options?.endpoint ?? "",
|
|
95434
|
+
token: options?.token
|
|
95435
|
+
});
|
|
95436
|
+
} catch {
|
|
95437
|
+
throw new Error("Clef Cloud KMS requires @clef-sh/client. Install it with: npm install @clef-sh/client");
|
|
95438
|
+
}
|
|
95439
|
+
}
|
|
95257
95440
|
default:
|
|
95258
95441
|
throw new Error(`Unknown KMS provider: ${provider}`);
|
|
95259
95442
|
}
|
|
@@ -95306,7 +95489,7 @@ var require_artifact_decryptor = __commonJS({
|
|
|
95306
95489
|
exports.ArtifactDecryptor = void 0;
|
|
95307
95490
|
var crypto6 = __importStar(__require("crypto"));
|
|
95308
95491
|
var decrypt_1 = require_decrypt();
|
|
95309
|
-
var kms_1 =
|
|
95492
|
+
var kms_1 = require_kms2();
|
|
95310
95493
|
var ArtifactDecryptor = class {
|
|
95311
95494
|
ageDecryptor = new decrypt_1.AgeDecryptor();
|
|
95312
95495
|
privateKey;
|
|
@@ -95355,7 +95538,7 @@ var require_artifact_decryptor = __commonJS({
|
|
|
95355
95538
|
const envelope = artifact.envelope;
|
|
95356
95539
|
let dek;
|
|
95357
95540
|
try {
|
|
95358
|
-
const kms = (0, kms_1.createKmsProvider)(envelope.provider);
|
|
95541
|
+
const kms = await (0, kms_1.createKmsProvider)(envelope.provider);
|
|
95359
95542
|
const wrappedKey = Buffer.from(envelope.wrappedKey, "base64");
|
|
95360
95543
|
dek = await kms.unwrap(envelope.keyId, wrappedKey, envelope.algorithm);
|
|
95361
95544
|
} catch (err) {
|
|
@@ -96623,11 +96806,11 @@ var require_dist3 = __commonJS({
|
|
|
96623
96806
|
Object.defineProperty(exports, "createVcsProvider", { enumerable: true, get: function() {
|
|
96624
96807
|
return index_1.createVcsProvider;
|
|
96625
96808
|
} });
|
|
96626
|
-
var kms_1 =
|
|
96809
|
+
var kms_1 = require_kms2();
|
|
96627
96810
|
Object.defineProperty(exports, "AwsKmsProvider", { enumerable: true, get: function() {
|
|
96628
96811
|
return kms_1.AwsKmsProvider;
|
|
96629
96812
|
} });
|
|
96630
|
-
var kms_2 =
|
|
96813
|
+
var kms_2 = require_kms2();
|
|
96631
96814
|
Object.defineProperty(exports, "createKmsProvider", { enumerable: true, get: function() {
|
|
96632
96815
|
return kms_2.createKmsProvider;
|
|
96633
96816
|
} });
|
|
@@ -97715,11 +97898,23 @@ function sym(key) {
|
|
|
97715
97898
|
}
|
|
97716
97899
|
|
|
97717
97900
|
// src/output/formatter.ts
|
|
97901
|
+
var _jsonMode = false;
|
|
97902
|
+
var _yesMode = false;
|
|
97903
|
+
function setJsonMode(json) {
|
|
97904
|
+
_jsonMode = json;
|
|
97905
|
+
}
|
|
97906
|
+
function isJsonMode() {
|
|
97907
|
+
return _jsonMode;
|
|
97908
|
+
}
|
|
97909
|
+
function setYesMode(yes) {
|
|
97910
|
+
_yesMode = yes;
|
|
97911
|
+
}
|
|
97718
97912
|
function color(fn, str2) {
|
|
97719
97913
|
return isPlainMode() ? str2 : fn(str2);
|
|
97720
97914
|
}
|
|
97721
97915
|
var formatter = {
|
|
97722
97916
|
success(message) {
|
|
97917
|
+
if (_jsonMode) return;
|
|
97723
97918
|
const icon = sym("success");
|
|
97724
97919
|
process.stdout.write(color(import_picocolors.default.green, `${icon} ${message}`) + "\n");
|
|
97725
97920
|
},
|
|
@@ -97736,15 +97931,18 @@ var formatter = {
|
|
|
97736
97931
|
process.stderr.write(color(import_picocolors.default.yellow, `${icon} ${message}`) + "\n");
|
|
97737
97932
|
},
|
|
97738
97933
|
info(message) {
|
|
97934
|
+
if (_jsonMode) return;
|
|
97739
97935
|
const icon = sym("info");
|
|
97740
97936
|
process.stdout.write(color(import_picocolors.default.blue, `${icon} ${message}`) + "\n");
|
|
97741
97937
|
},
|
|
97742
97938
|
hint(message) {
|
|
97939
|
+
if (_jsonMode) return;
|
|
97743
97940
|
const icon = sym("arrow");
|
|
97744
97941
|
process.stdout.write(`${icon} ${message}
|
|
97745
97942
|
`);
|
|
97746
97943
|
},
|
|
97747
97944
|
keyValue(key, value) {
|
|
97945
|
+
if (_jsonMode) return;
|
|
97748
97946
|
const icon = sym("key");
|
|
97749
97947
|
const arrow = sym("arrow");
|
|
97750
97948
|
const prefix2 = icon ? `${icon} ` : "";
|
|
@@ -97752,30 +97950,38 @@ var formatter = {
|
|
|
97752
97950
|
`);
|
|
97753
97951
|
},
|
|
97754
97952
|
pendingItem(key, days) {
|
|
97953
|
+
if (_jsonMode) return;
|
|
97755
97954
|
const icon = sym("pending");
|
|
97756
97955
|
const prefix2 = icon ? `${icon} ` : "[pending] ";
|
|
97757
97956
|
process.stdout.write(`${prefix2}${key} \u2014 ${days} day${days !== 1 ? "s" : ""} pending
|
|
97758
97957
|
`);
|
|
97759
97958
|
},
|
|
97760
97959
|
recipientItem(label2, keyPreview2) {
|
|
97960
|
+
if (_jsonMode) return;
|
|
97761
97961
|
const icon = sym("recipient");
|
|
97762
97962
|
const prefix2 = icon ? `${icon} ` : "";
|
|
97763
97963
|
process.stdout.write(`${prefix2}${label2.padEnd(15)}${keyPreview2}
|
|
97764
97964
|
`);
|
|
97765
97965
|
},
|
|
97766
97966
|
section(label2) {
|
|
97967
|
+
if (_jsonMode) return;
|
|
97767
97968
|
process.stdout.write(`
|
|
97768
97969
|
${label2}
|
|
97769
97970
|
|
|
97770
97971
|
`);
|
|
97771
97972
|
},
|
|
97772
97973
|
print(message) {
|
|
97974
|
+
if (_jsonMode) return;
|
|
97773
97975
|
process.stdout.write(message + "\n");
|
|
97774
97976
|
},
|
|
97775
97977
|
raw(message) {
|
|
97776
97978
|
process.stdout.write(message);
|
|
97777
97979
|
},
|
|
97980
|
+
json(data) {
|
|
97981
|
+
process.stdout.write(JSON.stringify(data) + "\n");
|
|
97982
|
+
},
|
|
97778
97983
|
table(rows, columns) {
|
|
97984
|
+
if (_jsonMode) return;
|
|
97779
97985
|
const widths = columns.map((col, i) => {
|
|
97780
97986
|
const maxDataWidth = rows.reduce(
|
|
97781
97987
|
(max, row) => Math.max(max, stripAnsi(row[i] ?? "").length),
|
|
@@ -97792,6 +97998,10 @@ ${label2}
|
|
|
97792
97998
|
}
|
|
97793
97999
|
},
|
|
97794
98000
|
async confirm(prompt) {
|
|
98001
|
+
if (_yesMode) return true;
|
|
98002
|
+
if (_jsonMode) {
|
|
98003
|
+
throw new Error("--json requires --yes for destructive operations");
|
|
98004
|
+
}
|
|
97795
98005
|
const rl = readline.createInterface({
|
|
97796
98006
|
input: process.stdin,
|
|
97797
98007
|
output: process.stderr
|
|
@@ -97826,6 +98036,9 @@ ${label2}
|
|
|
97826
98036
|
`);
|
|
97827
98037
|
},
|
|
97828
98038
|
async secretPrompt(prompt) {
|
|
98039
|
+
if (_jsonMode) {
|
|
98040
|
+
throw new Error("--json mode requires value on the command line (no interactive prompt)");
|
|
98041
|
+
}
|
|
97829
98042
|
return new Promise((resolve7, reject) => {
|
|
97830
98043
|
process.stderr.write(color(import_picocolors.default.cyan, `${prompt}: `));
|
|
97831
98044
|
if (process.stdin.isTTY) {
|
|
@@ -97868,7 +98081,14 @@ function pad(str2, width) {
|
|
|
97868
98081
|
}
|
|
97869
98082
|
|
|
97870
98083
|
// src/handle-error.ts
|
|
98084
|
+
function exitJsonError(message) {
|
|
98085
|
+
formatter.json({ error: true, message });
|
|
98086
|
+
process.exit(1);
|
|
98087
|
+
}
|
|
97871
98088
|
function handleCommandError(err) {
|
|
98089
|
+
if (isJsonMode()) {
|
|
98090
|
+
exitJsonError(err.message);
|
|
98091
|
+
}
|
|
97872
98092
|
if (err instanceof SopsMissingError || err instanceof SopsVersionError) {
|
|
97873
98093
|
formatter.formatDependencyError(err);
|
|
97874
98094
|
} else {
|
|
@@ -98368,7 +98588,7 @@ async function handleSecondDevOnboarding(repoRoot, clefConfigPath, deps2, option
|
|
|
98368
98588
|
"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."
|
|
98369
98589
|
);
|
|
98370
98590
|
let keyPath;
|
|
98371
|
-
if (options.nonInteractive || !process.stdin.isTTY) {
|
|
98591
|
+
if (options.nonInteractive || isJsonMode() || !process.stdin.isTTY) {
|
|
98372
98592
|
keyPath = process.env.CLEF_AGE_KEY_FILE || defaultAgeKeyPath(label2);
|
|
98373
98593
|
keyPath = path23.resolve(keyPath);
|
|
98374
98594
|
} else {
|
|
@@ -98401,6 +98621,10 @@ async function handleSecondDevOnboarding(repoRoot, clefConfigPath, deps2, option
|
|
|
98401
98621
|
formatter.success("Created .clef/.gitignore");
|
|
98402
98622
|
}
|
|
98403
98623
|
formatter.success(`Key label: ${label2}`);
|
|
98624
|
+
if (isJsonMode()) {
|
|
98625
|
+
formatter.json({ action: "onboarded", manifest: "clef.yaml", config: ".clef/config.yaml" });
|
|
98626
|
+
return;
|
|
98627
|
+
}
|
|
98404
98628
|
formatter.section("Next steps:");
|
|
98405
98629
|
formatter.hint("clef recipients request \u2014 request access to encrypted secrets");
|
|
98406
98630
|
formatter.hint("clef update \u2014 scaffold new environments");
|
|
@@ -98411,7 +98635,7 @@ async function handleFullSetup(repoRoot, manifestPath, clefConfigPath, deps2, op
|
|
|
98411
98635
|
let namespaces = options.namespaces ? options.namespaces.split(",").map((s) => s.trim()) : [];
|
|
98412
98636
|
const backend = options.backend ?? "age";
|
|
98413
98637
|
let secretsDir = options.secretsDir ?? "secrets";
|
|
98414
|
-
if (!options.nonInteractive && process.stdin.isTTY) {
|
|
98638
|
+
if (!options.nonInteractive && !isJsonMode() && process.stdin.isTTY) {
|
|
98415
98639
|
const envAnswer = await promptWithDefault(
|
|
98416
98640
|
"Environments (comma-separated)",
|
|
98417
98641
|
environments.join(",")
|
|
@@ -98466,7 +98690,7 @@ async function handleFullSetup(repoRoot, manifestPath, clefConfigPath, deps2, op
|
|
|
98466
98690
|
formatter.warn(
|
|
98467
98691
|
"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."
|
|
98468
98692
|
);
|
|
98469
|
-
if (!options.nonInteractive && process.stdin.isTTY) {
|
|
98693
|
+
if (!options.nonInteractive && !isJsonMode() && process.stdin.isTTY) {
|
|
98470
98694
|
const confirmed = await formatter.confirm("Write the private key to the filesystem?");
|
|
98471
98695
|
if (!confirmed) {
|
|
98472
98696
|
formatter.error(
|
|
@@ -98477,7 +98701,7 @@ async function handleFullSetup(repoRoot, manifestPath, clefConfigPath, deps2, op
|
|
|
98477
98701
|
}
|
|
98478
98702
|
}
|
|
98479
98703
|
let keyPath;
|
|
98480
|
-
if (options.nonInteractive || !process.stdin.isTTY) {
|
|
98704
|
+
if (options.nonInteractive || isJsonMode() || !process.stdin.isTTY) {
|
|
98481
98705
|
keyPath = defaultAgeKeyPath(label2);
|
|
98482
98706
|
if (await isInsideAnyGitRepo(path23.resolve(keyPath))) {
|
|
98483
98707
|
throw new Error(
|
|
@@ -98597,6 +98821,17 @@ async function handleFullSetup(repoRoot, manifestPath, clefConfigPath, deps2, op
|
|
|
98597
98821
|
formatter.print(" clef config set analytics false (permanent)\n");
|
|
98598
98822
|
} catch {
|
|
98599
98823
|
}
|
|
98824
|
+
if (isJsonMode()) {
|
|
98825
|
+
formatter.json({
|
|
98826
|
+
action: "initialized",
|
|
98827
|
+
manifest: "clef.yaml",
|
|
98828
|
+
environments: manifest.environments.map((e) => e.name),
|
|
98829
|
+
namespaces: manifest.namespaces.map((n) => n.name),
|
|
98830
|
+
backend,
|
|
98831
|
+
scaffolded: scaffoldedCount
|
|
98832
|
+
});
|
|
98833
|
+
return;
|
|
98834
|
+
}
|
|
98600
98835
|
formatter.section("Next steps:");
|
|
98601
98836
|
formatter.hint("clef set <namespace>/<env> <KEY> <value> \u2014 add a secret");
|
|
98602
98837
|
formatter.hint("clef scan \u2014 check for existing plaintext secrets");
|
|
@@ -98857,7 +99092,9 @@ function registerGetCommand(program3, deps2) {
|
|
|
98857
99092
|
return;
|
|
98858
99093
|
}
|
|
98859
99094
|
const val = decrypted.values[key];
|
|
98860
|
-
if (
|
|
99095
|
+
if (isJsonMode()) {
|
|
99096
|
+
formatter.json({ key, value: val, namespace, environment });
|
|
99097
|
+
} else if (opts2.raw) {
|
|
98861
99098
|
formatter.raw(val);
|
|
98862
99099
|
} else {
|
|
98863
99100
|
const copied = copyToClipboard(val);
|
|
@@ -98962,6 +99199,16 @@ function registerSetCommand(program3, deps2) {
|
|
|
98962
99199
|
pendingErrors.push(env.name);
|
|
98963
99200
|
}
|
|
98964
99201
|
}
|
|
99202
|
+
if (isJsonMode()) {
|
|
99203
|
+
formatter.json({
|
|
99204
|
+
key,
|
|
99205
|
+
namespace: namespace2,
|
|
99206
|
+
environments: manifest.environments.map((e) => e.name),
|
|
99207
|
+
action: "created",
|
|
99208
|
+
pending: true
|
|
99209
|
+
});
|
|
99210
|
+
return;
|
|
99211
|
+
}
|
|
98965
99212
|
formatter.success(
|
|
98966
99213
|
`'${key}' set in ${namespace2} across all environments ${sym("locked")}`
|
|
98967
99214
|
);
|
|
@@ -98985,6 +99232,16 @@ function registerSetCommand(program3, deps2) {
|
|
|
98985
99232
|
} catch {
|
|
98986
99233
|
}
|
|
98987
99234
|
}
|
|
99235
|
+
if (isJsonMode()) {
|
|
99236
|
+
formatter.json({
|
|
99237
|
+
key,
|
|
99238
|
+
namespace: namespace2,
|
|
99239
|
+
environments: manifest.environments.map((e) => e.name),
|
|
99240
|
+
action: "created",
|
|
99241
|
+
pending: false
|
|
99242
|
+
});
|
|
99243
|
+
return;
|
|
99244
|
+
}
|
|
98988
99245
|
formatter.success(`'${key}' set in ${namespace2} across all environments`);
|
|
98989
99246
|
formatter.hint(`git add ${namespace2}/ # stage all updated files`);
|
|
98990
99247
|
}
|
|
@@ -99048,6 +99305,10 @@ function registerSetCommand(program3, deps2) {
|
|
|
99048
99305
|
process.exit(1);
|
|
99049
99306
|
return;
|
|
99050
99307
|
}
|
|
99308
|
+
if (isJsonMode()) {
|
|
99309
|
+
formatter.json({ key, namespace, environment, action: "created", pending: true });
|
|
99310
|
+
return;
|
|
99311
|
+
}
|
|
99051
99312
|
formatter.success(`${key} set in ${namespace}/${environment} ${sym("locked")}`);
|
|
99052
99313
|
formatter.print(
|
|
99053
99314
|
` ${sym("pending")} Marked as pending \u2014 replace with a real value before deploying`
|
|
@@ -99062,6 +99323,10 @@ function registerSetCommand(program3, deps2) {
|
|
|
99062
99323
|
The value is saved. Run clef lint to check for stale pending markers.`
|
|
99063
99324
|
);
|
|
99064
99325
|
}
|
|
99326
|
+
if (isJsonMode()) {
|
|
99327
|
+
formatter.json({ key, namespace, environment, action: "created", pending: false });
|
|
99328
|
+
return;
|
|
99329
|
+
}
|
|
99065
99330
|
formatter.success(`${key} set in ${namespace}/${environment}`);
|
|
99066
99331
|
formatter.hint(
|
|
99067
99332
|
`Commit: git add ${manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", environment)}`
|
|
@@ -99130,7 +99395,10 @@ function registerCompareCommand(program3, deps2) {
|
|
|
99130
99395
|
compareBuf.copy(paddedCompare);
|
|
99131
99396
|
const timingEqual = crypto5.timingSafeEqual(paddedStored, paddedCompare);
|
|
99132
99397
|
const match = storedBuf.length === compareBuf.length && timingEqual;
|
|
99133
|
-
if (
|
|
99398
|
+
if (isJsonMode()) {
|
|
99399
|
+
formatter.json({ match, key, namespace, environment });
|
|
99400
|
+
if (!match) process.exit(1);
|
|
99401
|
+
} else if (match) {
|
|
99134
99402
|
formatter.success(`${key} ${sym("arrow")} values match`);
|
|
99135
99403
|
} else {
|
|
99136
99404
|
formatter.failure(`${key} ${sym("arrow")} values do not match`);
|
|
@@ -99178,6 +99446,15 @@ Type the key name to confirm:`
|
|
|
99178
99446
|
}
|
|
99179
99447
|
const bulkOps = new BulkOps();
|
|
99180
99448
|
await bulkOps.deleteAcrossEnvironments(namespace, key, manifest, sopsClient, repoRoot);
|
|
99449
|
+
if (isJsonMode()) {
|
|
99450
|
+
formatter.json({
|
|
99451
|
+
key,
|
|
99452
|
+
namespace,
|
|
99453
|
+
environments: manifest.environments.map((e) => e.name),
|
|
99454
|
+
action: "deleted"
|
|
99455
|
+
});
|
|
99456
|
+
return;
|
|
99457
|
+
}
|
|
99181
99458
|
formatter.success(`Deleted '${key}' from ${namespace} in all environments`);
|
|
99182
99459
|
} else {
|
|
99183
99460
|
const [namespace, environment] = parseTarget(target);
|
|
@@ -99216,6 +99493,10 @@ Type the key name to confirm:`
|
|
|
99216
99493
|
`Key deleted but pending metadata could not be cleaned up. Run clef lint to verify.`
|
|
99217
99494
|
);
|
|
99218
99495
|
}
|
|
99496
|
+
if (isJsonMode()) {
|
|
99497
|
+
formatter.json({ key, namespace, environment, action: "deleted" });
|
|
99498
|
+
return;
|
|
99499
|
+
}
|
|
99219
99500
|
formatter.success(`Deleted '${key}' from ${namespace}/${environment}`);
|
|
99220
99501
|
formatter.hint(
|
|
99221
99502
|
`Commit: git add ${manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", environment)}`
|
|
@@ -99234,10 +99515,11 @@ Type the key name to confirm:`
|
|
|
99234
99515
|
var import_picocolors2 = __toESM(require_picocolors());
|
|
99235
99516
|
init_src();
|
|
99236
99517
|
import * as path33 from "path";
|
|
99518
|
+
var MASKED = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
99237
99519
|
function registerDiffCommand(program3, deps2) {
|
|
99238
99520
|
program3.command("diff <namespace> <env-a> <env-b>").description(
|
|
99239
99521
|
"Compare secrets between two environments for a namespace.\n\nExit codes:\n 0 No differences\n 1 Differences found"
|
|
99240
|
-
).option("--show-identical", "Include identical keys in the output").option("--show-values", "Show plaintext values instead of masking them").
|
|
99522
|
+
).option("--show-identical", "Include identical keys in the output").option("--show-values", "Show plaintext values instead of masking them").action(
|
|
99241
99523
|
async (namespace, envA, envB, options) => {
|
|
99242
99524
|
try {
|
|
99243
99525
|
const repoRoot = program3.opts().dir || process.cwd();
|
|
@@ -99264,19 +99546,20 @@ function registerDiffCommand(program3, deps2) {
|
|
|
99264
99546
|
formatter.warn("Warning: printing plaintext values for protected environment.");
|
|
99265
99547
|
}
|
|
99266
99548
|
}
|
|
99267
|
-
if (
|
|
99268
|
-
const
|
|
99549
|
+
if (isJsonMode()) {
|
|
99550
|
+
const jsonData = options.showValues ? result : {
|
|
99269
99551
|
...result,
|
|
99270
99552
|
rows: result.rows.map((r) => ({
|
|
99271
99553
|
...r,
|
|
99272
|
-
valueA: r.valueA !== null ?
|
|
99273
|
-
valueB: r.valueB !== null ?
|
|
99554
|
+
valueA: r.valueA !== null ? MASKED : null,
|
|
99555
|
+
valueB: r.valueB !== null ? MASKED : null,
|
|
99274
99556
|
masked: true
|
|
99275
99557
|
}))
|
|
99276
99558
|
};
|
|
99277
|
-
formatter.
|
|
99559
|
+
formatter.json(jsonData);
|
|
99278
99560
|
const hasDiffs2 = result.rows.some((r) => r.status !== "identical");
|
|
99279
99561
|
process.exit(hasDiffs2 ? 1 : 0);
|
|
99562
|
+
return;
|
|
99280
99563
|
}
|
|
99281
99564
|
formatDiffOutput(
|
|
99282
99565
|
result,
|
|
@@ -99296,7 +99579,6 @@ function registerDiffCommand(program3, deps2) {
|
|
|
99296
99579
|
}
|
|
99297
99580
|
);
|
|
99298
99581
|
}
|
|
99299
|
-
var MASKED = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
|
|
99300
99582
|
function formatDiffOutput(result, envA, envB, showIdentical, showValues) {
|
|
99301
99583
|
const filteredRows = showIdentical ? result.rows : result.rows.filter((r) => r.status !== "identical");
|
|
99302
99584
|
if (filteredRows.length === 0) {
|
|
@@ -99578,7 +99860,7 @@ async function fetchCheckpoint(config) {
|
|
|
99578
99860
|
}
|
|
99579
99861
|
|
|
99580
99862
|
// package.json
|
|
99581
|
-
var version2 = "0.1.
|
|
99863
|
+
var version2 = "0.1.13-beta.92";
|
|
99582
99864
|
var package_default = {
|
|
99583
99865
|
name: "@clef-sh/cli",
|
|
99584
99866
|
version: version2,
|
|
@@ -99649,7 +99931,7 @@ var package_default = {
|
|
|
99649
99931
|
function registerLintCommand(program3, deps2) {
|
|
99650
99932
|
program3.command("lint").description(
|
|
99651
99933
|
"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"
|
|
99652
|
-
).option("--fix", "Auto-fix safe issues (scaffold missing files)").option("--
|
|
99934
|
+
).option("--fix", "Auto-fix safe issues (scaffold missing files)").option("--push", "Push results as OTLP to CLEF_TELEMETRY_URL").action(async (options) => {
|
|
99653
99935
|
try {
|
|
99654
99936
|
const repoRoot = program3.opts().dir || process.cwd();
|
|
99655
99937
|
const parser = new ManifestParser();
|
|
@@ -99684,8 +99966,8 @@ function registerLintCommand(program3, deps2) {
|
|
|
99684
99966
|
await pushOtlp(payload, config);
|
|
99685
99967
|
formatter.success("Lint results pushed to telemetry endpoint.");
|
|
99686
99968
|
}
|
|
99687
|
-
if (
|
|
99688
|
-
formatter.
|
|
99969
|
+
if (isJsonMode()) {
|
|
99970
|
+
formatter.json(result);
|
|
99689
99971
|
const hasErrors2 = result.issues.some((i) => i.severity === "error");
|
|
99690
99972
|
process.exit(hasErrors2 ? 1 : 0);
|
|
99691
99973
|
return;
|
|
@@ -99794,6 +100076,10 @@ function registerRotateCommand(program3, deps2) {
|
|
|
99794
100076
|
const relativeFile = manifest.file_pattern.replace("{namespace}", namespace).replace("{environment}", environment);
|
|
99795
100077
|
formatter.print(`${sym("working")} Rotating ${namespace}/${environment}...`);
|
|
99796
100078
|
await sopsClient.reEncrypt(filePath, options.newKey);
|
|
100079
|
+
if (isJsonMode()) {
|
|
100080
|
+
formatter.json({ namespace, environment, file: relativeFile, action: "rotated" });
|
|
100081
|
+
return;
|
|
100082
|
+
}
|
|
99797
100083
|
formatter.success(`Rotated. New values encrypted. ${sym("locked")}`);
|
|
99798
100084
|
formatter.hint(
|
|
99799
100085
|
`git add ${relativeFile} && git commit -m "rotate: ${namespace}/${environment}"`
|
|
@@ -99839,15 +100125,28 @@ function registerHooksCommand(program3, deps2) {
|
|
|
99839
100125
|
}
|
|
99840
100126
|
const git = new GitIntegration(deps2.runner);
|
|
99841
100127
|
await git.installPreCommitHook(repoRoot);
|
|
100128
|
+
let mergeDriverOk = false;
|
|
100129
|
+
try {
|
|
100130
|
+
await git.installMergeDriver(repoRoot);
|
|
100131
|
+
mergeDriverOk = true;
|
|
100132
|
+
} catch {
|
|
100133
|
+
}
|
|
100134
|
+
if (isJsonMode()) {
|
|
100135
|
+
formatter.json({
|
|
100136
|
+
preCommitHook: true,
|
|
100137
|
+
mergeDriver: mergeDriverOk,
|
|
100138
|
+
hookPath
|
|
100139
|
+
});
|
|
100140
|
+
return;
|
|
100141
|
+
}
|
|
99842
100142
|
formatter.success("Pre-commit hook installed");
|
|
99843
100143
|
formatter.print(` ${sym("pending")} ${hookPath}`);
|
|
99844
100144
|
formatter.hint(
|
|
99845
100145
|
"Hook checks SOPS metadata on staged .enc files and runs: clef scan --staged"
|
|
99846
100146
|
);
|
|
99847
|
-
|
|
99848
|
-
await git.installMergeDriver(repoRoot);
|
|
100147
|
+
if (mergeDriverOk) {
|
|
99849
100148
|
formatter.success("SOPS merge driver configured");
|
|
99850
|
-
}
|
|
100149
|
+
} else {
|
|
99851
100150
|
formatter.warn("Could not configure SOPS merge driver. Run inside a git repository.");
|
|
99852
100151
|
}
|
|
99853
100152
|
} catch (err) {
|
|
@@ -100146,6 +100445,14 @@ Usage: clef export payments/production --format env`
|
|
|
100146
100445
|
);
|
|
100147
100446
|
try {
|
|
100148
100447
|
const decrypted = await sopsClient.decrypt(filePath);
|
|
100448
|
+
if (isJsonMode()) {
|
|
100449
|
+
const pairs = Object.entries(decrypted.values).map(([k, v]) => ({
|
|
100450
|
+
key: k,
|
|
100451
|
+
value: v
|
|
100452
|
+
}));
|
|
100453
|
+
formatter.json({ pairs, namespace, environment });
|
|
100454
|
+
return;
|
|
100455
|
+
}
|
|
100149
100456
|
const consumption = new ConsumptionClient();
|
|
100150
100457
|
const output = consumption.formatExport(decrypted, "env", !options.export);
|
|
100151
100458
|
if (options.raw) {
|
|
@@ -100186,7 +100493,7 @@ import * as path41 from "path";
|
|
|
100186
100493
|
function registerDoctorCommand(program3, deps2) {
|
|
100187
100494
|
program3.command("doctor").description(
|
|
100188
100495
|
"Check your environment for required dependencies and configuration.\n\nExit codes:\n 0 All checks pass\n 1 One or more checks failed"
|
|
100189
|
-
).option("--
|
|
100496
|
+
).option("--fix", "Attempt to auto-fix issues").action(async (options) => {
|
|
100190
100497
|
const repoRoot = program3.opts().dir || process.cwd();
|
|
100191
100498
|
const clefVersion = program3.version() ?? "unknown";
|
|
100192
100499
|
const checks = [];
|
|
@@ -100273,7 +100580,7 @@ function registerDoctorCommand(program3, deps2) {
|
|
|
100273
100580
|
formatter.warn("--fix cannot resolve these issues automatically.");
|
|
100274
100581
|
}
|
|
100275
100582
|
}
|
|
100276
|
-
if (
|
|
100583
|
+
if (isJsonMode()) {
|
|
100277
100584
|
const json = {
|
|
100278
100585
|
clef: { version: clefVersion, ok: true },
|
|
100279
100586
|
sops: {
|
|
@@ -100303,7 +100610,7 @@ function registerDoctorCommand(program3, deps2) {
|
|
|
100303
100610
|
ok: mergeDriverOk
|
|
100304
100611
|
}
|
|
100305
100612
|
};
|
|
100306
|
-
formatter.
|
|
100613
|
+
formatter.json(json);
|
|
100307
100614
|
const hasFailures = checks.some((c) => !c.ok);
|
|
100308
100615
|
process.exit(hasFailures ? 1 : 0);
|
|
100309
100616
|
return;
|
|
@@ -100437,6 +100744,11 @@ function registerUpdateCommand(program3, deps2) {
|
|
|
100437
100744
|
);
|
|
100438
100745
|
}
|
|
100439
100746
|
}
|
|
100747
|
+
if (isJsonMode()) {
|
|
100748
|
+
formatter.json({ scaffolded: scaffoldedCount, failed: failedCount });
|
|
100749
|
+
process.exit(failedCount > 0 ? 1 : 0);
|
|
100750
|
+
return;
|
|
100751
|
+
}
|
|
100440
100752
|
if (scaffoldedCount > 0) {
|
|
100441
100753
|
formatter.success(`Scaffolded ${scaffoldedCount} encrypted file(s)`);
|
|
100442
100754
|
}
|
|
@@ -100462,69 +100774,57 @@ function registerScanCommand(program3, deps2) {
|
|
|
100462
100774
|
"--severity <level>",
|
|
100463
100775
|
"Detection level: all (patterns+entropy) or high (patterns only)",
|
|
100464
100776
|
"all"
|
|
100465
|
-
).
|
|
100466
|
-
|
|
100467
|
-
|
|
100468
|
-
|
|
100469
|
-
|
|
100470
|
-
|
|
100471
|
-
|
|
100472
|
-
|
|
100473
|
-
|
|
100474
|
-
|
|
100475
|
-
|
|
100476
|
-
formatter.error(err.message);
|
|
100477
|
-
}
|
|
100478
|
-
process.exit(2);
|
|
100479
|
-
return;
|
|
100480
|
-
}
|
|
100481
|
-
if (options.severity && options.severity !== "all" && options.severity !== "high") {
|
|
100482
|
-
formatter.error(`Invalid severity '${options.severity}'. Must be 'all' or 'high'.`);
|
|
100483
|
-
process.exit(2);
|
|
100484
|
-
return;
|
|
100485
|
-
}
|
|
100486
|
-
const severity = options.severity === "high" ? "high" : "all";
|
|
100487
|
-
const scanRunner = new ScanRunner(deps2.runner);
|
|
100488
|
-
if (!options.json) {
|
|
100489
|
-
formatter.print(import_picocolors4.default.dim("Scanning repository for unencrypted secrets..."));
|
|
100490
|
-
}
|
|
100491
|
-
let result;
|
|
100492
|
-
try {
|
|
100493
|
-
result = await scanRunner.scan(repoRoot, manifest, {
|
|
100494
|
-
stagedOnly: options.staged,
|
|
100495
|
-
paths: paths.length > 0 ? paths : void 0,
|
|
100496
|
-
severity
|
|
100497
|
-
});
|
|
100498
|
-
} catch (err) {
|
|
100499
|
-
formatter.error(`Scan failed: ${err.message}`);
|
|
100500
|
-
process.exit(2);
|
|
100501
|
-
return;
|
|
100502
|
-
}
|
|
100503
|
-
if (options.json) {
|
|
100504
|
-
const totalIssues = result.matches.length + result.unencryptedMatrixFiles.length;
|
|
100505
|
-
formatter.raw(
|
|
100506
|
-
JSON.stringify(
|
|
100507
|
-
{
|
|
100508
|
-
matches: result.matches,
|
|
100509
|
-
unencryptedMatrixFiles: result.unencryptedMatrixFiles,
|
|
100510
|
-
filesScanned: result.filesScanned,
|
|
100511
|
-
filesSkipped: result.filesSkipped,
|
|
100512
|
-
durationMs: result.durationMs,
|
|
100513
|
-
summary: `${totalIssues} issue${totalIssues !== 1 ? "s" : ""} found`
|
|
100514
|
-
},
|
|
100515
|
-
null,
|
|
100516
|
-
2
|
|
100517
|
-
) + "\n"
|
|
100518
|
-
);
|
|
100519
|
-
const hasIssues2 = result.matches.length > 0 || result.unencryptedMatrixFiles.length > 0;
|
|
100520
|
-
process.exit(hasIssues2 ? 1 : 0);
|
|
100521
|
-
return;
|
|
100777
|
+
).action(async (paths, options) => {
|
|
100778
|
+
const repoRoot = program3.opts().dir || process.cwd();
|
|
100779
|
+
let manifest;
|
|
100780
|
+
try {
|
|
100781
|
+
const parser = new ManifestParser();
|
|
100782
|
+
manifest = parser.parse(path43.join(repoRoot, "clef.yaml"));
|
|
100783
|
+
} catch (err) {
|
|
100784
|
+
if (err instanceof ManifestValidationError || err.message?.includes("clef.yaml")) {
|
|
100785
|
+
formatter.error("No clef.yaml found. Run 'clef init' to set up this repository.");
|
|
100786
|
+
} else {
|
|
100787
|
+
formatter.error(err.message);
|
|
100522
100788
|
}
|
|
100523
|
-
|
|
100524
|
-
|
|
100525
|
-
process.exit(hasIssues ? 1 : 0);
|
|
100789
|
+
process.exit(2);
|
|
100790
|
+
return;
|
|
100526
100791
|
}
|
|
100527
|
-
|
|
100792
|
+
if (options.severity && options.severity !== "all" && options.severity !== "high") {
|
|
100793
|
+
formatter.error(`Invalid severity '${options.severity}'. Must be 'all' or 'high'.`);
|
|
100794
|
+
process.exit(2);
|
|
100795
|
+
return;
|
|
100796
|
+
}
|
|
100797
|
+
const severity = options.severity === "high" ? "high" : "all";
|
|
100798
|
+
const scanRunner = new ScanRunner(deps2.runner);
|
|
100799
|
+
formatter.print(import_picocolors4.default.dim("Scanning repository for unencrypted secrets..."));
|
|
100800
|
+
let result;
|
|
100801
|
+
try {
|
|
100802
|
+
result = await scanRunner.scan(repoRoot, manifest, {
|
|
100803
|
+
stagedOnly: options.staged,
|
|
100804
|
+
paths: paths.length > 0 ? paths : void 0,
|
|
100805
|
+
severity
|
|
100806
|
+
});
|
|
100807
|
+
} catch (err) {
|
|
100808
|
+
formatter.error(`Scan failed: ${err.message}`);
|
|
100809
|
+
process.exit(2);
|
|
100810
|
+
return;
|
|
100811
|
+
}
|
|
100812
|
+
if (isJsonMode()) {
|
|
100813
|
+
formatter.json({
|
|
100814
|
+
matches: result.matches,
|
|
100815
|
+
unencryptedMatrixFiles: result.unencryptedMatrixFiles,
|
|
100816
|
+
filesScanned: result.filesScanned,
|
|
100817
|
+
filesSkipped: result.filesSkipped,
|
|
100818
|
+
durationMs: result.durationMs
|
|
100819
|
+
});
|
|
100820
|
+
const hasIssues2 = result.matches.length > 0 || result.unencryptedMatrixFiles.length > 0;
|
|
100821
|
+
process.exit(hasIssues2 ? 1 : 0);
|
|
100822
|
+
return;
|
|
100823
|
+
}
|
|
100824
|
+
formatScanOutput(result);
|
|
100825
|
+
const hasIssues = result.matches.length > 0 || result.unencryptedMatrixFiles.length > 0;
|
|
100826
|
+
process.exit(hasIssues ? 1 : 0);
|
|
100827
|
+
});
|
|
100528
100828
|
}
|
|
100529
100829
|
function formatScanOutput(result) {
|
|
100530
100830
|
const totalIssues = result.matches.length + result.unencryptedMatrixFiles.length;
|
|
@@ -100689,6 +100989,17 @@ function registerImportCommand(program3, deps2) {
|
|
|
100689
100989
|
process.exit(2);
|
|
100690
100990
|
return;
|
|
100691
100991
|
}
|
|
100992
|
+
if (isJsonMode()) {
|
|
100993
|
+
formatter.json({
|
|
100994
|
+
imported: result.imported,
|
|
100995
|
+
skipped: result.skipped,
|
|
100996
|
+
failed: result.failed,
|
|
100997
|
+
warnings: result.warnings,
|
|
100998
|
+
dryRun: opts2.dryRun
|
|
100999
|
+
});
|
|
101000
|
+
process.exit(result.failed.length > 0 ? 1 : 0);
|
|
101001
|
+
return;
|
|
101002
|
+
}
|
|
100692
101003
|
for (const warning of result.warnings) {
|
|
100693
101004
|
formatter.print(` ${sym("warning")} ${warning}`);
|
|
100694
101005
|
}
|
|
@@ -100748,6 +101059,7 @@ init_src();
|
|
|
100748
101059
|
import * as path45 from "path";
|
|
100749
101060
|
import * as readline4 from "readline";
|
|
100750
101061
|
function waitForEnter(message) {
|
|
101062
|
+
if (isJsonMode()) return Promise.resolve();
|
|
100751
101063
|
return new Promise((resolve7) => {
|
|
100752
101064
|
const rl = readline4.createInterface({
|
|
100753
101065
|
input: process.stdin,
|
|
@@ -100781,6 +101093,10 @@ function registerRecipientsCommand(program3, deps2) {
|
|
|
100781
101093
|
const sopsClient = await createSopsClient(repoRoot, deps2.runner);
|
|
100782
101094
|
const recipientManager = new RecipientManager(sopsClient, matrixManager);
|
|
100783
101095
|
const recipients = await recipientManager.list(manifest, repoRoot, opts2.environment);
|
|
101096
|
+
if (isJsonMode()) {
|
|
101097
|
+
formatter.json(recipients);
|
|
101098
|
+
return;
|
|
101099
|
+
}
|
|
100784
101100
|
if (recipients.length === 0) {
|
|
100785
101101
|
const scope2 = opts2.environment ? ` for environment '${opts2.environment}'` : "";
|
|
100786
101102
|
formatter.info(`No recipients configured${scope2}.`);
|
|
@@ -100811,7 +101127,7 @@ function registerRecipientsCommand(program3, deps2) {
|
|
|
100811
101127
|
return;
|
|
100812
101128
|
}
|
|
100813
101129
|
const normalizedKey = validation.key;
|
|
100814
|
-
const
|
|
101130
|
+
const result = await executeRecipientAdd(
|
|
100815
101131
|
repoRoot,
|
|
100816
101132
|
program3,
|
|
100817
101133
|
deps2,
|
|
@@ -100819,11 +101135,21 @@ function registerRecipientsCommand(program3, deps2) {
|
|
|
100819
101135
|
opts2.label,
|
|
100820
101136
|
opts2.environment
|
|
100821
101137
|
);
|
|
100822
|
-
if (
|
|
100823
|
-
|
|
100824
|
-
|
|
100825
|
-
|
|
100826
|
-
|
|
101138
|
+
if (result) {
|
|
101139
|
+
if (isJsonMode()) {
|
|
101140
|
+
formatter.json({
|
|
101141
|
+
action: "added",
|
|
101142
|
+
key: normalizedKey,
|
|
101143
|
+
label: opts2.label || keyPreview(normalizedKey),
|
|
101144
|
+
environment: opts2.environment,
|
|
101145
|
+
reEncryptedFiles: result.reEncryptedFiles.length
|
|
101146
|
+
});
|
|
101147
|
+
} else {
|
|
101148
|
+
const label2 = opts2.label || keyPreview(normalizedKey);
|
|
101149
|
+
formatter.hint(
|
|
101150
|
+
`git add clef.yaml && git add -A && git commit -m "add recipient: ${label2} [${opts2.environment}]"`
|
|
101151
|
+
);
|
|
101152
|
+
}
|
|
100827
101153
|
}
|
|
100828
101154
|
} catch (err) {
|
|
100829
101155
|
handleCommandError(err);
|
|
@@ -100919,6 +101245,16 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`
|
|
|
100919
101245
|
const relative7 = path45.relative(repoRoot, file);
|
|
100920
101246
|
formatter.print(` ${sym("success")} ${relative7}`);
|
|
100921
101247
|
}
|
|
101248
|
+
if (isJsonMode()) {
|
|
101249
|
+
formatter.json({
|
|
101250
|
+
action: "removed",
|
|
101251
|
+
key: trimmedKey,
|
|
101252
|
+
label: label2,
|
|
101253
|
+
environment: opts2.environment ?? null,
|
|
101254
|
+
reEncryptedFiles: result.reEncryptedFiles.length
|
|
101255
|
+
});
|
|
101256
|
+
return;
|
|
101257
|
+
}
|
|
100922
101258
|
formatter.success(
|
|
100923
101259
|
`${label2} removed. ${result.reEncryptedFiles.length} files re-encrypted. ${sym("locked")}`
|
|
100924
101260
|
);
|
|
@@ -100983,6 +101319,15 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`
|
|
|
100983
101319
|
return;
|
|
100984
101320
|
}
|
|
100985
101321
|
upsertRequest(repoRoot, publicKey, label2, opts2.environment);
|
|
101322
|
+
if (isJsonMode()) {
|
|
101323
|
+
formatter.json({
|
|
101324
|
+
action: "requested",
|
|
101325
|
+
label: label2,
|
|
101326
|
+
key: publicKey,
|
|
101327
|
+
environment: opts2.environment ?? null
|
|
101328
|
+
});
|
|
101329
|
+
return;
|
|
101330
|
+
}
|
|
100986
101331
|
const scope = opts2.environment ? ` for environment '${opts2.environment}'` : "";
|
|
100987
101332
|
formatter.success(`Access requested as '${label2}'${scope}`);
|
|
100988
101333
|
formatter.print(` Key: ${keyPreview(publicKey)}`);
|
|
@@ -100998,6 +101343,15 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`
|
|
|
100998
101343
|
try {
|
|
100999
101344
|
const repoRoot = program3.opts().dir || process.cwd();
|
|
101000
101345
|
const requests = loadRequests(repoRoot);
|
|
101346
|
+
if (isJsonMode()) {
|
|
101347
|
+
formatter.json(
|
|
101348
|
+
requests.map((r) => ({
|
|
101349
|
+
...r,
|
|
101350
|
+
requestedAt: r.requestedAt.toISOString()
|
|
101351
|
+
}))
|
|
101352
|
+
);
|
|
101353
|
+
return;
|
|
101354
|
+
}
|
|
101001
101355
|
if (requests.length === 0) {
|
|
101002
101356
|
formatter.info("No pending access requests.");
|
|
101003
101357
|
return;
|
|
@@ -101037,7 +101391,7 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`
|
|
|
101037
101391
|
process.exit(2);
|
|
101038
101392
|
return;
|
|
101039
101393
|
}
|
|
101040
|
-
const
|
|
101394
|
+
const result = await executeRecipientAdd(
|
|
101041
101395
|
repoRoot,
|
|
101042
101396
|
program3,
|
|
101043
101397
|
deps2,
|
|
@@ -101045,11 +101399,21 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`
|
|
|
101045
101399
|
request.label,
|
|
101046
101400
|
environment
|
|
101047
101401
|
);
|
|
101048
|
-
if (
|
|
101402
|
+
if (result) {
|
|
101049
101403
|
removeRequest(repoRoot, identifier);
|
|
101050
|
-
|
|
101051
|
-
|
|
101052
|
-
|
|
101404
|
+
if (isJsonMode()) {
|
|
101405
|
+
formatter.json({
|
|
101406
|
+
action: "approved",
|
|
101407
|
+
identifier,
|
|
101408
|
+
label: request.label,
|
|
101409
|
+
environment,
|
|
101410
|
+
reEncryptedFiles: result.reEncryptedFiles.length
|
|
101411
|
+
});
|
|
101412
|
+
} else {
|
|
101413
|
+
formatter.hint(
|
|
101414
|
+
`git add clef.yaml ${REQUESTS_FILENAME} && git add -A && git commit -m "approve recipient: ${request.label} [${environment}]"`
|
|
101415
|
+
);
|
|
101416
|
+
}
|
|
101053
101417
|
}
|
|
101054
101418
|
} catch (err) {
|
|
101055
101419
|
handleCommandError(err);
|
|
@@ -101065,7 +101429,7 @@ async function executeRecipientAdd(repoRoot, _program, deps2, key, label2, envir
|
|
|
101065
101429
|
`Environment '${environment}' not found. Available: ${manifest.environments.map((e) => e.name).join(", ")}`
|
|
101066
101430
|
);
|
|
101067
101431
|
process.exit(2);
|
|
101068
|
-
return
|
|
101432
|
+
return null;
|
|
101069
101433
|
}
|
|
101070
101434
|
const matrixManager = new MatrixManager();
|
|
101071
101435
|
const sopsClient = await createSopsClient(repoRoot, deps2.runner);
|
|
@@ -101074,7 +101438,7 @@ async function executeRecipientAdd(repoRoot, _program, deps2, key, label2, envir
|
|
|
101074
101438
|
if (existing.some((r) => r.key === key)) {
|
|
101075
101439
|
formatter.error(`Recipient '${keyPreview(key)}' is already present.`);
|
|
101076
101440
|
process.exit(2);
|
|
101077
|
-
return
|
|
101441
|
+
return null;
|
|
101078
101442
|
}
|
|
101079
101443
|
const allCells = matrixManager.resolveMatrix(manifest, repoRoot).filter((c) => c.exists && c.environment === environment);
|
|
101080
101444
|
const fileCount = allCells.length;
|
|
@@ -101091,7 +101455,7 @@ This will re-encrypt ${fileCount} files in the matrix.`);
|
|
|
101091
101455
|
const confirmed = await formatter.confirm("Proceed?");
|
|
101092
101456
|
if (!confirmed) {
|
|
101093
101457
|
formatter.info("Aborted.");
|
|
101094
|
-
return
|
|
101458
|
+
return null;
|
|
101095
101459
|
}
|
|
101096
101460
|
formatter.print(`
|
|
101097
101461
|
${sym("working")} Re-encrypting matrix...`);
|
|
@@ -101108,7 +101472,7 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`);
|
|
|
101108
101472
|
);
|
|
101109
101473
|
formatter.print("\nNo changes were applied. Investigate the error above and retry.");
|
|
101110
101474
|
process.exit(1);
|
|
101111
|
-
return
|
|
101475
|
+
return null;
|
|
101112
101476
|
}
|
|
101113
101477
|
for (const file of result.reEncryptedFiles) {
|
|
101114
101478
|
const relative7 = path45.relative(repoRoot, file);
|
|
@@ -101118,7 +101482,7 @@ ${sym("failure")} Re-encryption failed on ${path45.basename(failedFile)}`);
|
|
|
101118
101482
|
formatter.success(
|
|
101119
101483
|
`${displayLabel} added. ${result.reEncryptedFiles.length} files re-encrypted. ${sym("locked")}`
|
|
101120
101484
|
);
|
|
101121
|
-
return
|
|
101485
|
+
return { reEncryptedFiles: result.reEncryptedFiles };
|
|
101122
101486
|
}
|
|
101123
101487
|
|
|
101124
101488
|
// src/commands/merge-driver.ts
|
|
@@ -101281,6 +101645,16 @@ function registerServiceCommand(program3, deps2) {
|
|
|
101281
101645
|
repoRoot,
|
|
101282
101646
|
kmsEnvConfigs
|
|
101283
101647
|
);
|
|
101648
|
+
if (isJsonMode()) {
|
|
101649
|
+
formatter.json({
|
|
101650
|
+
action: "created",
|
|
101651
|
+
identity: result.identity.name,
|
|
101652
|
+
namespaces: result.identity.namespaces,
|
|
101653
|
+
environments: Object.keys(result.identity.environments),
|
|
101654
|
+
privateKeys: result.privateKeys
|
|
101655
|
+
});
|
|
101656
|
+
return;
|
|
101657
|
+
}
|
|
101284
101658
|
formatter.success(`Service identity '${name}' created.`);
|
|
101285
101659
|
formatter.print(`
|
|
101286
101660
|
Namespaces: ${result.identity.namespaces.join(", ")}`);
|
|
@@ -101334,6 +101708,10 @@ function registerServiceCommand(program3, deps2) {
|
|
|
101334
101708
|
const sopsClient = await createSopsClient(repoRoot, deps2.runner);
|
|
101335
101709
|
const manager = new ServiceIdentityManager(sopsClient, matrixManager);
|
|
101336
101710
|
const identities = manager.list(manifest);
|
|
101711
|
+
if (isJsonMode()) {
|
|
101712
|
+
formatter.json(identities);
|
|
101713
|
+
return;
|
|
101714
|
+
}
|
|
101337
101715
|
if (identities.length === 0) {
|
|
101338
101716
|
formatter.info("No service identities configured.");
|
|
101339
101717
|
return;
|
|
@@ -101363,6 +101741,10 @@ function registerServiceCommand(program3, deps2) {
|
|
|
101363
101741
|
process.exit(1);
|
|
101364
101742
|
return;
|
|
101365
101743
|
}
|
|
101744
|
+
if (isJsonMode()) {
|
|
101745
|
+
formatter.json(identity);
|
|
101746
|
+
return;
|
|
101747
|
+
}
|
|
101366
101748
|
formatter.print(`
|
|
101367
101749
|
Service Identity: ${identity.name}`);
|
|
101368
101750
|
formatter.print(`Description: ${identity.description}`);
|
|
@@ -101391,6 +101773,11 @@ Service Identity: ${identity.name}`);
|
|
|
101391
101773
|
const sopsClient = await createSopsClient(repoRoot, deps2.runner);
|
|
101392
101774
|
const manager = new ServiceIdentityManager(sopsClient, matrixManager);
|
|
101393
101775
|
const issues = await manager.validate(manifest, repoRoot);
|
|
101776
|
+
if (isJsonMode()) {
|
|
101777
|
+
formatter.json({ issues });
|
|
101778
|
+
process.exit(issues.length > 0 ? 1 : 0);
|
|
101779
|
+
return;
|
|
101780
|
+
}
|
|
101394
101781
|
if (issues.length === 0) {
|
|
101395
101782
|
formatter.success("All service identities are valid.");
|
|
101396
101783
|
return;
|
|
@@ -101441,6 +101828,17 @@ Service Identity: ${identity.name}`);
|
|
|
101441
101828
|
const manager = new ServiceIdentityManager(sopsClient, matrixManager);
|
|
101442
101829
|
formatter.print(`${sym("working")} Updating service identity '${name}'...`);
|
|
101443
101830
|
await manager.updateEnvironments(name, kmsEnvConfigs, manifest, repoRoot);
|
|
101831
|
+
if (isJsonMode()) {
|
|
101832
|
+
formatter.json({
|
|
101833
|
+
action: "updated",
|
|
101834
|
+
identity: name,
|
|
101835
|
+
changed: Object.entries(kmsEnvConfigs).map(([env, cfg]) => ({
|
|
101836
|
+
environment: env,
|
|
101837
|
+
provider: cfg.provider
|
|
101838
|
+
}))
|
|
101839
|
+
});
|
|
101840
|
+
return;
|
|
101841
|
+
}
|
|
101444
101842
|
formatter.success(`Service identity '${name}' updated.`);
|
|
101445
101843
|
for (const [envName, kmsConfig] of Object.entries(kmsEnvConfigs)) {
|
|
101446
101844
|
formatter.print(` ${envName}: switched to KMS envelope (${kmsConfig.provider})`);
|
|
@@ -101476,6 +101874,10 @@ Service Identity: ${identity.name}`);
|
|
|
101476
101874
|
const manager = new ServiceIdentityManager(sopsClient, matrixManager);
|
|
101477
101875
|
formatter.print(`${sym("working")} Deleting service identity '${name}'...`);
|
|
101478
101876
|
await manager.delete(name, manifest, repoRoot);
|
|
101877
|
+
if (isJsonMode()) {
|
|
101878
|
+
formatter.json({ action: "deleted", identity: name });
|
|
101879
|
+
return;
|
|
101880
|
+
}
|
|
101479
101881
|
formatter.success(`Service identity '${name}' deleted.`);
|
|
101480
101882
|
formatter.hint(
|
|
101481
101883
|
`git add clef.yaml && git commit -m "chore: delete service identity '${name}'"`
|
|
@@ -101512,6 +101914,15 @@ Service Identity: ${identity.name}`);
|
|
|
101512
101914
|
}
|
|
101513
101915
|
formatter.print(`${sym("working")} Rotating key for '${name}'...`);
|
|
101514
101916
|
const newKeys = await manager.rotateKey(name, manifest, repoRoot, opts2.environment);
|
|
101917
|
+
if (isJsonMode()) {
|
|
101918
|
+
formatter.json({
|
|
101919
|
+
action: "rotated",
|
|
101920
|
+
identity: name,
|
|
101921
|
+
environments: Object.keys(newKeys),
|
|
101922
|
+
privateKeys: newKeys
|
|
101923
|
+
});
|
|
101924
|
+
return;
|
|
101925
|
+
}
|
|
101515
101926
|
formatter.success(`Key rotated for '${name}'.`);
|
|
101516
101927
|
const entries = Object.entries(newKeys);
|
|
101517
101928
|
const block = entries.map(([env, key]) => `${env}: ${key}`).join("\n");
|
|
@@ -101652,7 +102063,7 @@ function registerPackCommand(program3, deps2) {
|
|
|
101652
102063
|
const envConfig = si?.environments[environment];
|
|
101653
102064
|
if (envConfig && isKmsEnvelope(envConfig)) {
|
|
101654
102065
|
const { createKmsProvider } = await Promise.resolve().then(() => __toESM(require_dist3()));
|
|
101655
|
-
kmsProvider = createKmsProvider(envConfig.kms.provider, {
|
|
102066
|
+
kmsProvider = await createKmsProvider(envConfig.kms.provider, {
|
|
101656
102067
|
region: envConfig.kms.region
|
|
101657
102068
|
});
|
|
101658
102069
|
}
|
|
@@ -101687,6 +102098,18 @@ function registerPackCommand(program3, deps2) {
|
|
|
101687
102098
|
const fileOut = new FilePackOutput(outputPath);
|
|
101688
102099
|
await fileOut.write(memOutput.artifact, memOutput.json);
|
|
101689
102100
|
}
|
|
102101
|
+
if (isJsonMode()) {
|
|
102102
|
+
formatter.json({
|
|
102103
|
+
identity,
|
|
102104
|
+
environment,
|
|
102105
|
+
keyCount: result.keyCount,
|
|
102106
|
+
namespaceCount: result.namespaceCount,
|
|
102107
|
+
artifactSize: result.artifactSize,
|
|
102108
|
+
revision: result.revision,
|
|
102109
|
+
output: outputPath ?? null
|
|
102110
|
+
});
|
|
102111
|
+
return;
|
|
102112
|
+
}
|
|
101690
102113
|
formatter.success(
|
|
101691
102114
|
`Artifact packed: ${result.keyCount} keys from ${result.namespaceCount} namespace(s).`
|
|
101692
102115
|
);
|
|
@@ -101764,6 +102187,15 @@ function registerRevokeCommand(program3, _deps) {
|
|
|
101764
102187
|
fs32.mkdirSync(artifactDir, { recursive: true });
|
|
101765
102188
|
fs32.writeFileSync(artifactPath, JSON.stringify(revoked, null, 2) + "\n", "utf-8");
|
|
101766
102189
|
const relPath = path49.relative(repoRoot, artifactPath);
|
|
102190
|
+
if (isJsonMode()) {
|
|
102191
|
+
formatter.json({
|
|
102192
|
+
identity,
|
|
102193
|
+
environment,
|
|
102194
|
+
revokedAt: revoked.revokedAt,
|
|
102195
|
+
markerPath: relPath
|
|
102196
|
+
});
|
|
102197
|
+
return;
|
|
102198
|
+
}
|
|
101767
102199
|
formatter.success(`Artifact revoked: ${relPath}`);
|
|
101768
102200
|
formatter.print("");
|
|
101769
102201
|
formatter.print(`${sym("arrow")} If your agent fetches artifacts from git (VCS source):`);
|
|
@@ -101792,37 +102224,35 @@ import * as path50 from "path";
|
|
|
101792
102224
|
function registerDriftCommand(program3, _deps) {
|
|
101793
102225
|
program3.command("drift <path>").description(
|
|
101794
102226
|
"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"
|
|
101795
|
-
).option("--
|
|
101796
|
-
|
|
101797
|
-
|
|
101798
|
-
|
|
101799
|
-
|
|
101800
|
-
|
|
101801
|
-
|
|
101802
|
-
|
|
101803
|
-
|
|
101804
|
-
|
|
101805
|
-
|
|
101806
|
-
process.exit(1);
|
|
101807
|
-
return;
|
|
101808
|
-
}
|
|
101809
|
-
const payload = driftResultToOtlp(result, version2);
|
|
101810
|
-
await pushOtlp(payload, config);
|
|
101811
|
-
formatter.success("Drift results pushed to telemetry endpoint.");
|
|
101812
|
-
}
|
|
101813
|
-
if (options.json) {
|
|
101814
|
-
formatter.raw(JSON.stringify(result, null, 2) + "\n");
|
|
101815
|
-
process.exit(result.issues.length > 0 ? 1 : 0);
|
|
102227
|
+
).option("--push", "Push results as OTLP to CLEF_TELEMETRY_URL").option("--namespace <name...>", "Scope to specific namespace(s)").action(async (remotePath, options) => {
|
|
102228
|
+
try {
|
|
102229
|
+
const localRoot = program3.opts().dir || process.cwd();
|
|
102230
|
+
const remoteRoot = path50.resolve(localRoot, remotePath);
|
|
102231
|
+
const detector = new DriftDetector();
|
|
102232
|
+
const result = detector.detect(localRoot, remoteRoot, options.namespace);
|
|
102233
|
+
if (options.push) {
|
|
102234
|
+
const config = resolveTelemetryConfig();
|
|
102235
|
+
if (!config) {
|
|
102236
|
+
formatter.error("--push requires CLEF_TELEMETRY_URL to be set.");
|
|
102237
|
+
process.exit(1);
|
|
101816
102238
|
return;
|
|
101817
102239
|
}
|
|
101818
|
-
|
|
102240
|
+
const payload = driftResultToOtlp(result, version2);
|
|
102241
|
+
await pushOtlp(payload, config);
|
|
102242
|
+
formatter.success("Drift results pushed to telemetry endpoint.");
|
|
102243
|
+
}
|
|
102244
|
+
if (isJsonMode()) {
|
|
102245
|
+
formatter.json(result);
|
|
101819
102246
|
process.exit(result.issues.length > 0 ? 1 : 0);
|
|
101820
|
-
|
|
101821
|
-
formatter.error(err.message);
|
|
101822
|
-
process.exit(1);
|
|
102247
|
+
return;
|
|
101823
102248
|
}
|
|
102249
|
+
formatDriftOutput(result);
|
|
102250
|
+
process.exit(result.issues.length > 0 ? 1 : 0);
|
|
102251
|
+
} catch (err) {
|
|
102252
|
+
formatter.error(err.message);
|
|
102253
|
+
process.exit(1);
|
|
101824
102254
|
}
|
|
101825
|
-
);
|
|
102255
|
+
});
|
|
101826
102256
|
}
|
|
101827
102257
|
function formatDriftOutput(result) {
|
|
101828
102258
|
if (result.namespacesCompared === 0) {
|
|
@@ -101915,7 +102345,7 @@ async function getHeadSha(repoRoot, runner2) {
|
|
|
101915
102345
|
function registerReportCommand(program3, deps2) {
|
|
101916
102346
|
program3.command("report").description(
|
|
101917
102347
|
"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"
|
|
101918
|
-
).option("--
|
|
102348
|
+
).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(
|
|
101919
102349
|
async (options) => {
|
|
101920
102350
|
try {
|
|
101921
102351
|
const repoRoot = program3.opts().dir || process.cwd();
|
|
@@ -101927,7 +102357,7 @@ function registerReportCommand(program3, deps2) {
|
|
|
101927
102357
|
deps2.runner
|
|
101928
102358
|
);
|
|
101929
102359
|
await maybePush(report, options.push);
|
|
101930
|
-
outputReport(report
|
|
102360
|
+
outputReport(report);
|
|
101931
102361
|
return;
|
|
101932
102362
|
}
|
|
101933
102363
|
const sopsClient = await createSopsClient(repoRoot, deps2.runner);
|
|
@@ -101945,7 +102375,7 @@ function registerReportCommand(program3, deps2) {
|
|
|
101945
102375
|
});
|
|
101946
102376
|
if (options.push) {
|
|
101947
102377
|
await pushWithGapFill(repoRoot, headReport, deps2.runner);
|
|
101948
|
-
outputReport(headReport
|
|
102378
|
+
outputReport(headReport);
|
|
101949
102379
|
return;
|
|
101950
102380
|
}
|
|
101951
102381
|
if (options.since) {
|
|
@@ -101959,8 +102389,8 @@ function registerReportCommand(program3, deps2) {
|
|
|
101959
102389
|
reports.push(await generateReportAtCommit(repoRoot, sha, version2, deps2.runner));
|
|
101960
102390
|
}
|
|
101961
102391
|
}
|
|
101962
|
-
if (
|
|
101963
|
-
formatter.
|
|
102392
|
+
if (isJsonMode()) {
|
|
102393
|
+
formatter.json(reports);
|
|
101964
102394
|
} else {
|
|
101965
102395
|
formatter.print(
|
|
101966
102396
|
`Generated ${reports.length} report(s) for commits since ${options.since.slice(0, 8)}`
|
|
@@ -101975,7 +102405,7 @@ function registerReportCommand(program3, deps2) {
|
|
|
101975
102405
|
process.exit(reports.some((r) => r.policy.issueCount.error > 0) ? 1 : 0);
|
|
101976
102406
|
return;
|
|
101977
102407
|
}
|
|
101978
|
-
outputReport(headReport
|
|
102408
|
+
outputReport(headReport);
|
|
101979
102409
|
} catch (err) {
|
|
101980
102410
|
handleCommandError(err);
|
|
101981
102411
|
}
|
|
@@ -102026,9 +102456,9 @@ async function maybePush(report, push) {
|
|
|
102026
102456
|
await pushOtlp(payload, config);
|
|
102027
102457
|
formatter.success("Report pushed to telemetry endpoint.");
|
|
102028
102458
|
}
|
|
102029
|
-
function outputReport(report
|
|
102030
|
-
if (
|
|
102031
|
-
formatter.
|
|
102459
|
+
function outputReport(report) {
|
|
102460
|
+
if (isJsonMode()) {
|
|
102461
|
+
formatter.json(report);
|
|
102032
102462
|
process.exit(report.policy.issueCount.error > 0 ? 1 : 0);
|
|
102033
102463
|
return;
|
|
102034
102464
|
}
|
|
@@ -102197,6 +102627,16 @@ function registerInstallCommand(program3, _deps) {
|
|
|
102197
102627
|
}
|
|
102198
102628
|
const manifestFile = files.find((f2) => f2.name === "broker.yaml");
|
|
102199
102629
|
const manifest = manifestFile ? (0, import_yaml.parse)(manifestFile.content) : {};
|
|
102630
|
+
if (isJsonMode()) {
|
|
102631
|
+
formatter.json({
|
|
102632
|
+
broker: entry.name,
|
|
102633
|
+
provider: entry.provider,
|
|
102634
|
+
tier: entry.tier,
|
|
102635
|
+
files: files.map((f2) => `brokers/${entry.name}/${f2.name}`)
|
|
102636
|
+
});
|
|
102637
|
+
process.exit(0);
|
|
102638
|
+
return;
|
|
102639
|
+
}
|
|
102200
102640
|
formatter.print("");
|
|
102201
102641
|
formatter.print(` ${sym("success")} ${entry.name}`);
|
|
102202
102642
|
formatter.print("");
|
|
@@ -102260,6 +102700,11 @@ function registerSearchCommand(program3, _deps) {
|
|
|
102260
102700
|
if (options.tier) {
|
|
102261
102701
|
results = results.filter((b) => b.tier === Number(options.tier));
|
|
102262
102702
|
}
|
|
102703
|
+
if (isJsonMode()) {
|
|
102704
|
+
formatter.json(results);
|
|
102705
|
+
process.exit(0);
|
|
102706
|
+
return;
|
|
102707
|
+
}
|
|
102263
102708
|
if (results.length === 0) {
|
|
102264
102709
|
formatter.info("No brokers found matching your query.");
|
|
102265
102710
|
process.exit(0);
|
|
@@ -102390,6 +102835,20 @@ ${sym("working")} Backend migration summary:`);
|
|
|
102390
102835
|
}
|
|
102391
102836
|
}
|
|
102392
102837
|
);
|
|
102838
|
+
if (isJsonMode()) {
|
|
102839
|
+
formatter.json({
|
|
102840
|
+
backend: target.backend,
|
|
102841
|
+
migratedFiles: result.migratedFiles,
|
|
102842
|
+
skippedFiles: result.skippedFiles,
|
|
102843
|
+
verifiedFiles: result.verifiedFiles,
|
|
102844
|
+
warnings: result.warnings,
|
|
102845
|
+
rolledBack: result.rolledBack,
|
|
102846
|
+
error: result.error ?? null,
|
|
102847
|
+
dryRun: opts2.dryRun ?? false
|
|
102848
|
+
});
|
|
102849
|
+
process.exit(result.rolledBack ? 1 : 0);
|
|
102850
|
+
return;
|
|
102851
|
+
}
|
|
102393
102852
|
if (result.rolledBack) {
|
|
102394
102853
|
formatter.error(`Migration failed: ${result.error}`);
|
|
102395
102854
|
formatter.info("All changes have been rolled back.");
|
|
@@ -102478,7 +102937,7 @@ function registerServeCommand(program3, deps2) {
|
|
|
102478
102937
|
const envConfig = si.environments[opts2.env];
|
|
102479
102938
|
if (envConfig && isKmsEnvelope(envConfig)) {
|
|
102480
102939
|
const { createKmsProvider } = await Promise.resolve().then(() => __toESM(require_dist3()));
|
|
102481
|
-
kmsProvider = createKmsProvider(envConfig.kms.provider, {
|
|
102940
|
+
kmsProvider = await createKmsProvider(envConfig.kms.provider, {
|
|
102482
102941
|
region: envConfig.kms.region
|
|
102483
102942
|
});
|
|
102484
102943
|
}
|
|
@@ -102540,12 +102999,18 @@ var VERSION = package_default.version;
|
|
|
102540
102999
|
var program2 = new Command();
|
|
102541
103000
|
var runner = new NodeSubprocessRunner();
|
|
102542
103001
|
var deps = { runner };
|
|
102543
|
-
program2.name("clef").option("--dir <path>", "Path to a local Clef repository root (default: current directory)").option("--plain", "Plain output, no emoji or colour");
|
|
103002
|
+
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)");
|
|
102544
103003
|
program2.hook("preAction", async () => {
|
|
102545
103004
|
const opts2 = program2.opts();
|
|
102546
103005
|
if (opts2.plain) {
|
|
102547
103006
|
setPlainMode(true);
|
|
102548
103007
|
}
|
|
103008
|
+
if (opts2.json) {
|
|
103009
|
+
setJsonMode(true);
|
|
103010
|
+
}
|
|
103011
|
+
if (opts2.yes) {
|
|
103012
|
+
setYesMode(true);
|
|
103013
|
+
}
|
|
102549
103014
|
});
|
|
102550
103015
|
program2.addHelpText("beforeAll", () => {
|
|
102551
103016
|
const clef = isPlainMode() ? "clef" : symbols.clef;
|
|
@@ -102644,6 +103109,9 @@ async function main() {
|
|
|
102644
103109
|
}
|
|
102645
103110
|
}
|
|
102646
103111
|
main().catch((err) => {
|
|
103112
|
+
if (isJsonMode()) {
|
|
103113
|
+
exitJsonError(err.message);
|
|
103114
|
+
}
|
|
102647
103115
|
formatter.error(err.message);
|
|
102648
103116
|
process.exit(1);
|
|
102649
103117
|
});
|