@seekrit/cli 1.2.0 → 1.2.2

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/README.md CHANGED
@@ -30,11 +30,12 @@ seekrit login --token skt_…
30
30
  # leaves this machine). Only needed once per account.
31
31
  seekrit keys setup
32
32
 
33
- # 3. Link the current directory to an org / app / environment. Writes
34
- # seekrit.json, which is safe to commit.
35
- seekrit init --org acme --app storefront --env production
33
+ # 3. Link the current directory to an org / app. Writes seekrit.json, which is
34
+ # safe to commit. The environment comes from the token at runtime.
35
+ seekrit init --org acme --app storefront
36
36
 
37
- # 4. Work with secrets
37
+ # 4. Work with secrets. A service token names its own environment; on a user
38
+ # session, add --env production.
38
39
  seekrit secrets set DATABASE_URL 'postgres://…'
39
40
  seekrit secrets list
40
41
  seekrit run -- ./server # runs ./server with secrets in its env
@@ -55,7 +56,7 @@ Run `seekrit <command> --help` for full flags. The
55
56
  | `login [--token \| --dev-user] [--api-url]` | Save credentials to the global config. |
56
57
  | `whoami` | Show the authenticated identity and org membership. |
57
58
  | `keys setup` | Generate your keypair and protect it with a passphrase. |
58
- | `init --org --app --env` | Link this directory to an environment (`seekrit.json`). |
59
+ | `init --org --app` | Link this directory to an org/app (`seekrit.json`). The environment comes from the token. |
59
60
  | `org create` / `app create` / `env create` | Create organizations, apps, and environments. |
60
61
  | `secrets list` | List secret names (never values). |
61
62
  | `secrets get <name> [--raw]` | Decrypt and print one secret value (`--raw` skips `${OTHER_SECRET}` expansion). |
package/dist/index.js CHANGED
@@ -5051,7 +5051,7 @@ async function createAgentTaskToken() {
5051
5051
  }
5052
5052
  //#endregion
5053
5053
  //#region package.json
5054
- var version = "1.2.0";
5054
+ var version = "1.2.2";
5055
5055
  //#endregion
5056
5056
  //#region ../../packages/api-client/src/index.ts
5057
5057
  var SeekritApiError = class extends Error {
@@ -6146,11 +6146,46 @@ async function resolveOrg(ctx, orgSlug) {
6146
6146
  fail("specify --org (or run `seekrit init`)");
6147
6147
  }
6148
6148
  /**
6149
+ * The environment a service token is bound to.
6150
+ *
6151
+ * The binding lives on the token row, not in the token string — `GET
6152
+ * /v1/resolve` is where the API publishes it, and `seekrit whoami` reads it the
6153
+ * same way. One round trip, and it also resolves `--branch` against the bound
6154
+ * environment, which is the only environment a token may name a branch of.
6155
+ */
6156
+ async function boundEnvTarget(ctx, opts) {
6157
+ let scope;
6158
+ try {
6159
+ ({scope} = await ctx.client.resolve(opts.branch ? { branch: opts.branch } : {}));
6160
+ } catch (err) {
6161
+ fail(`specify --env — this token has no environment of its own to fall back to (${err instanceof Error ? err.message : String(err)})`);
6162
+ }
6163
+ const env = scope.branchOf ?? {
6164
+ envId: scope.envId,
6165
+ envSlug: scope.envSlug
6166
+ };
6167
+ const label = scope.branchOf ? `${scope.appSlug}/${scope.branchOf.envSlug}#${scope.envSlug}` : `${scope.appSlug}/${scope.envSlug}`;
6168
+ if (opts.org && opts.org !== scope.orgSlug && opts.org !== scope.orgId) fail(`this token belongs to ${scope.orgSlug}, not "${opts.org}"`);
6169
+ if (opts.env && opts.env !== env.envSlug && opts.env !== env.envId) fail(`this token is bound to ${scope.appSlug}/${env.envSlug}, not "${opts.env}" — add --app (or --group) to target another environment you hold a key for`);
6170
+ return {
6171
+ orgId: scope.orgId,
6172
+ envId: scope.envId,
6173
+ label
6174
+ };
6175
+ }
6176
+ /**
6149
6177
  * Resolve an environment to operate on — an application env (`--app --env`,
6150
6178
  * or the config's app + `--env`), a branch of one (`--branch`), or a group env
6151
6179
  * (`--group --env`).
6180
+ *
6181
+ * `--env` is optional for a service token that names no `--app`/`--group`: it is
6182
+ * already bound to exactly one environment, so requiring the flag made every
6183
+ * documented one-liner (`seekrit secrets list`) fail for the CI job and the
6184
+ * agent that are the point of a token. The local MCP server has always inferred
6185
+ * it this way; this is the same rule, in one place, for both.
6152
6186
  */
6153
6187
  async function resolveEnvTarget(ctx, opts) {
6188
+ if (!opts.app && !opts.group && isTokenAuth(ctx)) return boundEnvTarget(ctx, opts);
6154
6189
  const org = await resolveOrg(ctx, opts.org);
6155
6190
  if (!opts.env) fail("specify --env");
6156
6191
  if (opts.group) {
@@ -12130,7 +12165,7 @@ function collect$2(value, acc) {
12130
12165
  * release-please-config.json), so the pin follows the crate without anyone
12131
12166
  * remembering to move it.
12132
12167
  */
12133
- const PROXY_VERSION = "0.10.0";
12168
+ const PROXY_VERSION = "0.10.2";
12134
12169
  const BIN = "seekrit-proxy";
12135
12170
  /**
12136
12171
  * Host → Rust target triple.
@@ -12166,7 +12201,7 @@ function versionPrefix(version) {
12166
12201
  return version.startsWith("v") ? version : `v${version}`;
12167
12202
  }
12168
12203
  function resolveVersion(explicit) {
12169
- return explicit ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.10.0";
12204
+ return explicit ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.10.2";
12170
12205
  }
12171
12206
  function resolveBaseUrl(explicit) {
12172
12207
  return (explicit ?? process.env.SEEKRIT_PROXY_BASE_URL ?? "https://proxy.seekrit.dev").replace(/\/+$/, "");
@@ -12518,10 +12553,10 @@ Next:
12518
12553
  });
12519
12554
  console.log(path);
12520
12555
  const { target } = detectTarget();
12521
- process.stderr.write(`seekrit-proxy ${versionPrefix(options.proxyVersion ?? "0.10.0")} (${target})\n`);
12556
+ process.stderr.write(`seekrit-proxy ${versionPrefix(options.proxyVersion ?? "0.10.2")} (${target})\n`);
12522
12557
  });
12523
12558
  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) => {
12524
- const version = options.proxyVersion ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.10.0";
12559
+ const version = options.proxyVersion ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.10.2";
12525
12560
  const override = process.env.SEEKRIT_PROXY_BIN;
12526
12561
  const { target, exe } = detectTarget();
12527
12562
  const path = override ?? proxyBinaryPath(version, target, exe);
@@ -12542,7 +12577,7 @@ Next:
12542
12577
  process.stdout.write(renderComposeSnippet(plan, {
12543
12578
  service: options.service,
12544
12579
  workload: options.workload,
12545
- image: options.image ?? `seekritdev/proxy:0.10.0`,
12580
+ image: options.image ?? `seekritdev/proxy:0.10.2`,
12546
12581
  publish: Boolean(options.publish)
12547
12582
  }));
12548
12583
  });
@@ -13959,9 +13994,16 @@ function parseVersion(raw) {
13959
13994
  if (!Number.isInteger(n) || n < 1) fail(`expected a positive whole number, got "${raw}"`);
13960
13995
  return n;
13961
13996
  }
13962
- /** Attach the environment-selection flags shared by every `secrets` command. */
13997
+ /**
13998
+ * Attach the environment-selection flags shared by every `secrets` command.
13999
+ *
14000
+ * `--env` is an ordinary option, not a required one: a service token is bound
14001
+ * to a single environment, so `seekrit secrets list` with nothing else on the
14002
+ * line is the whole invocation for CI and for agents. `resolveEnvTarget` fills
14003
+ * it in from the token, and still insists on it for a user session.
14004
+ */
13963
14005
  function withTarget(cmd) {
13964
- return cmd.option("--org <slug>", "organization slug").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--group <slug>", "target a group environment instead of an app").option("--branch <slug>", "operate on a branch of --env").requiredOption("--env <slug>", "environment slug");
14006
+ return cmd.option("--org <slug>", "organization slug").option("--app <slug>", "application slug (defaults to seekrit.json)").option("--group <slug>", "target a group environment instead of an app").option("--branch <slug>", "operate on a branch of --env").option("--env <slug>", "environment slug (a service token infers its own)");
13965
14007
  }
13966
14008
  const secrets = program.command("secrets").description("manage secrets in an application or group environment");
13967
14009
  withTarget(secrets.command("list").alias("ls").description("list secret names (no values)").option("--json", "print the listing as JSON (metadata only — never values)")).action(async (options) => {
@@ -14419,7 +14461,7 @@ registerKmsCommands(program);
14419
14461
  registerRotationCommands(program);
14420
14462
  registerRecoveryCommands(program);
14421
14463
  program.command("mcp").description("run an MCP server over stdio so AI agents can drive seekrit").action(async () => {
14422
- const { runMcpServer } = await import("./mcp-DR-zla_u.js");
14464
+ const { runMcpServer } = await import("./mcp-BOawfW_U.js");
14423
14465
  await runMcpServer();
14424
14466
  });
14425
14467
  registerAuditCommands(program);
@@ -180,22 +180,6 @@ const targetShape = {
180
180
  group: z.string().optional().describe("target a group environment instead of an app"),
181
181
  env: z.string().optional().describe("environment slug or id (a service token infers its own)")
182
182
  };
183
- /**
184
- * Resolve which environment a secret tool addresses. A service token with no
185
- * explicit app/group targets its own bound environment (no flags needed);
186
- * everyone else names app|group + env.
187
- */
188
- async function resolveTargetEnv(ctx, o) {
189
- if (isTokenAuth(ctx) && !o.app && !o.group) {
190
- const { scope } = await ctx.client.resolve();
191
- return {
192
- orgId: scope.orgId,
193
- envId: scope.envId,
194
- label: `${scope.appSlug}/${scope.envSlug}`
195
- };
196
- }
197
- return resolveEnvTarget(ctx, o);
198
- }
199
183
  async function runMcpServer(options = {}) {
200
184
  setFailThrows(true);
201
185
  await ensureM2mAdminToken();
@@ -476,7 +460,7 @@ async function runMcpServer(options = {}) {
476
460
  });
477
461
  tool("list_secrets", "List secret names + versions in an environment (never values).", ro, targetShape, async (o) => {
478
462
  const ctx = getCtx();
479
- const { orgId, envId } = await resolveTargetEnv(ctx, o);
463
+ const { orgId, envId } = await resolveEnvTarget(ctx, o);
480
464
  const { secrets } = await ctx.client.listSecrets(orgId, envId);
481
465
  return secrets.map((s) => ({
482
466
  name: s.name,
@@ -665,7 +649,7 @@ async function runMcpServer(options = {}) {
665
649
  }, async (o) => {
666
650
  const ctx = getCtx();
667
651
  ensureDecryptable(ctx);
668
- const { orgId, envId } = await resolveTargetEnv(ctx, o);
652
+ const { orgId, envId } = await resolveEnvTarget(ctx, o);
669
653
  await encryptAndSetSecret(ctx, orgId, envId, o.name, o.value);
670
654
  return {
671
655
  ok: true,
@@ -680,7 +664,7 @@ async function runMcpServer(options = {}) {
680
664
  version: z.number().int().positive().optional().describe("an earlier version from list_secret_versions (default: current)")
681
665
  }, async (o) => {
682
666
  const ctx = getCtx();
683
- const { orgId, envId } = await resolveTargetEnv(ctx, o);
667
+ const { orgId, envId } = await resolveEnvTarget(ctx, o);
684
668
  if (!o.reveal) {
685
669
  const { secrets } = await ctx.client.listSecrets(orgId, envId);
686
670
  const row = secrets.find((s) => s.name === o.name);
@@ -715,7 +699,7 @@ async function runMcpServer(options = {}) {
715
699
  limit: z.number().int().min(1).max(200).optional().describe("default 20")
716
700
  }, async (o) => {
717
701
  const ctx = getCtx();
718
- const { orgId, envId } = await resolveTargetEnv(ctx, o);
702
+ const { orgId, envId } = await resolveEnvTarget(ctx, o);
719
703
  const { versions, currentVersion } = await ctx.client.listSecretVersions(orgId, envId, o.name, { limit: o.limit ?? 20 });
720
704
  return {
721
705
  currentVersion,
@@ -733,7 +717,7 @@ async function runMcpServer(options = {}) {
733
717
  version: z.number().int().positive()
734
718
  }, async (o) => {
735
719
  const ctx = getCtx();
736
- const { orgId, envId } = await resolveTargetEnv(ctx, o);
720
+ const { orgId, envId } = await resolveEnvTarget(ctx, o);
737
721
  const { secret, restoredFrom } = await ctx.client.restoreSecret(orgId, envId, o.name, o.version);
738
722
  return {
739
723
  ok: true,
@@ -747,7 +731,7 @@ async function runMcpServer(options = {}) {
747
731
  name: z.string()
748
732
  }, async (o) => {
749
733
  const ctx = getCtx();
750
- const { orgId, envId } = await resolveTargetEnv(ctx, o);
734
+ const { orgId, envId } = await resolveEnvTarget(ctx, o);
751
735
  await ctx.client.deleteSecret(orgId, envId, o.name);
752
736
  return {
753
737
  ok: true,
@@ -870,7 +854,7 @@ async function runMcpServer(options = {}) {
870
854
  if (Boolean(o.user) === Boolean(o.token)) throw new Error("pass exactly one of user or token");
871
855
  const ctx = getCtx();
872
856
  ensureDecryptable(ctx);
873
- const { orgId, envId, label } = await resolveTargetEnv(ctx, o);
857
+ const { orgId, envId, label } = await resolveEnvTarget(ctx, o);
874
858
  const dek = await getDek(ctx, orgId, envId);
875
859
  let principalType;
876
860
  let principalId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seekrit/cli",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "End-to-end encrypted secrets manager CLI — inject decrypted secrets into any command.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -22,14 +22,14 @@
22
22
  "node": ">=20"
23
23
  },
24
24
  "dependencies": {
25
- "@modelcontextprotocol/sdk": "^1.29.0",
25
+ "@modelcontextprotocol/sdk": "^1.30.0",
26
26
  "commander": "^15.0.0",
27
- "zod": "^4.4.3"
27
+ "zod": "^4.5.4"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^26.1.0",
31
31
  "tsdown": "^0.22.3",
32
- "vitest": "^4.1.9",
32
+ "vitest": "^4.1.11",
33
33
  "@seekrit/api-client": "0.0.1",
34
34
  "@seekrit/core": "0.0.1",
35
35
  "@seekrit/crypto": "0.0.1"