@seekrit/cli 0.19.0 → 0.21.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
|
@@ -1976,7 +1976,7 @@ function isServiceToken(value) {
|
|
|
1976
1976
|
}
|
|
1977
1977
|
//#endregion
|
|
1978
1978
|
//#region package.json
|
|
1979
|
-
var version = "0.
|
|
1979
|
+
var version = "0.21.0";
|
|
1980
1980
|
//#endregion
|
|
1981
1981
|
//#region ../../packages/api-client/src/index.ts
|
|
1982
1982
|
var SeekritApiError = class extends Error {
|
|
@@ -2005,7 +2005,8 @@ var SeekritClient = class {
|
|
|
2005
2005
|
const token = await this.auth.getToken();
|
|
2006
2006
|
if (!token) throw new SeekritApiError(401, "unauthorized", "session expired");
|
|
2007
2007
|
headers.authorization = `Bearer ${token}`;
|
|
2008
|
-
} 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;
|
|
2009
2010
|
if (body !== void 0) headers["content-type"] = "application/json";
|
|
2010
2011
|
const res = await this.fetchImpl(`${this.baseUrl}${path}`, {
|
|
2011
2012
|
method,
|
|
@@ -2258,6 +2259,10 @@ var SeekritClient = class {
|
|
|
2258
2259
|
disableKmsKey(orgId, keyId) {
|
|
2259
2260
|
return this.request("POST", `/v1/orgs/${orgId}/kms/keys/${keyId}/disable`);
|
|
2260
2261
|
}
|
|
2262
|
+
/** Soft-delete a key: it drops out of every listing and read path. */
|
|
2263
|
+
deleteKmsKey(orgId, keyId) {
|
|
2264
|
+
return this.request("DELETE", `/v1/orgs/${orgId}/kms/keys/${keyId}`);
|
|
2265
|
+
}
|
|
2261
2266
|
/** The broker's public key — wrap the admin credential to it before registering a target. */
|
|
2262
2267
|
getLeaseBrokerKey(orgId) {
|
|
2263
2268
|
return this.request("GET", `/v1/orgs/${orgId}/leases/broker-key`);
|
|
@@ -2355,6 +2360,17 @@ function writeGlobalConfig(update) {
|
|
|
2355
2360
|
mkdirSync(dirname(path), { recursive: true });
|
|
2356
2361
|
writeFileSync(path, `${JSON.stringify(merged, null, 2)}\n`, { mode: 384 });
|
|
2357
2362
|
}
|
|
2363
|
+
/** The admin token previously auto-minted from these M2M credentials, if any. */
|
|
2364
|
+
function readCachedM2mToken(clientId) {
|
|
2365
|
+
return readGlobalConfig().m2mTokens?.[clientId];
|
|
2366
|
+
}
|
|
2367
|
+
/** Remember an auto-minted admin token so the next run reuses it. */
|
|
2368
|
+
function cacheM2mToken(clientId, token) {
|
|
2369
|
+
writeGlobalConfig({ m2mTokens: {
|
|
2370
|
+
...readGlobalConfig().m2mTokens,
|
|
2371
|
+
[clientId]: token
|
|
2372
|
+
} });
|
|
2373
|
+
}
|
|
2358
2374
|
/** Walk up from cwd looking for seekrit.json. */
|
|
2359
2375
|
function findProjectConfig(startDir = process.cwd()) {
|
|
2360
2376
|
let dir = startDir;
|
|
@@ -2451,7 +2467,7 @@ function tryBuildContext(dotenvVars = {}) {
|
|
|
2451
2467
|
}
|
|
2452
2468
|
function buildContext() {
|
|
2453
2469
|
const ctx = tryBuildContext();
|
|
2454
|
-
if (!ctx) fail("no credentials found — run `seekrit login --token skt_
|
|
2470
|
+
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");
|
|
2455
2471
|
return ctx;
|
|
2456
2472
|
}
|
|
2457
2473
|
function isTokenAuth(ctx) {
|
|
@@ -3101,6 +3117,13 @@ function registerKmsCommands(program) {
|
|
|
3101
3117
|
await ctx.client.disableKmsKey(org.id, key.id);
|
|
3102
3118
|
console.error(`disabled ${key.name}`);
|
|
3103
3119
|
});
|
|
3120
|
+
kms.command("delete").description("delete a key (hides it from all listings; the name frees up for reuse)").requiredOption("--key <name>", "key name or id").option("--org <slug>").action(async (options) => {
|
|
3121
|
+
const ctx = buildContext();
|
|
3122
|
+
const org = await resolveOrg(ctx, options.org);
|
|
3123
|
+
const key = await kmsResolveKey(ctx, org.id, options.key);
|
|
3124
|
+
await ctx.client.deleteKmsKey(org.id, key.id);
|
|
3125
|
+
console.error(`deleted ${key.name}`);
|
|
3126
|
+
});
|
|
3104
3127
|
kms.command("encrypt").description("encrypt stdin under a key (prints a ce1 ciphertext blob)").requiredOption("--key <name>", "key name or id").option("--org <slug>").option("--context <ctx>", "encryption context bound as AAD (required identically to decrypt)").action(async (options) => {
|
|
3105
3128
|
const ctx = buildContext();
|
|
3106
3129
|
const org = await resolveOrg(ctx, options.org);
|
|
@@ -3177,6 +3200,77 @@ function registerKmsCommands(program) {
|
|
|
3177
3200
|
});
|
|
3178
3201
|
}
|
|
3179
3202
|
//#endregion
|
|
3203
|
+
//#region src/m2m.ts
|
|
3204
|
+
/**
|
|
3205
|
+
* Resolve M2M client credentials from (in order) the process environment, a
|
|
3206
|
+
* `.env` overlay (for `seekrit run`), then saved config. Both halves must come
|
|
3207
|
+
* through for the credential to be usable.
|
|
3208
|
+
*/
|
|
3209
|
+
function readM2mCreds(dotenvVars = {}) {
|
|
3210
|
+
const config = readGlobalConfig();
|
|
3211
|
+
const fromEnv = (key) => process.env[key] ?? dotenvVars[key];
|
|
3212
|
+
const clientId = fromEnv("SEEKRIT_CLIENT_ID") ?? config.clientId;
|
|
3213
|
+
const clientSecret = fromEnv("SEEKRIT_CLIENT_SECRET") ?? config.clientSecret;
|
|
3214
|
+
if (!clientId || !clientSecret) return null;
|
|
3215
|
+
return {
|
|
3216
|
+
clientId,
|
|
3217
|
+
clientSecret
|
|
3218
|
+
};
|
|
3219
|
+
}
|
|
3220
|
+
/** True when a service/dev credential is already configured explicitly. */
|
|
3221
|
+
function hasExplicitCredential(dotenvVars) {
|
|
3222
|
+
const fromEnv = (key) => process.env[key] ?? dotenvVars[key];
|
|
3223
|
+
if (fromEnv("SEEKRIT_TOKEN") || fromEnv("SEEKRIT_DEV_USER")) return true;
|
|
3224
|
+
const config = readGlobalConfig();
|
|
3225
|
+
return Boolean(config.token || config.devUser);
|
|
3226
|
+
}
|
|
3227
|
+
/** Mint a fresh org-scoped admin token using M2M credentials (keyless). */
|
|
3228
|
+
async function mintAdminToken(apiUrl, creds) {
|
|
3229
|
+
const client = new SeekritClient({
|
|
3230
|
+
baseUrl: apiUrl,
|
|
3231
|
+
auth: {
|
|
3232
|
+
type: "m2m",
|
|
3233
|
+
clientId: creds.clientId,
|
|
3234
|
+
clientSecret: creds.clientSecret
|
|
3235
|
+
}
|
|
3236
|
+
});
|
|
3237
|
+
const { orgs } = await client.listOrgs();
|
|
3238
|
+
const org = orgs[0];
|
|
3239
|
+
if (!org) throw new Error("machine client has no organization — sign up first (POST /signup)");
|
|
3240
|
+
const created = await createServiceToken();
|
|
3241
|
+
await client.createToken(org.id, {
|
|
3242
|
+
name: "agent-admin (auto)",
|
|
3243
|
+
tokenId: created.tokenId,
|
|
3244
|
+
tokenHash: created.tokenHash,
|
|
3245
|
+
publicKeyJwk: created.publicKeyJwk,
|
|
3246
|
+
role: "admin",
|
|
3247
|
+
environmentId: null
|
|
3248
|
+
});
|
|
3249
|
+
return created.token;
|
|
3250
|
+
}
|
|
3251
|
+
/**
|
|
3252
|
+
* If M2M credentials are configured (and no explicit token/dev-user is), ensure
|
|
3253
|
+
* an admin token exists for them — minting and caching one on first use — and
|
|
3254
|
+
* expose it as `SEEKRIT_TOKEN` so the normal credential resolution
|
|
3255
|
+
* ({@link tryBuildContext}) picks it up. A no-op (returns null) when there is
|
|
3256
|
+
* nothing to bootstrap. Throws only when credentials ARE present but minting
|
|
3257
|
+
* fails, so the reason surfaces instead of a later opaque "no credentials".
|
|
3258
|
+
*/
|
|
3259
|
+
async function ensureM2mAdminToken(dotenvVars = {}) {
|
|
3260
|
+
if (hasExplicitCredential(dotenvVars)) return null;
|
|
3261
|
+
const creds = readM2mCreds(dotenvVars);
|
|
3262
|
+
if (!creds) return null;
|
|
3263
|
+
const cached = readCachedM2mToken(creds.clientId);
|
|
3264
|
+
if (cached) {
|
|
3265
|
+
process.env.SEEKRIT_TOKEN = cached;
|
|
3266
|
+
return cached;
|
|
3267
|
+
}
|
|
3268
|
+
const token = await mintAdminToken(process.env.SEEKRIT_API_URL ?? dotenvVars.SEEKRIT_API_URL ?? readGlobalConfig().apiUrl ?? "https://api.seekrit.dev", creds);
|
|
3269
|
+
cacheM2mToken(creds.clientId, token);
|
|
3270
|
+
process.env.SEEKRIT_TOKEN = token;
|
|
3271
|
+
return token;
|
|
3272
|
+
}
|
|
3273
|
+
//#endregion
|
|
3180
3274
|
//#region src/mongodb.ts
|
|
3181
3275
|
/**
|
|
3182
3276
|
* `seekrit mongodb` — temporary MongoDB credentials (Vault-style dynamic
|
|
@@ -4225,10 +4319,16 @@ function collectList(value, acc = []) {
|
|
|
4225
4319
|
return acc;
|
|
4226
4320
|
}
|
|
4227
4321
|
const program = new Command("seekrit").description("End-to-end encrypted secrets manager").version(version).enablePositionalOptions();
|
|
4228
|
-
program.
|
|
4322
|
+
program.hook("preAction", async () => {
|
|
4323
|
+
await ensureM2mAdminToken();
|
|
4324
|
+
});
|
|
4325
|
+
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) => {
|
|
4229
4326
|
if (options.token && !isServiceToken(options.token)) fail("token must start with skt_");
|
|
4327
|
+
if (Boolean(options.clientId) !== Boolean(options.clientSecret)) fail("--client-id and --client-secret must be given together");
|
|
4230
4328
|
writeGlobalConfig({
|
|
4231
4329
|
...options.token ? { token: options.token } : {},
|
|
4330
|
+
...options.clientId ? { clientId: options.clientId } : {},
|
|
4331
|
+
...options.clientSecret ? { clientSecret: options.clientSecret } : {},
|
|
4232
4332
|
...options.devUser ? { devUser: options.devUser } : {},
|
|
4233
4333
|
...options.apiUrl ? { apiUrl: options.apiUrl } : {}
|
|
4234
4334
|
});
|
|
@@ -4451,6 +4551,7 @@ async function materializeForRun(options) {
|
|
|
4451
4551
|
const dotenvVars = {};
|
|
4452
4552
|
overlayEnvFiles(dotenvVars, {}, envFiles);
|
|
4453
4553
|
try {
|
|
4554
|
+
await ensureM2mAdminToken(dotenvVars);
|
|
4454
4555
|
const ctx = tryBuildContext(dotenvVars);
|
|
4455
4556
|
if (!ctx) throw new Error("no credentials found (set SEEKRIT_TOKEN / SEEKRIT_DEV_USER or run `seekrit login`)");
|
|
4456
4557
|
return await materialize(ctx, options);
|
|
@@ -4615,7 +4716,7 @@ registerMongoCommands(program);
|
|
|
4615
4716
|
registerKmsCommands(program);
|
|
4616
4717
|
registerRecoveryCommands(program);
|
|
4617
4718
|
program.command("mcp").description("run an MCP server over stdio so AI agents can drive seekrit").action(async () => {
|
|
4618
|
-
const { runMcpServer } = await import("./mcp-
|
|
4719
|
+
const { runMcpServer } = await import("./mcp-DScHFOMG.js");
|
|
4619
4720
|
await runMcpServer();
|
|
4620
4721
|
});
|
|
4621
4722
|
program.command("audit").description("show the org audit trail").option("--org <slug>").option("--limit <n>", "entries to fetch", "50").action(async (options) => {
|
|
@@ -4629,4 +4730,4 @@ program.parseAsync(argv).catch((err) => {
|
|
|
4629
4730
|
fail(err instanceof Error ? err.message : String(err));
|
|
4630
4731
|
});
|
|
4631
4732
|
//#endregion
|
|
4632
|
-
export {
|
|
4733
|
+
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
|
/**
|
|
@@ -117,6 +117,7 @@ async function resolveTargetEnv(ctx, o) {
|
|
|
117
117
|
}
|
|
118
118
|
async function runMcpServer(options = {}) {
|
|
119
119
|
setFailThrows(true);
|
|
120
|
+
await ensureM2mAdminToken();
|
|
120
121
|
const server = new McpServer({
|
|
121
122
|
name: "seekrit",
|
|
122
123
|
version: options.version ?? version
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seekrit/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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/
|
|
31
|
-
"@seekrit/
|
|
30
|
+
"@seekrit/core": "0.0.1",
|
|
31
|
+
"@seekrit/crypto": "0.0.1"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsdown",
|