@seekrit/cli 1.7.1 → 1.9.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 +203 -26
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2560,7 +2560,9 @@ const SYNC_PROVIDER_KINDS = [
|
|
|
2560
2560
|
"gcp-secret-manager",
|
|
2561
2561
|
"langgraph-platform",
|
|
2562
2562
|
"azure-key-vault",
|
|
2563
|
-
"huggingface-spaces"
|
|
2563
|
+
"huggingface-spaces",
|
|
2564
|
+
"openai-vault",
|
|
2565
|
+
"claude-managed-agents"
|
|
2564
2566
|
];
|
|
2565
2567
|
z.enum(SYNC_PROVIDER_KINDS);
|
|
2566
2568
|
/**
|
|
@@ -2964,6 +2966,25 @@ const azureKeyVaultConnectionConfigSchema = z.object({
|
|
|
2964
2966
|
* would invite pointing a connection at a host that silently swallows secrets.
|
|
2965
2967
|
*/
|
|
2966
2968
|
const huggingfaceSpacesConnectionConfigSchema = z.object({ provider: z.literal("huggingface-spaces") });
|
|
2969
|
+
/** The wrapped credential is an OpenAI project API key with vault read/write access. */
|
|
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") });
|
|
2967
2988
|
const syncConnectionConfigSchema = z.discriminatedUnion("provider", [
|
|
2968
2989
|
vercelConnectionConfigSchema,
|
|
2969
2990
|
cloudflareWorkersConnectionConfigSchema,
|
|
@@ -2983,7 +3004,9 @@ const syncConnectionConfigSchema = z.discriminatedUnion("provider", [
|
|
|
2983
3004
|
gcpSecretManagerConnectionConfigSchema,
|
|
2984
3005
|
langgraphPlatformConnectionConfigSchema,
|
|
2985
3006
|
azureKeyVaultConnectionConfigSchema,
|
|
2986
|
-
huggingfaceSpacesConnectionConfigSchema
|
|
3007
|
+
huggingfaceSpacesConnectionConfigSchema,
|
|
3008
|
+
openaiVaultConnectionConfigSchema,
|
|
3009
|
+
claudeManagedAgentsConnectionConfigSchema
|
|
2987
3010
|
]);
|
|
2988
3011
|
/** Vercel's three deployment targets. A binding writes to one or more. */
|
|
2989
3012
|
const VERCEL_TARGETS = [
|
|
@@ -3808,6 +3831,91 @@ const huggingfaceSpacesDestinationSchema = z.object({
|
|
|
3808
3831
|
/** Space ID as `owner/name`, e.g. `acme/support-demo`. */
|
|
3809
3832
|
repoId: huggingfaceRepoIdSchema
|
|
3810
3833
|
});
|
|
3834
|
+
/** One existing OpenAI vault; every credential in this binding gets the same host boundary. */
|
|
3835
|
+
const openaiVaultDestinationSchema = z.object({
|
|
3836
|
+
provider: z.literal("openai-vault"),
|
|
3837
|
+
vaultId: z.string().trim().regex(/^vault_[A-Za-z0-9_-]+$/, "must be an OpenAI vault ID"),
|
|
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")
|
|
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
|
+
});
|
|
3811
3919
|
const syncDestinationSchema = z.discriminatedUnion("provider", [
|
|
3812
3920
|
vercelDestinationSchema,
|
|
3813
3921
|
cloudflareWorkersDestinationSchema,
|
|
@@ -3827,7 +3935,9 @@ const syncDestinationSchema = z.discriminatedUnion("provider", [
|
|
|
3827
3935
|
gcpSecretManagerDestinationSchema,
|
|
3828
3936
|
langgraphPlatformDestinationSchema,
|
|
3829
3937
|
azureKeyVaultDestinationSchema,
|
|
3830
|
-
huggingfaceSpacesDestinationSchema
|
|
3938
|
+
huggingfaceSpacesDestinationSchema,
|
|
3939
|
+
openaiVaultDestinationSchema,
|
|
3940
|
+
claudeManagedAgentsDestinationSchema
|
|
3831
3941
|
]);
|
|
3832
3942
|
/**
|
|
3833
3943
|
* How seekrit secret names become destination key names. Applied in order:
|
|
@@ -5115,7 +5225,7 @@ async function createAgentTaskToken() {
|
|
|
5115
5225
|
}
|
|
5116
5226
|
//#endregion
|
|
5117
5227
|
//#region package.json
|
|
5118
|
-
var version = "1.
|
|
5228
|
+
var version = "1.9.0";
|
|
5119
5229
|
//#endregion
|
|
5120
5230
|
//#region ../../packages/api-client/src/index.ts
|
|
5121
5231
|
/**
|
|
@@ -7679,7 +7789,7 @@ async function syncGrantsForGroupLink(ctx, orgId, envId, envSlug, groupId) {
|
|
|
7679
7789
|
//#endregion
|
|
7680
7790
|
//#region src/kms.ts
|
|
7681
7791
|
/** Collect a repeatable option into a list. */
|
|
7682
|
-
function collect$
|
|
7792
|
+
function collect$9(value, acc = []) {
|
|
7683
7793
|
acc.push(value);
|
|
7684
7794
|
return acc;
|
|
7685
7795
|
}
|
|
@@ -7747,7 +7857,7 @@ async function kmsRecoverMaterial(ctx, orgId, keyId, version) {
|
|
|
7747
7857
|
}
|
|
7748
7858
|
function registerKmsCommands(program) {
|
|
7749
7859
|
const kms = program.command("kms").description("managed keys for application-layer encryption & signing (client-side)");
|
|
7750
|
-
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) => {
|
|
7751
7861
|
if (options.purpose !== "encrypt" && options.purpose !== "sign") fail("--purpose must be encrypt or sign");
|
|
7752
7862
|
if (options.app && options.group) fail("pass at most one of --app or --group");
|
|
7753
7863
|
const ctx = buildContext();
|
|
@@ -7969,7 +8079,7 @@ function registerKmsCommands(program) {
|
|
|
7969
8079
|
//#endregion
|
|
7970
8080
|
//#region src/recovery.ts
|
|
7971
8081
|
/** Collect a repeatable option into a list. */
|
|
7972
|
-
function collect$
|
|
8082
|
+
function collect$8(value, acc = []) {
|
|
7973
8083
|
acc.push(value);
|
|
7974
8084
|
return acc;
|
|
7975
8085
|
}
|
|
@@ -8066,7 +8176,7 @@ function registerRecoveryCommands(program) {
|
|
|
8066
8176
|
for (const cst of status.custodians) console.log(` - ${cst.label ?? cst.principalId} (${cst.principalType}, share #${cst.shareIndex})`);
|
|
8067
8177
|
if (status.coverage.unprotectedEnvIds.length > 0) console.log(`${status.coverage.unprotectedEnvIds.length} environment(s) not yet protected — run \`seekrit recovery sync\``);
|
|
8068
8178
|
});
|
|
8069
|
-
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) => {
|
|
8070
8180
|
const ctx = buildContext();
|
|
8071
8181
|
const org = await resolveOrg(ctx, options.org);
|
|
8072
8182
|
const config = await buildRecoveryConfig(ctx, org.id, options.threshold, options.custodian);
|
|
@@ -8084,7 +8194,7 @@ function registerRecoveryCommands(program) {
|
|
|
8084
8194
|
const { wrapped, skipped } = await syncRecoveryGrants(ctx, (await resolveOrg(ctx, options.org)).id);
|
|
8085
8195
|
console.error(`recovery-protected ${wrapped} environment(s); skipped ${skipped} you cannot decrypt`);
|
|
8086
8196
|
});
|
|
8087
|
-
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) => {
|
|
8088
8198
|
const ctx = buildContext();
|
|
8089
8199
|
const org = await resolveOrg(ctx, options.org);
|
|
8090
8200
|
const config = await buildRecoveryConfig(ctx, org.id, options.threshold, options.custodian);
|
|
@@ -9227,7 +9337,7 @@ function formatSecrets(values, format) {
|
|
|
9227
9337
|
* `buildContext()` in any of them silently breaks the promise the feature makes.
|
|
9228
9338
|
*/
|
|
9229
9339
|
/** Collect a repeatable option into a list. */
|
|
9230
|
-
function collect$
|
|
9340
|
+
function collect$7(value, acc = []) {
|
|
9231
9341
|
acc.push(value);
|
|
9232
9342
|
return acc;
|
|
9233
9343
|
}
|
|
@@ -9504,7 +9614,7 @@ function registerArchiveCommands(program) {
|
|
|
9504
9614
|
} catch {}
|
|
9505
9615
|
fail("none of the recovery shares in this archive unwrap with that key");
|
|
9506
9616
|
});
|
|
9507
|
-
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) => {
|
|
9508
9618
|
if (!options.out && !options.stdout) fail("choose a destination: --out <dir> to write files, or --stdout to print plaintext");
|
|
9509
9619
|
if (![
|
|
9510
9620
|
"dotenv",
|
|
@@ -11551,7 +11661,7 @@ function parseTtlSeconds$4(input) {
|
|
|
11551
11661
|
}[m[2] || "s"] ?? 1);
|
|
11552
11662
|
}
|
|
11553
11663
|
/** Collect a repeatable option into an array. */
|
|
11554
|
-
function collect$
|
|
11664
|
+
function collect$6(value, previous) {
|
|
11555
11665
|
return [...previous, value];
|
|
11556
11666
|
}
|
|
11557
11667
|
/** Parse `readWrite@app` → { role, db } for a custom target. */
|
|
@@ -11576,7 +11686,7 @@ function resolveAdminUri(uri) {
|
|
|
11576
11686
|
function registerMongoCommands(program) {
|
|
11577
11687
|
const mongo = program.command("mongodb").description("temporary MongoDB credentials (createUser, zero-knowledge delivery)");
|
|
11578
11688
|
const target = mongo.command("target").description("manage MongoDB targets");
|
|
11579
|
-
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) => {
|
|
11580
11690
|
const ctx = buildContext();
|
|
11581
11691
|
const org = await resolveOrg(ctx, options.org);
|
|
11582
11692
|
const adminUri = resolveAdminUri(options.uri);
|
|
@@ -11738,7 +11848,7 @@ function generateUserName$1(prefix = "tmp") {
|
|
|
11738
11848
|
function registerMysqlCommands(program) {
|
|
11739
11849
|
const mysql = program.command("mysql").description("temporary MySQL/MariaDB credentials (short-lived, zero-knowledge)");
|
|
11740
11850
|
const target = mysql.command("target").description("manage provisioning targets");
|
|
11741
|
-
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) => {
|
|
11742
11852
|
const ctx = buildContext();
|
|
11743
11853
|
const org = await resolveOrg(ctx, options.org);
|
|
11744
11854
|
const executor = options.executor === "remote" ? "remote" : "in_do";
|
|
@@ -11841,7 +11951,7 @@ function registerMysqlCommands(program) {
|
|
|
11841
11951
|
});
|
|
11842
11952
|
}
|
|
11843
11953
|
/** Collect a repeatable option into an array. */
|
|
11844
|
-
function collect$
|
|
11954
|
+
function collect$5(value, acc) {
|
|
11845
11955
|
acc.push(value);
|
|
11846
11956
|
return acc;
|
|
11847
11957
|
}
|
|
@@ -12293,12 +12403,12 @@ function requirePluginDir(explicit) {
|
|
|
12293
12403
|
if (found) return found;
|
|
12294
12404
|
fail("could not find the Nomad plugin directory — pass --plugin-dir <path> matching your agent's client.common_plugin_dir");
|
|
12295
12405
|
}
|
|
12296
|
-
function collect$
|
|
12406
|
+
function collect$4(value, previous = []) {
|
|
12297
12407
|
return [...previous, value];
|
|
12298
12408
|
}
|
|
12299
12409
|
function registerNomadCommands(program) {
|
|
12300
12410
|
const nomad = program.command("nomad").description("wire seekrit into Nomad as a secret provider (`seekrit nomad --help`)");
|
|
12301
|
-
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) => {
|
|
12302
12412
|
const pluginDir = options.pluginDir ? resolve(options.pluginDir) : detectPluginDir() ?? PLUGIN_DIR_CANDIDATES[0];
|
|
12303
12413
|
const profile = options.profile ?? "default";
|
|
12304
12414
|
const tokenFile = options.tokenFile ?? `/etc/seekrit/tokens/${profile}`;
|
|
@@ -13417,7 +13527,7 @@ function generateRoleName(prefix = "tmp") {
|
|
|
13417
13527
|
function registerPgCommands(program) {
|
|
13418
13528
|
const pg = program.command("pg").description("temporary Postgres credentials (short-lived, zero-knowledge)");
|
|
13419
13529
|
const target = pg.command("target").description("manage provisioning targets");
|
|
13420
|
-
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) => {
|
|
13421
13531
|
const ctx = buildContext();
|
|
13422
13532
|
const org = await resolveOrg(ctx, options.org);
|
|
13423
13533
|
const executor = options.executor === "remote" ? "remote" : "in_do";
|
|
@@ -13533,7 +13643,7 @@ function registerPgCommands(program) {
|
|
|
13533
13643
|
});
|
|
13534
13644
|
}
|
|
13535
13645
|
/** Collect a repeatable option into an array. */
|
|
13536
|
-
function collect$
|
|
13646
|
+
function collect$3(value, acc) {
|
|
13537
13647
|
acc.push(value);
|
|
13538
13648
|
return acc;
|
|
13539
13649
|
}
|
|
@@ -13968,7 +14078,7 @@ function generateUserName(prefix = "tmp") {
|
|
|
13968
14078
|
function registerRedisCommands(program) {
|
|
13969
14079
|
const redis = program.command("redis").description("temporary Redis credentials (short-lived, zero-knowledge)");
|
|
13970
14080
|
const target = redis.command("target").description("manage provisioning targets");
|
|
13971
|
-
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) => {
|
|
13972
14082
|
const ctx = buildContext();
|
|
13973
14083
|
const org = await resolveOrg(ctx, options.org);
|
|
13974
14084
|
const executor = options.executor === "remote" ? "remote" : "in_do";
|
|
@@ -14071,7 +14181,7 @@ function registerRedisCommands(program) {
|
|
|
14071
14181
|
});
|
|
14072
14182
|
}
|
|
14073
14183
|
/** Collect a repeatable option into an array. */
|
|
14074
|
-
function collect$
|
|
14184
|
+
function collect$2(value, acc) {
|
|
14075
14185
|
acc.push(value);
|
|
14076
14186
|
return acc;
|
|
14077
14187
|
}
|
|
@@ -14270,7 +14380,7 @@ function parseTtlSeconds(input) {
|
|
|
14270
14380
|
function registerSshCommands(program) {
|
|
14271
14381
|
const ssh = program.command("ssh").description("temporary SSH access (short-lived certificates, zero-knowledge)");
|
|
14272
14382
|
const target = ssh.command("target").description("manage SSH CA targets");
|
|
14273
|
-
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) => {
|
|
14274
14384
|
const ctx = buildContext();
|
|
14275
14385
|
const org = await resolveOrg(ctx, options.org);
|
|
14276
14386
|
const ca = await generateSshCaKeyPair(`seekrit-ca:${options.name}`);
|
|
@@ -14326,7 +14436,7 @@ function registerSshCommands(program) {
|
|
|
14326
14436
|
await ctx.client.deleteLeaseTarget(org.id, targetId);
|
|
14327
14437
|
console.error(`deleted ${targetId}`);
|
|
14328
14438
|
});
|
|
14329
|
-
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) => {
|
|
14330
14440
|
const ctx = buildContext();
|
|
14331
14441
|
const org = await resolveOrg(ctx, options.org);
|
|
14332
14442
|
const { targets } = await ctx.client.listLeaseTargets(org.id);
|
|
@@ -14382,7 +14492,7 @@ function registerSshCommands(program) {
|
|
|
14382
14492
|
});
|
|
14383
14493
|
}
|
|
14384
14494
|
/** Collect a repeatable option into an array. */
|
|
14385
|
-
function collect(value, acc) {
|
|
14495
|
+
function collect$1(value, acc) {
|
|
14386
14496
|
acc.push(value);
|
|
14387
14497
|
return acc;
|
|
14388
14498
|
}
|
|
@@ -14392,6 +14502,36 @@ function collect(value, acc) {
|
|
|
14392
14502
|
function list(raw, fallback) {
|
|
14393
14503
|
return (raw ?? fallback).split(",").map((item) => item.trim()).filter(Boolean);
|
|
14394
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
|
+
}
|
|
14395
14535
|
/** Reject list members outside a known set, naming both the strays and the choices. */
|
|
14396
14536
|
function assertMembers(values, allowed, flag) {
|
|
14397
14537
|
const unknown = values.filter((v) => !allowed.includes(v));
|
|
@@ -14555,6 +14695,8 @@ function credentialNoun(provider) {
|
|
|
14555
14695
|
if (provider === "langgraph-platform") return "LangSmith API key";
|
|
14556
14696
|
if (provider === "azure-key-vault") return "client secret";
|
|
14557
14697
|
if (provider === "huggingface-spaces") return "user access token";
|
|
14698
|
+
if (provider === "openai-vault") return "OpenAI project API key";
|
|
14699
|
+
if (provider === "claude-managed-agents") return "Anthropic API key";
|
|
14558
14700
|
return "API token";
|
|
14559
14701
|
}
|
|
14560
14702
|
/**
|
|
@@ -14644,9 +14786,14 @@ function buildConfig(provider, options) {
|
|
|
14644
14786
|
};
|
|
14645
14787
|
}
|
|
14646
14788
|
case "huggingface-spaces": return { provider: "huggingface-spaces" };
|
|
14789
|
+
case "openai-vault": return { provider: "openai-vault" };
|
|
14790
|
+
case "claude-managed-agents": return { provider: "claude-managed-agents" };
|
|
14647
14791
|
}
|
|
14648
14792
|
}
|
|
14649
|
-
/**
|
|
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
|
+
*/
|
|
14650
14797
|
function buildDestination(provider, options) {
|
|
14651
14798
|
switch (provider) {
|
|
14652
14799
|
case "vercel": {
|
|
@@ -14861,6 +15008,30 @@ function buildDestination(provider, options) {
|
|
|
14861
15008
|
provider: "huggingface-spaces",
|
|
14862
15009
|
repoId: assertHuggingfaceSpace(options.hfSpace)
|
|
14863
15010
|
};
|
|
15011
|
+
case "openai-vault":
|
|
15012
|
+
if (!options.openaiVault) fail("--openai-vault is required for openai-vault (vault_… ID)");
|
|
15013
|
+
if (!options.openaiHosts) fail("--openai-hosts is required for openai-vault");
|
|
15014
|
+
return {
|
|
15015
|
+
provider: "openai-vault",
|
|
15016
|
+
vaultId: options.openaiVault.trim(),
|
|
15017
|
+
allowedHosts: list(options.openaiHosts, "")
|
|
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
|
+
}
|
|
14864
15035
|
}
|
|
14865
15036
|
}
|
|
14866
15037
|
/** One-line description of a destination, for list output. */
|
|
@@ -14884,6 +15055,12 @@ function describeDestination(destination) {
|
|
|
14884
15055
|
case "langgraph-platform": return `deployment ${destination.deploymentId}`;
|
|
14885
15056
|
case "azure-key-vault": return `${destination.vault}${destination.prefix ? ` (${destination.prefix}*)` : ""}`;
|
|
14886
15057
|
case "huggingface-spaces": return `space ${destination.repoId}`;
|
|
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
|
+
}
|
|
14887
15064
|
case "github-actions": switch (destination.kind) {
|
|
14888
15065
|
case "repo": return `${destination.owner}/${destination.repo}`;
|
|
14889
15066
|
case "environment": return `${destination.owner}/${destination.repo} @${destination.environment}`;
|
|
@@ -14899,7 +15076,7 @@ function describeDestination(destination) {
|
|
|
14899
15076
|
* application whose environment the binding reads from.
|
|
14900
15077
|
*/
|
|
14901
15078
|
function destinationOptions(command) {
|
|
14902
|
-
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("--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`);
|
|
14903
15080
|
}
|
|
14904
15081
|
/** Find a connection by id or name — nobody keeps `syc_…` ids in their head. */
|
|
14905
15082
|
async function resolveConnection(ctx, orgId, ref) {
|