@clef-sh/agent 0.1.8 → 0.1.9-beta.57

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/agent.cjs CHANGED
@@ -44607,22 +44607,22 @@ var require_crypto2 = __commonJS({
44607
44607
  "use strict";
44608
44608
  Object.defineProperty(exports2, "__esModule", { value: true });
44609
44609
  exports2.NodeCrypto = void 0;
44610
- var crypto16 = require("crypto");
44610
+ var crypto17 = require("crypto");
44611
44611
  var NodeCrypto = class {
44612
44612
  async sha256DigestBase64(str) {
44613
- return crypto16.createHash("sha256").update(str).digest("base64");
44613
+ return crypto17.createHash("sha256").update(str).digest("base64");
44614
44614
  }
44615
44615
  randomBytesBase64(count) {
44616
- return crypto16.randomBytes(count).toString("base64");
44616
+ return crypto17.randomBytes(count).toString("base64");
44617
44617
  }
44618
44618
  async verify(pubkey, data, signature) {
44619
- const verifier = crypto16.createVerify("RSA-SHA256");
44619
+ const verifier = crypto17.createVerify("RSA-SHA256");
44620
44620
  verifier.update(data);
44621
44621
  verifier.end();
44622
44622
  return verifier.verify(pubkey, signature, "base64");
44623
44623
  }
44624
44624
  async sign(privateKey, data) {
44625
- const signer = crypto16.createSign("RSA-SHA256");
44625
+ const signer = crypto17.createSign("RSA-SHA256");
44626
44626
  signer.update(data);
44627
44627
  signer.end();
44628
44628
  return signer.sign(privateKey, "base64");
@@ -44640,7 +44640,7 @@ var require_crypto2 = __commonJS({
44640
44640
  * string in hexadecimal encoding.
44641
44641
  */
44642
44642
  async sha256DigestHex(str) {
44643
- return crypto16.createHash("sha256").update(str).digest("hex");
44643
+ return crypto17.createHash("sha256").update(str).digest("hex");
44644
44644
  }
44645
44645
  /**
44646
44646
  * Computes the HMAC hash of a message using the provided crypto key and the
@@ -44652,7 +44652,7 @@ var require_crypto2 = __commonJS({
44652
44652
  */
44653
44653
  async signWithHmacSha256(key, msg) {
44654
44654
  const cryptoKey = typeof key === "string" ? key : toBuffer(key);
44655
- return toArrayBuffer(crypto16.createHmac("sha256", cryptoKey).update(msg).digest());
44655
+ return toArrayBuffer(crypto17.createHmac("sha256", cryptoKey).update(msg).digest());
44656
44656
  }
44657
44657
  };
44658
44658
  exports2.NodeCrypto = NodeCrypto;
@@ -45430,10 +45430,10 @@ var require_oauth2client = __commonJS({
45430
45430
  * https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/oauth2-codeVerifier.js
45431
45431
  */
45432
45432
  async generateCodeVerifierAsync() {
45433
- const crypto16 = (0, crypto_1.createCrypto)();
45434
- const randomString = crypto16.randomBytesBase64(96);
45433
+ const crypto17 = (0, crypto_1.createCrypto)();
45434
+ const randomString = crypto17.randomBytesBase64(96);
45435
45435
  const codeVerifier = randomString.replace(/\+/g, "~").replace(/=/g, "_").replace(/\//g, "-");
45436
- const unencodedCodeChallenge = await crypto16.sha256DigestBase64(codeVerifier);
45436
+ const unencodedCodeChallenge = await crypto17.sha256DigestBase64(codeVerifier);
45437
45437
  const codeChallenge = unencodedCodeChallenge.split("=")[0].replace(/\+/g, "-").replace(/\//g, "_");
45438
45438
  return { codeVerifier, codeChallenge };
45439
45439
  }
@@ -45877,7 +45877,7 @@ var require_oauth2client = __commonJS({
45877
45877
  * @return Returns a promise resolving to LoginTicket on verification.
45878
45878
  */
45879
45879
  async verifySignedJwtWithCertsAsync(jwt2, certs, requiredAudience, issuers, maxExpiry) {
45880
- const crypto16 = (0, crypto_1.createCrypto)();
45880
+ const crypto17 = (0, crypto_1.createCrypto)();
45881
45881
  if (!maxExpiry) {
45882
45882
  maxExpiry = _OAuth2Client.DEFAULT_MAX_TOKEN_LIFETIME_SECS_;
45883
45883
  }
@@ -45890,7 +45890,7 @@ var require_oauth2client = __commonJS({
45890
45890
  let envelope;
45891
45891
  let payload;
45892
45892
  try {
45893
- envelope = JSON.parse(crypto16.decodeBase64StringUtf8(segments[0]));
45893
+ envelope = JSON.parse(crypto17.decodeBase64StringUtf8(segments[0]));
45894
45894
  } catch (err) {
45895
45895
  if (err instanceof Error) {
45896
45896
  err.message = `Can't parse token envelope: ${segments[0]}': ${err.message}`;
@@ -45901,7 +45901,7 @@ var require_oauth2client = __commonJS({
45901
45901
  throw new Error("Can't parse token envelope: " + segments[0]);
45902
45902
  }
45903
45903
  try {
45904
- payload = JSON.parse(crypto16.decodeBase64StringUtf8(segments[1]));
45904
+ payload = JSON.parse(crypto17.decodeBase64StringUtf8(segments[1]));
45905
45905
  } catch (err) {
45906
45906
  if (err instanceof Error) {
45907
45907
  err.message = `Can't parse token payload '${segments[0]}`;
@@ -45918,7 +45918,7 @@ var require_oauth2client = __commonJS({
45918
45918
  if (envelope.alg === "ES256") {
45919
45919
  signature = formatEcdsa.joseToDer(signature, "ES256").toString("base64");
45920
45920
  }
45921
- const verified = await crypto16.verify(cert, signed, signature);
45921
+ const verified = await crypto17.verify(cert, signed, signature);
45922
45922
  if (!verified) {
45923
45923
  throw new Error("Invalid token signature: " + jwt2);
45924
45924
  }
@@ -46286,14 +46286,14 @@ var require_buffer_equal_constant_time = __commonJS({
46286
46286
  var require_jwa = __commonJS({
46287
46287
  "../../node_modules/jwa/index.js"(exports2, module2) {
46288
46288
  var Buffer3 = require_safe_buffer().Buffer;
46289
- var crypto16 = require("crypto");
46289
+ var crypto17 = require("crypto");
46290
46290
  var formatEcdsa = require_ecdsa_sig_formatter();
46291
46291
  var util2 = require("util");
46292
46292
  var MSG_INVALID_ALGORITHM = '"%s" is not a valid algorithm.\n Supported algorithms are:\n "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512" and "none".';
46293
46293
  var MSG_INVALID_SECRET = "secret must be a string or buffer";
46294
46294
  var MSG_INVALID_VERIFIER_KEY = "key must be a string or a buffer";
46295
46295
  var MSG_INVALID_SIGNER_KEY = "key must be a string, a buffer or an object";
46296
- var supportsKeyObjects = typeof crypto16.createPublicKey === "function";
46296
+ var supportsKeyObjects = typeof crypto17.createPublicKey === "function";
46297
46297
  if (supportsKeyObjects) {
46298
46298
  MSG_INVALID_VERIFIER_KEY += " or a KeyObject";
46299
46299
  MSG_INVALID_SECRET += "or a KeyObject";
@@ -46383,17 +46383,17 @@ var require_jwa = __commonJS({
46383
46383
  return function sign2(thing, secret) {
46384
46384
  checkIsSecretKey(secret);
46385
46385
  thing = normalizeInput(thing);
46386
- var hmac = crypto16.createHmac("sha" + bits, secret);
46386
+ var hmac = crypto17.createHmac("sha" + bits, secret);
46387
46387
  var sig = (hmac.update(thing), hmac.digest("base64"));
46388
46388
  return fromBase64(sig);
46389
46389
  };
46390
46390
  }
46391
46391
  var bufferEqual;
46392
- var timingSafeEqual2 = "timingSafeEqual" in crypto16 ? function timingSafeEqual3(a, b) {
46392
+ var timingSafeEqual2 = "timingSafeEqual" in crypto17 ? function timingSafeEqual3(a, b) {
46393
46393
  if (a.byteLength !== b.byteLength) {
46394
46394
  return false;
46395
46395
  }
46396
- return crypto16.timingSafeEqual(a, b);
46396
+ return crypto17.timingSafeEqual(a, b);
46397
46397
  } : function timingSafeEqual3(a, b) {
46398
46398
  if (!bufferEqual) {
46399
46399
  bufferEqual = require_buffer_equal_constant_time();
@@ -46410,7 +46410,7 @@ var require_jwa = __commonJS({
46410
46410
  return function sign2(thing, privateKey) {
46411
46411
  checkIsPrivateKey(privateKey);
46412
46412
  thing = normalizeInput(thing);
46413
- var signer = crypto16.createSign("RSA-SHA" + bits);
46413
+ var signer = crypto17.createSign("RSA-SHA" + bits);
46414
46414
  var sig = (signer.update(thing), signer.sign(privateKey, "base64"));
46415
46415
  return fromBase64(sig);
46416
46416
  };
@@ -46420,7 +46420,7 @@ var require_jwa = __commonJS({
46420
46420
  checkIsPublicKey(publicKey);
46421
46421
  thing = normalizeInput(thing);
46422
46422
  signature = toBase64(signature);
46423
- var verifier = crypto16.createVerify("RSA-SHA" + bits);
46423
+ var verifier = crypto17.createVerify("RSA-SHA" + bits);
46424
46424
  verifier.update(thing);
46425
46425
  return verifier.verify(publicKey, signature, "base64");
46426
46426
  };
@@ -46429,11 +46429,11 @@ var require_jwa = __commonJS({
46429
46429
  return function sign2(thing, privateKey) {
46430
46430
  checkIsPrivateKey(privateKey);
46431
46431
  thing = normalizeInput(thing);
46432
- var signer = crypto16.createSign("RSA-SHA" + bits);
46432
+ var signer = crypto17.createSign("RSA-SHA" + bits);
46433
46433
  var sig = (signer.update(thing), signer.sign({
46434
46434
  key: privateKey,
46435
- padding: crypto16.constants.RSA_PKCS1_PSS_PADDING,
46436
- saltLength: crypto16.constants.RSA_PSS_SALTLEN_DIGEST
46435
+ padding: crypto17.constants.RSA_PKCS1_PSS_PADDING,
46436
+ saltLength: crypto17.constants.RSA_PSS_SALTLEN_DIGEST
46437
46437
  }, "base64"));
46438
46438
  return fromBase64(sig);
46439
46439
  };
@@ -46443,12 +46443,12 @@ var require_jwa = __commonJS({
46443
46443
  checkIsPublicKey(publicKey);
46444
46444
  thing = normalizeInput(thing);
46445
46445
  signature = toBase64(signature);
46446
- var verifier = crypto16.createVerify("RSA-SHA" + bits);
46446
+ var verifier = crypto17.createVerify("RSA-SHA" + bits);
46447
46447
  verifier.update(thing);
46448
46448
  return verifier.verify({
46449
46449
  key: publicKey,
46450
- padding: crypto16.constants.RSA_PKCS1_PSS_PADDING,
46451
- saltLength: crypto16.constants.RSA_PSS_SALTLEN_DIGEST
46450
+ padding: crypto17.constants.RSA_PKCS1_PSS_PADDING,
46451
+ saltLength: crypto17.constants.RSA_PSS_SALTLEN_DIGEST
46452
46452
  }, signature, "base64");
46453
46453
  };
46454
46454
  }
@@ -48606,14 +48606,14 @@ var require_awsrequestsigner = __commonJS({
48606
48606
  }
48607
48607
  };
48608
48608
  exports2.AwsRequestSigner = AwsRequestSigner;
48609
- async function sign2(crypto16, key, msg) {
48610
- return await crypto16.signWithHmacSha256(key, msg);
48611
- }
48612
- async function getSigningKey(crypto16, key, dateStamp, region, serviceName) {
48613
- const kDate = await sign2(crypto16, `AWS4${key}`, dateStamp);
48614
- const kRegion = await sign2(crypto16, kDate, region);
48615
- const kService = await sign2(crypto16, kRegion, serviceName);
48616
- const kSigning = await sign2(crypto16, kService, "aws4_request");
48609
+ async function sign2(crypto17, key, msg) {
48610
+ return await crypto17.signWithHmacSha256(key, msg);
48611
+ }
48612
+ async function getSigningKey(crypto17, key, dateStamp, region, serviceName) {
48613
+ const kDate = await sign2(crypto17, `AWS4${key}`, dateStamp);
48614
+ const kRegion = await sign2(crypto17, kDate, region);
48615
+ const kService = await sign2(crypto17, kRegion, serviceName);
48616
+ const kSigning = await sign2(crypto17, kService, "aws4_request");
48617
48617
  return kSigning;
48618
48618
  }
48619
48619
  async function generateAuthenticationHeaderMap(options) {
@@ -50198,24 +50198,24 @@ var require_googleauth = __commonJS({
50198
50198
  const signed = await client.sign(data);
50199
50199
  return signed.signedBlob;
50200
50200
  }
50201
- const crypto16 = (0, crypto_1.createCrypto)();
50201
+ const crypto17 = (0, crypto_1.createCrypto)();
50202
50202
  if (client instanceof jwtclient_1.JWT && client.key) {
50203
- const sign2 = await crypto16.sign(client.key, data);
50203
+ const sign2 = await crypto17.sign(client.key, data);
50204
50204
  return sign2;
50205
50205
  }
50206
50206
  const creds = await this.getCredentials();
50207
50207
  if (!creds.client_email) {
50208
50208
  throw new Error("Cannot sign data without `client_email`.");
50209
50209
  }
50210
- return this.signBlob(crypto16, creds.client_email, data, endpoint);
50210
+ return this.signBlob(crypto17, creds.client_email, data, endpoint);
50211
50211
  }
50212
- async signBlob(crypto16, emailOrUniqueId, data, endpoint) {
50212
+ async signBlob(crypto17, emailOrUniqueId, data, endpoint) {
50213
50213
  const url = new URL(endpoint + `${emailOrUniqueId}:signBlob`);
50214
50214
  const res = await this.request({
50215
50215
  method: "POST",
50216
50216
  url: url.href,
50217
50217
  data: {
50218
- payload: crypto16.encodeBase64StringUtf8(data)
50218
+ payload: crypto17.encodeBase64StringUtf8(data)
50219
50219
  },
50220
50220
  retry: true,
50221
50221
  retryConfig: {
@@ -50632,7 +50632,7 @@ var require_src10 = __commonJS({
50632
50632
  var require_object_hash = __commonJS({
50633
50633
  "../../node_modules/object-hash/index.js"(exports2, module2) {
50634
50634
  "use strict";
50635
- var crypto16 = require("crypto");
50635
+ var crypto17 = require("crypto");
50636
50636
  exports2 = module2.exports = objectHash;
50637
50637
  function objectHash(object, options) {
50638
50638
  options = applyDefaults(object, options);
@@ -50650,7 +50650,7 @@ var require_object_hash = __commonJS({
50650
50650
  exports2.keysMD5 = function(object) {
50651
50651
  return objectHash(object, { algorithm: "md5", encoding: "hex", excludeValues: true });
50652
50652
  };
50653
- var hashes = crypto16.getHashes ? crypto16.getHashes().slice() : ["sha1", "md5"];
50653
+ var hashes = crypto17.getHashes ? crypto17.getHashes().slice() : ["sha1", "md5"];
50654
50654
  hashes.push("passthrough");
50655
50655
  var encodings = ["buffer", "hex", "binary", "base64"];
50656
50656
  function applyDefaults(object, sourceOptions) {
@@ -50696,7 +50696,7 @@ var require_object_hash = __commonJS({
50696
50696
  function hash(object, options) {
50697
50697
  var hashingStream;
50698
50698
  if (options.algorithm !== "passthrough") {
50699
- hashingStream = crypto16.createHash(options.algorithm);
50699
+ hashingStream = crypto17.createHash(options.algorithm);
50700
50700
  } else {
50701
50701
  hashingStream = new PassThrough();
50702
50702
  }
@@ -150124,14 +150124,14 @@ var require_etag = __commonJS({
150124
150124
  "../../node_modules/etag/index.js"(exports2, module2) {
150125
150125
  "use strict";
150126
150126
  module2.exports = etag;
150127
- var crypto16 = require("crypto");
150127
+ var crypto17 = require("crypto");
150128
150128
  var Stats = require("fs").Stats;
150129
150129
  var toString = Object.prototype.toString;
150130
150130
  function entitytag(entity) {
150131
150131
  if (entity.length === 0) {
150132
150132
  return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"';
150133
150133
  }
150134
- var hash = crypto16.createHash("sha1").update(entity, "utf8").digest("base64").substring(0, 27);
150134
+ var hash = crypto17.createHash("sha1").update(entity, "utf8").digest("base64").substring(0, 27);
150135
150135
  var len = typeof entity === "string" ? Buffer.byteLength(entity, "utf8") : entity.length;
150136
150136
  return '"' + len.toString(16) + "-" + hash + '"';
150137
150137
  }
@@ -172482,17 +172482,17 @@ var require_content_disposition = __commonJS({
172482
172482
  // ../../node_modules/cookie-signature/index.js
172483
172483
  var require_cookie_signature = __commonJS({
172484
172484
  "../../node_modules/cookie-signature/index.js"(exports2) {
172485
- var crypto16 = require("crypto");
172485
+ var crypto17 = require("crypto");
172486
172486
  exports2.sign = function(val, secret) {
172487
172487
  if ("string" != typeof val) throw new TypeError("Cookie value must be provided as a string.");
172488
172488
  if (null == secret) throw new TypeError("Secret key must be provided.");
172489
- return val + "." + crypto16.createHmac("sha256", secret).update(val).digest("base64").replace(/\=+$/, "");
172489
+ return val + "." + crypto17.createHmac("sha256", secret).update(val).digest("base64").replace(/\=+$/, "");
172490
172490
  };
172491
172491
  exports2.unsign = function(input, secret) {
172492
172492
  if ("string" != typeof input) throw new TypeError("Signed cookie string must be provided.");
172493
172493
  if (null == secret) throw new TypeError("Secret key must be provided.");
172494
172494
  var tentativeValue = input.slice(0, input.lastIndexOf(".")), expectedInput = exports2.sign(tentativeValue, secret), expectedBuffer = Buffer.from(expectedInput), inputBuffer = Buffer.from(input);
172495
- return expectedBuffer.length === inputBuffer.length && crypto16.timingSafeEqual(expectedBuffer, inputBuffer) ? tentativeValue : false;
172495
+ return expectedBuffer.length === inputBuffer.length && crypto17.timingSafeEqual(expectedBuffer, inputBuffer) ? tentativeValue : false;
172496
172496
  };
172497
172497
  }
172498
172498
  });
@@ -183402,9 +183402,9 @@ function resolveConfig(env = process.env) {
183402
183402
  }
183403
183403
  const cacheTtlStr = env.CLEF_AGENT_CACHE_TTL ?? "300";
183404
183404
  const cacheTtl = parseInt(cacheTtlStr, 10);
183405
- if (isNaN(cacheTtl) || cacheTtl < 30) {
183405
+ if (isNaN(cacheTtl) || cacheTtl < 0 || cacheTtl > 0 && cacheTtl < 30) {
183406
183406
  throw new ConfigError(
183407
- `Invalid CLEF_AGENT_CACHE_TTL '${cacheTtlStr}'. Must be an integer >= 30.`
183407
+ `Invalid CLEF_AGENT_CACHE_TTL '${cacheTtlStr}'. Must be 0 (just-in-time mode) or an integer >= 30.`
183408
183408
  );
183409
183409
  }
183410
183410
  const ageKey = env.CLEF_AGENT_AGE_KEY;
@@ -183434,6 +183434,11 @@ var SecretsCache = class {
183434
183434
  snapshot = null;
183435
183435
  /** Replace the cached secrets in a single reference assignment. */
183436
183436
  swap(values, keys, revision) {
183437
+ if (this.snapshot) {
183438
+ for (const k of Object.keys(this.snapshot.values)) {
183439
+ this.snapshot.values[k] = "";
183440
+ }
183441
+ }
183437
183442
  this.snapshot = { values: { ...values }, keys: [...keys], revision, swappedAt: Date.now() };
183438
183443
  }
183439
183444
  /** Whether the cache has exceeded the given TTL (seconds). */
@@ -183441,8 +183446,13 @@ var SecretsCache = class {
183441
183446
  if (!this.snapshot) return false;
183442
183447
  return (Date.now() - this.snapshot.swappedAt) / 1e3 > ttlSeconds;
183443
183448
  }
183444
- /** Clear the cached snapshot. */
183449
+ /** Clear the cached snapshot, zeroing values first (best-effort). */
183445
183450
  wipe() {
183451
+ if (this.snapshot) {
183452
+ for (const k of Object.keys(this.snapshot.values)) {
183453
+ this.snapshot.values[k] = "";
183454
+ }
183455
+ }
183446
183456
  this.snapshot = null;
183447
183457
  }
183448
183458
  /** Epoch ms when the cache was last swapped, or null if never loaded. */
@@ -183507,20 +183517,16 @@ var DiskCache = class {
183507
183517
  }
183508
183518
  /** Get the SHA from the cached metadata, if available. */
183509
183519
  getCachedSha() {
183510
- try {
183511
- const raw = fs.readFileSync(this.metaPath, "utf-8");
183512
- const meta = JSON.parse(raw);
183513
- return meta.sha;
183514
- } catch {
183515
- return void 0;
183516
- }
183520
+ return this.readMeta()?.sha;
183517
183521
  }
183518
183522
  /** Get the fetchedAt timestamp from metadata, if available. */
183519
183523
  getFetchedAt() {
183524
+ return this.readMeta()?.fetchedAt;
183525
+ }
183526
+ readMeta() {
183520
183527
  try {
183521
183528
  const raw = fs.readFileSync(this.metaPath, "utf-8");
183522
- const meta = JSON.parse(raw);
183523
- return meta.fetchedAt;
183529
+ return JSON.parse(raw);
183524
183530
  } catch {
183525
183531
  return void 0;
183526
183532
  }
@@ -183576,7 +183582,10 @@ var AgeDecryptor = class {
183576
183582
  };
183577
183583
 
183578
183584
  // ../runtime/src/poller.ts
183579
- var crypto15 = __toESM(require("crypto"));
183585
+ var crypto16 = __toESM(require("crypto"));
183586
+
183587
+ // ../runtime/src/artifact-decryptor.ts
183588
+ var crypto14 = __toESM(require("crypto"));
183580
183589
 
183581
183590
  // ../runtime/src/kms/aws.ts
183582
183591
  var AwsKmsProvider = class {
@@ -183741,11 +183750,106 @@ function createKmsProvider(provider, options) {
183741
183750
  }
183742
183751
  }
183743
183752
 
183753
+ // ../runtime/src/artifact-decryptor.ts
183754
+ var ArtifactDecryptor = class {
183755
+ ageDecryptor = new AgeDecryptor();
183756
+ privateKey;
183757
+ telemetryOverride;
183758
+ initialTelemetry;
183759
+ constructor(options) {
183760
+ this.privateKey = options.privateKey;
183761
+ this.initialTelemetry = options.telemetry;
183762
+ }
183763
+ /** Set or replace the telemetry emitter. */
183764
+ setTelemetry(emitter) {
183765
+ this.telemetryOverride = emitter;
183766
+ }
183767
+ get telemetry() {
183768
+ return this.telemetryOverride ?? this.initialTelemetry;
183769
+ }
183770
+ /**
183771
+ * Decrypt an artifact envelope into plaintext key-value pairs.
183772
+ *
183773
+ * @throws On KMS unwrap failure, AES-GCM auth failure, age decrypt failure,
183774
+ * missing private key (config error), or malformed plaintext JSON.
183775
+ */
183776
+ async decrypt(artifact) {
183777
+ let plaintext;
183778
+ if (artifact.envelope) {
183779
+ plaintext = await this.decryptKmsEnvelope(artifact);
183780
+ } else {
183781
+ plaintext = await this.decryptAge(artifact);
183782
+ }
183783
+ let values;
183784
+ try {
183785
+ values = JSON.parse(plaintext);
183786
+ } catch (err) {
183787
+ this.telemetry?.artifactInvalid({
183788
+ reason: "payload_parse",
183789
+ error: err instanceof Error ? err.message : String(err)
183790
+ });
183791
+ throw err;
183792
+ } finally {
183793
+ plaintext = "";
183794
+ }
183795
+ return { values, keys: artifact.keys, revision: artifact.revision };
183796
+ }
183797
+ /** KMS envelope: unwrap DEK via KMS, then AES-256-GCM decrypt. */
183798
+ async decryptKmsEnvelope(artifact) {
183799
+ const envelope = artifact.envelope;
183800
+ let dek;
183801
+ try {
183802
+ const kms = createKmsProvider(envelope.provider);
183803
+ const wrappedKey = Buffer.from(envelope.wrappedKey, "base64");
183804
+ dek = await kms.unwrap(envelope.keyId, wrappedKey, envelope.algorithm);
183805
+ } catch (err) {
183806
+ this.telemetry?.artifactInvalid({
183807
+ reason: "kms_unwrap",
183808
+ error: err instanceof Error ? err.message : String(err)
183809
+ });
183810
+ throw err;
183811
+ }
183812
+ try {
183813
+ const iv = Buffer.from(envelope.iv, "base64");
183814
+ const authTag = Buffer.from(envelope.authTag, "base64");
183815
+ const ciphertextBuf = Buffer.from(artifact.ciphertext, "base64");
183816
+ const decipher = crypto14.createDecipheriv("aes-256-gcm", dek, iv);
183817
+ decipher.setAuthTag(authTag);
183818
+ return Buffer.concat([decipher.update(ciphertextBuf), decipher.final()]).toString("utf-8");
183819
+ } catch (err) {
183820
+ this.telemetry?.artifactInvalid({
183821
+ reason: "decrypt",
183822
+ error: err instanceof Error ? err.message : String(err)
183823
+ });
183824
+ throw err;
183825
+ } finally {
183826
+ dek.fill(0);
183827
+ }
183828
+ }
183829
+ /** Age-only: decrypt with the static private key. */
183830
+ async decryptAge(artifact) {
183831
+ if (!this.privateKey) {
183832
+ throw new Error(
183833
+ "Artifact requires an age private key. Set CLEF_AGENT_AGE_KEY or use KMS envelope encryption."
183834
+ );
183835
+ }
183836
+ try {
183837
+ return await this.ageDecryptor.decrypt(artifact.ciphertext, this.privateKey);
183838
+ } catch (err) {
183839
+ this.telemetry?.artifactInvalid({
183840
+ reason: err instanceof SyntaxError ? "payload_parse" : "decrypt",
183841
+ error: err instanceof Error ? err.message : String(err)
183842
+ });
183843
+ throw err;
183844
+ }
183845
+ }
183846
+ };
183847
+
183744
183848
  // ../runtime/src/signature.ts
183745
- var crypto14 = __toESM(require("crypto"));
183849
+ var crypto15 = __toESM(require("crypto"));
183746
183850
  function buildSigningPayload(artifact) {
183747
183851
  const fields = [
183748
- "clef-sig-v1",
183852
+ "clef-sig-v2",
183749
183853
  String(artifact.version),
183750
183854
  artifact.identity,
183751
183855
  artifact.environment,
@@ -183757,12 +183861,14 @@ function buildSigningPayload(artifact) {
183757
183861
  artifact.envelope?.provider ?? "",
183758
183862
  artifact.envelope?.keyId ?? "",
183759
183863
  artifact.envelope?.wrappedKey ?? "",
183760
- artifact.envelope?.algorithm ?? ""
183864
+ artifact.envelope?.algorithm ?? "",
183865
+ artifact.envelope?.iv ?? "",
183866
+ artifact.envelope?.authTag ?? ""
183761
183867
  ];
183762
183868
  return Buffer.from(fields.join("\n"), "utf-8");
183763
183869
  }
183764
183870
  function verifySignature(payload, signatureBase64, publicKeyBase64) {
183765
- const keyObj = crypto14.createPublicKey({
183871
+ const keyObj = crypto15.createPublicKey({
183766
183872
  key: Buffer.from(publicKeyBase64, "base64"),
183767
183873
  format: "der",
183768
183874
  type: "spki"
@@ -183770,10 +183876,10 @@ function verifySignature(payload, signatureBase64, publicKeyBase64) {
183770
183876
  const signature = Buffer.from(signatureBase64, "base64");
183771
183877
  const keyType = keyObj.asymmetricKeyType;
183772
183878
  if (keyType === "ed25519") {
183773
- return crypto14.verify(null, payload, keyObj, signature);
183879
+ return crypto15.verify(null, payload, keyObj, signature);
183774
183880
  }
183775
183881
  if (keyType === "ec") {
183776
- return crypto14.verify("sha256", payload, keyObj, signature);
183882
+ return crypto15.verify("sha256", payload, keyObj, signature);
183777
183883
  }
183778
183884
  throw new Error(`Unsupported key type for signature verification: ${keyType}`);
183779
183885
  }
@@ -183785,28 +183891,73 @@ var ArtifactPoller = class {
183785
183891
  lastContentHash = null;
183786
183892
  lastRevision = null;
183787
183893
  lastExpiresAt = null;
183788
- decryptor = new AgeDecryptor();
183894
+ decryptor;
183789
183895
  options;
183896
+ jitMode;
183790
183897
  telemetryOverride;
183791
183898
  constructor(options) {
183792
183899
  this.options = options;
183900
+ this.jitMode = !!options.encryptedStore;
183901
+ this.decryptor = new ArtifactDecryptor({
183902
+ privateKey: options.privateKey,
183903
+ telemetry: options.telemetry
183904
+ });
183905
+ }
183906
+ /** Get the decryptor instance (for JIT mode server wiring). */
183907
+ getDecryptor() {
183908
+ return this.decryptor;
183793
183909
  }
183794
183910
  /** Set or replace the telemetry emitter (e.g. after resolving token from secrets). */
183795
183911
  setTelemetry(emitter) {
183796
183912
  this.telemetryOverride = emitter;
183913
+ this.decryptor.setTelemetry(emitter);
183797
183914
  }
183798
183915
  get telemetry() {
183799
183916
  return this.telemetryOverride ?? this.options.telemetry;
183800
183917
  }
183801
- /** Fetch, validate, decrypt, and cache the artifact. */
183918
+ /**
183919
+ * Fetch, validate, decrypt, and cache the artifact.
183920
+ * Used in cached mode (cacheTtl > 0).
183921
+ */
183802
183922
  async fetchAndDecrypt() {
183923
+ const result = await this.fetchRaw();
183924
+ if (!result) return;
183925
+ await this.validateDecryptAndCache(result.artifact, result.contentHash);
183926
+ }
183927
+ /**
183928
+ * Fetch and validate the artifact without decrypting.
183929
+ * Stores the validated envelope in the encryptedStore for on-demand decryption.
183930
+ * Used in JIT mode (cacheTtl = 0).
183931
+ */
183932
+ async fetchAndValidate() {
183933
+ const result = await this.fetchRaw();
183934
+ if (!result) return;
183935
+ const artifact = this.validateArtifact(result.artifact);
183936
+ this.options.encryptedStore.swap(artifact);
183937
+ this.lastRevision = artifact.revision;
183938
+ this.lastContentHash = result.contentHash ?? null;
183939
+ this.lastExpiresAt = artifact.expiresAt ?? null;
183940
+ this.options.onRefresh?.(artifact.revision);
183941
+ this.telemetry?.artifactRefreshed({
183942
+ revision: artifact.revision,
183943
+ keyCount: artifact.keys.length,
183944
+ kmsEnvelope: !!artifact.envelope
183945
+ });
183946
+ }
183947
+ /**
183948
+ * Fetch the raw artifact from the source (with disk cache fallback),
183949
+ * parse JSON, and check for revocation.
183950
+ *
183951
+ * Returns null when the content hash is unchanged (short-circuit).
183952
+ */
183953
+ async fetchRaw() {
183803
183954
  let raw;
183804
183955
  let contentHash;
183805
183956
  try {
183806
183957
  const result = await this.options.source.fetch();
183807
183958
  raw = result.raw;
183808
183959
  contentHash = result.contentHash;
183809
- if (contentHash && contentHash === this.lastContentHash) return;
183960
+ if (contentHash && contentHash === this.lastContentHash) return null;
183810
183961
  this.options.diskCache?.write(raw, contentHash);
183811
183962
  } catch (err) {
183812
183963
  this.telemetry?.fetchFailed({
@@ -183817,7 +183968,7 @@ var ArtifactPoller = class {
183817
183968
  if (this.options.diskCache) {
183818
183969
  const cached = this.options.diskCache.read();
183819
183970
  if (cached) {
183820
- if (ttl !== void 0) {
183971
+ if (ttl !== void 0 && ttl > 0) {
183821
183972
  const fetchedAt = this.options.diskCache.getFetchedAt();
183822
183973
  if (fetchedAt && (Date.now() - new Date(fetchedAt).getTime()) / 1e3 > ttl) {
183823
183974
  this.options.cache.wipe();
@@ -183831,9 +183982,9 @@ var ArtifactPoller = class {
183831
183982
  }
183832
183983
  raw = cached;
183833
183984
  contentHash = this.options.diskCache.getCachedSha();
183834
- if (contentHash && contentHash === this.lastContentHash) return;
183985
+ if (contentHash && contentHash === this.lastContentHash) return null;
183835
183986
  } else {
183836
- if (ttl !== void 0 && this.options.cache.isExpired(ttl)) {
183987
+ if (ttl !== void 0 && ttl > 0 && this.options.cache.isExpired(ttl)) {
183837
183988
  this.options.cache.wipe();
183838
183989
  this.telemetry?.cacheExpired({
183839
183990
  cacheTtlSeconds: ttl,
@@ -183844,7 +183995,7 @@ var ArtifactPoller = class {
183844
183995
  throw err;
183845
183996
  }
183846
183997
  } else {
183847
- if (ttl !== void 0 && this.options.cache.isExpired(ttl)) {
183998
+ if (ttl !== void 0 && ttl > 0 && this.options.cache.isExpired(ttl)) {
183848
183999
  this.options.cache.wipe();
183849
184000
  this.telemetry?.cacheExpired({
183850
184001
  cacheTtlSeconds: ttl,
@@ -183858,6 +184009,7 @@ var ArtifactPoller = class {
183858
184009
  const parsed = JSON.parse(raw);
183859
184010
  if (parsed.revokedAt) {
183860
184011
  this.options.cache.wipe();
184012
+ this.options.encryptedStore?.wipe();
183861
184013
  this.options.diskCache?.purge();
183862
184014
  this.lastRevision = null;
183863
184015
  this.lastContentHash = null;
@@ -183868,17 +184020,18 @@ var ArtifactPoller = class {
183868
184020
  `Artifact revoked: ${parsed.identity}/${parsed.environment} at ${parsed.revokedAt}`
183869
184021
  );
183870
184022
  }
183871
- await this.validateDecryptAndCache(raw, contentHash);
184023
+ return { artifact: parsed, contentHash };
183872
184024
  }
183873
184025
  /**
183874
- * Validate the artifact, decrypt it, and swap the cache.
183875
- * Emits `artifact.invalid` on any validation or decryption failure,
183876
- * and `artifact.expired` / `artifact.refreshed` on their respective paths.
184026
+ * Validate the artifact envelope: version, required fields, expiry,
184027
+ * revision dedup, integrity hash, and signature.
184028
+ * Emits `artifact.invalid` / `artifact.expired` telemetry on failure.
184029
+ * Returns the validated artifact, or throws.
183877
184030
  */
183878
- async validateDecryptAndCache(raw, contentHash) {
184031
+ validateArtifact(parsed) {
183879
184032
  let artifact;
183880
184033
  try {
183881
- artifact = this.parseAndValidate(raw);
184034
+ artifact = this.validateEnvelope(parsed);
183882
184035
  } catch (err) {
183883
184036
  this.telemetry?.artifactInvalid({
183884
184037
  reason: classifyValidationError(err),
@@ -183888,12 +184041,13 @@ var ArtifactPoller = class {
183888
184041
  }
183889
184042
  if (artifact.expiresAt && Date.now() > new Date(artifact.expiresAt).getTime()) {
183890
184043
  this.options.cache.wipe();
184044
+ this.options.encryptedStore?.wipe();
183891
184045
  this.options.diskCache?.purge();
183892
184046
  this.telemetry?.artifactExpired({ expiresAt: artifact.expiresAt });
183893
184047
  throw new Error(`Artifact expired at ${artifact.expiresAt}`);
183894
184048
  }
183895
- if (artifact.revision === this.lastRevision) return;
183896
- const hash = crypto15.createHash("sha256").update(artifact.ciphertext).digest("hex");
184049
+ if (artifact.revision === this.lastRevision) return artifact;
184050
+ const hash = crypto16.createHash("sha256").update(artifact.ciphertext).digest("hex");
183897
184051
  if (hash !== artifact.ciphertextHash) {
183898
184052
  const err = new Error(
183899
184053
  `Artifact integrity check failed: expected hash ${artifact.ciphertextHash}, got ${hash}`
@@ -183940,59 +184094,33 @@ var ArtifactPoller = class {
183940
184094
  throw err;
183941
184095
  }
183942
184096
  }
183943
- let agePrivateKey;
183944
- if (artifact.envelope) {
183945
- try {
183946
- const kms = createKmsProvider(artifact.envelope.provider);
183947
- const wrappedKey = Buffer.from(artifact.envelope.wrappedKey, "base64");
183948
- const unwrapped = await kms.unwrap(
183949
- artifact.envelope.keyId,
183950
- wrappedKey,
183951
- artifact.envelope.algorithm
183952
- );
183953
- agePrivateKey = unwrapped.toString("utf-8");
183954
- unwrapped.fill(0);
183955
- } catch (err) {
183956
- this.telemetry?.artifactInvalid({
183957
- reason: "kms_unwrap",
183958
- error: err instanceof Error ? err.message : String(err)
183959
- });
183960
- throw err;
183961
- }
183962
- } else {
183963
- if (!this.options.privateKey) {
183964
- throw new Error(
183965
- "Artifact requires an age private key. Set CLEF_AGENT_AGE_KEY or use KMS envelope encryption."
183966
- );
183967
- }
183968
- agePrivateKey = this.options.privateKey;
183969
- }
183970
- try {
183971
- const plaintext = await this.decryptor.decrypt(artifact.ciphertext, agePrivateKey);
183972
- const values = JSON.parse(plaintext);
183973
- this.options.cache.swap(values, artifact.keys, artifact.revision);
183974
- this.lastRevision = artifact.revision;
183975
- this.lastContentHash = contentHash ?? null;
183976
- this.lastExpiresAt = artifact.expiresAt ?? null;
183977
- this.options.onRefresh?.(artifact.revision);
183978
- this.telemetry?.artifactRefreshed({
183979
- revision: artifact.revision,
183980
- keyCount: artifact.keys.length,
183981
- kmsEnvelope: !!artifact.envelope
183982
- });
183983
- } catch (err) {
183984
- if (err instanceof Error && !err.message.includes("integrity check failed")) {
183985
- this.telemetry?.artifactInvalid({
183986
- reason: err instanceof SyntaxError ? "payload_parse" : "decrypt",
183987
- error: err.message
183988
- });
183989
- }
183990
- throw err;
183991
- }
184097
+ return artifact;
184098
+ }
184099
+ /**
184100
+ * Validate then decrypt and cache. Used by fetchAndDecrypt (cached mode).
184101
+ */
184102
+ async validateDecryptAndCache(parsed, contentHash) {
184103
+ const artifact = this.validateArtifact(parsed);
184104
+ if (artifact.revision === this.lastRevision) return;
184105
+ const { values } = await this.decryptor.decrypt(artifact);
184106
+ this.options.cache.swap(values, artifact.keys, artifact.revision);
184107
+ this.lastRevision = artifact.revision;
184108
+ this.lastContentHash = contentHash ?? null;
184109
+ this.lastExpiresAt = artifact.expiresAt ?? null;
184110
+ this.options.onRefresh?.(artifact.revision);
184111
+ this.telemetry?.artifactRefreshed({
184112
+ revision: artifact.revision,
184113
+ keyCount: artifact.keys.length,
184114
+ kmsEnvelope: !!artifact.envelope
184115
+ });
183992
184116
  }
183993
184117
  /** Start the polling loop. Performs an initial fetch immediately. */
183994
184118
  async start() {
183995
- await this.fetchAndDecrypt();
184119
+ if (this.jitMode) {
184120
+ await this.fetchAndValidate();
184121
+ } else {
184122
+ await this.fetchAndDecrypt();
184123
+ }
183996
184124
  this.scheduleNext();
183997
184125
  }
183998
184126
  /** Start only the polling schedule (no initial fetch). */
@@ -184017,7 +184145,11 @@ var ArtifactPoller = class {
184017
184145
  this.timer = setTimeout(async () => {
184018
184146
  this.timer = null;
184019
184147
  try {
184020
- await this.fetchAndDecrypt();
184148
+ if (this.jitMode) {
184149
+ await this.fetchAndValidate();
184150
+ } else {
184151
+ await this.fetchAndDecrypt();
184152
+ }
184021
184153
  } catch (err) {
184022
184154
  this.options.onError?.(err instanceof Error ? err : new Error(String(err)));
184023
184155
  }
@@ -184033,14 +184165,14 @@ var ArtifactPoller = class {
184033
184165
  }
184034
184166
  return MIN_POLL_MS;
184035
184167
  }
184168
+ if (this.jitMode) return MIN_POLL_MS;
184036
184169
  const ttl = this.options.cacheTtl;
184037
184170
  if (ttl !== void 0) {
184038
184171
  return Math.max(ttl / 10 * 1e3, MIN_POLL_MS);
184039
184172
  }
184040
184173
  return 3e4;
184041
184174
  }
184042
- parseAndValidate(raw) {
184043
- const artifact = JSON.parse(raw);
184175
+ validateEnvelope(artifact) {
184044
184176
  if (artifact.version !== 1) {
184045
184177
  throw new Error(`Unsupported artifact version: ${artifact.version}`);
184046
184178
  }
@@ -184048,7 +184180,7 @@ var ArtifactPoller = class {
184048
184180
  throw new Error("Invalid artifact: missing required fields.");
184049
184181
  }
184050
184182
  if (artifact.envelope) {
184051
- if (!artifact.envelope.provider || !artifact.envelope.keyId || !artifact.envelope.wrappedKey || !artifact.envelope.algorithm) {
184183
+ if (!artifact.envelope.provider || !artifact.envelope.keyId || !artifact.envelope.wrappedKey || !artifact.envelope.algorithm || !artifact.envelope.iv || !artifact.envelope.authTag) {
184052
184184
  throw new Error("Invalid artifact: incomplete envelope fields.");
184053
184185
  }
184054
184186
  }
@@ -184065,6 +184197,42 @@ function classifyValidationError(err) {
184065
184197
  return "unknown";
184066
184198
  }
184067
184199
 
184200
+ // ../runtime/src/encrypted-artifact-store.ts
184201
+ var EncryptedArtifactStore = class {
184202
+ artifact = null;
184203
+ _storedAt = null;
184204
+ /** Atomically replace the stored artifact. */
184205
+ swap(artifact) {
184206
+ this.artifact = artifact;
184207
+ this._storedAt = Date.now();
184208
+ }
184209
+ /** Get the current encrypted artifact. Returns null if not yet loaded. */
184210
+ get() {
184211
+ return this.artifact;
184212
+ }
184213
+ /** Whether an artifact has been stored. */
184214
+ isReady() {
184215
+ return this.artifact !== null;
184216
+ }
184217
+ /** Epoch ms of last store, or null. */
184218
+ getStoredAt() {
184219
+ return this._storedAt;
184220
+ }
184221
+ /** Get key names from the stored artifact metadata (no decryption needed). */
184222
+ getKeys() {
184223
+ return this.artifact ? [...this.artifact.keys] : [];
184224
+ }
184225
+ /** Get the revision from the stored artifact. */
184226
+ getRevision() {
184227
+ return this.artifact?.revision ?? null;
184228
+ }
184229
+ /** Clear the stored artifact (on revocation/expiry). */
184230
+ wipe() {
184231
+ this.artifact = null;
184232
+ this._storedAt = null;
184233
+ }
184234
+ };
184235
+
184068
184236
  // ../runtime/src/telemetry.ts
184069
184237
  var SEVERITY = {
184070
184238
  "agent.started": { number: 9, text: "INFO" },
@@ -184357,14 +184525,23 @@ var HttpArtifactSource = class {
184357
184525
  async fetch() {
184358
184526
  const res = await fetch(this.url);
184359
184527
  if (!res.ok) {
184360
- throw new Error(`Failed to fetch artifact from ${this.url}: ${res.status}`);
184528
+ throw new Error(`Failed to fetch artifact from ${this.describe()}: ${res.status}`);
184361
184529
  }
184362
184530
  const raw = await res.text();
184363
184531
  const etag = res.headers.get("etag") ?? void 0;
184364
184532
  return { raw, contentHash: etag };
184365
184533
  }
184366
184534
  describe() {
184367
- return `HTTP ${this.url}`;
184535
+ try {
184536
+ const parsed = new URL(this.url);
184537
+ if (parsed.username || parsed.password) {
184538
+ parsed.username = "***";
184539
+ parsed.password = "";
184540
+ }
184541
+ return `HTTP ${parsed.href}`;
184542
+ } catch {
184543
+ return "HTTP <invalid-url>";
184544
+ }
184368
184545
  }
184369
184546
  };
184370
184547
 
@@ -184410,41 +184587,59 @@ var import_crypto16 = require("crypto");
184410
184587
  var import_express = __toESM(require_express2());
184411
184588
 
184412
184589
  // src/health.ts
184413
- function healthHandler(cache, cacheTtl) {
184590
+ function healthHandler(cache, cacheTtl, encryptedStore) {
184414
184591
  return (_req, res) => {
184415
- const expired = cacheTtl !== void 0 && cache.isExpired(cacheTtl);
184416
- res.json({
184417
- status: "ok",
184418
- revision: cache.getRevision(),
184419
- lastRefreshAt: cache.getSwappedAt(),
184420
- expired
184421
- });
184592
+ if (encryptedStore) {
184593
+ res.json({
184594
+ status: "ok",
184595
+ mode: "jit",
184596
+ revision: encryptedStore.getRevision(),
184597
+ lastRefreshAt: encryptedStore.getStoredAt(),
184598
+ expired: false
184599
+ });
184600
+ } else {
184601
+ const expired = cacheTtl !== void 0 && cache.isExpired(cacheTtl);
184602
+ res.json({
184603
+ status: "ok",
184604
+ mode: "cached",
184605
+ revision: cache.getRevision(),
184606
+ lastRefreshAt: cache.getSwappedAt(),
184607
+ expired
184608
+ });
184609
+ }
184422
184610
  };
184423
184611
  }
184424
- function readyHandler(cache, cacheTtl) {
184612
+ function readyHandler(cache, cacheTtl, encryptedStore) {
184425
184613
  return (_req, res) => {
184426
- if (!cache.isReady()) {
184427
- res.status(503).json({ ready: false, reason: "not_loaded" });
184428
- return;
184429
- }
184430
- if (cacheTtl !== void 0 && cache.isExpired(cacheTtl)) {
184431
- res.status(503).json({ ready: false, reason: "cache_expired" });
184432
- return;
184614
+ if (encryptedStore) {
184615
+ if (!encryptedStore.isReady()) {
184616
+ res.status(503).json({ ready: false, reason: "not_loaded" });
184617
+ return;
184618
+ }
184619
+ res.status(200).json({ ready: true });
184620
+ } else {
184621
+ if (!cache.isReady()) {
184622
+ res.status(503).json({ ready: false, reason: "not_loaded" });
184623
+ return;
184624
+ }
184625
+ if (cacheTtl !== void 0 && cache.isExpired(cacheTtl)) {
184626
+ res.status(503).json({ ready: false, reason: "cache_expired" });
184627
+ return;
184628
+ }
184629
+ res.status(200).json({ ready: true });
184433
184630
  }
184434
- res.status(200).json({ ready: true });
184435
184631
  };
184436
184632
  }
184437
184633
 
184438
184634
  // src/server.ts
184439
184635
  function startAgentServer(options) {
184440
- const { port, token, cache, cacheTtl } = options;
184636
+ const { port, token, cache, cacheTtl, decryptor, encryptedStore } = options;
184637
+ const jitMode = !!decryptor && !!encryptedStore;
184441
184638
  const app = (0, import_express.default)();
184442
- app.use(import_express.default.json());
184639
+ const allowedHosts = /* @__PURE__ */ new Set([`127.0.0.1:${port}`, "127.0.0.1"]);
184443
184640
  app.use("/v1", (req, res, next) => {
184444
184641
  const host = req.headers.host ?? "";
184445
- const actualPort = req.socket.address()?.port ?? port;
184446
- const allowedHosts = [`127.0.0.1:${actualPort}`, `127.0.0.1:${port}`];
184447
- if (!allowedHosts.includes(host)) {
184642
+ if (!allowedHosts.has(host)) {
184448
184643
  res.status(403).json({ error: "Forbidden: invalid Host header" });
184449
184644
  return;
184450
184645
  }
@@ -184454,12 +184649,17 @@ function startAgentServer(options) {
184454
184649
  res.setHeader("Cache-Control", "no-store");
184455
184650
  next();
184456
184651
  });
184457
- app.get("/v1/health", healthHandler(cache, cacheTtl));
184458
- app.get("/v1/ready", readyHandler(cache, cacheTtl));
184652
+ app.get("/v1/health", healthHandler(cache, cacheTtl, encryptedStore));
184653
+ app.get("/v1/ready", readyHandler(cache, cacheTtl, encryptedStore));
184459
184654
  app.use("/v1/secrets", authMiddleware(token));
184460
184655
  app.use("/v1/keys", authMiddleware(token));
184461
184656
  const ttlGuard = (_req, res, next) => {
184462
- if (cacheTtl !== void 0 && cache.isExpired(cacheTtl)) {
184657
+ if (jitMode) {
184658
+ if (!encryptedStore.isReady()) {
184659
+ res.status(503).json({ error: "Secrets not yet loaded" });
184660
+ return;
184661
+ }
184662
+ } else if (cacheTtl !== void 0 && cache.isExpired(cacheTtl)) {
184463
184663
  res.status(503).json({ error: "Secrets expired" });
184464
184664
  return;
184465
184665
  }
@@ -184467,24 +184667,35 @@ function startAgentServer(options) {
184467
184667
  };
184468
184668
  app.use("/v1/secrets", ttlGuard);
184469
184669
  app.use("/v1/keys", ttlGuard);
184470
- app.get("/v1/secrets", (_req, res) => {
184471
- const all = cache.getAll();
184472
- if (!all) {
184473
- res.status(503).json({ error: "Secrets not yet loaded" });
184474
- return;
184475
- }
184476
- res.json(all);
184477
- });
184478
- app.get("/v1/secrets/:key", (req, res) => {
184479
- const value = cache.get(req.params.key);
184480
- if (value === void 0) {
184481
- res.status(404).json({ error: `Secret '${req.params.key}' not found` });
184482
- return;
184670
+ app.get("/v1/secrets", async (_req, res) => {
184671
+ if (jitMode) {
184672
+ const artifact = encryptedStore.get();
184673
+ if (!artifact) {
184674
+ res.status(503).json({ error: "Secrets not yet loaded" });
184675
+ return;
184676
+ }
184677
+ try {
184678
+ const { values } = await decryptor.decrypt(artifact);
184679
+ res.json(values);
184680
+ } catch (err) {
184681
+ const message = err instanceof Error ? err.message : String(err);
184682
+ res.status(503).json({ error: "Decryption failed", detail: message });
184683
+ }
184684
+ } else {
184685
+ const all = cache.getAll();
184686
+ if (!all) {
184687
+ res.status(503).json({ error: "Secrets not yet loaded" });
184688
+ return;
184689
+ }
184690
+ res.json(all);
184483
184691
  }
184484
- res.json({ value });
184485
184692
  });
184486
184693
  app.get("/v1/keys", (_req, res) => {
184487
- res.json(cache.getKeys());
184694
+ if (jitMode) {
184695
+ res.json(encryptedStore.getKeys());
184696
+ } else {
184697
+ res.json(cache.getKeys());
184698
+ }
184488
184699
  });
184489
184700
  const url = `http://127.0.0.1:${port}`;
184490
184701
  return new Promise((resolve, reject) => {
@@ -184510,11 +184721,11 @@ function startAgentServer(options) {
184510
184721
  });
184511
184722
  }
184512
184723
  function authMiddleware(token) {
184724
+ const expectedBuf = Buffer.from(token);
184513
184725
  return (req, res, next) => {
184514
184726
  const authHeader = req.headers.authorization ?? "";
184515
184727
  const provided = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : "";
184516
184728
  const providedBuf = Buffer.from(provided);
184517
- const expectedBuf = Buffer.from(token);
184518
184729
  if (!provided || providedBuf.length !== expectedBuf.length || !(0, import_crypto16.timingSafeEqual)(providedBuf, expectedBuf)) {
184519
184730
  res.status(401).json({ error: "Unauthorized" });
184520
184731
  return;
@@ -184530,6 +184741,8 @@ var Daemon = class {
184530
184741
  shutdownResolve;
184531
184742
  shutdownPromise;
184532
184743
  startedAt;
184744
+ sigTermHandler;
184745
+ sigIntHandler;
184533
184746
  constructor(options) {
184534
184747
  this.options = options;
184535
184748
  this.startedAt = Date.now();
@@ -184544,6 +184757,8 @@ var Daemon = class {
184544
184757
  if (this.shutdownRequested) return;
184545
184758
  this.shutdownRequested = true;
184546
184759
  onLog?.("Shutting down...");
184760
+ if (this.sigTermHandler) process.off("SIGTERM", this.sigTermHandler);
184761
+ if (this.sigIntHandler) process.off("SIGINT", this.sigIntHandler);
184547
184762
  poller.stop();
184548
184763
  telemetry?.agentStopped({
184549
184764
  reason: "signal",
@@ -184560,14 +184775,16 @@ var Daemon = class {
184560
184775
  onLog?.("Shutdown complete.");
184561
184776
  this.shutdownResolve?.();
184562
184777
  };
184563
- process.on("SIGTERM", () => {
184778
+ this.sigTermHandler = () => {
184564
184779
  shutdown().catch(() => {
184565
184780
  });
184566
- });
184567
- process.on("SIGINT", () => {
184781
+ };
184782
+ this.sigIntHandler = () => {
184568
184783
  shutdown().catch(() => {
184569
184784
  });
184570
- });
184785
+ };
184786
+ process.on("SIGTERM", this.sigTermHandler);
184787
+ process.on("SIGINT", this.sigIntHandler);
184571
184788
  onLog?.(`Agent server listening at ${server.url}`);
184572
184789
  poller.startPolling();
184573
184790
  onLog?.("Agent ready. Polling for updates.");
@@ -184579,11 +184796,12 @@ var Daemon = class {
184579
184796
  };
184580
184797
 
184581
184798
  // package.json
184582
- var version5 = "0.1.8";
184799
+ var version5 = "0.1.9-beta.57";
184583
184800
 
184584
184801
  // src/main.ts
184585
184802
  async function main() {
184586
184803
  const config = resolveConfig();
184804
+ const jitMode = config.cacheTtl === 0;
184587
184805
  let privateKey;
184588
184806
  try {
184589
184807
  const decryptor = new AgeDecryptor();
@@ -184607,6 +184825,7 @@ async function main() {
184607
184825
  }
184608
184826
  const diskCache = config.cachePath && config.vcs ? new DiskCache(config.cachePath, config.vcs.identity, config.vcs.environment) : void 0;
184609
184827
  const cache = new SecretsCache();
184828
+ const encryptedStore = jitMode ? new EncryptedArtifactStore() : void 0;
184610
184829
  const poller = new ArtifactPoller({
184611
184830
  source,
184612
184831
  privateKey,
@@ -184614,9 +184833,17 @@ async function main() {
184614
184833
  diskCache,
184615
184834
  cacheTtl: config.cacheTtl,
184616
184835
  verifyKey: config.verifyKey,
184836
+ encryptedStore,
184617
184837
  onError: (err) => console.error(`[clef-agent] poll error: ${err.message}`)
184618
184838
  });
184619
- await poller.fetchAndDecrypt();
184839
+ if (jitMode) {
184840
+ await poller.fetchAndValidate();
184841
+ const artifact = encryptedStore.get();
184842
+ const { values } = await poller.getDecryptor().decrypt(artifact);
184843
+ cache.swap(values, artifact.keys, artifact.revision);
184844
+ } else {
184845
+ await poller.fetchAndDecrypt();
184846
+ }
184620
184847
  let telemetry;
184621
184848
  if (config.telemetry) {
184622
184849
  const headers = {};
@@ -184642,11 +184869,15 @@ async function main() {
184642
184869
  });
184643
184870
  poller.setTelemetry(telemetry);
184644
184871
  }
184872
+ if (jitMode) {
184873
+ cache.wipe();
184874
+ }
184645
184875
  const server = await startAgentServer({
184646
184876
  port: config.port,
184647
184877
  token: config.token,
184648
184878
  cache,
184649
- cacheTtl: config.cacheTtl
184879
+ cacheTtl: config.cacheTtl,
184880
+ ...jitMode ? { decryptor: poller.getDecryptor(), encryptedStore } : {}
184650
184881
  });
184651
184882
  const daemon = new Daemon({
184652
184883
  poller,
@@ -184655,7 +184886,8 @@ async function main() {
184655
184886
  onLog: (msg) => console.log(`[clef-agent] ${msg}`)
184656
184887
  });
184657
184888
  telemetry?.agentStarted({ version: version5 });
184658
- console.log(`[clef-agent] token: [${config.token.length} chars]`);
184889
+ console.log(`[clef-agent] mode: ${jitMode ? "jit" : "cached"}`);
184890
+ console.log(`[clef-agent] token: [set]`);
184659
184891
  await daemon.start();
184660
184892
  }
184661
184893
  main().catch((err) => {