@seekrit/cli 0.34.0 → 0.36.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 +271 -10
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -669,7 +669,7 @@ const redisSha256VerifierSchema = z.string().regex(/^[0-9a-f]{64}$/, "must be a
|
|
|
669
669
|
*/
|
|
670
670
|
const awsRoleArnSchema = z.string().regex(/^arn:aws(?:-us-gov|-cn)?:iam::\d{12}:role\/[\w+=,.@/-]{1,512}$/, "must be an IAM role ARN (arn:aws:iam::<account>:role/<name>)");
|
|
671
671
|
/** An AWS region id, e.g. `us-east-1`, `eu-west-2`, `us-gov-west-1`. */
|
|
672
|
-
const awsRegionSchema = z.string().regex(/^[a-z]{2}(?:-[a-z]+)+-\d$/, "must be an AWS region id (e.g. us-east-1)");
|
|
672
|
+
const awsRegionSchema$1 = z.string().regex(/^[a-z]{2}(?:-[a-z]+)+-\d$/, "must be an AWS region id (e.g. us-east-1)");
|
|
673
673
|
/**
|
|
674
674
|
* An STS external id — the shared string a role's trust policy can require so a
|
|
675
675
|
* confused-deputy can't assume it. AWS allows a broad charset; we keep to the
|
|
@@ -811,7 +811,7 @@ const awsTargetConfigSchema = z.object({
|
|
|
811
811
|
provider: z.literal("aws"),
|
|
812
812
|
executor: z.literal("in_do"),
|
|
813
813
|
roleArn: awsRoleArnSchema,
|
|
814
|
-
region: awsRegionSchema,
|
|
814
|
+
region: awsRegionSchema$1,
|
|
815
815
|
externalId: awsExternalIdSchema.optional(),
|
|
816
816
|
sessionPolicy: z.string().min(1).max(4e3).optional(),
|
|
817
817
|
maxTtlSeconds: z.number().int().min(900).max(AWS_MAX_TTL_SECONDS).optional()
|
|
@@ -1517,7 +1517,10 @@ const SYNC_PROVIDER_KINDS = [
|
|
|
1517
1517
|
"vercel",
|
|
1518
1518
|
"cloudflare-workers",
|
|
1519
1519
|
"cloudflare-pages",
|
|
1520
|
-
"cloudflare-secrets-store"
|
|
1520
|
+
"cloudflare-secrets-store",
|
|
1521
|
+
"railway",
|
|
1522
|
+
"aws-secrets-manager",
|
|
1523
|
+
"aws-parameter-store"
|
|
1521
1524
|
];
|
|
1522
1525
|
z.enum(SYNC_PROVIDER_KINDS);
|
|
1523
1526
|
/**
|
|
@@ -1565,11 +1568,92 @@ const cloudflareSecretsStoreConnectionConfigSchema = z.object({
|
|
|
1565
1568
|
provider: z.literal("cloudflare-secrets-store"),
|
|
1566
1569
|
accountId: cloudflareAccountIdSchema
|
|
1567
1570
|
});
|
|
1571
|
+
/**
|
|
1572
|
+
* A Railway id — every project, environment, and service is a UUID. Validated
|
|
1573
|
+
* by shape for the same reason Cloudflare's account id is: the alternative is a
|
|
1574
|
+
* bare GraphQL "Problem processing request" hours later inside an alarm, with
|
|
1575
|
+
* nobody watching.
|
|
1576
|
+
*/
|
|
1577
|
+
const railwayIdSchema = z.string().trim().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, "must be a Railway UUID");
|
|
1578
|
+
/**
|
|
1579
|
+
* Which kind of Railway API token the connection holds. This is connection
|
|
1580
|
+
* scope rather than a destination detail because it decides the *header* the
|
|
1581
|
+
* request carries, and getting it wrong fails every call identically:
|
|
1582
|
+
*
|
|
1583
|
+
* - `account` — a personal or workspace token, sent as `Authorization: Bearer`.
|
|
1584
|
+
* Reaches every project the token's owner can see.
|
|
1585
|
+
* - `project` — a project token, sent as `Project-Access-Token`. Scoped to one
|
|
1586
|
+
* project and environment by Railway itself, which makes it the
|
|
1587
|
+
* least-privilege choice when a connection serves a single destination.
|
|
1588
|
+
*
|
|
1589
|
+
* Railway rejects a project token sent as a bearer, so this cannot be sniffed
|
|
1590
|
+
* at request time — the operator states it once, when they paste the token.
|
|
1591
|
+
*/
|
|
1592
|
+
const RAILWAY_TOKEN_KINDS = ["account", "project"];
|
|
1593
|
+
/**
|
|
1594
|
+
* Railway account scope. The token is never here — it is wrapped to the
|
|
1595
|
+
* connection's public key and stored as ciphertext, exactly as Vercel's is.
|
|
1596
|
+
*
|
|
1597
|
+
* There is no workspace/team id to carry: Railway ids are globally unique and
|
|
1598
|
+
* a destination names its project outright, so the token plus the destination
|
|
1599
|
+
* is the whole address.
|
|
1600
|
+
*/
|
|
1601
|
+
const railwayConnectionConfigSchema = z.object({
|
|
1602
|
+
provider: z.literal("railway"),
|
|
1603
|
+
tokenKind: z.enum(RAILWAY_TOKEN_KINDS).default("account")
|
|
1604
|
+
});
|
|
1605
|
+
/**
|
|
1606
|
+
* An AWS region id (`us-east-1`, `eu-central-1`, `us-gov-west-1`).
|
|
1607
|
+
*
|
|
1608
|
+
* Validated by shape rather than against a list, because AWS adds regions
|
|
1609
|
+
* faster than we ship. The endpoint host is built from this string, so a typo
|
|
1610
|
+
* would otherwise surface as a DNS failure inside an alarm with nobody
|
|
1611
|
+
* watching — which is a much worse place to learn about it than this form.
|
|
1612
|
+
*/
|
|
1613
|
+
const awsRegionSchema = z.string().trim().regex(/^[a-z]{2}(-[a-z]+)+-\d$/, "must be an AWS region ID, e.g. us-east-1");
|
|
1614
|
+
/**
|
|
1615
|
+
* The IAM access key id seekrit signs with.
|
|
1616
|
+
*
|
|
1617
|
+
* This lives in `config` — the *non-secret* half — on purpose: an access key id
|
|
1618
|
+
* is an identifier, not a credential. It appears in CloudTrail, in the IAM
|
|
1619
|
+
* console, and in the `Authorization` header of every signed request; only the
|
|
1620
|
+
* **secret access key** is secret, and that is what gets wrapped to the
|
|
1621
|
+
* connection's public key. Keeping the id here also lets the dashboard say
|
|
1622
|
+
* which key a connection is using, which is the first thing you want to know
|
|
1623
|
+
* when a connection starts failing after a key rotation.
|
|
1624
|
+
*
|
|
1625
|
+
* Long-lived IAM user keys only. `ASIA…` session credentials from STS expire
|
|
1626
|
+
* within hours, and a sync connection has to keep working unattended.
|
|
1627
|
+
*/
|
|
1628
|
+
const awsAccessKeyIdSchema = z.string().trim().regex(/^[A-Z0-9]{16,128}$/, "must be an AWS access key ID, e.g. AKIAIOSFODNN7EXAMPLE");
|
|
1629
|
+
/**
|
|
1630
|
+
* AWS account scope, shared by both AWS providers: which region to call and
|
|
1631
|
+
* which key to sign with. There is no account id — every endpoint seekrit calls
|
|
1632
|
+
* is reached through the regional host and authorizes off the signature, so the
|
|
1633
|
+
* account is whichever one the key belongs to.
|
|
1634
|
+
*
|
|
1635
|
+
* Two providers rather than one `aws` with a mode field, for the same reason
|
|
1636
|
+
* the three Cloudflare kinds are separate: different APIs, different
|
|
1637
|
+
* destinations, different IAM actions.
|
|
1638
|
+
*/
|
|
1639
|
+
const awsSecretsManagerConnectionConfigSchema = z.object({
|
|
1640
|
+
provider: z.literal("aws-secrets-manager"),
|
|
1641
|
+
region: awsRegionSchema,
|
|
1642
|
+
accessKeyId: awsAccessKeyIdSchema
|
|
1643
|
+
});
|
|
1644
|
+
const awsParameterStoreConnectionConfigSchema = z.object({
|
|
1645
|
+
provider: z.literal("aws-parameter-store"),
|
|
1646
|
+
region: awsRegionSchema,
|
|
1647
|
+
accessKeyId: awsAccessKeyIdSchema
|
|
1648
|
+
});
|
|
1568
1649
|
const syncConnectionConfigSchema = z.discriminatedUnion("provider", [
|
|
1569
1650
|
vercelConnectionConfigSchema,
|
|
1570
1651
|
cloudflareWorkersConnectionConfigSchema,
|
|
1571
1652
|
cloudflarePagesConnectionConfigSchema,
|
|
1572
|
-
cloudflareSecretsStoreConnectionConfigSchema
|
|
1653
|
+
cloudflareSecretsStoreConnectionConfigSchema,
|
|
1654
|
+
railwayConnectionConfigSchema,
|
|
1655
|
+
awsSecretsManagerConnectionConfigSchema,
|
|
1656
|
+
awsParameterStoreConnectionConfigSchema
|
|
1573
1657
|
]);
|
|
1574
1658
|
/** Vercel's three deployment targets. A binding writes to one or more. */
|
|
1575
1659
|
const VERCEL_TARGETS = [
|
|
@@ -1628,11 +1712,122 @@ const cloudflareSecretsStoreDestinationSchema = z.object({
|
|
|
1628
1712
|
/** Scopes applied to secrets this binding creates. At least one. */
|
|
1629
1713
|
scopes: z.array(z.enum(CLOUDFLARE_SECRETS_STORE_SCOPES)).min(1)
|
|
1630
1714
|
});
|
|
1715
|
+
/**
|
|
1716
|
+
* Where inside Railway a binding writes.
|
|
1717
|
+
*
|
|
1718
|
+
* Railway variables are addressed by (project, environment, service) — the
|
|
1719
|
+
* environment here is *Railway's* (`production`, `pr-42`), not the seekrit
|
|
1720
|
+
* environment the binding reads from; a binding is precisely the mapping
|
|
1721
|
+
* between the two.
|
|
1722
|
+
*
|
|
1723
|
+
* Omitting `serviceId` targets the project's **shared** variables for that
|
|
1724
|
+
* environment, which services opt into with `${{shared.NAME}}`. That is a
|
|
1725
|
+
* genuinely different destination from any one service's variables, so it is an
|
|
1726
|
+
* absent field rather than a sentinel.
|
|
1727
|
+
*/
|
|
1728
|
+
const railwayDestinationSchema = z.object({
|
|
1729
|
+
provider: z.literal("railway"),
|
|
1730
|
+
/** Railway project id (a UUID, from the project's Settings page or URL). */
|
|
1731
|
+
projectId: railwayIdSchema,
|
|
1732
|
+
/** Railway environment id (a UUID) — the deployment environment to write. */
|
|
1733
|
+
environmentId: railwayIdSchema,
|
|
1734
|
+
/** Service to write. Omit to write the environment's shared variables. */
|
|
1735
|
+
serviceId: railwayIdSchema.optional(),
|
|
1736
|
+
/**
|
|
1737
|
+
* Suppress the redeploy Railway triggers when a variable changes.
|
|
1738
|
+
*
|
|
1739
|
+
* Left off (the default), a sync that changes a value redeploys the service,
|
|
1740
|
+
* which is what makes the new value actually reach the running process —
|
|
1741
|
+
* Railway applies variables at deploy time. Turn it on when deploys are
|
|
1742
|
+
* gated behind a release process and a secrets push must not start one; the
|
|
1743
|
+
* values then sit staged until the next deploy.
|
|
1744
|
+
*/
|
|
1745
|
+
skipDeploys: z.boolean().optional()
|
|
1746
|
+
});
|
|
1747
|
+
/**
|
|
1748
|
+
* A customer-managed KMS key to encrypt with, as a key id, ARN, or alias
|
|
1749
|
+
* (`alias/seekrit`). Omitted means the AWS-managed default for that service
|
|
1750
|
+
* (`aws/secretsmanager`, `aws/ssm`), which is what most accounts want.
|
|
1751
|
+
*
|
|
1752
|
+
* Deliberately loose: a KMS key can be named five different ways, half of them
|
|
1753
|
+
* cross-account ARNs, and rejecting a valid one here would be worse than
|
|
1754
|
+
* letting KMS give its own (very clear) error.
|
|
1755
|
+
*/
|
|
1756
|
+
const awsKmsKeyIdSchema = z.string().trim().min(1).max(2048);
|
|
1757
|
+
/**
|
|
1758
|
+
* How a binding lays its secrets out in Secrets Manager.
|
|
1759
|
+
*
|
|
1760
|
+
* - `secret-per-name` — one AWS secret per seekrit secret. The direct
|
|
1761
|
+
* translation, and what you want if consumers read secrets individually.
|
|
1762
|
+
* - `json-bundle` — every value as one JSON object in a single AWS secret. The
|
|
1763
|
+
* shape ECS task definitions and Lambda read with `secret-arn:json-key::`,
|
|
1764
|
+
* and the reason it exists is billing: Secrets Manager charges per secret per
|
|
1765
|
+
* month, so fifty names cost fifty times as much stored separately.
|
|
1766
|
+
*/
|
|
1767
|
+
const AWS_SECRETS_MANAGER_LAYOUTS = ["secret-per-name", "json-bundle"];
|
|
1768
|
+
/**
|
|
1769
|
+
* Where in Secrets Manager a binding writes.
|
|
1770
|
+
*
|
|
1771
|
+
* `pathPrefix` exists rather than reusing {@link NameTransform}'s `prefix`
|
|
1772
|
+
* because the two answer different questions: a name transform produces a
|
|
1773
|
+
* *variable name* (`[A-Za-z0-9_]`, no slashes), while this produces a
|
|
1774
|
+
* *namespace* — `prod/storefront/` — and slashes are the whole point of it.
|
|
1775
|
+
*/
|
|
1776
|
+
const awsSecretsManagerDestinationSchema = z.object({
|
|
1777
|
+
provider: z.literal("aws-secrets-manager"),
|
|
1778
|
+
layout: z.enum(AWS_SECRETS_MANAGER_LAYOUTS).default("secret-per-name"),
|
|
1779
|
+
/**
|
|
1780
|
+
* `secret-per-name` only: prepended to every secret's name, e.g.
|
|
1781
|
+
* `prod/storefront/`. Optional, but strongly advised in an account that
|
|
1782
|
+
* holds anything else — without it a binding writes at the root of a
|
|
1783
|
+
* namespace it does not own.
|
|
1784
|
+
*/
|
|
1785
|
+
pathPrefix: z.string().trim().max(400).regex(/^[A-Za-z0-9/_+=.@-]*$/, "may contain letters, digits, and / _ + = . @ -").optional(),
|
|
1786
|
+
/** `json-bundle` only: the one secret that holds every value, e.g. `prod/storefront/env`. */
|
|
1787
|
+
secretName: z.string().trim().min(1).max(512).regex(/^[A-Za-z0-9/_+=.@-]+$/, "may contain letters, digits, and / _ + = . @ -").optional(),
|
|
1788
|
+
kmsKeyId: awsKmsKeyIdSchema.optional()
|
|
1789
|
+
}).refine((d) => d.layout !== "json-bundle" || d.secretName !== void 0, {
|
|
1790
|
+
message: "a json-bundle destination needs the name of the secret to write",
|
|
1791
|
+
path: ["secretName"]
|
|
1792
|
+
});
|
|
1793
|
+
/** Parameter Store's two value types. `SecureString` is KMS-encrypted; `String` is not. */
|
|
1794
|
+
const AWS_PARAMETER_TYPES = ["SecureString", "String"];
|
|
1795
|
+
/** Parameter Store's storage tiers. Standard is free and caps values at 4KB. */
|
|
1796
|
+
const AWS_PARAMETER_TIERS = [
|
|
1797
|
+
"Standard",
|
|
1798
|
+
"Advanced",
|
|
1799
|
+
"Intelligent-Tiering"
|
|
1800
|
+
];
|
|
1801
|
+
/**
|
|
1802
|
+
* The Parameter Store hierarchy a binding owns, e.g. `/prod/storefront/`.
|
|
1803
|
+
*
|
|
1804
|
+
* A path rather than a free-form prefix because that is what the API is built
|
|
1805
|
+
* around: `GetParametersByPath` is how an application reads a whole
|
|
1806
|
+
* environment in one call, and it only works on `/`-delimited names. Leading
|
|
1807
|
+
* and trailing slashes are required so the binding's names concatenate
|
|
1808
|
+
* unambiguously — `/prod/storefront/` + `DB_URL`.
|
|
1809
|
+
*/
|
|
1810
|
+
const awsParameterStoreDestinationSchema = z.object({
|
|
1811
|
+
provider: z.literal("aws-parameter-store"),
|
|
1812
|
+
/** Must start and end with `/`. `aws`/`ssm` are reserved by AWS as the first segment. */
|
|
1813
|
+
path: z.string().trim().max(1011).regex(/^\/([A-Za-z0-9_.-]+\/)*$/, "must be a parameter path like /prod/storefront/"),
|
|
1814
|
+
type: z.enum(AWS_PARAMETER_TYPES).default("SecureString"),
|
|
1815
|
+
/**
|
|
1816
|
+
* Standard caps a value at 4KB and costs nothing; Advanced raises that to 8KB
|
|
1817
|
+
* and is billed per parameter per month. `Intelligent-Tiering` lets AWS pick,
|
|
1818
|
+
* upgrading only the parameters that need it.
|
|
1819
|
+
*/
|
|
1820
|
+
tier: z.enum(AWS_PARAMETER_TIERS).default("Standard"),
|
|
1821
|
+
kmsKeyId: awsKmsKeyIdSchema.optional()
|
|
1822
|
+
});
|
|
1631
1823
|
const syncDestinationSchema = z.discriminatedUnion("provider", [
|
|
1632
1824
|
vercelDestinationSchema,
|
|
1633
1825
|
cloudflareWorkersDestinationSchema,
|
|
1634
1826
|
cloudflarePagesDestinationSchema,
|
|
1635
|
-
cloudflareSecretsStoreDestinationSchema
|
|
1827
|
+
cloudflareSecretsStoreDestinationSchema,
|
|
1828
|
+
railwayDestinationSchema,
|
|
1829
|
+
awsSecretsManagerDestinationSchema,
|
|
1830
|
+
awsParameterStoreDestinationSchema
|
|
1636
1831
|
]);
|
|
1637
1832
|
/**
|
|
1638
1833
|
* How seekrit secret names become destination key names. Applied in order:
|
|
@@ -2837,7 +3032,7 @@ function isCliSessionToken(value) {
|
|
|
2837
3032
|
}
|
|
2838
3033
|
//#endregion
|
|
2839
3034
|
//#region package.json
|
|
2840
|
-
var version = "0.
|
|
3035
|
+
var version = "0.36.0";
|
|
2841
3036
|
//#endregion
|
|
2842
3037
|
//#region ../../packages/api-client/src/index.ts
|
|
2843
3038
|
var SeekritApiError = class extends Error {
|
|
@@ -6652,6 +6847,19 @@ function assertMembers(values, allowed, flag) {
|
|
|
6652
6847
|
if (unknown.length > 0) fail(`unknown ${flag} ${unknown.join(", ")} — one of: ${allowed.join(", ")}`);
|
|
6653
6848
|
return values;
|
|
6654
6849
|
}
|
|
6850
|
+
/** Every Railway id is a UUID; a name or a URL slug in the slot is the common slip. */
|
|
6851
|
+
function assertRailwayId(value, flag) {
|
|
6852
|
+
if (!value) fail(`${flag} is required for railway (a UUID)`);
|
|
6853
|
+
const id = value.trim();
|
|
6854
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) fail(`${flag} should be a Railway UUID, not "${id}"`);
|
|
6855
|
+
return id;
|
|
6856
|
+
}
|
|
6857
|
+
/** Reject a single value outside a known set, naming the choices. */
|
|
6858
|
+
function assertMember(value, allowed, flag, fallback) {
|
|
6859
|
+
if (value === void 0) return fallback;
|
|
6860
|
+
if (!allowed.includes(value)) fail(`unknown ${flag} "${value}" — one of: ${allowed.join(", ")}`);
|
|
6861
|
+
return value;
|
|
6862
|
+
}
|
|
6655
6863
|
function assertProvider(value) {
|
|
6656
6864
|
if (!SYNC_PROVIDER_KINDS.includes(value)) fail(`unknown provider "${value}" — one of: ${SYNC_PROVIDER_KINDS.join(", ")}`);
|
|
6657
6865
|
return value;
|
|
@@ -6671,6 +6879,23 @@ function buildConfig(provider, options) {
|
|
|
6671
6879
|
provider,
|
|
6672
6880
|
accountId: options.accountId
|
|
6673
6881
|
};
|
|
6882
|
+
case "railway": {
|
|
6883
|
+
const tokenKind = options.tokenKind ?? "account";
|
|
6884
|
+
if (!RAILWAY_TOKEN_KINDS.includes(tokenKind)) fail(`unknown --token-kind "${tokenKind}" — one of: ${RAILWAY_TOKEN_KINDS.join(", ")}`);
|
|
6885
|
+
return {
|
|
6886
|
+
provider: "railway",
|
|
6887
|
+
tokenKind
|
|
6888
|
+
};
|
|
6889
|
+
}
|
|
6890
|
+
case "aws-secrets-manager":
|
|
6891
|
+
case "aws-parameter-store":
|
|
6892
|
+
if (!options.region) fail(`--region is required for ${provider} — an AWS region ID, e.g. us-east-1`);
|
|
6893
|
+
if (!options.accessKeyId) fail(`--access-key-id is required for ${provider} — the IAM access key ID (AKIA…)`);
|
|
6894
|
+
return {
|
|
6895
|
+
provider,
|
|
6896
|
+
region: options.region,
|
|
6897
|
+
accessKeyId: options.accessKeyId
|
|
6898
|
+
};
|
|
6674
6899
|
}
|
|
6675
6900
|
}
|
|
6676
6901
|
/** Where inside the platform a binding writes. */
|
|
@@ -6712,6 +6937,38 @@ function buildDestination(provider, options) {
|
|
|
6712
6937
|
scopes
|
|
6713
6938
|
};
|
|
6714
6939
|
}
|
|
6940
|
+
case "railway": {
|
|
6941
|
+
const projectId = assertRailwayId(options.railwayProject, "--railway-project");
|
|
6942
|
+
const environmentId = assertRailwayId(options.railwayEnvironment, "--railway-environment");
|
|
6943
|
+
const serviceId = options.service ? assertRailwayId(options.service, "--service") : void 0;
|
|
6944
|
+
return {
|
|
6945
|
+
provider: "railway",
|
|
6946
|
+
projectId,
|
|
6947
|
+
environmentId,
|
|
6948
|
+
...serviceId ? { serviceId } : {},
|
|
6949
|
+
...options.skipDeploys ? { skipDeploys: true } : {}
|
|
6950
|
+
};
|
|
6951
|
+
}
|
|
6952
|
+
case "aws-secrets-manager": {
|
|
6953
|
+
const layout = assertMember(options.layout, AWS_SECRETS_MANAGER_LAYOUTS, "--layout", "secret-per-name");
|
|
6954
|
+
if (layout === "json-bundle" && !options.secretName) fail("--secret-name is required for --layout json-bundle (the one secret to write)");
|
|
6955
|
+
return {
|
|
6956
|
+
provider: "aws-secrets-manager",
|
|
6957
|
+
layout,
|
|
6958
|
+
...options.path ? { pathPrefix: options.path } : {},
|
|
6959
|
+
...options.secretName ? { secretName: options.secretName } : {},
|
|
6960
|
+
...options.kmsKeyId ? { kmsKeyId: options.kmsKeyId } : {}
|
|
6961
|
+
};
|
|
6962
|
+
}
|
|
6963
|
+
case "aws-parameter-store":
|
|
6964
|
+
if (!options.path) fail("--path is required for aws-parameter-store, e.g. /prod/storefront/");
|
|
6965
|
+
return {
|
|
6966
|
+
provider: "aws-parameter-store",
|
|
6967
|
+
path: options.path,
|
|
6968
|
+
type: assertMember(options.paramType, AWS_PARAMETER_TYPES, "--param-type", "SecureString"),
|
|
6969
|
+
tier: assertMember(options.tier, AWS_PARAMETER_TIERS, "--tier", "Standard"),
|
|
6970
|
+
...options.kmsKeyId ? { kmsKeyId: options.kmsKeyId } : {}
|
|
6971
|
+
};
|
|
6715
6972
|
}
|
|
6716
6973
|
}
|
|
6717
6974
|
/** One-line description of a destination, for list output. */
|
|
@@ -6721,6 +6978,9 @@ function describeDestination(destination) {
|
|
|
6721
6978
|
case "cloudflare-workers": return destination.scriptName;
|
|
6722
6979
|
case "cloudflare-pages": return `${destination.projectName} (${destination.environments.join(", ")})`;
|
|
6723
6980
|
case "cloudflare-secrets-store": return `store ${destination.storeId} (${destination.scopes.join(", ")})`;
|
|
6981
|
+
case "railway": return `${destination.projectId} / ${destination.environmentId} (${destination.serviceId ? `service ${destination.serviceId}` : "shared"})`;
|
|
6982
|
+
case "aws-secrets-manager": return destination.layout === "json-bundle" ? `${destination.secretName} (json bundle)` : `${destination.pathPrefix ?? ""}* (secret per name)`;
|
|
6983
|
+
case "aws-parameter-store": return `${destination.path}* (${destination.type})`;
|
|
6724
6984
|
}
|
|
6725
6985
|
}
|
|
6726
6986
|
/**
|
|
@@ -6728,7 +6988,7 @@ function describeDestination(destination) {
|
|
|
6728
6988
|
* into accepting different ways of naming the same destination.
|
|
6729
6989
|
*/
|
|
6730
6990
|
function destinationOptions(command) {
|
|
6731
|
-
return command.option("--project <id>", "vercel: project id or name · cloudflare-pages: project name").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");
|
|
6991
|
+
return command.option("--project <id>", "vercel: project id or name · cloudflare-pages: project name").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)").option("--skip-deploys", "railway: stage values without triggering a redeploy").option("--path <path>", "aws-parameter-store: hierarchy, e.g. /prod/storefront/ · aws-secrets-manager: 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");
|
|
6732
6992
|
}
|
|
6733
6993
|
/** Find a connection by id or name — nobody keeps `syc_…` ids in their head. */
|
|
6734
6994
|
async function resolveConnection(ctx, orgId, ref) {
|
|
@@ -6752,12 +7012,13 @@ function registerSyncCommands(program) {
|
|
|
6752
7012
|
col("id", (c) => c.id)
|
|
6753
7013
|
], "no connections — add one with `seekrit sync connect`"));
|
|
6754
7014
|
});
|
|
6755
|
-
sync.command("connect").description("register a destination account (reads its API token from stdin)").option("--org <slug>").requiredOption("--name <name>", "what to call this account, e.g. acme-vercel").option("--provider <kind>", SYNC_PROVIDER_KINDS.join(" | "), "vercel").option("--team-id <id>", "vercel: Team id (omit for a personal account)").option("--account-id <id>", "cloudflare: account ID (32 hex, from the dashboard sidebar)").action(async (options) => {
|
|
7015
|
+
sync.command("connect").description("register a destination account (reads its API token from stdin)").option("--org <slug>").requiredOption("--name <name>", "what to call this account, e.g. acme-vercel").option("--provider <kind>", SYNC_PROVIDER_KINDS.join(" | "), "vercel").option("--team-id <id>", "vercel: Team id (omit for a personal account)").option("--token-kind <kind>", `railway: ${RAILWAY_TOKEN_KINDS.join(" | ")}`, "account").option("--account-id <id>", "cloudflare: account ID (32 hex, from the dashboard sidebar)").option("--region <region>", "aws: region ID, e.g. us-east-1").option("--access-key-id <id>", "aws: IAM access key ID (the secret key is read from stdin)").action(async (options) => {
|
|
6756
7016
|
const provider = assertProvider(options.provider);
|
|
6757
7017
|
const ctx = buildContext();
|
|
6758
7018
|
const ref = await resolveOrg(ctx, options.org);
|
|
6759
|
-
const
|
|
6760
|
-
|
|
7019
|
+
const noun = provider.startsWith("aws-") ? "secret access key" : "API token";
|
|
7020
|
+
const credential = (process.stdin.isTTY ? await promptHidden(`${provider} ${noun}: `) : await readStdin()).trim();
|
|
7021
|
+
if (!credential) fail(`no ${noun} given`);
|
|
6761
7022
|
const id = randomId("syc");
|
|
6762
7023
|
const { publicKeyJwk } = await ctx.client.getSyncConnectionKey(ref.id, id);
|
|
6763
7024
|
const created = await ctx.client.createSyncConnection(ref.id, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seekrit/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"description": "End-to-end encrypted secrets manager CLI — inject decrypted secrets into any command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"@types/node": "^26.1.0",
|
|
28
28
|
"tsdown": "^0.22.3",
|
|
29
29
|
"vitest": "^4.1.9",
|
|
30
|
+
"@seekrit/api-client": "0.0.1",
|
|
30
31
|
"@seekrit/core": "0.0.1",
|
|
31
|
-
"@seekrit/crypto": "0.0.1"
|
|
32
|
-
"@seekrit/api-client": "0.0.1"
|
|
32
|
+
"@seekrit/crypto": "0.0.1"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "tsdown",
|