envpkt 0.8.0 → 0.8.1

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
@@ -825,14 +825,14 @@ const extractFnoxKeys = (config) => new Set(Object.keys(config.secrets));
825
825
  //#region src/core/keygen.ts
826
826
  /** Resolve the age identity file path: ENVPKT_AGE_KEY_FILE env var > ~/.envpkt/age-key.txt */
827
827
  const resolveKeyPath = () => process.env["ENVPKT_AGE_KEY_FILE"] ?? join(homedir(), ".envpkt", "age-key.txt");
828
- /** Generate an age keypair and write to disk */
828
+ /** Generate an age keypair and write to disk. Refuses to overwrite if the file already exists. */
829
829
  const generateKeypair = (options) => {
830
830
  if (!ageAvailable()) return Left({
831
831
  _tag: "AgeNotFound",
832
832
  message: "age-keygen CLI not found on PATH. Install age: https://github.com/FiloSottile/age"
833
833
  });
834
834
  const outputPath = options?.outputPath ?? resolveKeyPath();
835
- if (existsSync(outputPath) && !options?.force) return Left({
835
+ if (existsSync(outputPath)) return Left({
836
836
  _tag: "KeyExists",
837
837
  path: outputPath
838
838
  });
@@ -2738,15 +2738,26 @@ const tildeShorten = (p) => {
2738
2738
  };
2739
2739
  /** Derive a default identity name from the config path's parent directory */
2740
2740
  const deriveIdentityName = (configPath) => basename(dirname(resolve(configPath)));
2741
+ /**
2742
+ * Derive a project-specific key path from the config path.
2743
+ * - `envpkt.toml` → `~/.envpkt/<dir>-key.txt`
2744
+ * - `prod.envpkt.toml` → `~/.envpkt/<dir>-prod-key.txt`
2745
+ * - `foo.envpkt.toml` → `~/.envpkt/<dir>-foo-key.txt`
2746
+ */
2747
+ const deriveKeyPath = (configPath) => {
2748
+ const abs = resolve(configPath);
2749
+ const dir = basename(dirname(abs));
2750
+ const stem = basename(abs).replace(/\.envpkt\.toml$/, "").replace(/\.toml$/, "");
2751
+ const name = stem === "envpkt" || stem === "" ? dir : `${dir}-${stem}`;
2752
+ return join(homedir(), ".envpkt", `${name}-key.txt`);
2753
+ };
2741
2754
  const runKeygen = (options) => {
2742
- const outputPath = options.output ?? resolveKeyPath();
2743
- generateKeypair({
2744
- force: options.force,
2745
- outputPath
2746
- }).fold((err) => {
2755
+ const configPath = resolve(options.config ?? join(process.cwd(), "envpkt.toml"));
2756
+ generateKeypair({ outputPath: options.output ?? (options.global ? resolveKeyPath() : deriveKeyPath(configPath)) }).fold((err) => {
2747
2757
  if (err._tag === "KeyExists") {
2748
2758
  console.error(`${YELLOW}Warning:${RESET} Identity file already exists: ${CYAN}${err.path}${RESET}`);
2749
- console.error(`${DIM}Use --force to overwrite.${RESET}`);
2759
+ console.error(`${DIM}To replace it: remove the file first, then re-run keygen.${RESET}`);
2760
+ console.error(`${DIM}To use a different path: pass -o <path>.${RESET}`);
2750
2761
  process.exit(1);
2751
2762
  }
2752
2763
  console.error(formatError(err));
@@ -2755,7 +2766,6 @@ const runKeygen = (options) => {
2755
2766
  console.log(`${GREEN}Generated${RESET} age identity: ${CYAN}${identityPath}${RESET}`);
2756
2767
  console.log(`${BOLD}Recipient:${RESET} ${recipient}`);
2757
2768
  console.log("");
2758
- const configPath = resolve(options.config ?? join(process.cwd(), "envpkt.toml"));
2759
2769
  if (existsSync(configPath)) {
2760
2770
  const name = deriveIdentityName(configPath);
2761
2771
  const keyFile = tildeShorten(identityPath);
@@ -3661,7 +3671,7 @@ program.name("envpkt").description("Credential lifecycle and fleet management fo
3661
3671
  program.command("init").description("Initialize a new envpkt.toml in the current directory").option("--from-fnox [path]", "Scaffold from fnox.toml (optionally specify path)").option("--catalog <path>", "Path to shared secret catalog").option("--identity", "Include [identity] section").option("--name <name>", "Identity name (requires --identity)").option("--capabilities <caps>", "Comma-separated capabilities (requires --identity)").option("--expires <date>", "Credential expiration YYYY-MM-DD (requires --identity)").option("--force", "Overwrite existing envpkt.toml").action((options) => {
3662
3672
  runInit(process.cwd(), options);
3663
3673
  });
3664
- program.command("keygen").description("Generate an age keypair for sealing secrets — run this before `seal` if you don't have a key yet").option("-c, --config <path>", "Path to envpkt.toml (updates identity.recipient if found)").option("--force", "Overwrite existing identity file").option("-o, --output <path>", "Output path for identity file (default: ~/.envpkt/age-key.txt)").action((options) => {
3674
+ program.command("keygen").description("Generate an age keypair for sealing secrets — run this before `seal` if you don't have a key yet").option("-c, --config <path>", "Path to envpkt.toml (updates identity.recipient if found)").option("-o, --output <path>", "Output path for identity file (default: ~/.envpkt/<project>-key.txt)").option("--global", "Write key to the shared default path (~/.envpkt/age-key.txt) instead of a project-specific one").action((options) => {
3665
3675
  runKeygen(options);
3666
3676
  });
3667
3677
  program.command("audit").description("Audit credential health from envpkt.toml (use --strict in CI pipelines to gate deploys)").option("-c, --config <path>", "Path to envpkt.toml").option("--format <format>", "Output format: table | json | minimal", "table").option("--expiring <days>", "Show secrets expiring within N days", parseInt).option("--status <status>", "Filter by status: healthy | expiring_soon | expired | stale | missing").option("--strict", "Exit non-zero on any non-healthy secret").option("--all", "Show both secrets and env defaults").option("--env-only", "Show only env defaults (drift detection)").option("--sealed", "Show only secrets with encrypted_value").option("--external", "Show only secrets without encrypted_value").action((options) => {
package/dist/index.d.ts CHANGED
@@ -453,9 +453,8 @@ declare const unsealSecrets: (meta: Readonly<Record<string, SecretMeta>>, identi
453
453
  declare const resolveKeyPath: () => string;
454
454
  /** Resolve an inline age key from ENVPKT_AGE_KEY env var (for CI) */
455
455
  declare const resolveInlineKey: () => Option<string>;
456
- /** Generate an age keypair and write to disk */
456
+ /** Generate an age keypair and write to disk. Refuses to overwrite if the file already exists. */
457
457
  declare const generateKeypair: (options?: {
458
- readonly force?: boolean;
459
458
  readonly outputPath?: string;
460
459
  }) => Either<KeygenError, KeygenResult>;
461
460
  type UpdateIdentityOptions = {
package/dist/index.js CHANGED
@@ -1429,14 +1429,14 @@ const resolveInlineKey = () => {
1429
1429
  const key = process.env["ENVPKT_AGE_KEY"];
1430
1430
  return key ? Some(key) : None();
1431
1431
  };
1432
- /** Generate an age keypair and write to disk */
1432
+ /** Generate an age keypair and write to disk. Refuses to overwrite if the file already exists. */
1433
1433
  const generateKeypair = (options) => {
1434
1434
  if (!ageAvailable()) return Left({
1435
1435
  _tag: "AgeNotFound",
1436
1436
  message: "age-keygen CLI not found on PATH. Install age: https://github.com/FiloSottile/age"
1437
1437
  });
1438
1438
  const outputPath = options?.outputPath ?? resolveKeyPath();
1439
- if (existsSync(outputPath) && !options?.force) return Left({
1439
+ if (existsSync(outputPath)) return Left({
1440
1440
  _tag: "KeyExists",
1441
1441
  path: outputPath
1442
1442
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envpkt",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Credential lifecycle and fleet management for AI agents",
5
5
  "keywords": [
6
6
  "credentials",