@seekrit/cli 1.7.0 → 1.8.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/index.js CHANGED
@@ -2174,7 +2174,12 @@ z.object({
2174
2174
  z.object({
2175
2175
  groupId: z.string().min(1),
2176
2176
  /** Precedence among an env's groups (higher wins). Appended if omitted. */
2177
- position: z.number().int().min(0).optional()
2177
+ position: z.number().int().min(0).optional(),
2178
+ /** Client-wrapped group DEK for each sync connection newly reading this group. */
2179
+ syncGrants: z.array(z.object({
2180
+ connectionId: z.string().min(1),
2181
+ wrappedDek: z.string().min(1)
2182
+ })).optional()
2178
2183
  });
2179
2184
  z.object({
2180
2185
  name: nameSchema,
@@ -2555,7 +2560,8 @@ const SYNC_PROVIDER_KINDS = [
2555
2560
  "gcp-secret-manager",
2556
2561
  "langgraph-platform",
2557
2562
  "azure-key-vault",
2558
- "huggingface-spaces"
2563
+ "huggingface-spaces",
2564
+ "openai-vault"
2559
2565
  ];
2560
2566
  z.enum(SYNC_PROVIDER_KINDS);
2561
2567
  /**
@@ -2959,6 +2965,8 @@ const azureKeyVaultConnectionConfigSchema = z.object({
2959
2965
  * would invite pointing a connection at a host that silently swallows secrets.
2960
2966
  */
2961
2967
  const huggingfaceSpacesConnectionConfigSchema = z.object({ provider: z.literal("huggingface-spaces") });
2968
+ /** The wrapped credential is an OpenAI project API key with vault read/write access. */
2969
+ const openaiVaultConnectionConfigSchema = z.object({ provider: z.literal("openai-vault") });
2962
2970
  const syncConnectionConfigSchema = z.discriminatedUnion("provider", [
2963
2971
  vercelConnectionConfigSchema,
2964
2972
  cloudflareWorkersConnectionConfigSchema,
@@ -2978,7 +2986,8 @@ const syncConnectionConfigSchema = z.discriminatedUnion("provider", [
2978
2986
  gcpSecretManagerConnectionConfigSchema,
2979
2987
  langgraphPlatformConnectionConfigSchema,
2980
2988
  azureKeyVaultConnectionConfigSchema,
2981
- huggingfaceSpacesConnectionConfigSchema
2989
+ huggingfaceSpacesConnectionConfigSchema,
2990
+ openaiVaultConnectionConfigSchema
2982
2991
  ]);
2983
2992
  /** Vercel's three deployment targets. A binding writes to one or more. */
2984
2993
  const VERCEL_TARGETS = [
@@ -3803,6 +3812,12 @@ const huggingfaceSpacesDestinationSchema = z.object({
3803
3812
  /** Space ID as `owner/name`, e.g. `acme/support-demo`. */
3804
3813
  repoId: huggingfaceRepoIdSchema
3805
3814
  });
3815
+ /** One existing OpenAI vault; every credential in this binding gets the same host boundary. */
3816
+ const openaiVaultDestinationSchema = z.object({
3817
+ provider: z.literal("openai-vault"),
3818
+ vaultId: z.string().trim().regex(/^vault_[A-Za-z0-9_-]+$/, "must be an OpenAI vault ID"),
3819
+ allowedHosts: z.array(z.string().trim().toLowerCase().max(253).regex(/^(?=.{1,253}$)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/, "must be an exact hostname without scheme, port, path, or wildcard")).min(1).max(100).refine((hosts) => new Set(hosts).size === hosts.length, "hostnames must be unique")
3820
+ });
3806
3821
  const syncDestinationSchema = z.discriminatedUnion("provider", [
3807
3822
  vercelDestinationSchema,
3808
3823
  cloudflareWorkersDestinationSchema,
@@ -3822,7 +3837,8 @@ const syncDestinationSchema = z.discriminatedUnion("provider", [
3822
3837
  gcpSecretManagerDestinationSchema,
3823
3838
  langgraphPlatformDestinationSchema,
3824
3839
  azureKeyVaultDestinationSchema,
3825
- huggingfaceSpacesDestinationSchema
3840
+ huggingfaceSpacesDestinationSchema,
3841
+ openaiVaultDestinationSchema
3826
3842
  ]);
3827
3843
  /**
3828
3844
  * How seekrit secret names become destination key names. Applied in order:
@@ -5110,7 +5126,7 @@ async function createAgentTaskToken() {
5110
5126
  }
5111
5127
  //#endregion
5112
5128
  //#region package.json
5113
- var version = "1.7.0";
5129
+ var version = "1.8.0";
5114
5130
  //#endregion
5115
5131
  //#region ../../packages/api-client/src/index.ts
5116
5132
  /**
@@ -7646,6 +7662,32 @@ function registerEnvDiff(env) {
7646
7662
  });
7647
7663
  }
7648
7664
  //#endregion
7665
+ //#region src/group-sync-grants.ts
7666
+ /** Wrap a newly composed group layer for connections already syncing the app env or its branches. */
7667
+ async function syncGrantsForGroupLink(ctx, orgId, envId, envSlug, groupId) {
7668
+ const [{ groups }, { bindings }, { branches }] = await Promise.all([
7669
+ ctx.client.listEnvGroups(orgId, envId),
7670
+ ctx.client.listSyncBindings(orgId),
7671
+ ctx.client.listBranches(orgId, envId)
7672
+ ]);
7673
+ if (groups.some((group) => group.groupId === groupId)) return void 0;
7674
+ const targets = /* @__PURE__ */ new Set([envId, ...branches.map((branch) => branch.id)]);
7675
+ const connectionIds = new Set(bindings.filter((binding) => targets.has(binding.environmentId)).map((b) => b.connectionId));
7676
+ if (connectionIds.size === 0) return void 0;
7677
+ const [{ environments }, { connections }] = await Promise.all([ctx.client.listGroupEnvs(orgId, groupId), ctx.client.listSyncConnections(orgId)]);
7678
+ const groupEnv = environments.find((entry) => entry.slug === envSlug);
7679
+ if (!groupEnv) fail(`create a ${envSlug} environment in this group before composing it`);
7680
+ const dek = await getDek(ctx, orgId, groupEnv.id);
7681
+ return Promise.all([...connectionIds].map(async (connectionId) => {
7682
+ const connection = connections.find((entry) => entry.id === connectionId);
7683
+ if (!connection) fail("a sync connection could not be found; refresh and retry");
7684
+ return {
7685
+ connectionId,
7686
+ wrappedDek: await wrapDek(dek, connection.publicKeyJwk)
7687
+ };
7688
+ }));
7689
+ }
7690
+ //#endregion
7649
7691
  //#region src/kms.ts
7650
7692
  /** Collect a repeatable option into a list. */
7651
7693
  function collect$8(value, acc = []) {
@@ -8308,9 +8350,11 @@ function registerAppCommands(program) {
8308
8350
  org: options.org,
8309
8351
  group: options.group
8310
8352
  });
8353
+ const syncGrants = await syncGrantsForGroupLink(ctx, target.orgId, target.envId, target.envSlug, group.id);
8311
8354
  await ctx.client.linkEnvGroup(target.orgId, target.envId, {
8312
8355
  groupId: group.id,
8313
- position: options.position === void 0 ? void 0 : Number.parseInt(options.position, 10)
8356
+ position: options.position === void 0 ? void 0 : Number.parseInt(options.position, 10),
8357
+ syncGrants
8314
8358
  });
8315
8359
  console.error(`composed ${group.slug} into ${target.appSlug}/${target.envSlug}`);
8316
8360
  });
@@ -11848,7 +11892,7 @@ function collect$4(value, acc) {
11848
11892
  * release-please-config.json), so the pin follows the crate without anyone
11849
11893
  * remembering to move it.
11850
11894
  */
11851
- const PROXY_VERSION = "0.12.1";
11895
+ const PROXY_VERSION = "0.12.2";
11852
11896
  const BIN = "seekrit-proxy";
11853
11897
  /**
11854
11898
  * Host → Rust target triple.
@@ -11884,7 +11928,7 @@ function versionPrefix(version) {
11884
11928
  return version.startsWith("v") ? version : `v${version}`;
11885
11929
  }
11886
11930
  function resolveVersion(explicit) {
11887
- return explicit ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.12.1";
11931
+ return explicit ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.12.2";
11888
11932
  }
11889
11933
  function resolveBaseUrl(explicit) {
11890
11934
  return (explicit ?? process.env.SEEKRIT_PROXY_BASE_URL ?? "https://proxy.seekrit.dev").replace(/\/+$/, "");
@@ -12042,7 +12086,7 @@ const PLUGIN_BIN = "seekrit-nomad-secrets";
12042
12086
  * entry in release-please-config.json), so the pin follows the crate without
12043
12087
  * anyone remembering to move it — the same arrangement `PROXY_VERSION` has.
12044
12088
  */
12045
- const NOMAD_PLUGIN_VERSION = "0.2.0";
12089
+ const NOMAD_PLUGIN_VERSION = "0.2.1";
12046
12090
  /** Public artifact host. Mirrors GitHub Releases; public, no egress fees. */
12047
12091
  const DEFAULT_BASE_URL = "https://nomad.seekrit.dev";
12048
12092
  /** Where the plugin reads its node config, matching `config::DEFAULT_CONFIG_PATH`. */
@@ -12129,7 +12173,7 @@ async function fetchBytes(url) {
12129
12173
  * let Nomad fingerprint a half-written executable.
12130
12174
  */
12131
12175
  async function installPlugin(pluginDir, options = {}) {
12132
- const version = options.version ?? "0.2.0";
12176
+ const version = options.version ?? "0.2.1";
12133
12177
  const baseUrl = (options.baseUrl ?? "https://nomad.seekrit.dev").replace(/\/+$/, "");
12134
12178
  const { target, exe } = detectTarget();
12135
12179
  if (exe !== "") fail("Nomad secret provider plugins are not supported on Windows clients");
@@ -13834,10 +13878,10 @@ Next:
13834
13878
  });
13835
13879
  console.log(path);
13836
13880
  const { target } = detectTarget();
13837
- process.stderr.write(`seekrit-proxy ${versionPrefix(options.proxyVersion ?? "0.12.1")} (${target})\n`);
13881
+ process.stderr.write(`seekrit-proxy ${versionPrefix(options.proxyVersion ?? "0.12.2")} (${target})\n`);
13838
13882
  });
13839
13883
  proxy.command("where").description("show which binary `seekrit proxy run` would use, without fetching it").option("--proxy-version <version>", `version to report (default: ${PROXY_VERSION})`).option("--json", "machine-readable output").action((options) => {
13840
- const version = options.proxyVersion ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.12.1";
13884
+ const version = options.proxyVersion ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.12.2";
13841
13885
  const override = process.env.SEEKRIT_PROXY_BIN;
13842
13886
  const { target, exe } = detectTarget();
13843
13887
  const path = override ?? proxyBinaryPath(version, target, exe);
@@ -13896,7 +13940,7 @@ Next:
13896
13940
  process.stdout.write(renderComposeSnippet(plan, {
13897
13941
  service: options.service,
13898
13942
  workload: options.workload,
13899
- image: options.image ?? `seekritdev/proxy:0.12.1`,
13943
+ image: options.image ?? `seekritdev/proxy:0.12.2`,
13900
13944
  publish: Boolean(options.publish)
13901
13945
  }));
13902
13946
  });
@@ -14522,6 +14566,7 @@ function credentialNoun(provider) {
14522
14566
  if (provider === "langgraph-platform") return "LangSmith API key";
14523
14567
  if (provider === "azure-key-vault") return "client secret";
14524
14568
  if (provider === "huggingface-spaces") return "user access token";
14569
+ if (provider === "openai-vault") return "OpenAI project API key";
14525
14570
  return "API token";
14526
14571
  }
14527
14572
  /**
@@ -14611,6 +14656,7 @@ function buildConfig(provider, options) {
14611
14656
  };
14612
14657
  }
14613
14658
  case "huggingface-spaces": return { provider: "huggingface-spaces" };
14659
+ case "openai-vault": return { provider: "openai-vault" };
14614
14660
  }
14615
14661
  }
14616
14662
  /** Where inside the platform a binding writes. */
@@ -14828,6 +14874,14 @@ function buildDestination(provider, options) {
14828
14874
  provider: "huggingface-spaces",
14829
14875
  repoId: assertHuggingfaceSpace(options.hfSpace)
14830
14876
  };
14877
+ case "openai-vault":
14878
+ if (!options.openaiVault) fail("--openai-vault is required for openai-vault (vault_… ID)");
14879
+ if (!options.openaiHosts) fail("--openai-hosts is required for openai-vault");
14880
+ return {
14881
+ provider: "openai-vault",
14882
+ vaultId: options.openaiVault.trim(),
14883
+ allowedHosts: list(options.openaiHosts, "")
14884
+ };
14831
14885
  }
14832
14886
  }
14833
14887
  /** One-line description of a destination, for list output. */
@@ -14851,6 +14905,7 @@ function describeDestination(destination) {
14851
14905
  case "langgraph-platform": return `deployment ${destination.deploymentId}`;
14852
14906
  case "azure-key-vault": return `${destination.vault}${destination.prefix ? ` (${destination.prefix}*)` : ""}`;
14853
14907
  case "huggingface-spaces": return `space ${destination.repoId}`;
14908
+ case "openai-vault": return `${destination.vaultId} (${destination.allowedHosts.join(", ")})`;
14854
14909
  case "github-actions": switch (destination.kind) {
14855
14910
  case "repo": return `${destination.owner}/${destination.repo}`;
14856
14911
  case "environment": return `${destination.owner}/${destination.repo} @${destination.environment}`;
@@ -14866,7 +14921,7 @@ function describeDestination(destination) {
14866
14921
  * application whose environment the binding reads from.
14867
14922
  */
14868
14923
  function destinationOptions(command) {
14869
- return command.option("--project <id>", "vercel: project id or name · cloudflare-pages / northflank: project name or slug · bunnyshell: project ID (writes variables new environments inherit)").option("--target <list>", "vercel / cloudflare-pages: comma-separated deployment targets", "production").option("--git-branch <branch>", "vercel: restrict preview writes to one branch").option("--script <name>", "cloudflare-workers: Worker script name").option("--store-id <id>", "cloudflare-secrets-store: store ID (32 hex)").option("--scopes <list>", "cloudflare-secrets-store: comma-separated scopes", "workers").option("--railway-project <id>", "railway: project ID (a UUID)").option("--railway-environment <id>", "railway: environment ID (a UUID)").option("--service <id>", "railway: service ID (omit for the environment's shared variables) · render: service ID (srv-…, or crn-… for a cron job)").option("--skip-deploys", "railway: stage values without triggering a redeploy").option("--path <path>", "aws-parameter-store: hierarchy, e.g. /prod/storefront/ · aws-secrets-manager, azure-key-vault: name prefix").option("--layout <layout>", `aws-secrets-manager: ${AWS_SECRETS_MANAGER_LAYOUTS.join(" | ")}`).option("--secret-name <name>", "aws-secrets-manager: the secret a json-bundle writes to").option("--param-type <type>", `aws-parameter-store: ${AWS_PARAMETER_TYPES.join(" | ")}`).option("--tier <tier>", `aws-parameter-store: ${AWS_PARAMETER_TIERS.join(" | ")}`).option("--kms-key-id <id>", "aws: customer-managed KMS key id, ARN, or alias").option("--env-group <id>", "render: environment group ID (evg-…)").option("--fly-app <name>", "fly: app name, as `fly apps list` shows it").option("--secret-group <id>", "northflank: secret group ID (the slug in its URL)").option("--do-app <id>", "digitalocean: App Platform app ID (the UUID in its URL)").option("--component <name>", "digitalocean: write to one component's variables (omit for app-level)").option("--env-scope <scope>", `digitalocean: ${DIGITALOCEAN_ENV_SCOPES.join(" | ")}`).option("--heroku-app <name>", "heroku: app name, as `heroku apps` shows it (or its UUID)").option("--netlify-site <id>", "netlify: site API ID (the UUID under Project configuration)").option("--no-netlify-secret", "netlify: create readable variables instead of Netlify secrets (write-only)").option("--bunnyshell-environment <id>", "bunnyshell: environment ID (omit to write a project)").option("--no-bunnyshell-secret", "bunnyshell: create variables visible in the dashboard instead of secret ones").option("--gh-repo <owner/name>", "github-actions: repository, e.g. acme/storefront").option("--gh-environment <name>", "github-actions: write to one deployment environment's secrets (needs --gh-repo)").option("--gh-org <login>", "github-actions: write organization secrets instead of a repo's").option("--gh-visibility <v>", `github-actions org secrets: ${GITHUB_ACTIONS_VISIBILITIES.join(" | ")}`).option("--gh-repo-ids <ids>", "github-actions: comma-separated numeric repository IDs for --gh-visibility selected").option("--langgraph-deployment <id>", "langgraph-platform: deployment UUID (the one in its dashboard URL)").option("--gcp-prefix <prefix>", "gcp: prepended to every secret ID, e.g. prod-storefront-").option("--gcp-replication <policy>", `gcp: ${GCP_REPLICATION_POLICIES.join(" | ")}`, "automatic").option("--gcp-locations <list>", "gcp: regions for user-managed replication, e.g. us-east1").option("--gcp-kms-key <name>", "gcp: Cloud KMS key (projects/…/cryptoKeys/…)").option("--gcp-prune-versions", "gcp: destroy the version each push supersedes, keeping one active version").option("--vault <name>", "azure-key-vault: vault name, e.g. acme-prod").option("--hf-space <owner/name>", "huggingface-spaces: Space ID, e.g. acme/support-demo (its owner and name, not a URL)").option("--name-mode <mode>", `azure-key-vault: ${AZURE_KEY_VAULT_NAME_MODES.join(" | ")} — Key Vault stores no underscores, so DATABASE_URL becomes DATABASE-URL unless you reject instead`);
14924
+ return command.option("--project <id>", "vercel: project id or name · cloudflare-pages / northflank: project name or slug · bunnyshell: project ID (writes variables new environments inherit)").option("--target <list>", "vercel / cloudflare-pages: comma-separated deployment targets", "production").option("--git-branch <branch>", "vercel: restrict preview writes to one branch").option("--script <name>", "cloudflare-workers: Worker script name").option("--store-id <id>", "cloudflare-secrets-store: store ID (32 hex)").option("--scopes <list>", "cloudflare-secrets-store: comma-separated scopes", "workers").option("--railway-project <id>", "railway: project ID (a UUID)").option("--railway-environment <id>", "railway: environment ID (a UUID)").option("--service <id>", "railway: service ID (omit for the environment's shared variables) · render: service ID (srv-…, or crn-… for a cron job)").option("--skip-deploys", "railway: stage values without triggering a redeploy").option("--path <path>", "aws-parameter-store: hierarchy, e.g. /prod/storefront/ · aws-secrets-manager, azure-key-vault: name prefix").option("--layout <layout>", `aws-secrets-manager: ${AWS_SECRETS_MANAGER_LAYOUTS.join(" | ")}`).option("--secret-name <name>", "aws-secrets-manager: the secret a json-bundle writes to").option("--param-type <type>", `aws-parameter-store: ${AWS_PARAMETER_TYPES.join(" | ")}`).option("--tier <tier>", `aws-parameter-store: ${AWS_PARAMETER_TIERS.join(" | ")}`).option("--kms-key-id <id>", "aws: customer-managed KMS key id, ARN, or alias").option("--env-group <id>", "render: environment group ID (evg-…)").option("--fly-app <name>", "fly: app name, as `fly apps list` shows it").option("--secret-group <id>", "northflank: secret group ID (the slug in its URL)").option("--do-app <id>", "digitalocean: App Platform app ID (the UUID in its URL)").option("--component <name>", "digitalocean: write to one component's variables (omit for app-level)").option("--env-scope <scope>", `digitalocean: ${DIGITALOCEAN_ENV_SCOPES.join(" | ")}`).option("--heroku-app <name>", "heroku: app name, as `heroku apps` shows it (or its UUID)").option("--netlify-site <id>", "netlify: site API ID (the UUID under Project configuration)").option("--no-netlify-secret", "netlify: create readable variables instead of Netlify secrets (write-only)").option("--bunnyshell-environment <id>", "bunnyshell: environment ID (omit to write a project)").option("--no-bunnyshell-secret", "bunnyshell: create variables visible in the dashboard instead of secret ones").option("--gh-repo <owner/name>", "github-actions: repository, e.g. acme/storefront").option("--gh-environment <name>", "github-actions: write to one deployment environment's secrets (needs --gh-repo)").option("--gh-org <login>", "github-actions: write organization secrets instead of a repo's").option("--gh-visibility <v>", `github-actions org secrets: ${GITHUB_ACTIONS_VISIBILITIES.join(" | ")}`).option("--gh-repo-ids <ids>", "github-actions: comma-separated numeric repository IDs for --gh-visibility selected").option("--langgraph-deployment <id>", "langgraph-platform: deployment UUID (the one in its dashboard URL)").option("--gcp-prefix <prefix>", "gcp: prepended to every secret ID, e.g. prod-storefront-").option("--gcp-replication <policy>", `gcp: ${GCP_REPLICATION_POLICIES.join(" | ")}`, "automatic").option("--gcp-locations <list>", "gcp: regions for user-managed replication, e.g. us-east1").option("--gcp-kms-key <name>", "gcp: Cloud KMS key (projects/…/cryptoKeys/…)").option("--gcp-prune-versions", "gcp: destroy the version each push supersedes, keeping one active version").option("--vault <name>", "azure-key-vault: vault name, e.g. acme-prod").option("--openai-vault <id>", "openai-vault: existing OpenAI Vault ID (vault_…)").option("--openai-hosts <hosts>", "openai-vault: comma-separated exact HTTPS hostnames").option("--hf-space <owner/name>", "huggingface-spaces: Space ID, e.g. acme/support-demo (its owner and name, not a URL)").option("--name-mode <mode>", `azure-key-vault: ${AZURE_KEY_VAULT_NAME_MODES.join(" | ")} — Key Vault stores no underscores, so DATABASE_URL becomes DATABASE-URL unless you reject instead`);
14870
14925
  }
14871
14926
  /** Find a connection by id or name — nobody keeps `syc_…` ids in their head. */
14872
14927
  async function resolveConnection(ctx, orgId, ref) {
@@ -15796,7 +15851,7 @@ registerKmsCommands(program);
15796
15851
  registerRotationCommands(program);
15797
15852
  registerRecoveryCommands(program);
15798
15853
  program.command("mcp").description("run an MCP server over stdio so AI agents can drive seekrit").action(async () => {
15799
- const { runMcpServer } = await import("./mcp-C2YR45UJ.js");
15854
+ const { runMcpServer } = await import("./mcp-ZYu1Q6Ft.js");
15800
15855
  await runMcpServer();
15801
15856
  });
15802
15857
  registerAuditCommands(program);
@@ -15811,4 +15866,4 @@ program.parseAsync(argv).catch((err) => {
15811
15866
  fail(describeError(err));
15812
15867
  });
15813
15868
  //#endregion
15814
- export { verifyMessage as A, toBase64 as B, isServiceToken as C, importVerifyingKey as D, importSigningKey as E, kmsBlobKeyRef as F, kmsDecrypt as I, kmsEncrypt as L, generateMysqlCredential as M, generateDataKey as N, signMessage as O, generateEncryptKeyMaterial as P, wrapDek as R, createServiceToken as S, generateSigningKeyMaterial as T, parseBranchTtl as V, isTokenAuth as _, kmsResolveRecipient as a, writeProjectConfig as b, fetchDecryptedVersion as c, resolveAppEnv as d, resolveBranch as f, getDek as g, resolveOrg as h, kmsResolveKey as i, generatePostgresCredential as j, signatureKeyRef as k, materializeEnv as l, resolveGroup as m, kmsCallerIdentity as n, encryptAndSetSecret as o, resolveEnvTarget as p, kmsRecoverMaterial as r, fetchDecryptedSecrets as s, ensureM2mAdminToken as t, resolveApp as u, tryBuildContext as v, parseServiceToken as w, version as x, setFailThrows as y, generateDek as z };
15869
+ export { signatureKeyRef as A, generateDek as B, createServiceToken as C, importSigningKey as D, generateSigningKeyMaterial as E, generateEncryptKeyMaterial as F, parseBranchTtl as H, kmsBlobKeyRef as I, kmsDecrypt as L, generatePostgresCredential as M, generateMysqlCredential as N, importVerifyingKey as O, generateDataKey as P, kmsEncrypt as R, version as S, parseServiceToken as T, toBase64 as V, getDek as _, kmsResolveRecipient as a, setFailThrows as b, fetchDecryptedSecrets as c, resolveApp as d, resolveAppEnv as f, resolveOrg as g, resolveGroup as h, kmsResolveKey as i, verifyMessage as j, signMessage as k, fetchDecryptedVersion as l, resolveEnvTarget as m, kmsCallerIdentity as n, syncGrantsForGroupLink as o, resolveBranch as p, kmsRecoverMaterial as r, encryptAndSetSecret as s, ensureM2mAdminToken as t, materializeEnv as u, isTokenAuth as v, isServiceToken as w, writeProjectConfig as x, tryBuildContext as y, wrapDek as z };
@@ -1,4 +1,4 @@
1
- import { A as verifyMessage, B as toBase64, C as isServiceToken, D as importVerifyingKey, E as importSigningKey, F as kmsBlobKeyRef, I as kmsDecrypt, L as kmsEncrypt, M as generateMysqlCredential, N as generateDataKey, O as signMessage, P as generateEncryptKeyMaterial, R as wrapDek, S as createServiceToken, T as generateSigningKeyMaterial, V as parseBranchTtl, _ as isTokenAuth, a as kmsResolveRecipient, b as writeProjectConfig, c as fetchDecryptedVersion, d as resolveAppEnv, f as resolveBranch, g as getDek, h as resolveOrg, i as kmsResolveKey, j as generatePostgresCredential, k as signatureKeyRef, l as materializeEnv, m as resolveGroup, n as kmsCallerIdentity, o as encryptAndSetSecret, p as resolveEnvTarget, r as kmsRecoverMaterial, s as fetchDecryptedSecrets, t as ensureM2mAdminToken, u as resolveApp, v as tryBuildContext, w as parseServiceToken, x as version, y as setFailThrows, z as generateDek } from "./index.js";
1
+ import { A as signatureKeyRef, B as generateDek, C as createServiceToken, D as importSigningKey, E as generateSigningKeyMaterial, F as generateEncryptKeyMaterial, H as parseBranchTtl, I as kmsBlobKeyRef, L as kmsDecrypt, M as generatePostgresCredential, N as generateMysqlCredential, O as importVerifyingKey, P as generateDataKey, R as kmsEncrypt, S as version, T as parseServiceToken, V as toBase64, _ as getDek, a as kmsResolveRecipient, b as setFailThrows, c as fetchDecryptedSecrets, d as resolveApp, f as resolveAppEnv, g as resolveOrg, h as resolveGroup, i as kmsResolveKey, j as verifyMessage, k as signMessage, l as fetchDecryptedVersion, m as resolveEnvTarget, n as kmsCallerIdentity, o as syncGrantsForGroupLink, p as resolveBranch, r as kmsRecoverMaterial, s as encryptAndSetSecret, t as ensureM2mAdminToken, u as materializeEnv, v as isTokenAuth, w as isServiceToken, x as writeProjectConfig, y as tryBuildContext, z as wrapDek } from "./index.js";
2
2
  import { spawn } from "node:child_process";
3
3
  import { z } from "zod";
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -621,9 +621,11 @@ async function runMcpServer(options = {}) {
621
621
  org,
622
622
  group
623
623
  });
624
+ const syncGrants = await syncGrantsForGroupLink(ctx, target.orgId, target.envId, target.envSlug, g.id);
624
625
  return (await ctx.client.linkEnvGroup(target.orgId, target.envId, {
625
626
  groupId: g.id,
626
- position
627
+ position,
628
+ syncGrants
627
629
  })).group;
628
630
  });
629
631
  tool("uncompose_group", "Remove a composed group from an application environment.", { idempotent: true }, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seekrit/cli",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "End-to-end encrypted secrets manager CLI — inject decrypted secrets into any command.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -24,12 +24,12 @@
24
24
  "dependencies": {
25
25
  "@modelcontextprotocol/sdk": "^1.30.0",
26
26
  "commander": "^15.0.0",
27
- "zod": "^4.6.2"
27
+ "zod": "^4.6.5"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/node": "^26.5.1",
30
+ "@types/node": "^26.6.1",
31
31
  "tsdown": "^0.23.0",
32
- "vitest": "^4.1.11",
32
+ "vitest": "^5.0.1",
33
33
  "@seekrit/api-client": "0.0.1",
34
34
  "@seekrit/core": "0.0.1",
35
35
  "@seekrit/crypto": "0.0.1"