@seekrit/cli 0.18.0 → 0.20.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 +127 -11
- package/dist/{mcp-CVhEQDfd.js → mcp-DScHFOMG.js} +5 -4
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -96,26 +96,40 @@ const ENTITLEMENT_KEYS = Object.keys({
|
|
|
96
96
|
metric: "monthly_resolves"
|
|
97
97
|
}
|
|
98
98
|
});
|
|
99
|
-
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region ../../packages/core/src/plans.ts
|
|
101
|
+
const PLAN_FAMILIES = {
|
|
100
102
|
free: {
|
|
101
103
|
id: "free",
|
|
102
104
|
name: "Free",
|
|
103
105
|
description: "Get started with the essentials.",
|
|
104
|
-
current: 1
|
|
106
|
+
current: 1,
|
|
107
|
+
hidden: false
|
|
108
|
+
},
|
|
109
|
+
team: {
|
|
110
|
+
id: "team",
|
|
111
|
+
name: "Team",
|
|
112
|
+
description: "For small teams collaborating on secrets.",
|
|
113
|
+
current: 1,
|
|
114
|
+
hidden: false
|
|
105
115
|
},
|
|
106
116
|
pro: {
|
|
107
117
|
id: "pro",
|
|
108
118
|
name: "Pro",
|
|
109
119
|
description: "For teams running secrets in production.",
|
|
110
|
-
current: 1
|
|
120
|
+
current: 1,
|
|
121
|
+
hidden: true
|
|
111
122
|
},
|
|
112
123
|
enterprise: {
|
|
113
124
|
id: "enterprise",
|
|
114
125
|
name: "Enterprise",
|
|
115
126
|
description: "Unlimited scale with advanced governance.",
|
|
116
|
-
current: 1
|
|
127
|
+
current: 1,
|
|
128
|
+
hidden: false
|
|
117
129
|
}
|
|
118
|
-
}
|
|
130
|
+
};
|
|
131
|
+
const PLAN_FAMILY_IDS = Object.keys(PLAN_FAMILIES);
|
|
132
|
+
PLAN_FAMILY_IDS.filter((family) => !PLAN_FAMILIES[family].hidden);
|
|
119
133
|
//#endregion
|
|
120
134
|
//#region ../../packages/core/src/billing.ts
|
|
121
135
|
/**
|
|
@@ -665,6 +679,7 @@ z.object({
|
|
|
665
679
|
z.object({ name: nameSchema });
|
|
666
680
|
z.object({ name: nameSchema });
|
|
667
681
|
z.object({ name: nameSchema });
|
|
682
|
+
z.object({ required: z.boolean() });
|
|
668
683
|
z.object({
|
|
669
684
|
email: emailSchema,
|
|
670
685
|
role: inviteRoleSchema.default("member")
|
|
@@ -1961,7 +1976,7 @@ function isServiceToken(value) {
|
|
|
1961
1976
|
}
|
|
1962
1977
|
//#endregion
|
|
1963
1978
|
//#region package.json
|
|
1964
|
-
var version = "0.
|
|
1979
|
+
var version = "0.20.0";
|
|
1965
1980
|
//#endregion
|
|
1966
1981
|
//#region ../../packages/api-client/src/index.ts
|
|
1967
1982
|
var SeekritApiError = class extends Error {
|
|
@@ -1990,7 +2005,8 @@ var SeekritClient = class {
|
|
|
1990
2005
|
const token = await this.auth.getToken();
|
|
1991
2006
|
if (!token) throw new SeekritApiError(401, "unauthorized", "session expired");
|
|
1992
2007
|
headers.authorization = `Bearer ${token}`;
|
|
1993
|
-
} else
|
|
2008
|
+
} else if (this.auth.type === "m2m") headers.authorization = `Basic ${btoa(`${this.auth.clientId}:${this.auth.clientSecret}`)}`;
|
|
2009
|
+
else headers["x-seekrit-dev-user"] = this.auth.email;
|
|
1994
2010
|
if (body !== void 0) headers["content-type"] = "application/json";
|
|
1995
2011
|
const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
1996
2012
|
method,
|
|
@@ -2038,6 +2054,17 @@ var SeekritClient = class {
|
|
|
2038
2054
|
listMembers(orgId) {
|
|
2039
2055
|
return this.request("GET", `/v1/orgs/${orgId}/members`);
|
|
2040
2056
|
}
|
|
2057
|
+
/**
|
|
2058
|
+
* The org-wide "require a second factor for all members" policy. `configured`
|
|
2059
|
+
* is false when the identity provider isn't wired up (local dev), in which
|
|
2060
|
+
* case the toggle is inert.
|
|
2061
|
+
*/
|
|
2062
|
+
getMfaPolicy(orgId) {
|
|
2063
|
+
return this.request("GET", `/v1/orgs/${orgId}/mfa-policy`);
|
|
2064
|
+
}
|
|
2065
|
+
setMfaPolicy(orgId, input) {
|
|
2066
|
+
return this.request("PATCH", `/v1/orgs/${orgId}/mfa-policy`, input);
|
|
2067
|
+
}
|
|
2041
2068
|
listInvites(orgId) {
|
|
2042
2069
|
return this.request("GET", `/v1/orgs/${orgId}/invites`);
|
|
2043
2070
|
}
|
|
@@ -2329,6 +2356,17 @@ function writeGlobalConfig(update) {
|
|
|
2329
2356
|
mkdirSync(dirname(path), { recursive: true });
|
|
2330
2357
|
writeFileSync(path, `${JSON.stringify(merged, null, 2)}\n`, { mode: 384 });
|
|
2331
2358
|
}
|
|
2359
|
+
/** The admin token previously auto-minted from these M2M credentials, if any. */
|
|
2360
|
+
function readCachedM2mToken(clientId) {
|
|
2361
|
+
return readGlobalConfig().m2mTokens?.[clientId];
|
|
2362
|
+
}
|
|
2363
|
+
/** Remember an auto-minted admin token so the next run reuses it. */
|
|
2364
|
+
function cacheM2mToken(clientId, token) {
|
|
2365
|
+
writeGlobalConfig({ m2mTokens: {
|
|
2366
|
+
...readGlobalConfig().m2mTokens,
|
|
2367
|
+
[clientId]: token
|
|
2368
|
+
} });
|
|
2369
|
+
}
|
|
2332
2370
|
/** Walk up from cwd looking for seekrit.json. */
|
|
2333
2371
|
function findProjectConfig(startDir = process.cwd()) {
|
|
2334
2372
|
let dir = startDir;
|
|
@@ -2425,7 +2463,7 @@ function tryBuildContext(dotenvVars = {}) {
|
|
|
2425
2463
|
}
|
|
2426
2464
|
function buildContext() {
|
|
2427
2465
|
const ctx = tryBuildContext();
|
|
2428
|
-
if (!ctx) fail("no credentials found — run `seekrit login --token skt_
|
|
2466
|
+
if (!ctx) fail("no credentials found — run `seekrit login --token skt_…` (or `--client-id … --client-secret …`), or set SEEKRIT_TOKEN / SEEKRIT_CLIENT_ID + SEEKRIT_CLIENT_SECRET / SEEKRIT_DEV_USER");
|
|
2429
2467
|
return ctx;
|
|
2430
2468
|
}
|
|
2431
2469
|
function isTokenAuth(ctx) {
|
|
@@ -3151,6 +3189,77 @@ function registerKmsCommands(program) {
|
|
|
3151
3189
|
});
|
|
3152
3190
|
}
|
|
3153
3191
|
//#endregion
|
|
3192
|
+
//#region src/m2m.ts
|
|
3193
|
+
/**
|
|
3194
|
+
* Resolve M2M client credentials from (in order) the process environment, a
|
|
3195
|
+
* `.env` overlay (for `seekrit run`), then saved config. Both halves must come
|
|
3196
|
+
* through for the credential to be usable.
|
|
3197
|
+
*/
|
|
3198
|
+
function readM2mCreds(dotenvVars = {}) {
|
|
3199
|
+
const config = readGlobalConfig();
|
|
3200
|
+
const fromEnv = (key) => process.env[key] ?? dotenvVars[key];
|
|
3201
|
+
const clientId = fromEnv("SEEKRIT_CLIENT_ID") ?? config.clientId;
|
|
3202
|
+
const clientSecret = fromEnv("SEEKRIT_CLIENT_SECRET") ?? config.clientSecret;
|
|
3203
|
+
if (!clientId || !clientSecret) return null;
|
|
3204
|
+
return {
|
|
3205
|
+
clientId,
|
|
3206
|
+
clientSecret
|
|
3207
|
+
};
|
|
3208
|
+
}
|
|
3209
|
+
/** True when a service/dev credential is already configured explicitly. */
|
|
3210
|
+
function hasExplicitCredential(dotenvVars) {
|
|
3211
|
+
const fromEnv = (key) => process.env[key] ?? dotenvVars[key];
|
|
3212
|
+
if (fromEnv("SEEKRIT_TOKEN") || fromEnv("SEEKRIT_DEV_USER")) return true;
|
|
3213
|
+
const config = readGlobalConfig();
|
|
3214
|
+
return Boolean(config.token || config.devUser);
|
|
3215
|
+
}
|
|
3216
|
+
/** Mint a fresh org-scoped admin token using M2M credentials (keyless). */
|
|
3217
|
+
async function mintAdminToken(apiUrl, creds) {
|
|
3218
|
+
const client = new SeekritClient({
|
|
3219
|
+
baseUrl: apiUrl,
|
|
3220
|
+
auth: {
|
|
3221
|
+
type: "m2m",
|
|
3222
|
+
clientId: creds.clientId,
|
|
3223
|
+
clientSecret: creds.clientSecret
|
|
3224
|
+
}
|
|
3225
|
+
});
|
|
3226
|
+
const { orgs } = await client.listOrgs();
|
|
3227
|
+
const org = orgs[0];
|
|
3228
|
+
if (!org) throw new Error("machine client has no organization — sign up first (POST /signup)");
|
|
3229
|
+
const created = await createServiceToken();
|
|
3230
|
+
await client.createToken(org.id, {
|
|
3231
|
+
name: "agent-admin (auto)",
|
|
3232
|
+
tokenId: created.tokenId,
|
|
3233
|
+
tokenHash: created.tokenHash,
|
|
3234
|
+
publicKeyJwk: created.publicKeyJwk,
|
|
3235
|
+
role: "admin",
|
|
3236
|
+
environmentId: null
|
|
3237
|
+
});
|
|
3238
|
+
return created.token;
|
|
3239
|
+
}
|
|
3240
|
+
/**
|
|
3241
|
+
* If M2M credentials are configured (and no explicit token/dev-user is), ensure
|
|
3242
|
+
* an admin token exists for them — minting and caching one on first use — and
|
|
3243
|
+
* expose it as `SEEKRIT_TOKEN` so the normal credential resolution
|
|
3244
|
+
* ({@link tryBuildContext}) picks it up. A no-op (returns null) when there is
|
|
3245
|
+
* nothing to bootstrap. Throws only when credentials ARE present but minting
|
|
3246
|
+
* fails, so the reason surfaces instead of a later opaque "no credentials".
|
|
3247
|
+
*/
|
|
3248
|
+
async function ensureM2mAdminToken(dotenvVars = {}) {
|
|
3249
|
+
if (hasExplicitCredential(dotenvVars)) return null;
|
|
3250
|
+
const creds = readM2mCreds(dotenvVars);
|
|
3251
|
+
if (!creds) return null;
|
|
3252
|
+
const cached = readCachedM2mToken(creds.clientId);
|
|
3253
|
+
if (cached) {
|
|
3254
|
+
process.env.SEEKRIT_TOKEN = cached;
|
|
3255
|
+
return cached;
|
|
3256
|
+
}
|
|
3257
|
+
const token = await mintAdminToken(process.env.SEEKRIT_API_URL ?? dotenvVars.SEEKRIT_API_URL ?? readGlobalConfig().apiUrl ?? "https://api.seekrit.dev", creds);
|
|
3258
|
+
cacheM2mToken(creds.clientId, token);
|
|
3259
|
+
process.env.SEEKRIT_TOKEN = token;
|
|
3260
|
+
return token;
|
|
3261
|
+
}
|
|
3262
|
+
//#endregion
|
|
3154
3263
|
//#region src/mongodb.ts
|
|
3155
3264
|
/**
|
|
3156
3265
|
* `seekrit mongodb` — temporary MongoDB credentials (Vault-style dynamic
|
|
@@ -4199,10 +4308,16 @@ function collectList(value, acc = []) {
|
|
|
4199
4308
|
return acc;
|
|
4200
4309
|
}
|
|
4201
4310
|
const program = new Command("seekrit").description("End-to-end encrypted secrets manager").version(version).enablePositionalOptions();
|
|
4202
|
-
program.
|
|
4311
|
+
program.hook("preAction", async () => {
|
|
4312
|
+
await ensureM2mAdminToken();
|
|
4313
|
+
});
|
|
4314
|
+
program.command("login").description("store credentials for the API").option("--token <token>", "service token (skt_…)").option("--client-id <id>", "machine (M2M) client id — auto-mints an admin token").option("--client-secret <secret>", "machine (M2M) client secret").option("--dev-user <email>", "dev-mode identity (local API with AUTH_MODE=dev)").option("--api-url <url>", "API base URL").action((options) => {
|
|
4203
4315
|
if (options.token && !isServiceToken(options.token)) fail("token must start with skt_");
|
|
4316
|
+
if (Boolean(options.clientId) !== Boolean(options.clientSecret)) fail("--client-id and --client-secret must be given together");
|
|
4204
4317
|
writeGlobalConfig({
|
|
4205
4318
|
...options.token ? { token: options.token } : {},
|
|
4319
|
+
...options.clientId ? { clientId: options.clientId } : {},
|
|
4320
|
+
...options.clientSecret ? { clientSecret: options.clientSecret } : {},
|
|
4206
4321
|
...options.devUser ? { devUser: options.devUser } : {},
|
|
4207
4322
|
...options.apiUrl ? { apiUrl: options.apiUrl } : {}
|
|
4208
4323
|
});
|
|
@@ -4425,6 +4540,7 @@ async function materializeForRun(options) {
|
|
|
4425
4540
|
const dotenvVars = {};
|
|
4426
4541
|
overlayEnvFiles(dotenvVars, {}, envFiles);
|
|
4427
4542
|
try {
|
|
4543
|
+
await ensureM2mAdminToken(dotenvVars);
|
|
4428
4544
|
const ctx = tryBuildContext(dotenvVars);
|
|
4429
4545
|
if (!ctx) throw new Error("no credentials found (set SEEKRIT_TOKEN / SEEKRIT_DEV_USER or run `seekrit login`)");
|
|
4430
4546
|
return await materialize(ctx, options);
|
|
@@ -4589,7 +4705,7 @@ registerMongoCommands(program);
|
|
|
4589
4705
|
registerKmsCommands(program);
|
|
4590
4706
|
registerRecoveryCommands(program);
|
|
4591
4707
|
program.command("mcp").description("run an MCP server over stdio so AI agents can drive seekrit").action(async () => {
|
|
4592
|
-
const { runMcpServer } = await import("./mcp-
|
|
4708
|
+
const { runMcpServer } = await import("./mcp-DScHFOMG.js");
|
|
4593
4709
|
await runMcpServer();
|
|
4594
4710
|
});
|
|
4595
4711
|
program.command("audit").description("show the org audit trail").option("--org <slug>").option("--limit <n>", "entries to fetch", "50").action(async (options) => {
|
|
@@ -4603,4 +4719,4 @@ program.parseAsync(argv).catch((err) => {
|
|
|
4603
4719
|
fail(err instanceof Error ? err.message : String(err));
|
|
4604
4720
|
});
|
|
4605
4721
|
//#endregion
|
|
4606
|
-
export {
|
|
4722
|
+
export { generateDataKey as A, importSigningKey as C, verifyMessage as D, signatureKeyRef as E, wrapDek as F, generateDek as I, toBase64 as L, kmsBlobKeyRef as M, kmsDecrypt as N, generatePostgresCredential as O, kmsEncrypt as P, generateSigningKeyMaterial as S, signMessage as T, writeProjectConfig as _, kmsCallerIdentity as a, isServiceToken as b, kmsResolveRecipient as c, resolveGroup as d, resolveOrg as f, setFailThrows as g, tryBuildContext as h, ensureM2mAdminToken as i, generateEncryptKeyMaterial as j, generateMysqlCredential as k, resolveAppEnv as l, isTokenAuth as m, fetchDecryptedSecrets as n, kmsRecoverMaterial as o, getDek as p, materializeEnv as r, kmsResolveKey as s, encryptAndSetSecret as t, resolveEnvTarget as u, version as v, importVerifyingKey as w, parseServiceToken as x, createServiceToken as y };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as generateDataKey, C as importSigningKey, D as verifyMessage, E as signatureKeyRef, F as wrapDek, I as generateDek, L as toBase64, M as kmsBlobKeyRef, N as kmsDecrypt, O as generatePostgresCredential, P as kmsEncrypt, S as generateSigningKeyMaterial, T as signMessage, _ as writeProjectConfig, a as kmsCallerIdentity, b as isServiceToken, c as kmsResolveRecipient, d as resolveGroup, f as resolveOrg, g as setFailThrows, h as tryBuildContext, i as ensureM2mAdminToken, j as generateEncryptKeyMaterial, k as generateMysqlCredential, l as resolveAppEnv, m as isTokenAuth, n as fetchDecryptedSecrets, o as kmsRecoverMaterial, p as getDek, r as materializeEnv, s as kmsResolveKey, t as encryptAndSetSecret, u as resolveEnvTarget, v as version, w as importVerifyingKey, x as parseServiceToken, y as createServiceToken } 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";
|
|
@@ -29,7 +29,7 @@ function errText(err) {
|
|
|
29
29
|
/** Build the client context or throw a friendly, agent-readable error. */
|
|
30
30
|
function getCtx() {
|
|
31
31
|
const ctx = tryBuildContext();
|
|
32
|
-
if (!ctx) throw new Error("no credentials — set SEEKRIT_TOKEN (a skt_… token)
|
|
32
|
+
if (!ctx) throw new Error("no credentials — set SEEKRIT_CLIENT_ID + SEEKRIT_CLIENT_SECRET (machine credentials from POST /signup) or SEEKRIT_TOKEN (a skt_… token) in the MCP server env, or run `seekrit login` first");
|
|
33
33
|
return ctx;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
@@ -115,11 +115,12 @@ async function resolveTargetEnv(ctx, o) {
|
|
|
115
115
|
}
|
|
116
116
|
return resolveEnvTarget(ctx, o);
|
|
117
117
|
}
|
|
118
|
-
async function runMcpServer() {
|
|
118
|
+
async function runMcpServer(options = {}) {
|
|
119
119
|
setFailThrows(true);
|
|
120
|
+
await ensureM2mAdminToken();
|
|
120
121
|
const server = new McpServer({
|
|
121
122
|
name: "seekrit",
|
|
122
|
-
version
|
|
123
|
+
version: options.version ?? version
|
|
123
124
|
});
|
|
124
125
|
/** Register a tool whose handler returns data (serialized) or throws (→ isError). */
|
|
125
126
|
const tool = (name, description, shape, handler) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seekrit/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "End-to-end encrypted secrets manager CLI — inject decrypted secrets into any command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
"bin": {
|
|
10
10
|
"seekrit": "./dist/index.js"
|
|
11
11
|
},
|
|
12
|
+
"exports": {
|
|
13
|
+
"./mcp": "./src/mcp.ts"
|
|
14
|
+
},
|
|
12
15
|
"files": [
|
|
13
16
|
"dist"
|
|
14
17
|
],
|