@seekrit/cli 1.8.0 → 1.9.1
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 +188 -33
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2561,7 +2561,8 @@ const SYNC_PROVIDER_KINDS = [
|
|
|
2561
2561
|
"langgraph-platform",
|
|
2562
2562
|
"azure-key-vault",
|
|
2563
2563
|
"huggingface-spaces",
|
|
2564
|
-
"openai-vault"
|
|
2564
|
+
"openai-vault",
|
|
2565
|
+
"claude-managed-agents"
|
|
2565
2566
|
];
|
|
2566
2567
|
z.enum(SYNC_PROVIDER_KINDS);
|
|
2567
2568
|
/**
|
|
@@ -2967,6 +2968,23 @@ const azureKeyVaultConnectionConfigSchema = z.object({
|
|
|
2967
2968
|
const huggingfaceSpacesConnectionConfigSchema = z.object({ provider: z.literal("huggingface-spaces") });
|
|
2968
2969
|
/** The wrapped credential is an OpenAI project API key with vault read/write access. */
|
|
2969
2970
|
const openaiVaultConnectionConfigSchema = z.object({ provider: z.literal("openai-vault") });
|
|
2971
|
+
/**
|
|
2972
|
+
* Claude Managed Agents account scope — empty, for Hugging Face's reason.
|
|
2973
|
+
*
|
|
2974
|
+
* An Anthropic API key belongs to exactly one **workspace**, and vaults are
|
|
2975
|
+
* workspace-scoped: the key can reach every vault in its workspace and none
|
|
2976
|
+
* outside it. A vault ID is globally unique. So the key plus the destination is
|
|
2977
|
+
* the whole address, and the API's `anthropic-workspace-id` header — which
|
|
2978
|
+
* exists for credentials that span several workspaces — has nothing to select.
|
|
2979
|
+
*
|
|
2980
|
+
* There is no `baseUrl`, and adding one would not be enough. Bedrock, Vertex
|
|
2981
|
+
* AI, and Foundry do not offer Managed Agents at all. Claude Platform on AWS
|
|
2982
|
+
* does, at `aws-external-anthropic.{region}.api.aws` — but it authenticates
|
|
2983
|
+
* with SigV4 over an IAM credential, not an API key, so it is a different
|
|
2984
|
+
* credential shape and a different connector (one that would reuse
|
|
2985
|
+
* `lib/leases/aws-sigv4.ts`), not a different host for this one.
|
|
2986
|
+
*/
|
|
2987
|
+
const claudeManagedAgentsConnectionConfigSchema = z.object({ provider: z.literal("claude-managed-agents") });
|
|
2970
2988
|
const syncConnectionConfigSchema = z.discriminatedUnion("provider", [
|
|
2971
2989
|
vercelConnectionConfigSchema,
|
|
2972
2990
|
cloudflareWorkersConnectionConfigSchema,
|
|
@@ -2987,7 +3005,8 @@ const syncConnectionConfigSchema = z.discriminatedUnion("provider", [
|
|
|
2987
3005
|
langgraphPlatformConnectionConfigSchema,
|
|
2988
3006
|
azureKeyVaultConnectionConfigSchema,
|
|
2989
3007
|
huggingfaceSpacesConnectionConfigSchema,
|
|
2990
|
-
openaiVaultConnectionConfigSchema
|
|
3008
|
+
openaiVaultConnectionConfigSchema,
|
|
3009
|
+
claudeManagedAgentsConnectionConfigSchema
|
|
2991
3010
|
]);
|
|
2992
3011
|
/** Vercel's three deployment targets. A binding writes to one or more. */
|
|
2993
3012
|
const VERCEL_TARGETS = [
|
|
@@ -3818,6 +3837,85 @@ const openaiVaultDestinationSchema = z.object({
|
|
|
3818
3837
|
vaultId: z.string().trim().regex(/^vault_[A-Za-z0-9_-]+$/, "must be an OpenAI vault ID"),
|
|
3819
3838
|
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
3839
|
});
|
|
3840
|
+
/**
|
|
3841
|
+
* A vault ID as the Claude Console and API show it: `vlt_` and an opaque
|
|
3842
|
+
* alphanumeric tail. Held to that shape because the habitual slip — pasting the
|
|
3843
|
+
* vault's display name — is a 404 from inside an alarm, which says nothing
|
|
3844
|
+
* about which half of the address was wrong.
|
|
3845
|
+
*/
|
|
3846
|
+
const claudeVaultIdSchema = z.string().trim().regex(/^vlt_[A-Za-z0-9]{1,128}$/, "must be a vault ID, e.g. vlt_01ABC… — not its display name");
|
|
3847
|
+
/** Ceiling on per-name host rules; a vault itself holds at most 20 credentials. */
|
|
3848
|
+
const CLAUDE_VAULT_MAX_HOST_RULES = 100;
|
|
3849
|
+
/** A hostname of two or more labels whose last label starts with a letter. */
|
|
3850
|
+
const CLAUDE_VAULT_HOSTNAME = /^(?:\*\.)?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
3851
|
+
const CLAUDE_VAULT_IPV4 = /^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/;
|
|
3852
|
+
/**
|
|
3853
|
+
* One host a secret's value may be substituted on, in the three forms the vault
|
|
3854
|
+
* API takes: a bare hostname (`api.openai.com`), a `*.`-prefixed wildcard
|
|
3855
|
+
* (`*.stripe.com`), or an IPv4 address.
|
|
3856
|
+
*
|
|
3857
|
+
* The API refuses URLs, ports, paths, and IPv6, so those are refused here too —
|
|
3858
|
+
* pasting `https://api.openai.com/v1` is the slip this exists for. Note the
|
|
3859
|
+
* wildcard's documented reach: `*.stripe.com` matches every subdomain of
|
|
3860
|
+
* `stripe.com` **but not `stripe.com` itself**, so a service reached at its apex
|
|
3861
|
+
* needs both entries. A bare TLD wildcard (`*.com`) is refused by shape.
|
|
3862
|
+
*/
|
|
3863
|
+
const claudeVaultHostSchema = z.string().trim().toLowerCase().max(253).refine((host) => CLAUDE_VAULT_HOSTNAME.test(host) || CLAUDE_VAULT_IPV4.test(host), "must be a bare hostname (api.example.com), a *.example.com wildcard, or an IPv4 address — not a URL");
|
|
3864
|
+
/**
|
|
3865
|
+
* Where one secret's value may be substituted: a list of hosts, or the literal
|
|
3866
|
+
* `"unrestricted"`, which lets it reach any host the agent's *environment*
|
|
3867
|
+
* permits. The literal is spelled out rather than implied by an empty list, so
|
|
3868
|
+
* the widest setting is never something a binding arrives at by omission.
|
|
3869
|
+
*/
|
|
3870
|
+
const claudeVaultHostRuleSchema = z.union([z.literal("unrestricted"), z.array(claudeVaultHostSchema).min(1).max(16)]);
|
|
3871
|
+
/**
|
|
3872
|
+
* Which parts of an outbound request a secret's placeholder is swapped in. The
|
|
3873
|
+
* API's own default when the field is omitted is both; seekrit's is `header`,
|
|
3874
|
+
* the narrower one — request bodies are often assembled from content the agent
|
|
3875
|
+
* is working with, and most services read an API key from a header. It is also
|
|
3876
|
+
* what the Claude Console creates by default, so the two agree.
|
|
3877
|
+
*/
|
|
3878
|
+
const CLAUDE_VAULT_INJECTION_LOCATIONS = [
|
|
3879
|
+
"header",
|
|
3880
|
+
"body",
|
|
3881
|
+
"both"
|
|
3882
|
+
];
|
|
3883
|
+
/**
|
|
3884
|
+
* One **Claude Managed Agents vault**, whose `environment_variable` credentials
|
|
3885
|
+
* a binding owns.
|
|
3886
|
+
*
|
|
3887
|
+
* Like `openai-vault`, a destination where the workload never holds the
|
|
3888
|
+
* plaintext either: an agent's sandbox sees an opaque placeholder in the
|
|
3889
|
+
* variable, and Anthropic's egress proxy swaps the real value in only on a
|
|
3890
|
+
* request to a host the credential names. So every name needs somewhere it is
|
|
3891
|
+
* allowed to go, and that is most of this shape:
|
|
3892
|
+
*
|
|
3893
|
+
* - `allowedHostsByName` — per destination name, after `nameTransform`. The
|
|
3894
|
+
* right tool: a Stripe key belongs on `api.stripe.com` and nowhere else.
|
|
3895
|
+
* - `allowedHosts` — for every name without its own rule.
|
|
3896
|
+
*
|
|
3897
|
+
* At least one must be given. A pushed name covered by neither **fails** rather
|
|
3898
|
+
* than going out `unrestricted` — the default-deny stance `apps/proxy` takes, so
|
|
3899
|
+
* a secret added to the environment later cannot silently widen where values
|
|
3900
|
+
* flow. Both levels apply beside the agent environment's own network policy,
|
|
3901
|
+
* which must also let the host through; this shape cannot see that policy.
|
|
3902
|
+
*/
|
|
3903
|
+
const claudeManagedAgentsDestinationSchema = z.object({
|
|
3904
|
+
provider: z.literal("claude-managed-agents"),
|
|
3905
|
+
/** The vault's ID, e.g. `vlt_01ABC…`. */
|
|
3906
|
+
vaultId: claudeVaultIdSchema,
|
|
3907
|
+
/** Hosts for every name without its own entry in `allowedHostsByName`. */
|
|
3908
|
+
allowedHosts: claudeVaultHostRuleSchema.optional(),
|
|
3909
|
+
/** Destination name → the hosts that one value may be substituted on. */
|
|
3910
|
+
allowedHostsByName: z.record(secretNameSchema, claudeVaultHostRuleSchema).optional(),
|
|
3911
|
+
injectionLocation: z.enum(CLAUDE_VAULT_INJECTION_LOCATIONS).default("header")
|
|
3912
|
+
}).refine((d) => d.allowedHosts !== void 0 || Object.keys(d.allowedHostsByName ?? {}).length > 0, {
|
|
3913
|
+
message: "name the hosts each secret may be used on — allowedHosts for all of them, allowedHostsByName per name, or \"unrestricted\"",
|
|
3914
|
+
path: ["allowedHosts"]
|
|
3915
|
+
}).refine((d) => Object.keys(d.allowedHostsByName ?? {}).length <= CLAUDE_VAULT_MAX_HOST_RULES, {
|
|
3916
|
+
message: `at most ${CLAUDE_VAULT_MAX_HOST_RULES} per-name host rules`,
|
|
3917
|
+
path: ["allowedHostsByName"]
|
|
3918
|
+
});
|
|
3821
3919
|
const syncDestinationSchema = z.discriminatedUnion("provider", [
|
|
3822
3920
|
vercelDestinationSchema,
|
|
3823
3921
|
cloudflareWorkersDestinationSchema,
|
|
@@ -3838,7 +3936,8 @@ const syncDestinationSchema = z.discriminatedUnion("provider", [
|
|
|
3838
3936
|
langgraphPlatformDestinationSchema,
|
|
3839
3937
|
azureKeyVaultDestinationSchema,
|
|
3840
3938
|
huggingfaceSpacesDestinationSchema,
|
|
3841
|
-
openaiVaultDestinationSchema
|
|
3939
|
+
openaiVaultDestinationSchema,
|
|
3940
|
+
claudeManagedAgentsDestinationSchema
|
|
3842
3941
|
]);
|
|
3843
3942
|
/**
|
|
3844
3943
|
* How seekrit secret names become destination key names. Applied in order:
|
|
@@ -5126,7 +5225,7 @@ async function createAgentTaskToken() {
|
|
|
5126
5225
|
}
|
|
5127
5226
|
//#endregion
|
|
5128
5227
|
//#region package.json
|
|
5129
|
-
var version = "1.
|
|
5228
|
+
var version = "1.9.1";
|
|
5130
5229
|
//#endregion
|
|
5131
5230
|
//#region ../../packages/api-client/src/index.ts
|
|
5132
5231
|
/**
|
|
@@ -7690,7 +7789,7 @@ async function syncGrantsForGroupLink(ctx, orgId, envId, envSlug, groupId) {
|
|
|
7690
7789
|
//#endregion
|
|
7691
7790
|
//#region src/kms.ts
|
|
7692
7791
|
/** Collect a repeatable option into a list. */
|
|
7693
|
-
function collect$
|
|
7792
|
+
function collect$9(value, acc = []) {
|
|
7694
7793
|
acc.push(value);
|
|
7695
7794
|
return acc;
|
|
7696
7795
|
}
|
|
@@ -7758,7 +7857,7 @@ async function kmsRecoverMaterial(ctx, orgId, keyId, version) {
|
|
|
7758
7857
|
}
|
|
7759
7858
|
function registerKmsCommands(program) {
|
|
7760
7859
|
const kms = program.command("kms").description("managed keys for application-layer encryption & signing (client-side)");
|
|
7761
|
-
kms.command("create").description("create a managed key (material is generated locally and wrapped, never sent)").requiredOption("--name <name>", "org-unique key name").requiredOption("--purpose <purpose>", "encrypt | sign").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "scope the key to an application").option("--group <slug>", "scope the key to a group").option("--grant-user <email>", "also grant an org member (repeatable)", collect$
|
|
7860
|
+
kms.command("create").description("create a managed key (material is generated locally and wrapped, never sent)").requiredOption("--name <name>", "org-unique key name").requiredOption("--purpose <purpose>", "encrypt | sign").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--app <slug>", "scope the key to an application").option("--group <slug>", "scope the key to a group").option("--grant-user <email>", "also grant an org member (repeatable)", collect$9, []).option("--grant-token <tokenId>", "also grant a service token (repeatable)", collect$9, []).action(async (options) => {
|
|
7762
7861
|
if (options.purpose !== "encrypt" && options.purpose !== "sign") fail("--purpose must be encrypt or sign");
|
|
7763
7862
|
if (options.app && options.group) fail("pass at most one of --app or --group");
|
|
7764
7863
|
const ctx = buildContext();
|
|
@@ -7980,7 +8079,7 @@ function registerKmsCommands(program) {
|
|
|
7980
8079
|
//#endregion
|
|
7981
8080
|
//#region src/recovery.ts
|
|
7982
8081
|
/** Collect a repeatable option into a list. */
|
|
7983
|
-
function collect$
|
|
8082
|
+
function collect$8(value, acc = []) {
|
|
7984
8083
|
acc.push(value);
|
|
7985
8084
|
return acc;
|
|
7986
8085
|
}
|
|
@@ -8077,7 +8176,7 @@ function registerRecoveryCommands(program) {
|
|
|
8077
8176
|
for (const cst of status.custodians) console.log(` - ${cst.label ?? cst.principalId} (${cst.principalType}, share #${cst.shareIndex})`);
|
|
8078
8177
|
if (status.coverage.unprotectedEnvIds.length > 0) console.log(`${status.coverage.unprotectedEnvIds.length} environment(s) not yet protected — run \`seekrit recovery sync\``);
|
|
8079
8178
|
});
|
|
8080
|
-
recovery.command("setup").description("enable recovery: split a fresh recovery key across custodians").requiredOption("--threshold <M>", "custodians required to recover (M in M-of-N)").option("--custodian <email|skt_id>", "a recovery custodian (repeatable)", collect$
|
|
8179
|
+
recovery.command("setup").description("enable recovery: split a fresh recovery key across custodians").requiredOption("--threshold <M>", "custodians required to recover (M in M-of-N)").option("--custodian <email|skt_id>", "a recovery custodian (repeatable)", collect$8, []).option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
8081
8180
|
const ctx = buildContext();
|
|
8082
8181
|
const org = await resolveOrg(ctx, options.org);
|
|
8083
8182
|
const config = await buildRecoveryConfig(ctx, org.id, options.threshold, options.custodian);
|
|
@@ -8095,7 +8194,7 @@ function registerRecoveryCommands(program) {
|
|
|
8095
8194
|
const { wrapped, skipped } = await syncRecoveryGrants(ctx, (await resolveOrg(ctx, options.org)).id);
|
|
8096
8195
|
console.error(`recovery-protected ${wrapped} environment(s); skipped ${skipped} you cannot decrypt`);
|
|
8097
8196
|
});
|
|
8098
|
-
recovery.command("rotate").description("rotate the recovery key (new keypair, custodians, and env re-wraps)").requiredOption("--threshold <M>", "custodians required to recover (M in M-of-N)").option("--custodian <email|skt_id>", "a recovery custodian (repeatable)", collect$
|
|
8197
|
+
recovery.command("rotate").description("rotate the recovery key (new keypair, custodians, and env re-wraps)").requiredOption("--threshold <M>", "custodians required to recover (M in M-of-N)").option("--custodian <email|skt_id>", "a recovery custodian (repeatable)", collect$8, []).option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
8099
8198
|
const ctx = buildContext();
|
|
8100
8199
|
const org = await resolveOrg(ctx, options.org);
|
|
8101
8200
|
const config = await buildRecoveryConfig(ctx, org.id, options.threshold, options.custodian);
|
|
@@ -9238,7 +9337,7 @@ function formatSecrets(values, format) {
|
|
|
9238
9337
|
* `buildContext()` in any of them silently breaks the promise the feature makes.
|
|
9239
9338
|
*/
|
|
9240
9339
|
/** Collect a repeatable option into a list. */
|
|
9241
|
-
function collect$
|
|
9340
|
+
function collect$7(value, acc = []) {
|
|
9242
9341
|
acc.push(value);
|
|
9243
9342
|
return acc;
|
|
9244
9343
|
}
|
|
@@ -9515,7 +9614,7 @@ function registerArchiveCommands(program) {
|
|
|
9515
9614
|
} catch {}
|
|
9516
9615
|
fail("none of the recovery shares in this archive unwrap with that key");
|
|
9517
9616
|
});
|
|
9518
|
-
archive.command("decrypt <file>").description("decrypt an archive's secrets with your own key (offline)").option("-o, --out <dir>", "write one file per environment into this directory").option("--stdout", "print plaintext to stdout instead of writing files").option("--env <label>", "only this environment (app/env, group@env, slug, or id)").option("--format <format>", "dotenv | json | shell", "dotenv").option("--token <skt_…>", "decrypt as a service token instead of a passphrase").option("--key-file <path>", "decrypt with a private key JWK file").option("--share <file>", "custodian share for an offline quorum (repeatable)", collect$
|
|
9617
|
+
archive.command("decrypt <file>").description("decrypt an archive's secrets with your own key (offline)").option("-o, --out <dir>", "write one file per environment into this directory").option("--stdout", "print plaintext to stdout instead of writing files").option("--env <label>", "only this environment (app/env, group@env, slug, or id)").option("--format <format>", "dotenv | json | shell", "dotenv").option("--token <skt_…>", "decrypt as a service token instead of a passphrase").option("--key-file <path>", "decrypt with a private key JWK file").option("--share <file>", "custodian share for an offline quorum (repeatable)", collect$7).option("-y, --yes", "skip the confirmation when printing plaintext to stdout").action(async (file, options) => {
|
|
9519
9618
|
if (!options.out && !options.stdout) fail("choose a destination: --out <dir> to write files, or --stdout to print plaintext");
|
|
9520
9619
|
if (![
|
|
9521
9620
|
"dotenv",
|
|
@@ -11562,7 +11661,7 @@ function parseTtlSeconds$4(input) {
|
|
|
11562
11661
|
}[m[2] || "s"] ?? 1);
|
|
11563
11662
|
}
|
|
11564
11663
|
/** Collect a repeatable option into an array. */
|
|
11565
|
-
function collect$
|
|
11664
|
+
function collect$6(value, previous) {
|
|
11566
11665
|
return [...previous, value];
|
|
11567
11666
|
}
|
|
11568
11667
|
/** Parse `readWrite@app` → { role, db } for a custom target. */
|
|
@@ -11587,7 +11686,7 @@ function resolveAdminUri(uri) {
|
|
|
11587
11686
|
function registerMongoCommands(program) {
|
|
11588
11687
|
const mongo = program.command("mongodb").description("temporary MongoDB credentials (createUser, zero-knowledge delivery)");
|
|
11589
11688
|
const target = mongo.command("target").description("manage MongoDB targets");
|
|
11590
|
-
target.command("add").description("register a MongoDB cluster to issue temporary credentials from").requiredOption("--name <name>", "display name, e.g. prod-app").requiredOption("--database <db>", "the database leased users get access to, e.g. app").option("--uri <uri>", "admin connection string (else SEEKRIT_MONGODB_ADMIN_URL)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--role <role@db>", "grant for a custom target (repeatable)", collect$
|
|
11689
|
+
target.command("add").description("register a MongoDB cluster to issue temporary credentials from").requiredOption("--name <name>", "display name, e.g. prod-app").requiredOption("--database <db>", "the database leased users get access to, e.g. app").option("--uri <uri>", "admin connection string (else SEEKRIT_MONGODB_ADMIN_URL)").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--role <role@db>", "grant for a custom target (repeatable)", collect$6, []).option("--auth-source <db>", "authentication database (default admin)").option("--max-ttl <duration>", "clamp requested credential lifetime, e.g. 8h").option("--no-tls", "disable TLS to the cluster (TLS is on by default)").action(async (options) => {
|
|
11591
11690
|
const ctx = buildContext();
|
|
11592
11691
|
const org = await resolveOrg(ctx, options.org);
|
|
11593
11692
|
const adminUri = resolveAdminUri(options.uri);
|
|
@@ -11749,7 +11848,7 @@ function generateUserName$1(prefix = "tmp") {
|
|
|
11749
11848
|
function registerMysqlCommands(program) {
|
|
11750
11849
|
const mysql = program.command("mysql").description("temporary MySQL/MariaDB credentials (short-lived, zero-knowledge)");
|
|
11751
11850
|
const target = mysql.command("target").description("manage provisioning targets");
|
|
11752
|
-
target.command("add").description("register a MySQL/MariaDB server to lease credentials from").requiredOption("--name <name>", "display name, e.g. prod-db").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--host <host>", "database host").option("--port <port>", "database port", "3306").requiredOption("--database <name>", "database to grant access to").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--user-host <host>", "host part of created accounts ('name'@'<host>')", "%").option("--executor <mode>", "in_do | remote", "in_do").option("--provisioner-url <url>", "customer provisioner URL (remote executor)").option("--hmac-key <base64>", "shared HMAC key for the remote executor (or set SEEKRIT_PROVISIONER_HMAC_KEY); `seekrit provisioner keygen` mints one").option("--admin-url <url>", "in_do: admin mysql:// connection string (or set SEEKRIT_MYSQL_ADMIN_URL); wrapped locally").option("--create-statement <sql>", "custom CREATE template (repeatable)", collect$
|
|
11851
|
+
target.command("add").description("register a MySQL/MariaDB server to lease credentials from").requiredOption("--name <name>", "display name, e.g. prod-db").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--host <host>", "database host").option("--port <port>", "database port", "3306").requiredOption("--database <name>", "database to grant access to").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--user-host <host>", "host part of created accounts ('name'@'<host>')", "%").option("--executor <mode>", "in_do | remote", "in_do").option("--provisioner-url <url>", "customer provisioner URL (remote executor)").option("--hmac-key <base64>", "shared HMAC key for the remote executor (or set SEEKRIT_PROVISIONER_HMAC_KEY); `seekrit provisioner keygen` mints one").option("--admin-url <url>", "in_do: admin mysql:// connection string (or set SEEKRIT_MYSQL_ADMIN_URL); wrapped locally").option("--create-statement <sql>", "custom CREATE template (repeatable)", collect$5, []).option("--revoke-statement <sql>", "custom REVOKE template (repeatable)", collect$5, []).action(async (options) => {
|
|
11753
11852
|
const ctx = buildContext();
|
|
11754
11853
|
const org = await resolveOrg(ctx, options.org);
|
|
11755
11854
|
const executor = options.executor === "remote" ? "remote" : "in_do";
|
|
@@ -11852,7 +11951,7 @@ function registerMysqlCommands(program) {
|
|
|
11852
11951
|
});
|
|
11853
11952
|
}
|
|
11854
11953
|
/** Collect a repeatable option into an array. */
|
|
11855
|
-
function collect$
|
|
11954
|
+
function collect$5(value, acc) {
|
|
11856
11955
|
acc.push(value);
|
|
11857
11956
|
return acc;
|
|
11858
11957
|
}
|
|
@@ -11892,7 +11991,7 @@ function collect$4(value, acc) {
|
|
|
11892
11991
|
* release-please-config.json), so the pin follows the crate without anyone
|
|
11893
11992
|
* remembering to move it.
|
|
11894
11993
|
*/
|
|
11895
|
-
const PROXY_VERSION = "0.12.
|
|
11994
|
+
const PROXY_VERSION = "0.12.3";
|
|
11896
11995
|
const BIN = "seekrit-proxy";
|
|
11897
11996
|
/**
|
|
11898
11997
|
* Host → Rust target triple.
|
|
@@ -11928,7 +12027,7 @@ function versionPrefix(version) {
|
|
|
11928
12027
|
return version.startsWith("v") ? version : `v${version}`;
|
|
11929
12028
|
}
|
|
11930
12029
|
function resolveVersion(explicit) {
|
|
11931
|
-
return explicit ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.12.
|
|
12030
|
+
return explicit ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.12.3";
|
|
11932
12031
|
}
|
|
11933
12032
|
function resolveBaseUrl(explicit) {
|
|
11934
12033
|
return (explicit ?? process.env.SEEKRIT_PROXY_BASE_URL ?? "https://proxy.seekrit.dev").replace(/\/+$/, "");
|
|
@@ -12086,7 +12185,7 @@ const PLUGIN_BIN = "seekrit-nomad-secrets";
|
|
|
12086
12185
|
* entry in release-please-config.json), so the pin follows the crate without
|
|
12087
12186
|
* anyone remembering to move it — the same arrangement `PROXY_VERSION` has.
|
|
12088
12187
|
*/
|
|
12089
|
-
const NOMAD_PLUGIN_VERSION = "0.2.
|
|
12188
|
+
const NOMAD_PLUGIN_VERSION = "0.2.2";
|
|
12090
12189
|
/** Public artifact host. Mirrors GitHub Releases; public, no egress fees. */
|
|
12091
12190
|
const DEFAULT_BASE_URL = "https://nomad.seekrit.dev";
|
|
12092
12191
|
/** Where the plugin reads its node config, matching `config::DEFAULT_CONFIG_PATH`. */
|
|
@@ -12173,7 +12272,7 @@ async function fetchBytes(url) {
|
|
|
12173
12272
|
* let Nomad fingerprint a half-written executable.
|
|
12174
12273
|
*/
|
|
12175
12274
|
async function installPlugin(pluginDir, options = {}) {
|
|
12176
|
-
const version = options.version ?? "0.2.
|
|
12275
|
+
const version = options.version ?? "0.2.2";
|
|
12177
12276
|
const baseUrl = (options.baseUrl ?? "https://nomad.seekrit.dev").replace(/\/+$/, "");
|
|
12178
12277
|
const { target, exe } = detectTarget();
|
|
12179
12278
|
if (exe !== "") fail("Nomad secret provider plugins are not supported on Windows clients");
|
|
@@ -12304,12 +12403,12 @@ function requirePluginDir(explicit) {
|
|
|
12304
12403
|
if (found) return found;
|
|
12305
12404
|
fail("could not find the Nomad plugin directory — pass --plugin-dir <path> matching your agent's client.common_plugin_dir");
|
|
12306
12405
|
}
|
|
12307
|
-
function collect$
|
|
12406
|
+
function collect$4(value, previous = []) {
|
|
12308
12407
|
return [...previous, value];
|
|
12309
12408
|
}
|
|
12310
12409
|
function registerNomadCommands(program) {
|
|
12311
12410
|
const nomad = program.command("nomad").description("wire seekrit into Nomad as a secret provider (`seekrit nomad --help`)");
|
|
12312
|
-
nomad.command("init").description("print the agent stanza, plugin config, and job snippet for this client").option("--plugin-dir <path>", "the agent's client.common_plugin_dir").option("--profile <name>", "profile name jobs will reference in `path`", "default").option("--token-file <path>", "where the profile's service token lives").option("--namespace <ns>", "restrict the profile to a Nomad namespace (repeatable)", collect$
|
|
12411
|
+
nomad.command("init").description("print the agent stanza, plugin config, and job snippet for this client").option("--plugin-dir <path>", "the agent's client.common_plugin_dir").option("--profile <name>", "profile name jobs will reference in `path`", "default").option("--token-file <path>", "where the profile's service token lives").option("--namespace <ns>", "restrict the profile to a Nomad namespace (repeatable)", collect$4).option("--job <id>", "restrict the profile to a Nomad job id (repeatable)", collect$4).option("--config <path>", "plugin config path", DEFAULT_CONFIG_PATH).option("--write", "write the plugin config file instead of printing it").option("--json", "machine-readable output").action((options) => {
|
|
12313
12412
|
const pluginDir = options.pluginDir ? resolve(options.pluginDir) : detectPluginDir() ?? PLUGIN_DIR_CANDIDATES[0];
|
|
12314
12413
|
const profile = options.profile ?? "default";
|
|
12315
12414
|
const tokenFile = options.tokenFile ?? `/etc/seekrit/tokens/${profile}`;
|
|
@@ -13428,7 +13527,7 @@ function generateRoleName(prefix = "tmp") {
|
|
|
13428
13527
|
function registerPgCommands(program) {
|
|
13429
13528
|
const pg = program.command("pg").description("temporary Postgres credentials (short-lived, zero-knowledge)");
|
|
13430
13529
|
const target = pg.command("target").description("manage provisioning targets");
|
|
13431
|
-
target.command("add").description("register a Postgres cluster to lease credentials from").requiredOption("--name <name>", "display name, e.g. prod-db").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--host <host>", "database host").option("--port <port>", "database port", "5432").requiredOption("--database <name>", "database to connect to").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--schema <name>", "schema for preset grants", "public").option("--executor <mode>", "in_do | remote", "in_do").option("--provisioner-url <url>", "customer provisioner URL (remote executor)").option("--hmac-key <base64>", "shared HMAC key for the remote executor (or set SEEKRIT_PROVISIONER_HMAC_KEY); `seekrit provisioner keygen` mints one").option("--admin-url <url>", "in_do: admin postgres:// connection string (or set SEEKRIT_PG_ADMIN_URL); wrapped locally").option("--create-statement <sql>", "custom CREATE template (repeatable)", collect$
|
|
13530
|
+
target.command("add").description("register a Postgres cluster to lease credentials from").requiredOption("--name <name>", "display name, e.g. prod-db").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--host <host>", "database host").option("--port <port>", "database port", "5432").requiredOption("--database <name>", "database to connect to").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--schema <name>", "schema for preset grants", "public").option("--executor <mode>", "in_do | remote", "in_do").option("--provisioner-url <url>", "customer provisioner URL (remote executor)").option("--hmac-key <base64>", "shared HMAC key for the remote executor (or set SEEKRIT_PROVISIONER_HMAC_KEY); `seekrit provisioner keygen` mints one").option("--admin-url <url>", "in_do: admin postgres:// connection string (or set SEEKRIT_PG_ADMIN_URL); wrapped locally").option("--create-statement <sql>", "custom CREATE template (repeatable)", collect$3, []).option("--revoke-statement <sql>", "custom REVOKE template (repeatable)", collect$3, []).action(async (options) => {
|
|
13432
13531
|
const ctx = buildContext();
|
|
13433
13532
|
const org = await resolveOrg(ctx, options.org);
|
|
13434
13533
|
const executor = options.executor === "remote" ? "remote" : "in_do";
|
|
@@ -13544,7 +13643,7 @@ function registerPgCommands(program) {
|
|
|
13544
13643
|
});
|
|
13545
13644
|
}
|
|
13546
13645
|
/** Collect a repeatable option into an array. */
|
|
13547
|
-
function collect$
|
|
13646
|
+
function collect$3(value, acc) {
|
|
13548
13647
|
acc.push(value);
|
|
13549
13648
|
return acc;
|
|
13550
13649
|
}
|
|
@@ -13878,10 +13977,10 @@ Next:
|
|
|
13878
13977
|
});
|
|
13879
13978
|
console.log(path);
|
|
13880
13979
|
const { target } = detectTarget();
|
|
13881
|
-
process.stderr.write(`seekrit-proxy ${versionPrefix(options.proxyVersion ?? "0.12.
|
|
13980
|
+
process.stderr.write(`seekrit-proxy ${versionPrefix(options.proxyVersion ?? "0.12.3")} (${target})\n`);
|
|
13882
13981
|
});
|
|
13883
13982
|
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) => {
|
|
13884
|
-
const version = options.proxyVersion ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.12.
|
|
13983
|
+
const version = options.proxyVersion ?? process.env.SEEKRIT_PROXY_VERSION ?? "0.12.3";
|
|
13885
13984
|
const override = process.env.SEEKRIT_PROXY_BIN;
|
|
13886
13985
|
const { target, exe } = detectTarget();
|
|
13887
13986
|
const path = override ?? proxyBinaryPath(version, target, exe);
|
|
@@ -13940,7 +14039,7 @@ Next:
|
|
|
13940
14039
|
process.stdout.write(renderComposeSnippet(plan, {
|
|
13941
14040
|
service: options.service,
|
|
13942
14041
|
workload: options.workload,
|
|
13943
|
-
image: options.image ?? `seekritdev/proxy:0.12.
|
|
14042
|
+
image: options.image ?? `seekritdev/proxy:0.12.3`,
|
|
13944
14043
|
publish: Boolean(options.publish)
|
|
13945
14044
|
}));
|
|
13946
14045
|
});
|
|
@@ -13979,7 +14078,7 @@ function generateUserName(prefix = "tmp") {
|
|
|
13979
14078
|
function registerRedisCommands(program) {
|
|
13980
14079
|
const redis = program.command("redis").description("temporary Redis credentials (short-lived, zero-knowledge)");
|
|
13981
14080
|
const target = redis.command("target").description("manage provisioning targets");
|
|
13982
|
-
target.command("add").description("register a Redis server to lease credentials from").requiredOption("--name <name>", "display name, e.g. prod-cache").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--host <host>", "redis host").option("--port <port>", "redis port", "6379").option("--db <index>", "logical database index (the /<n> in the URL)").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--executor <mode>", "in_do | remote", "in_do").option("--provisioner-url <url>", "customer provisioner URL (remote executor)").option("--hmac-key <base64>", "shared HMAC key for the remote executor (or set SEEKRIT_PROVISIONER_HMAC_KEY); `seekrit provisioner keygen` mints one").option("--admin-url <url>", "in_do: admin redis:// (or rediss://) connection string (or set SEEKRIT_REDIS_ADMIN_URL); wrapped locally").option("--create-statement <cmd>", "custom SETUSER template (repeatable)", collect$
|
|
14081
|
+
target.command("add").description("register a Redis server to lease credentials from").requiredOption("--name <name>", "display name, e.g. prod-cache").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").requiredOption("--host <host>", "redis host").option("--port <port>", "redis port", "6379").option("--db <index>", "logical database index (the /<n> in the URL)").option("--access <level>", "readonly | readwrite | custom", "readonly").option("--executor <mode>", "in_do | remote", "in_do").option("--provisioner-url <url>", "customer provisioner URL (remote executor)").option("--hmac-key <base64>", "shared HMAC key for the remote executor (or set SEEKRIT_PROVISIONER_HMAC_KEY); `seekrit provisioner keygen` mints one").option("--admin-url <url>", "in_do: admin redis:// (or rediss://) connection string (or set SEEKRIT_REDIS_ADMIN_URL); wrapped locally").option("--create-statement <cmd>", "custom SETUSER template (repeatable)", collect$2, []).option("--revoke-statement <cmd>", "custom DELUSER template (repeatable)", collect$2, []).action(async (options) => {
|
|
13983
14082
|
const ctx = buildContext();
|
|
13984
14083
|
const org = await resolveOrg(ctx, options.org);
|
|
13985
14084
|
const executor = options.executor === "remote" ? "remote" : "in_do";
|
|
@@ -14082,7 +14181,7 @@ function registerRedisCommands(program) {
|
|
|
14082
14181
|
});
|
|
14083
14182
|
}
|
|
14084
14183
|
/** Collect a repeatable option into an array. */
|
|
14085
|
-
function collect$
|
|
14184
|
+
function collect$2(value, acc) {
|
|
14086
14185
|
acc.push(value);
|
|
14087
14186
|
return acc;
|
|
14088
14187
|
}
|
|
@@ -14281,7 +14380,7 @@ function parseTtlSeconds(input) {
|
|
|
14281
14380
|
function registerSshCommands(program) {
|
|
14282
14381
|
const ssh = program.command("ssh").description("temporary SSH access (short-lived certificates, zero-knowledge)");
|
|
14283
14382
|
const target = ssh.command("target").description("manage SSH CA targets");
|
|
14284
|
-
target.command("add").description("create an SSH certificate authority to issue certs from").requiredOption("--name <name>", "display name, e.g. prod-fleet").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--host <host>", "default host the printed ssh command connects to").option("--user <login>", "default login user (a cert principal)").option("--principal <name>", "allow-list a principal certs may request (repeatable)", collect, []).option("--extension <name>", "cert extension to grant, e.g. permit-pty (repeatable)", collect, []).option("--max-ttl <duration>", "clamp requested cert lifetime, e.g. 8h").action(async (options) => {
|
|
14383
|
+
target.command("add").description("create an SSH certificate authority to issue certs from").requiredOption("--name <name>", "display name, e.g. prod-fleet").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--host <host>", "default host the printed ssh command connects to").option("--user <login>", "default login user (a cert principal)").option("--principal <name>", "allow-list a principal certs may request (repeatable)", collect$1, []).option("--extension <name>", "cert extension to grant, e.g. permit-pty (repeatable)", collect$1, []).option("--max-ttl <duration>", "clamp requested cert lifetime, e.g. 8h").action(async (options) => {
|
|
14285
14384
|
const ctx = buildContext();
|
|
14286
14385
|
const org = await resolveOrg(ctx, options.org);
|
|
14287
14386
|
const ca = await generateSshCaKeyPair(`seekrit-ca:${options.name}`);
|
|
@@ -14337,7 +14436,7 @@ function registerSshCommands(program) {
|
|
|
14337
14436
|
await ctx.client.deleteLeaseTarget(org.id, targetId);
|
|
14338
14437
|
console.error(`deleted ${targetId}`);
|
|
14339
14438
|
});
|
|
14340
|
-
ssh.command("lease <target>").description("mint a short-lived SSH certificate; prints a ready-to-run ssh command").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--principal <name>", "login user to request (repeatable; default from target)", collect, []).option("--ttl <duration>", "certificate lifetime, e.g. 30m, 1h, 8h", "1h").option("--out <dir>", "directory to write the key + cert (default: a temp dir)").option("--json", "print key/cert paths and the certificate as JSON").action(async (targetRef, options) => {
|
|
14439
|
+
ssh.command("lease <target>").description("mint a short-lived SSH certificate; prints a ready-to-run ssh command").option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").option("--principal <name>", "login user to request (repeatable; default from target)", collect$1, []).option("--ttl <duration>", "certificate lifetime, e.g. 30m, 1h, 8h", "1h").option("--out <dir>", "directory to write the key + cert (default: a temp dir)").option("--json", "print key/cert paths and the certificate as JSON").action(async (targetRef, options) => {
|
|
14341
14440
|
const ctx = buildContext();
|
|
14342
14441
|
const org = await resolveOrg(ctx, options.org);
|
|
14343
14442
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -14393,7 +14492,7 @@ function registerSshCommands(program) {
|
|
|
14393
14492
|
});
|
|
14394
14493
|
}
|
|
14395
14494
|
/** Collect a repeatable option into an array. */
|
|
14396
|
-
function collect(value, acc) {
|
|
14495
|
+
function collect$1(value, acc) {
|
|
14397
14496
|
acc.push(value);
|
|
14398
14497
|
return acc;
|
|
14399
14498
|
}
|
|
@@ -14403,6 +14502,36 @@ function collect(value, acc) {
|
|
|
14403
14502
|
function list(raw, fallback) {
|
|
14404
14503
|
return (raw ?? fallback).split(",").map((item) => item.trim()).filter(Boolean);
|
|
14405
14504
|
}
|
|
14505
|
+
/** Accumulate a repeatable flag, in the order given. */
|
|
14506
|
+
function collect(value, acc = []) {
|
|
14507
|
+
return [...acc, value];
|
|
14508
|
+
}
|
|
14509
|
+
/**
|
|
14510
|
+
* One host rule as typed on the command line: a comma-separated host list, or
|
|
14511
|
+
* the literal `unrestricted`. The schema does the real validation; this only
|
|
14512
|
+
* turns the spelling into the shape it checks.
|
|
14513
|
+
*/
|
|
14514
|
+
function hostRule(raw) {
|
|
14515
|
+
const trimmed = raw.trim();
|
|
14516
|
+
if (trimmed.toLowerCase() === "unrestricted") return "unrestricted";
|
|
14517
|
+
return list(trimmed, "");
|
|
14518
|
+
}
|
|
14519
|
+
/**
|
|
14520
|
+
* `--allowed-hosts-for NAME=host,host`, repeated — one per destination name.
|
|
14521
|
+
* A name given twice is refused rather than merged or last-wins, since either
|
|
14522
|
+
* reading would silently widen or narrow what the other occurrence said.
|
|
14523
|
+
*/
|
|
14524
|
+
function hostRulesByName(entries) {
|
|
14525
|
+
const rules = {};
|
|
14526
|
+
for (const entry of entries ?? []) {
|
|
14527
|
+
const eq = entry.indexOf("=");
|
|
14528
|
+
const name = eq > 0 ? entry.slice(0, eq).trim() : "";
|
|
14529
|
+
if (!name || eq === entry.length - 1) fail(`--allowed-hosts-for takes NAME=host[,host…] (or NAME=unrestricted), not "${entry}"`);
|
|
14530
|
+
if (Object.hasOwn(rules, name)) fail(`--allowed-hosts-for names ${name} twice`);
|
|
14531
|
+
rules[name] = hostRule(entry.slice(eq + 1));
|
|
14532
|
+
}
|
|
14533
|
+
return rules;
|
|
14534
|
+
}
|
|
14406
14535
|
/** Reject list members outside a known set, naming both the strays and the choices. */
|
|
14407
14536
|
function assertMembers(values, allowed, flag) {
|
|
14408
14537
|
const unknown = values.filter((v) => !allowed.includes(v));
|
|
@@ -14567,6 +14696,7 @@ function credentialNoun(provider) {
|
|
|
14567
14696
|
if (provider === "azure-key-vault") return "client secret";
|
|
14568
14697
|
if (provider === "huggingface-spaces") return "user access token";
|
|
14569
14698
|
if (provider === "openai-vault") return "OpenAI project API key";
|
|
14699
|
+
if (provider === "claude-managed-agents") return "Anthropic API key";
|
|
14570
14700
|
return "API token";
|
|
14571
14701
|
}
|
|
14572
14702
|
/**
|
|
@@ -14657,9 +14787,13 @@ function buildConfig(provider, options) {
|
|
|
14657
14787
|
}
|
|
14658
14788
|
case "huggingface-spaces": return { provider: "huggingface-spaces" };
|
|
14659
14789
|
case "openai-vault": return { provider: "openai-vault" };
|
|
14790
|
+
case "claude-managed-agents": return { provider: "claude-managed-agents" };
|
|
14660
14791
|
}
|
|
14661
14792
|
}
|
|
14662
|
-
/**
|
|
14793
|
+
/**
|
|
14794
|
+
* Where inside the platform a binding writes. Exported for its tests: the
|
|
14795
|
+
* flags are the one part of a binding the API cannot see as typed.
|
|
14796
|
+
*/
|
|
14663
14797
|
function buildDestination(provider, options) {
|
|
14664
14798
|
switch (provider) {
|
|
14665
14799
|
case "vercel": {
|
|
@@ -14882,6 +15016,22 @@ function buildDestination(provider, options) {
|
|
|
14882
15016
|
vaultId: options.openaiVault.trim(),
|
|
14883
15017
|
allowedHosts: list(options.openaiHosts, "")
|
|
14884
15018
|
};
|
|
15019
|
+
case "claude-managed-agents": {
|
|
15020
|
+
if (!options.vault) fail("--vault is required for claude-managed-agents — the vault ID, e.g. vlt_01ABC…");
|
|
15021
|
+
const byName = hostRulesByName(options.allowedHostsFor);
|
|
15022
|
+
const parsed = claudeManagedAgentsDestinationSchema.safeParse({
|
|
15023
|
+
provider: "claude-managed-agents",
|
|
15024
|
+
vaultId: options.vault,
|
|
15025
|
+
...options.allowedHosts !== void 0 ? { allowedHosts: hostRule(options.allowedHosts) } : {},
|
|
15026
|
+
...Object.keys(byName).length > 0 ? { allowedHostsByName: byName } : {},
|
|
15027
|
+
injectionLocation: assertMember(options.injectionLocation, CLAUDE_VAULT_INJECTION_LOCATIONS, "--injection-location", "header")
|
|
15028
|
+
});
|
|
15029
|
+
if (!parsed.success) {
|
|
15030
|
+
const issue = parsed.error.issues[0];
|
|
15031
|
+
fail(`claude-managed-agents: ${issue?.path.length ? `${issue.path.join(".")}: ` : ""}${issue?.message ?? "invalid destination"} — pass --allowed-hosts <host,…> for every name, and/or --allowed-hosts-for NAME=<host,…> per name`);
|
|
15032
|
+
}
|
|
15033
|
+
return parsed.data;
|
|
15034
|
+
}
|
|
14885
15035
|
}
|
|
14886
15036
|
}
|
|
14887
15037
|
/** One-line description of a destination, for list output. */
|
|
@@ -14906,6 +15056,11 @@ function describeDestination(destination) {
|
|
|
14906
15056
|
case "azure-key-vault": return `${destination.vault}${destination.prefix ? ` (${destination.prefix}*)` : ""}`;
|
|
14907
15057
|
case "huggingface-spaces": return `space ${destination.repoId}`;
|
|
14908
15058
|
case "openai-vault": return `${destination.vaultId} (${destination.allowedHosts.join(", ")})`;
|
|
15059
|
+
case "claude-managed-agents": {
|
|
15060
|
+
const rules = Object.keys(destination.allowedHostsByName ?? {}).length;
|
|
15061
|
+
const hosts = destination.allowedHosts === void 0 ? "no default hosts" : destination.allowedHosts === "unrestricted" ? "unrestricted by default" : destination.allowedHosts.join(", ");
|
|
15062
|
+
return `vault ${destination.vaultId} (${hosts}${rules ? ` · ${rules} per-name` : ""} · ${destination.injectionLocation})`;
|
|
15063
|
+
}
|
|
14909
15064
|
case "github-actions": switch (destination.kind) {
|
|
14910
15065
|
case "repo": return `${destination.owner}/${destination.repo}`;
|
|
14911
15066
|
case "environment": return `${destination.owner}/${destination.repo} @${destination.environment}`;
|
|
@@ -14921,7 +15076,7 @@ function describeDestination(destination) {
|
|
|
14921
15076
|
* application whose environment the binding reads from.
|
|
14922
15077
|
*/
|
|
14923
15078
|
function destinationOptions(command) {
|
|
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`);
|
|
15079
|
+
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 · claude-managed-agents: vault ID (vlt_…)").option("--allowed-hosts <list>", "claude-managed-agents: hosts every secret may be used on, comma-separated (api.example.com, *.example.com, an IPv4) — or `unrestricted`").option("--allowed-hosts-for <NAME=hosts>", "claude-managed-agents: hosts for one secret, overriding --allowed-hosts (repeatable)", collect).option("--injection-location <where>", `claude-managed-agents: ${CLAUDE_VAULT_INJECTION_LOCATIONS.join(" | ")} — which parts of a request the value is substituted into (default header)`).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`);
|
|
14925
15080
|
}
|
|
14926
15081
|
/** Find a connection by id or name — nobody keeps `syc_…` ids in their head. */
|
|
14927
15082
|
async function resolveConnection(ctx, orgId, ref) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seekrit/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "End-to-end encrypted secrets manager CLI — inject decrypted secrets into any command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"node": ">=20"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@modelcontextprotocol/sdk": "^1.30.
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.30.1",
|
|
26
26
|
"commander": "^15.0.0",
|
|
27
27
|
"zod": "^4.6.5"
|
|
28
28
|
},
|