envpkt 0.10.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -8,6 +8,7 @@ import { TypeCompiler } from "@sinclair/typebox/compiler";
8
8
  import { Env, Fs, Path, Platform } from "functype-os";
9
9
  import { TomlDate, parse, stringify } from "smol-toml";
10
10
  import { FormatRegistry, Type } from "@sinclair/typebox";
11
+ import { directSilentLogger } from "functype-log";
11
12
  import { execFileSync } from "node:child_process";
12
13
  import { homedir } from "node:os";
13
14
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -88,8 +89,11 @@ const computeAudit = (config, fnoxKeys, today, aliasTable) => {
88
89
  const nonAliasMetaKeys = new Set(nonAliasEntries.map(([k]) => k));
89
90
  const nonAliasHealth = nonAliasEntries.map(([key, meta]) => classifySecret(key, meta, keys, staleWarningDays, requireExpiration, requireService, now));
90
91
  const healthByKey = new Map(nonAliasHealth.map((h) => [h.key, h]));
92
+ const parseTargetKey = (from_key) => {
93
+ return /^secret\.(.+)$/.exec(from_key)?.[1];
94
+ };
91
95
  const aliasHealth = aliasEntries.map(([key, meta]) => {
92
- const targetKey = (aliasTable?.entries.get(`secret.${key}`))?.targetKey;
96
+ const targetKey = (aliasTable?.entries.get(`secret.${key}`))?.targetKey ?? (meta.from_key !== void 0 ? parseTargetKey(meta.from_key) : void 0);
93
97
  const targetHealth = targetKey !== void 0 ? healthByKey.get(targetKey) : void 0;
94
98
  const targetRef = meta.from_key ?? (targetKey !== void 0 ? `secret.${targetKey}` : "");
95
99
  if (!targetHealth) return {
@@ -547,12 +551,14 @@ const formatAuditJson = (audit) => JSON.stringify({
547
551
  missing: audit.missing,
548
552
  missing_metadata: audit.missing_metadata,
549
553
  orphaned: audit.orphaned,
554
+ aliases: audit.aliases,
550
555
  secrets: audit.secrets.map((s) => ({
551
556
  key: s.key,
552
557
  service: s.service.fold(() => null, (sv) => sv),
553
558
  status: s.status,
554
559
  days_remaining: s.days_remaining.fold(() => null, (d) => d),
555
560
  rotation_url: s.rotation_url.fold(() => null, (u) => u),
561
+ alias_of: s.alias_of.fold(() => null, (a) => a),
556
562
  purpose: s.purpose.fold(() => null, (p) => p),
557
563
  issues: s.issues.toArray()
558
564
  })).toArray()
@@ -1279,7 +1285,15 @@ const bootSafe = (options) => {
1279
1285
  const inject = opts.inject !== false;
1280
1286
  const failOnExpired = opts.failOnExpired !== false;
1281
1287
  const warnOnly = opts.warnOnly ?? false;
1282
- return resolveAndLoad(opts).flatMap(({ config, configPath, configDir, configSource }) => validateAliases(config).fold((err) => Left(err), (aliasTable) => {
1288
+ const log = (opts.logger ?? directSilentLogger).withContext({ component: "envpkt.boot" });
1289
+ return resolveAndLoad(opts).flatMap(({ config, configPath, configDir, configSource }) => validateAliases(config).fold((err) => {
1290
+ log.warn("alias.validate.failed", {
1291
+ tag: err._tag,
1292
+ key: "key" in err ? err.key : void 0
1293
+ });
1294
+ return Left(err);
1295
+ }, (aliasTable) => {
1296
+ log.debug("alias.validate.success", { aliases: aliasTable.entries.size });
1283
1297
  const secretEntries = config.secret ?? {};
1284
1298
  const envEntries = config.env ?? {};
1285
1299
  const nonAliasSecretEntries = Object.fromEntries(Object.entries(secretEntries).filter(([, meta]) => meta.from_key === void 0));
@@ -1308,19 +1322,28 @@ const bootSafe = (options) => {
1308
1322
  const sealedKeys = /* @__PURE__ */ new Set();
1309
1323
  const identityFilePath = resolveIdentityFilePath(config, configDir, true);
1310
1324
  if (hasSealedValues) identityFilePath.fold(() => {
1325
+ log.warn("phase.sealed.no_identity_file", { sealed_keys: nonAliasMetaKeys.filter((k) => !!nonAliasSecretEntries[k]?.encrypted_value).length });
1311
1326
  warnings.push("Sealed values found but no identity file available for decryption");
1312
1327
  }, (idPath) => {
1313
1328
  unsealSecrets(nonAliasSecretEntries, idPath).fold((err) => {
1329
+ log.warn("phase.sealed.decrypt_failed", { message: err.message });
1314
1330
  warnings.push(`Sealed value decryption failed: ${err.message}`);
1315
1331
  }, (unsealed) => {
1316
1332
  const unsealedEntries = Object.entries(unsealed);
1317
1333
  Object.assign(secrets, unsealed);
1318
1334
  injected.push(...unsealedEntries.map(([key]) => key));
1319
- unsealedEntries.map(([key]) => key).forEach((key) => sealedKeys.add(key));
1335
+ unsealedEntries.forEach(([key]) => {
1336
+ sealedKeys.add(key);
1337
+ log.debug("phase.sealed.resolved", { key });
1338
+ });
1320
1339
  });
1321
1340
  });
1322
1341
  const remainingKeys = nonAliasMetaKeys.filter((k) => !sealedKeys.has(k));
1323
1342
  if (remainingKeys.length > 0) if (fnoxAvailable()) fnoxExport(opts.profile, identityKey.orUndefined()).fold((err) => {
1343
+ log.warn("phase.fnox.export_failed", {
1344
+ message: err.message,
1345
+ skipped: remainingKeys.length
1346
+ });
1324
1347
  warnings.push(`fnox export failed: ${err.message}`);
1325
1348
  skipped.push(...remainingKeys);
1326
1349
  }, (exported) => {
@@ -1328,11 +1351,22 @@ const bootSafe = (options) => {
1328
1351
  const notFound = remainingKeys.filter((key) => !(key in exported));
1329
1352
  found.forEach((key) => {
1330
1353
  secrets[key] = exported[key];
1354
+ log.debug("phase.fnox.resolved", {
1355
+ key,
1356
+ profile: opts.profile
1357
+ });
1358
+ });
1359
+ notFound.forEach((key) => {
1360
+ log.debug("phase.fnox.not_in_export", {
1361
+ key,
1362
+ profile: opts.profile
1363
+ });
1331
1364
  });
1332
1365
  injected.push(...found);
1333
1366
  skipped.push(...notFound);
1334
1367
  });
1335
1368
  else {
1369
+ log.debug("phase.fnox.unavailable", { skipped: remainingKeys.length });
1336
1370
  if (!hasSealedValues) warnings.push("fnox not available — no secrets injected");
1337
1371
  else warnings.push("fnox not available — unsealed secrets could not be resolved");
1338
1372
  skipped.push(...remainingKeys);
@@ -1344,7 +1378,17 @@ const bootSafe = (options) => {
1344
1378
  if (targetValue !== void 0) {
1345
1379
  secrets[aliasKey] = targetValue;
1346
1380
  injected.push(aliasKey);
1347
- } else skipped.push(aliasKey);
1381
+ log.debug("phase.alias.copied", {
1382
+ alias: aliasKey,
1383
+ target: entry.targetKey
1384
+ });
1385
+ } else {
1386
+ skipped.push(aliasKey);
1387
+ log.debug("phase.alias.target_unresolved", {
1388
+ alias: aliasKey,
1389
+ target: entry.targetKey
1390
+ });
1391
+ }
1348
1392
  });
1349
1393
  aliasEnvKeys.forEach((aliasKey) => {
1350
1394
  const entry = aliasTable.entries.get(`env.${aliasKey}`);
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as _$_sinclair_typebox0 from "@sinclair/typebox";
2
2
  import { Static } from "@sinclair/typebox";
3
3
  import { Either, List, Option } from "functype";
4
+ import { DirectLogger, DirectLogger as DirectLogger$1, DirectTestLoggerHandle, LogEntry, LogLevel, LogMetadata, createDirectConsoleLogger, createDirectTestLogger, directSilentLogger } from "functype-log";
4
5
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
6
  import { CallToolResult, ReadResourceResult, Resource } from "@modelcontextprotocol/sdk/types.js";
6
7
 
@@ -285,6 +286,13 @@ type BootOptions = {
285
286
  readonly inject?: boolean;
286
287
  readonly failOnExpired?: boolean;
287
288
  readonly warnOnly?: boolean;
289
+ /**
290
+ * Optional diagnostic logger. Defaults to a silent logger (zero overhead).
291
+ * Receives structured trace events at boot resolution decision points
292
+ * (alias validation, sealed phase, fnox phase, alias copy phase). Useful
293
+ * for debugging why a particular secret landed in skipped[] vs injected[].
294
+ */
295
+ readonly logger?: DirectLogger$1;
288
296
  };
289
297
  type BootResult = {
290
298
  readonly audit: AuditResult;
@@ -622,4 +630,4 @@ type ToolDef = {
622
630
  declare const toolDefinitions: readonly ToolDef[];
623
631
  declare const callTool: (name: string, args: Record<string, unknown>) => CallToolResult;
624
632
  //#endregion
625
- export { type AgentIdentity, AgentIdentitySchema, type AliasError, type AliasTable, type AuditResult, type BootError, type BootOptions, type BootResult, type CallbackConfig, CallbackConfigSchema, type CatalogError, type CheckResult, type ConfidenceLevel, type ConfigError, type ConfigSource, type ConsumerType, type CredentialPattern, type DriftEntry, type DriftStatus, type EnvAuditResult, type EnvDriftEntry, type EnvDriftStatus, type EnvMeta, EnvMetaSchema, EnvpktBootError, type EnvpktConfig, EnvpktConfigSchema, type FleetAgent, type FleetHealth, type FnoxConfig, type FnoxError, type FnoxSecret, type FormatPacketOptions, type HealthStatus, type Identity, type IdentityError, IdentitySchema, type KeygenError, type KeygenResult, type LifecycleConfig, LifecycleConfigSchema, type MatchResult, type ResolveOptions, type ResolveResult, type ResolvedPath, type ScanOptions, type ScanResult, type SealError, type SecretDisplay, type SecretHealth, type SecretMeta, SecretMetaSchema, type SecretStatus, type TomlEditError, type ToolsConfig, ToolsConfigSchema, ageAvailable, ageDecrypt, ageEncrypt, appendSection, boot, bootSafe, callTool, compareFnoxAndEnvpkt, computeAudit, computeEnvAudit, createServer, deriveServiceFromName, detectFnox, discoverConfig, envCheck, envScan, extractFnoxKeys, findConfigPath, fnoxAvailable, fnoxExport, fnoxGet, formatAliasError, formatPacket, generateKeypair, generateTomlFromScan, isEnvAlias, isSecretAlias, loadCatalog, loadConfig, loadConfigFromCwd, maskValue, matchEnvVar, matchValueShape, parseToml, readConfigFile, readFnoxConfig, readResource, removeSection, renameSection, resolveConfig, resolveConfigPath, resolveInlineKey, resolveKeyPath, resolveSecrets, resolveValues, resourceDefinitions, scanEnv, scanFleet, sealSecrets, startServer, toolDefinitions, unsealSecrets, unwrapAgentKey, updateConfigIdentity, updateSectionFields, validateAliases, validateConfig };
633
+ export { type AgentIdentity, AgentIdentitySchema, type AliasError, type AliasTable, type AuditResult, type BootError, type BootOptions, type BootResult, type CallbackConfig, CallbackConfigSchema, type CatalogError, type CheckResult, type ConfidenceLevel, type ConfigError, type ConfigSource, type ConsumerType, type CredentialPattern, type DirectLogger, type DirectTestLoggerHandle, type DriftEntry, type DriftStatus, type EnvAuditResult, type EnvDriftEntry, type EnvDriftStatus, type EnvMeta, EnvMetaSchema, EnvpktBootError, type EnvpktConfig, EnvpktConfigSchema, type FleetAgent, type FleetHealth, type FnoxConfig, type FnoxError, type FnoxSecret, type FormatPacketOptions, type HealthStatus, type Identity, type IdentityError, IdentitySchema, type KeygenError, type KeygenResult, type LifecycleConfig, LifecycleConfigSchema, type LogEntry, type LogLevel, type LogMetadata, type MatchResult, type ResolveOptions, type ResolveResult, type ResolvedPath, type ScanOptions, type ScanResult, type SealError, type SecretDisplay, type SecretHealth, type SecretMeta, SecretMetaSchema, type SecretStatus, type TomlEditError, type ToolsConfig, ToolsConfigSchema, ageAvailable, ageDecrypt, ageEncrypt, appendSection, boot, bootSafe, callTool, compareFnoxAndEnvpkt, computeAudit, computeEnvAudit, createDirectConsoleLogger, createDirectTestLogger, createServer, deriveServiceFromName, detectFnox, directSilentLogger, discoverConfig, envCheck, envScan, extractFnoxKeys, findConfigPath, fnoxAvailable, fnoxExport, fnoxGet, formatAliasError, formatPacket, generateKeypair, generateTomlFromScan, isEnvAlias, isSecretAlias, loadCatalog, loadConfig, loadConfigFromCwd, maskValue, matchEnvVar, matchValueShape, parseToml, readConfigFile, readFnoxConfig, readResource, removeSection, renameSection, resolveConfig, resolveConfigPath, resolveInlineKey, resolveKeyPath, resolveSecrets, resolveValues, resourceDefinitions, scanEnv, scanFleet, sealSecrets, startServer, toolDefinitions, unsealSecrets, unwrapAgentKey, updateConfigIdentity, updateSectionFields, validateAliases, validateConfig };
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { Cond, Either, Left, List, None, Option, Right, Some, Try } from "functy
5
5
  import { Env, Fs, Path, Platform } from "functype-os";
6
6
  import { TomlDate, parse } from "smol-toml";
7
7
  import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
8
+ import { createDirectConsoleLogger, createDirectTestLogger, directSilentLogger, directSilentLogger as directSilentLogger$1 } from "functype-log";
8
9
  import { execFileSync } from "node:child_process";
9
10
  import { homedir } from "node:os";
10
11
  import { createInterface } from "node:readline";
@@ -650,8 +651,11 @@ const computeAudit = (config, fnoxKeys, today, aliasTable) => {
650
651
  const nonAliasMetaKeys = new Set(nonAliasEntries.map(([k]) => k));
651
652
  const nonAliasHealth = nonAliasEntries.map(([key, meta]) => classifySecret(key, meta, keys, staleWarningDays, requireExpiration, requireService, now));
652
653
  const healthByKey = new Map(nonAliasHealth.map((h) => [h.key, h]));
654
+ const parseTargetKey = (from_key) => {
655
+ return /^secret\.(.+)$/.exec(from_key)?.[1];
656
+ };
653
657
  const aliasHealth = aliasEntries.map(([key, meta]) => {
654
- const targetKey = (aliasTable?.entries.get(`secret.${key}`))?.targetKey;
658
+ const targetKey = (aliasTable?.entries.get(`secret.${key}`))?.targetKey ?? (meta.from_key !== void 0 ? parseTargetKey(meta.from_key) : void 0);
655
659
  const targetHealth = targetKey !== void 0 ? healthByKey.get(targetKey) : void 0;
656
660
  const targetRef = meta.from_key ?? (targetKey !== void 0 ? `secret.${targetKey}` : "");
657
661
  if (!targetHealth) return {
@@ -1927,7 +1931,15 @@ const bootSafe = (options) => {
1927
1931
  const inject = opts.inject !== false;
1928
1932
  const failOnExpired = opts.failOnExpired !== false;
1929
1933
  const warnOnly = opts.warnOnly ?? false;
1930
- return resolveAndLoad(opts).flatMap(({ config, configPath, configDir, configSource }) => validateAliases(config).fold((err) => Left(err), (aliasTable) => {
1934
+ const log = (opts.logger ?? directSilentLogger$1).withContext({ component: "envpkt.boot" });
1935
+ return resolveAndLoad(opts).flatMap(({ config, configPath, configDir, configSource }) => validateAliases(config).fold((err) => {
1936
+ log.warn("alias.validate.failed", {
1937
+ tag: err._tag,
1938
+ key: "key" in err ? err.key : void 0
1939
+ });
1940
+ return Left(err);
1941
+ }, (aliasTable) => {
1942
+ log.debug("alias.validate.success", { aliases: aliasTable.entries.size });
1931
1943
  const secretEntries = config.secret ?? {};
1932
1944
  const envEntries = config.env ?? {};
1933
1945
  const nonAliasSecretEntries = Object.fromEntries(Object.entries(secretEntries).filter(([, meta]) => meta.from_key === void 0));
@@ -1956,19 +1968,28 @@ const bootSafe = (options) => {
1956
1968
  const sealedKeys = /* @__PURE__ */ new Set();
1957
1969
  const identityFilePath = resolveIdentityFilePath(config, configDir, true);
1958
1970
  if (hasSealedValues) identityFilePath.fold(() => {
1971
+ log.warn("phase.sealed.no_identity_file", { sealed_keys: nonAliasMetaKeys.filter((k) => !!nonAliasSecretEntries[k]?.encrypted_value).length });
1959
1972
  warnings.push("Sealed values found but no identity file available for decryption");
1960
1973
  }, (idPath) => {
1961
1974
  unsealSecrets(nonAliasSecretEntries, idPath).fold((err) => {
1975
+ log.warn("phase.sealed.decrypt_failed", { message: err.message });
1962
1976
  warnings.push(`Sealed value decryption failed: ${err.message}`);
1963
1977
  }, (unsealed) => {
1964
1978
  const unsealedEntries = Object.entries(unsealed);
1965
1979
  Object.assign(secrets, unsealed);
1966
1980
  injected.push(...unsealedEntries.map(([key]) => key));
1967
- unsealedEntries.map(([key]) => key).forEach((key) => sealedKeys.add(key));
1981
+ unsealedEntries.forEach(([key]) => {
1982
+ sealedKeys.add(key);
1983
+ log.debug("phase.sealed.resolved", { key });
1984
+ });
1968
1985
  });
1969
1986
  });
1970
1987
  const remainingKeys = nonAliasMetaKeys.filter((k) => !sealedKeys.has(k));
1971
1988
  if (remainingKeys.length > 0) if (fnoxAvailable()) fnoxExport(opts.profile, identityKey.orUndefined()).fold((err) => {
1989
+ log.warn("phase.fnox.export_failed", {
1990
+ message: err.message,
1991
+ skipped: remainingKeys.length
1992
+ });
1972
1993
  warnings.push(`fnox export failed: ${err.message}`);
1973
1994
  skipped.push(...remainingKeys);
1974
1995
  }, (exported) => {
@@ -1976,11 +1997,22 @@ const bootSafe = (options) => {
1976
1997
  const notFound = remainingKeys.filter((key) => !(key in exported));
1977
1998
  found.forEach((key) => {
1978
1999
  secrets[key] = exported[key];
2000
+ log.debug("phase.fnox.resolved", {
2001
+ key,
2002
+ profile: opts.profile
2003
+ });
2004
+ });
2005
+ notFound.forEach((key) => {
2006
+ log.debug("phase.fnox.not_in_export", {
2007
+ key,
2008
+ profile: opts.profile
2009
+ });
1979
2010
  });
1980
2011
  injected.push(...found);
1981
2012
  skipped.push(...notFound);
1982
2013
  });
1983
2014
  else {
2015
+ log.debug("phase.fnox.unavailable", { skipped: remainingKeys.length });
1984
2016
  if (!hasSealedValues) warnings.push("fnox not available — no secrets injected");
1985
2017
  else warnings.push("fnox not available — unsealed secrets could not be resolved");
1986
2018
  skipped.push(...remainingKeys);
@@ -1992,7 +2024,17 @@ const bootSafe = (options) => {
1992
2024
  if (targetValue !== void 0) {
1993
2025
  secrets[aliasKey] = targetValue;
1994
2026
  injected.push(aliasKey);
1995
- } else skipped.push(aliasKey);
2027
+ log.debug("phase.alias.copied", {
2028
+ alias: aliasKey,
2029
+ target: entry.targetKey
2030
+ });
2031
+ } else {
2032
+ skipped.push(aliasKey);
2033
+ log.debug("phase.alias.target_unresolved", {
2034
+ alias: aliasKey,
2035
+ target: entry.targetKey
2036
+ });
2037
+ }
1996
2038
  });
1997
2039
  aliasEnvKeys.forEach((aliasKey) => {
1998
2040
  const entry = aliasTable.entries.get(`env.${aliasKey}`);
@@ -2637,4 +2679,4 @@ const startServer = async () => {
2637
2679
  await server.connect(transport);
2638
2680
  };
2639
2681
  //#endregion
2640
- export { AgentIdentitySchema, CallbackConfigSchema, ConsumerType, EnvMetaSchema, EnvpktBootError, EnvpktConfigSchema, IdentitySchema, LifecycleConfigSchema, SecretMetaSchema, ToolsConfigSchema, ageAvailable, ageDecrypt, ageEncrypt, appendSection, boot, bootSafe, callTool, compareFnoxAndEnvpkt, computeAudit, computeEnvAudit, createServer, deriveServiceFromName, detectFnox, discoverConfig, envCheck, envScan, extractFnoxKeys, findConfigPath, fnoxAvailable, fnoxExport, fnoxGet, formatAliasError, formatPacket, generateKeypair, generateTomlFromScan, isEnvAlias, isSecretAlias, loadCatalog, loadConfig, loadConfigFromCwd, maskValue, matchEnvVar, matchValueShape, parseToml, readConfigFile, readFnoxConfig, readResource, removeSection, renameSection, resolveConfig, resolveConfigPath, resolveInlineKey, resolveKeyPath, resolveSecrets, resolveValues, resourceDefinitions, scanEnv, scanFleet, sealSecrets, startServer, toolDefinitions, unsealSecrets, unwrapAgentKey, updateConfigIdentity, updateSectionFields, validateAliases, validateConfig };
2682
+ export { AgentIdentitySchema, CallbackConfigSchema, ConsumerType, EnvMetaSchema, EnvpktBootError, EnvpktConfigSchema, IdentitySchema, LifecycleConfigSchema, SecretMetaSchema, ToolsConfigSchema, ageAvailable, ageDecrypt, ageEncrypt, appendSection, boot, bootSafe, callTool, compareFnoxAndEnvpkt, computeAudit, computeEnvAudit, createDirectConsoleLogger, createDirectTestLogger, createServer, deriveServiceFromName, detectFnox, directSilentLogger, discoverConfig, envCheck, envScan, extractFnoxKeys, findConfigPath, fnoxAvailable, fnoxExport, fnoxGet, formatAliasError, formatPacket, generateKeypair, generateTomlFromScan, isEnvAlias, isSecretAlias, loadCatalog, loadConfig, loadConfigFromCwd, maskValue, matchEnvVar, matchValueShape, parseToml, readConfigFile, readFnoxConfig, readResource, removeSection, renameSection, resolveConfig, resolveConfigPath, resolveInlineKey, resolveKeyPath, resolveSecrets, resolveValues, resourceDefinitions, scanEnv, scanFleet, sealSecrets, startServer, toolDefinitions, unsealSecrets, unwrapAgentKey, updateConfigIdentity, updateSectionFields, validateAliases, validateConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envpkt",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "Credential lifecycle and fleet management for AI agents",
5
5
  "keywords": [
6
6
  "credentials",
@@ -43,6 +43,7 @@
43
43
  "@sinclair/typebox": "^0.34.49",
44
44
  "commander": "^14.0.3",
45
45
  "functype": "^0.60.0",
46
+ "functype-log": "^0.60.0",
46
47
  "functype-os": "^0.60.0",
47
48
  "smol-toml": "^1.6.1"
48
49
  },