@seekrit/cli 0.25.0 → 0.27.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.
@@ -1,4 +1,4 @@
1
- import { A as generateMysqlCredential, C as generateSigningKeyMaterial, D as signatureKeyRef, E as signMessage, F as kmsEncrypt, I as wrapDek, L as generateDek, M as generateEncryptKeyMaterial, N as kmsBlobKeyRef, O as verifyMessage, P as kmsDecrypt, R as toBase64, S as parseServiceToken, T as importVerifyingKey, _ as setFailThrows, a as ensureM2mAdminToken, b as createServiceToken, c as kmsResolveKey, d as resolveEnvTarget, f as resolveGroup, g as tryBuildContext, h as isTokenAuth, i as materializeEnv, j as generateDataKey, k as generatePostgresCredential, l as kmsResolveRecipient, m as getDek, n as fetchDecryptedSecrets, o as kmsCallerIdentity, p as resolveOrg, r as fetchDecryptedVersion, s as kmsRecoverMaterial, t as encryptAndSetSecret, u as resolveAppEnv, v as writeProjectConfig, w as importSigningKey, x as isServiceToken, y as version } from "./index.js";
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 ensureM2mAdminToken, b as writeProjectConfig, c as kmsResolveKey, d as resolveAppEnv, f as resolveBranch, g as getDek, h as resolveOrg, i as materializeEnv, j as generatePostgresCredential, k as signatureKeyRef, l as kmsResolveRecipient, m as resolveGroup, n as fetchDecryptedSecrets, o as kmsCallerIdentity, p as resolveEnvTarget, r as fetchDecryptedVersion, s as kmsRecoverMaterial, t as encryptAndSetSecret, u as resolveApp, v as tryBuildContext, w as parseServiceToken, x as version, y as setFailThrows, z as generateDek } 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";
@@ -159,6 +159,7 @@ async function materializeFor(ctx, o) {
159
159
  if (!isTokenAuth(ctx)) envId = (await resolveAppEnv(ctx, o)).envId;
160
160
  return materializeEnv(ctx, {
161
161
  envId,
162
+ branch: o.branch,
162
163
  with: o.with,
163
164
  envFiles: o.envFile ?? [".env"]
164
165
  });
@@ -252,6 +253,24 @@ async function runMcpServer(options = {}) {
252
253
  if (!appRow) throw new Error(`no app "${app}" in ${orgRef.slug}`);
253
254
  return (await ctx.client.listEnvs(orgRef.id, appRow.id)).environments;
254
255
  });
256
+ tool("list_branches", "List ephemeral branch configs in an application (optionally just one environment's).", {
257
+ org: z.string().optional(),
258
+ app: z.string(),
259
+ env: z.string().optional()
260
+ }, async ({ org, app, env }) => {
261
+ const ctx = getCtx();
262
+ const appRef = await resolveApp(ctx, {
263
+ org,
264
+ app
265
+ });
266
+ if (!env) return (await ctx.client.listAppBranches(appRef.orgId, appRef.id)).branches;
267
+ const parent = await resolveAppEnv(ctx, {
268
+ org,
269
+ app,
270
+ env
271
+ });
272
+ return (await ctx.client.listBranches(parent.orgId, parent.envId)).branches;
273
+ });
255
274
  tool("list_groups", "List shared groups (reusable secret bags) in an organization.", { org: z.string().optional() }, async ({ org }) => {
256
275
  const ctx = getCtx();
257
276
  const orgRef = await resolveOrg(ctx, org);
@@ -514,6 +533,55 @@ async function runMcpServer(options = {}) {
514
533
  wrappedDek
515
534
  })).environment;
516
535
  });
536
+ tool("create_branch", "Fork an environment into an ephemeral branch (a per-PR / preview config). The branch inherits the parent's secrets by layering at read time — nothing is copied or re-encrypted — and holds only the values you override on it. Generates the branch's data key locally, grants it to the caller, and shares it with the parent's other readers.", {
537
+ org: z.string().optional(),
538
+ app: z.string(),
539
+ from: z.string().describe("the environment to branch"),
540
+ slug: z.string().describe("the branch name, e.g. pr-142"),
541
+ ttl: z.string().optional().describe("lifetime: 12h, 7d, 2w, … or `never` (default 7d)")
542
+ }, async ({ org, app, from, slug, ttl }) => {
543
+ const ctx = getCtx();
544
+ const parent = await resolveAppEnv(ctx, {
545
+ org,
546
+ app,
547
+ env: from
548
+ });
549
+ const parsedTtl = parseBranchTtl(ttl ?? "7d");
550
+ if (parsedTtl === null) throw new Error(`invalid ttl "${ttl}" (try 12h, 7d, 2w, or never)`);
551
+ const me = await kmsCallerIdentity(ctx);
552
+ const dek = generateDek();
553
+ const wrappedDek = await wrapDek(dek, me.publicKeyJwk);
554
+ const grants = [];
555
+ const { grantees } = await ctx.client.listGrantees(parent.orgId, parent.envId);
556
+ for (const grantee of grantees) {
557
+ if (grantee.principalType === me.principalType && grantee.principalId === me.principalId) continue;
558
+ grants.push({
559
+ principalType: grantee.principalType,
560
+ principalId: grantee.principalId,
561
+ wrappedDek: await wrapDek(dek, grantee.publicKeyJwk)
562
+ });
563
+ }
564
+ return (await ctx.client.createBranch(parent.orgId, parent.envId, {
565
+ slug,
566
+ ttlSeconds: Number.isFinite(parsedTtl) ? parsedTtl : null,
567
+ wrappedDek,
568
+ grants
569
+ })).branch;
570
+ });
571
+ tool("delete_branch", "Tear down a branch config and every value it overrode. The parent environment is untouched.", {
572
+ org: z.string().optional(),
573
+ app: z.string(),
574
+ branch: z.string()
575
+ }, async ({ org, app, branch }) => {
576
+ const ctx = getCtx();
577
+ const appRef = await resolveApp(ctx, {
578
+ org,
579
+ app
580
+ });
581
+ const target = await resolveBranch(ctx, appRef, branch);
582
+ await ctx.client.deleteBranch(appRef.orgId, target.id);
583
+ return { deleted: target.slug };
584
+ });
517
585
  tool("create_group_env", "Create a group environment. Generates the data key locally and grants it to the caller.", {
518
586
  org: z.string().optional(),
519
587
  group: z.string(),
@@ -675,6 +743,7 @@ async function runMcpServer(options = {}) {
675
743
  org: z.string().optional(),
676
744
  app: z.string().optional(),
677
745
  env: z.string().optional().describe("environment slug (token auth infers this)"),
746
+ branch: z.string().optional().describe("read an ephemeral branch of that environment"),
678
747
  with: z.record(z.string(), z.string()).optional().describe("group=env slice overrides"),
679
748
  envFile: z.array(z.string()).optional().describe(".env files to overlay (default [.env])"),
680
749
  cwd: z.string().optional()
@@ -696,6 +765,7 @@ async function runMcpServer(options = {}) {
696
765
  org: z.string().optional(),
697
766
  app: z.string().optional(),
698
767
  env: z.string().optional(),
768
+ branch: z.string().optional().describe("read an ephemeral branch of that environment"),
699
769
  with: z.record(z.string(), z.string()).optional()
700
770
  }, async (o) => {
701
771
  const ctx = getCtx();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seekrit/cli",
3
- "version": "0.25.0",
3
+ "version": "0.27.0",
4
4
  "description": "End-to-end encrypted secrets manager CLI — inject decrypted secrets into any command.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -27,8 +27,8 @@
27
27
  "@types/node": "^26.1.0",
28
28
  "tsdown": "^0.22.3",
29
29
  "@seekrit/api-client": "0.0.1",
30
- "@seekrit/crypto": "0.0.1",
31
- "@seekrit/core": "0.0.1"
30
+ "@seekrit/core": "0.0.1",
31
+ "@seekrit/crypto": "0.0.1"
32
32
  },
33
33
  "scripts": {
34
34
  "build": "tsdown",