envpkt 0.9.1 → 0.10.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.
Files changed (2) hide show
  1. package/dist/cli.js +150 -2
  2. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -871,9 +871,9 @@ const readFnoxConfig = (path) => Try(() => readFileSync(path, "utf-8")).fold((er
871
871
  const extractFnoxKeys = (config) => new Set(Object.keys(config.secrets));
872
872
  //#endregion
873
873
  //#region src/core/alias.ts
874
- const ALIAS_REF_RE = /^(secret|env)\.(.+)$/;
874
+ const ALIAS_REF_RE$2 = /^(secret|env)\.(.+)$/;
875
875
  const parseRef = (raw) => {
876
- const match = ALIAS_REF_RE.exec(raw);
876
+ const match = ALIAS_REF_RE$2.exec(raw);
877
877
  if (!match) return Option(void 0);
878
878
  const kind = match[1];
879
879
  const key = match[2];
@@ -2535,6 +2535,77 @@ const runEnvRm = (name, options) => {
2535
2535
  });
2536
2536
  });
2537
2537
  };
2538
+ const ALIAS_REF_RE$1 = /^(secret|env)\.(.+)$/;
2539
+ const buildEnvAliasBlock = (name, options) => {
2540
+ const lines = [`[env.${name}]`, `from_key = "${options.from}"`];
2541
+ if (options.purpose) lines.push(`purpose = "${options.purpose}"`);
2542
+ if (options.comment) lines.push(`comment = "${options.comment}"`);
2543
+ if (options.tags) {
2544
+ const pairs = options.tags.split(",").map((pair) => {
2545
+ const [k, v] = pair.split("=").map((s) => s.trim());
2546
+ return `${k} = "${v}"`;
2547
+ });
2548
+ lines.push(`tags = { ${pairs.join(", ")} }`);
2549
+ }
2550
+ return `${lines.join("\n")}\n`;
2551
+ };
2552
+ const runEnvAlias = (name, options) => {
2553
+ const match = ALIAS_REF_RE$1.exec(options.from);
2554
+ if (!match) {
2555
+ console.error(`${RED}Error:${RESET} --from "${options.from}" must be formatted as "secret.<KEY>" or "env.<KEY>"`);
2556
+ process.exit(1);
2557
+ }
2558
+ const [, targetKind, targetKey] = match;
2559
+ if (targetKind !== "env") {
2560
+ console.error(`${RED}Error:${RESET} env alias must point at another env entry — got "${options.from}". Use \`envpkt secret alias\` for secret→secret aliases.`);
2561
+ process.exit(1);
2562
+ }
2563
+ resolveConfigPath(options.config).fold((err) => {
2564
+ console.error(formatError(err));
2565
+ process.exit(2);
2566
+ }, ({ path: configPath, source }) => {
2567
+ const sourceMsg = formatConfigSource(configPath, source);
2568
+ if (sourceMsg) console.error(sourceMsg);
2569
+ loadConfig(configPath).fold((err) => {
2570
+ console.error(formatError(err));
2571
+ process.exit(2);
2572
+ }, (config) => {
2573
+ const envEntries = config.env ?? {};
2574
+ if (name === targetKey) {
2575
+ console.error(`${RED}Error:${RESET} alias "${name}" cannot reference itself`);
2576
+ process.exit(1);
2577
+ }
2578
+ const target = envEntries[targetKey];
2579
+ if (!target) {
2580
+ console.error(`${RED}Error:${RESET} alias target "${options.from}" not found in ${configPath}. Add the target env entry first.`);
2581
+ process.exit(1);
2582
+ }
2583
+ if (target.from_key !== void 0) {
2584
+ console.error(`${RED}Error:${RESET} alias target "${options.from}" is itself an alias. Chained aliases are not supported — point at the canonical entry instead.`);
2585
+ process.exit(1);
2586
+ }
2587
+ const existing = envEntries[name];
2588
+ if (existing) {
2589
+ if (!options.force) {
2590
+ console.error(`${YELLOW}Warning:${RESET} env entry "${name}" already exists in ${configPath} (${existing.from_key ? `currently alias → ${existing.from_key}` : "currently a regular entry"}).`);
2591
+ console.error(` Pass ${BOLD}--force${RESET} to overwrite, or use a different name.`);
2592
+ process.exit(1);
2593
+ }
2594
+ console.error(`${YELLOW}Warning:${RESET} overwriting existing env entry "${name}" (${existing.from_key ? `was alias → ${existing.from_key}` : "was a regular entry"})`);
2595
+ }
2596
+ const block = buildEnvAliasBlock(name, options);
2597
+ if (options.dryRun) {
2598
+ console.log(`${DIM}# Preview (--dry-run):${RESET}\n`);
2599
+ if (existing) console.log(`${DIM}# (would replace existing [env.${name}] block)${RESET}\n`);
2600
+ console.log(block);
2601
+ return;
2602
+ }
2603
+ const raw = readFileSync(configPath, "utf-8");
2604
+ writeFileSync(configPath, appendSection(existing ? removeSection(raw, `[env.${name}]`).fold(() => raw, (r) => r) : raw, block), "utf-8");
2605
+ console.log(`${GREEN}✓${RESET} Aliased ${BOLD}${name}${RESET} → ${BOLD}${options.from}${RESET} in ${CYAN}${configPath}${RESET}`);
2606
+ });
2607
+ });
2608
+ };
2538
2609
  const runEnvRename = (oldName, newName, options) => {
2539
2610
  withConfig$1(Option(options.config), (configPath, raw) => {
2540
2611
  renameSection(raw, `[env.${oldName}]`, `[env.${newName}]`).fold((err) => {
@@ -2574,6 +2645,9 @@ const registerEnvCommands = (program) => {
2574
2645
  env.command("rename").description("Rename an env entry, preserving all fields").argument("<old>", "Current env variable name").argument("<new>", "New env variable name").option("-c, --config <path>", "Path to envpkt.toml").option("--dry-run", "Preview the result without writing").action((oldName, newName, options) => {
2575
2646
  runEnvRename(oldName, newName, options);
2576
2647
  });
2648
+ env.command("alias").description("Create an alias entry that reuses another env entry's resolved value").argument("<name>", "Alias name (becomes the env var key)").requiredOption("--from <ref>", "Target reference — must be \"env.<KEY>\"").option("-c, --config <path>", "Path to envpkt.toml").option("--purpose <purpose>", "Why this alias exists (local metadata)").option("--comment <comment>", "Free-form annotation").option("--tags <tags>", "Comma-separated key=value tags (e.g. env=prod,team=payments)").option("--force", "Overwrite the entry if <name> already exists").option("--dry-run", "Preview the TOML block without writing").action((name, options) => {
2649
+ runEnvAlias(name, options);
2650
+ });
2577
2651
  };
2578
2652
  //#endregion
2579
2653
  //#region src/cli/commands/exec.ts
@@ -3799,6 +3873,77 @@ const runSecretRename = (oldName, newName, options) => {
3799
3873
  });
3800
3874
  });
3801
3875
  };
3876
+ const ALIAS_REF_RE = /^(secret|env)\.(.+)$/;
3877
+ const buildSecretAliasBlock = (name, options) => {
3878
+ const lines = [`[secret.${name}]`, `from_key = "${options.from}"`];
3879
+ if (options.purpose) lines.push(`purpose = "${options.purpose}"`);
3880
+ if (options.comment) lines.push(`comment = "${options.comment}"`);
3881
+ if (options.tags) {
3882
+ const pairs = options.tags.split(",").map((pair) => {
3883
+ const [k, v] = pair.split("=").map((s) => s.trim());
3884
+ return `${k} = "${v}"`;
3885
+ });
3886
+ lines.push(`tags = { ${pairs.join(", ")} }`);
3887
+ }
3888
+ return `${lines.join("\n")}\n`;
3889
+ };
3890
+ const runSecretAlias = (name, options) => {
3891
+ const match = ALIAS_REF_RE.exec(options.from);
3892
+ if (!match) {
3893
+ console.error(`${RED}Error:${RESET} --from "${options.from}" must be formatted as "secret.<KEY>" or "env.<KEY>"`);
3894
+ process.exit(1);
3895
+ }
3896
+ const [, targetKind, targetKey] = match;
3897
+ if (targetKind !== "secret") {
3898
+ console.error(`${RED}Error:${RESET} secret alias must point at another secret — got "${options.from}". Use \`envpkt env alias\` for env→env aliases.`);
3899
+ process.exit(1);
3900
+ }
3901
+ resolveConfigPath(options.config).fold((err) => {
3902
+ console.error(formatError(err));
3903
+ process.exit(2);
3904
+ }, ({ path: configPath, source }) => {
3905
+ const sourceMsg = formatConfigSource(configPath, source);
3906
+ if (sourceMsg) console.error(sourceMsg);
3907
+ loadConfig(configPath).fold((err) => {
3908
+ console.error(formatError(err));
3909
+ process.exit(2);
3910
+ }, (config) => {
3911
+ const secrets = config.secret ?? {};
3912
+ if (name === targetKey) {
3913
+ console.error(`${RED}Error:${RESET} alias "${name}" cannot reference itself`);
3914
+ process.exit(1);
3915
+ }
3916
+ const target = secrets[targetKey];
3917
+ if (!target) {
3918
+ console.error(`${RED}Error:${RESET} alias target "${options.from}" not found in ${configPath}. Add the target secret first.`);
3919
+ process.exit(1);
3920
+ }
3921
+ if (target.from_key !== void 0) {
3922
+ console.error(`${RED}Error:${RESET} alias target "${options.from}" is itself an alias. Chained aliases are not supported — point at the canonical entry instead.`);
3923
+ process.exit(1);
3924
+ }
3925
+ const existing = secrets[name];
3926
+ if (existing) {
3927
+ if (!options.force) {
3928
+ console.error(`${YELLOW}Warning:${RESET} secret "${name}" already exists in ${configPath} (${existing.from_key ? `currently alias → ${existing.from_key}` : "currently a regular entry"}).`);
3929
+ console.error(` Pass ${BOLD}--force${RESET} to overwrite, or use a different name.`);
3930
+ process.exit(1);
3931
+ }
3932
+ console.error(`${YELLOW}Warning:${RESET} overwriting existing entry "${name}" (${existing.from_key ? `was alias → ${existing.from_key}` : "was a regular entry"})`);
3933
+ }
3934
+ const block = buildSecretAliasBlock(name, options);
3935
+ if (options.dryRun) {
3936
+ console.log(`${DIM}# Preview (--dry-run):${RESET}\n`);
3937
+ if (existing) console.log(`${DIM}# (would replace existing [secret.${name}] block)${RESET}\n`);
3938
+ console.log(block);
3939
+ return;
3940
+ }
3941
+ const raw = readFileSync(configPath, "utf-8");
3942
+ writeFileSync(configPath, appendSection(existing ? removeSection(raw, `[secret.${name}]`).fold(() => raw, (r) => r) : raw, block), "utf-8");
3943
+ console.log(`${GREEN}✓${RESET} Aliased ${BOLD}${name}${RESET} → ${BOLD}${options.from}${RESET} in ${CYAN}${configPath}${RESET}`);
3944
+ });
3945
+ });
3946
+ };
3802
3947
  const addSecretFlags = (cmd) => cmd.option("--service <service>", "Service this secret authenticates to").option("--purpose <purpose>", "Why this secret exists").option("--comment <comment>", "Free-form annotation").option("--expires <date>", "Expiration date (YYYY-MM-DD)").option("--capabilities <caps>", "Comma-separated capabilities (e.g. read,write)").option("--rotates <schedule>", "Rotation schedule (e.g. 90d, quarterly)").option("--rate-limit <limit>", "Rate limit info (e.g. 1000/min)").option("--model-hint <hint>", "Suggested model or tier").option("--source <source>", "Where the value originates (e.g. vault, ci)").option("--rotation-url <url>", "URL for secret rotation procedure").option("--tags <tags>", "Comma-separated key=value tags (e.g. env=prod,team=payments)");
3803
3948
  const registerSecretCommands = (program) => {
3804
3949
  const secret = program.command("secret").description("Manage secret entries in envpkt.toml");
@@ -3814,6 +3959,9 @@ const registerSecretCommands = (program) => {
3814
3959
  secret.command("rename").description("Rename a secret entry, preserving all metadata").argument("<old>", "Current secret name").argument("<new>", "New secret name").option("-c, --config <path>", "Path to envpkt.toml").option("--dry-run", "Preview the result without writing").action((oldName, newName, options) => {
3815
3960
  runSecretRename(oldName, newName, options);
3816
3961
  });
3962
+ secret.command("alias").description("Create an alias entry that reuses another secret's resolved value").argument("<name>", "Alias name (becomes the env var key)").requiredOption("--from <ref>", "Target reference — must be \"secret.<KEY>\"").option("-c, --config <path>", "Path to envpkt.toml").option("--purpose <purpose>", "Why this alias exists (local metadata)").option("--comment <comment>", "Free-form annotation").option("--tags <tags>", "Comma-separated key=value tags (e.g. env=prod,team=payments)").option("--force", "Overwrite the entry if <name> already exists").option("--dry-run", "Preview the TOML block without writing").action((name, options) => {
3963
+ runSecretAlias(name, options);
3964
+ });
3817
3965
  };
3818
3966
  //#endregion
3819
3967
  //#region src/cli/commands/shell-hook.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "envpkt",
3
- "version": "0.9.1",
3
+ "version": "0.10.1",
4
4
  "description": "Credential lifecycle and fleet management for AI agents",
5
5
  "keywords": [
6
6
  "credentials",
@@ -42,8 +42,8 @@
42
42
  "@modelcontextprotocol/sdk": "^1.29.0",
43
43
  "@sinclair/typebox": "^0.34.49",
44
44
  "commander": "^14.0.3",
45
- "functype": "^0.58.1",
46
- "functype-os": "^0.4.2",
45
+ "functype": "^0.60.0",
46
+ "functype-os": "^0.60.0",
47
47
  "smol-toml": "^1.6.1"
48
48
  },
49
49
  "devDependencies": {