@seekrit/cli 2.5.1 → 2.6.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 +1441 -965
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import
|
|
3
|
+
import * as z$1 from "zod";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { arch, homedir, hostname, platform, tmpdir, userInfo } from "node:os";
|
|
6
6
|
import { basename, dirname, join, parse, resolve } from "node:path";
|
|
@@ -17,48 +17,48 @@ const POLICY_DEFAULT_TTL_SECONDS = 604800;
|
|
|
17
17
|
const POLICY_MIN_TTL_SECONDS = 3600;
|
|
18
18
|
const POLICY_MAX_TTL_SECONDS = 7776e3;
|
|
19
19
|
/** A bare hostname: no scheme, no port, no path, no wildcard. */
|
|
20
|
-
const policyHostSchema = z.string().trim().min(1).max(253).toLowerCase().refine((h) => !/[:/\s*]/.test(h), { message: "host must be a bare hostname (no scheme, port, path, or wildcard)" }).refine((h) => /^[a-z0-9.-]+$/.test(h), { message: "host contains invalid characters" });
|
|
21
|
-
const policyMethodSchema = z.string().trim().toUpperCase().regex(/^[A-Z]{3,10}$/, "method must be an HTTP method name");
|
|
20
|
+
const policyHostSchema = z$1.string().trim().min(1).max(253).toLowerCase().refine((h) => !/[:/\s*]/.test(h), { message: "host must be a bare hostname (no scheme, port, path, or wildcard)" }).refine((h) => /^[a-z0-9.-]+$/.test(h), { message: "host contains invalid characters" });
|
|
21
|
+
const policyMethodSchema = z$1.string().trim().toUpperCase().regex(/^[A-Z]{3,10}$/, "method must be an HTTP method name");
|
|
22
22
|
/**
|
|
23
23
|
* A path pattern. Must be absolute, because it is matched against a request
|
|
24
24
|
* path — a relative pattern is a mistake that would silently match nothing.
|
|
25
25
|
*/
|
|
26
|
-
const policyPathSchema = z.string().trim().min(1).max(512).startsWith("/", "path pattern must start with /").refine((p) => !p.includes("?"), { message: "path patterns match the path only, not the query" });
|
|
27
|
-
const policySecretNameSchema = z.string().trim().regex(/^[A-Za-z0-9_]+$/, "secret names are letters, digits, and underscores");
|
|
26
|
+
const policyPathSchema = z$1.string().trim().min(1).max(512).startsWith("/", "path pattern must start with /").refine((p) => !p.includes("?"), { message: "path patterns match the path only, not the query" });
|
|
27
|
+
const policySecretNameSchema = z$1.string().trim().regex(/^[A-Za-z0-9_]+$/, "secret names are letters, digits, and underscores");
|
|
28
28
|
/** A TCP port. 0 is refused: it never matches a connection, so a rule naming it is a mistake. */
|
|
29
|
-
const policyPortSchema = z.number().int().min(1).max(65535);
|
|
30
|
-
const agentPolicyRuleSchema = z.object({
|
|
29
|
+
const policyPortSchema = z$1.number().int().min(1).max(65535);
|
|
30
|
+
const agentPolicyRuleSchema = z$1.object({
|
|
31
31
|
host: policyHostSchema,
|
|
32
|
-
methods: z.array(policyMethodSchema).max(16).default([]),
|
|
33
|
-
paths: z.array(policyPathSchema).max(64).default([]),
|
|
34
|
-
allow: z.array(policySecretNameSchema).max(64).default([]),
|
|
35
|
-
https_ports: z.array(policyPortSchema).max(32).optional(),
|
|
36
|
-
http_ports: z.array(policyPortSchema).max(32).optional(),
|
|
37
|
-
label: z.string().trim().max(120).optional()
|
|
32
|
+
methods: z$1.array(policyMethodSchema).max(16).default([]),
|
|
33
|
+
paths: z$1.array(policyPathSchema).max(64).default([]),
|
|
34
|
+
allow: z$1.array(policySecretNameSchema).max(64).default([]),
|
|
35
|
+
https_ports: z$1.array(policyPortSchema).max(32).optional(),
|
|
36
|
+
http_ports: z$1.array(policyPortSchema).max(32).optional(),
|
|
37
|
+
label: z$1.string().trim().max(120).optional()
|
|
38
38
|
});
|
|
39
|
-
z.object({
|
|
39
|
+
z$1.object({
|
|
40
40
|
/** `ap1.<body>.<signature>` — opaque to the server. */
|
|
41
|
-
bundle: z.string().min(16).max(262144) });
|
|
42
|
-
z.object({
|
|
43
|
-
name: z.string().trim().min(1).max(120),
|
|
44
|
-
slug: z.string().trim().min(1).max(64).regex(/^[a-z0-9][a-z0-9-]*$/, "slug is lowercase letters, digits, and dashes"),
|
|
41
|
+
bundle: z$1.string().min(16).max(262144) });
|
|
42
|
+
z$1.object({
|
|
43
|
+
name: z$1.string().trim().min(1).max(120),
|
|
44
|
+
slug: z$1.string().trim().min(1).max(64).regex(/^[a-z0-9][a-z0-9-]*$/, "slug is lowercase letters, digits, and dashes"),
|
|
45
45
|
/** The environment whose secrets this agent's policy may name. Optional. */
|
|
46
|
-
environmentId: z.string().trim().min(1).max(64).optional()
|
|
46
|
+
environmentId: z$1.string().trim().min(1).max(64).optional()
|
|
47
47
|
});
|
|
48
|
-
z.object({
|
|
49
|
-
name: z.string().trim().min(1).max(120).optional(),
|
|
50
|
-
enabled: z.boolean().optional(),
|
|
51
|
-
environmentId: z.string().trim().min(1).max(64).nullish()
|
|
48
|
+
z$1.object({
|
|
49
|
+
name: z$1.string().trim().min(1).max(120).optional(),
|
|
50
|
+
enabled: z$1.boolean().optional(),
|
|
51
|
+
environmentId: z$1.string().trim().min(1).max(64).nullish()
|
|
52
52
|
});
|
|
53
|
-
z.object({
|
|
53
|
+
z$1.object({
|
|
54
54
|
host: policyHostSchema,
|
|
55
55
|
/** How the agent reaches the host. Omit for a destination the operator fixed. */
|
|
56
|
-
transport: z.object({
|
|
57
|
-
scheme: z.enum(["https", "http"]),
|
|
56
|
+
transport: z$1.object({
|
|
57
|
+
scheme: z$1.enum(["https", "http"]),
|
|
58
58
|
port: policyPortSchema
|
|
59
59
|
}).optional(),
|
|
60
60
|
method: policyMethodSchema,
|
|
61
|
-
path: z.string().trim().min(1).max(2048),
|
|
61
|
+
path: z$1.string().trim().min(1).max(2048),
|
|
62
62
|
secret: policySecretNameSchema.optional()
|
|
63
63
|
});
|
|
64
64
|
/**
|
|
@@ -170,7 +170,7 @@ async function policySignerThumbprint(jwk) {
|
|
|
170
170
|
y: jwk.y
|
|
171
171
|
});
|
|
172
172
|
const digest = await crypto.subtle.digest("SHA-256", utf8(canonical));
|
|
173
|
-
return base64url(new Uint8Array(digest));
|
|
173
|
+
return base64url$1(new Uint8Array(digest));
|
|
174
174
|
}
|
|
175
175
|
/**
|
|
176
176
|
* Sign a draft bundle with the publishing admin's key, returning the `ap1.`
|
|
@@ -197,8 +197,8 @@ async function signAgentPolicy(privateKey, signerJwk, draft) {
|
|
|
197
197
|
}, privateKey, bytes));
|
|
198
198
|
return [
|
|
199
199
|
"ap1",
|
|
200
|
-
base64url(bytes),
|
|
201
|
-
base64url(sig)
|
|
200
|
+
base64url$1(bytes),
|
|
201
|
+
base64url$1(sig)
|
|
202
202
|
].join(".");
|
|
203
203
|
}
|
|
204
204
|
/** A structurally invalid, unverifiable, or expired bundle. */
|
|
@@ -386,7 +386,7 @@ function utf8(text) {
|
|
|
386
386
|
function utf8Decode$1(bytes) {
|
|
387
387
|
return new TextDecoder().decode(bytes);
|
|
388
388
|
}
|
|
389
|
-
function base64url(bytes) {
|
|
389
|
+
function base64url$1(bytes) {
|
|
390
390
|
let binary = "";
|
|
391
391
|
for (const b of bytes) binary += String.fromCharCode(b);
|
|
392
392
|
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
@@ -399,10 +399,10 @@ function fromBase64url(text) {
|
|
|
399
399
|
return out;
|
|
400
400
|
}
|
|
401
401
|
/** One aggregated cell: a dimension tuple and how many times it happened. */
|
|
402
|
-
const activityEntrySchema = z.object({
|
|
402
|
+
const activityEntrySchema = z$1.object({
|
|
403
403
|
host: policyHostSchema,
|
|
404
404
|
method: policyMethodSchema,
|
|
405
|
-
decision: z.enum([
|
|
405
|
+
decision: z$1.enum([
|
|
406
406
|
"allow",
|
|
407
407
|
"no_rule",
|
|
408
408
|
"port_not_allowed",
|
|
@@ -419,27 +419,27 @@ const activityEntrySchema = z.object({
|
|
|
419
419
|
* a review, because "rule 3 refused this" and "nothing covered this" call for
|
|
420
420
|
* opposite changes.
|
|
421
421
|
*/
|
|
422
|
-
ruleIndex: z.number().int().min(0).max(255).nullable(),
|
|
423
|
-
count: z.number().int().min(1).max(1e6),
|
|
422
|
+
ruleIndex: z$1.number().int().min(0).max(255).nullable(),
|
|
423
|
+
count: z$1.number().int().min(1).max(1e6),
|
|
424
424
|
/**
|
|
425
425
|
* Secret names actually injected, name → count. Only meaningful on `allow`.
|
|
426
426
|
* This is what lets a review say "rule 2 permits three secrets and the agent
|
|
427
427
|
* has only ever used one" — the most useful narrowing there is, and impossible
|
|
428
428
|
* to see from policy alone.
|
|
429
429
|
*/
|
|
430
|
-
secrets: z.record(policySecretNameSchema, z.number().int().min(1)).optional()
|
|
430
|
+
secrets: z$1.record(policySecretNameSchema, z$1.number().int().min(1)).optional()
|
|
431
431
|
});
|
|
432
|
-
z.object({
|
|
432
|
+
z$1.object({
|
|
433
433
|
/** Start of the window these counts cover (ISO 8601). */
|
|
434
|
-
windowStart: z.string().trim().min(20).max(40),
|
|
434
|
+
windowStart: z$1.string().trim().min(20).max(40),
|
|
435
435
|
/** Policy version in force while they were collected, for the ledger. */
|
|
436
|
-
policyVersion: z.number().int().min(0).optional(),
|
|
436
|
+
policyVersion: z$1.number().int().min(0).optional(),
|
|
437
437
|
/**
|
|
438
438
|
* Capped so one report cannot be unbounded work. A proxy with more distinct
|
|
439
439
|
* cells than this in a window has a policy far broader than a review can help
|
|
440
440
|
* with, and truncating loudly beats accepting anything.
|
|
441
441
|
*/
|
|
442
|
-
entries: z.array(activityEntrySchema).min(1).max(500)
|
|
442
|
+
entries: z$1.array(activityEntrySchema).min(1).max(500)
|
|
443
443
|
});
|
|
444
444
|
// ../../packages/core/src/agent-login.ts
|
|
445
445
|
/** Where each kind of login is stored unless told otherwise. */
|
|
@@ -703,38 +703,38 @@ function countApplicable(proposals) {
|
|
|
703
703
|
*/
|
|
704
704
|
const TASK_MAX_TTL_SECONDS = 43200;
|
|
705
705
|
/** An EC P-256 public JWK, for a sender-constraint proof key. */
|
|
706
|
-
const taskProofJwkSchema = z.object({
|
|
707
|
-
kty: z.literal("EC"),
|
|
708
|
-
crv: z.literal("P-256"),
|
|
709
|
-
x: z.string().min(1).max(128),
|
|
710
|
-
y: z.string().min(1).max(128)
|
|
706
|
+
const taskProofJwkSchema = z$1.object({
|
|
707
|
+
kty: z$1.literal("EC"),
|
|
708
|
+
crv: z$1.literal("P-256"),
|
|
709
|
+
x: z$1.string().min(1).max(128),
|
|
710
|
+
y: z$1.string().min(1).max(128)
|
|
711
711
|
});
|
|
712
|
-
z.object({
|
|
712
|
+
z$1.object({
|
|
713
713
|
/**
|
|
714
714
|
* The public `skd_…` segment of the minted token. Sent because the API never
|
|
715
715
|
* sees the token at dispatch and still needs a readable handle for the audit
|
|
716
716
|
* row and for a revoke to name — the id half of a credential, without the
|
|
717
717
|
* secret half.
|
|
718
718
|
*/
|
|
719
|
-
taskRef: z.string().trim().regex(/^skd_[0-9A-Za-z]+$/, "taskRef must be the skd_… segment of the minted token"),
|
|
719
|
+
taskRef: z$1.string().trim().regex(/^skd_[0-9A-Za-z]+$/, "taskRef must be the skd_… segment of the minted token"),
|
|
720
720
|
/**
|
|
721
721
|
* SHA-256 (base64url) of the token the dispatcher minted. The token itself
|
|
722
722
|
* never reaches this API on the dispatch path — only on introspection, where
|
|
723
723
|
* it is hashed and discarded.
|
|
724
724
|
*/
|
|
725
|
-
tokenHash: z.string().trim().min(16).max(128),
|
|
725
|
+
tokenHash: z$1.string().trim().min(16).max(128),
|
|
726
726
|
/**
|
|
727
727
|
* Secret names this run may use. Omit for "whatever the agent's policy
|
|
728
728
|
* allows" — mirroring `Session.scopes: Option<BTreeSet<String>>` in the proxy,
|
|
729
729
|
* so absent means unnarrowed in both places.
|
|
730
730
|
*/
|
|
731
|
-
scopes: z.array(policySecretNameSchema).max(64).optional(),
|
|
732
|
-
ttlSeconds: z.number().int().min(60).max(TASK_MAX_TTL_SECONDS).optional(),
|
|
731
|
+
scopes: z$1.array(policySecretNameSchema).max(64).optional(),
|
|
732
|
+
ttlSeconds: z$1.number().int().min(60).max(TASK_MAX_TTL_SECONDS).optional(),
|
|
733
733
|
/**
|
|
734
734
|
* What this run is for, for the audit row and the operator's task list. Free
|
|
735
735
|
* text, and **not** a security input: never put a secret value in it.
|
|
736
736
|
*/
|
|
737
|
-
label: z.string().trim().max(200).optional(),
|
|
737
|
+
label: z$1.string().trim().max(200).optional(),
|
|
738
738
|
/**
|
|
739
739
|
* Public half of a proof key the presenter holds, recorded as an RFC 7638
|
|
740
740
|
* thumbprint. See `AgentTaskSession.proofThumbprint` for what this does and —
|
|
@@ -742,9 +742,9 @@ z.object({
|
|
|
742
742
|
*/
|
|
743
743
|
proofJwk: taskProofJwkSchema.optional()
|
|
744
744
|
});
|
|
745
|
-
z.object({
|
|
745
|
+
z$1.object({
|
|
746
746
|
/** The presented token. In the body, never a URL — it is a credential. */
|
|
747
|
-
token: z.string().trim().min(8).max(512) });
|
|
747
|
+
token: z$1.string().trim().min(8).max(512) });
|
|
748
748
|
// ../../packages/core/src/archive.ts
|
|
749
749
|
/**
|
|
750
750
|
* The **break-glass archive** format: one signed JSON file holding everything
|
|
@@ -920,43 +920,43 @@ async function verifySignature(manifest, signature) {
|
|
|
920
920
|
const publicKey = await crypto.subtle.importKey("raw", hexToBytes(signature.publicKey), { name: "Ed25519" }, false, ["verify"]);
|
|
921
921
|
return crypto.subtle.verify({ name: "Ed25519" }, publicKey, hexToBytes(signature.value), signingPayload(manifest));
|
|
922
922
|
}
|
|
923
|
-
const sectionHeaderSchema = z.object({
|
|
924
|
-
name: z.enum(ARCHIVE_SECTIONS),
|
|
925
|
-
count: z.number().int().min(0),
|
|
926
|
-
truncated: z.boolean(),
|
|
927
|
-
digest: z.string().regex(/^sha256:[0-9a-f]{64}$/)
|
|
923
|
+
const sectionHeaderSchema = z$1.object({
|
|
924
|
+
name: z$1.enum(ARCHIVE_SECTIONS),
|
|
925
|
+
count: z$1.number().int().min(0),
|
|
926
|
+
truncated: z$1.boolean(),
|
|
927
|
+
digest: z$1.string().regex(/^sha256:[0-9a-f]{64}$/)
|
|
928
928
|
});
|
|
929
|
-
const manifestSchema = z.object({
|
|
930
|
-
archiveId: z.string().min(1),
|
|
931
|
-
createdAt: z.string().min(1),
|
|
932
|
-
org: z.object({
|
|
933
|
-
id: z.string(),
|
|
934
|
-
slug: z.string(),
|
|
935
|
-
name: z.string()
|
|
929
|
+
const manifestSchema = z$1.object({
|
|
930
|
+
archiveId: z$1.string().min(1),
|
|
931
|
+
createdAt: z$1.string().min(1),
|
|
932
|
+
org: z$1.object({
|
|
933
|
+
id: z$1.string(),
|
|
934
|
+
slug: z$1.string(),
|
|
935
|
+
name: z$1.string()
|
|
936
936
|
}),
|
|
937
|
-
producer: z.object({
|
|
938
|
-
service: z.string(),
|
|
939
|
-
environment: z.string(),
|
|
940
|
-
formatVersion: z.string()
|
|
937
|
+
producer: z$1.object({
|
|
938
|
+
service: z$1.string(),
|
|
939
|
+
environment: z$1.string(),
|
|
940
|
+
formatVersion: z$1.string()
|
|
941
941
|
}),
|
|
942
|
-
requestedBy: z.object({
|
|
943
|
-
actorType: z.string(),
|
|
944
|
-
actorId: z.string(),
|
|
945
|
-
label: z.string().nullable()
|
|
942
|
+
requestedBy: z$1.object({
|
|
943
|
+
actorType: z$1.string(),
|
|
944
|
+
actorId: z$1.string(),
|
|
945
|
+
label: z$1.string().nullable()
|
|
946
946
|
}),
|
|
947
|
-
options: z.object({
|
|
948
|
-
includeVersions: z.boolean(),
|
|
949
|
-
includeAudit: z.boolean(),
|
|
950
|
-
auditLimit: z.number().int().min(0)
|
|
947
|
+
options: z$1.object({
|
|
948
|
+
includeVersions: z$1.boolean(),
|
|
949
|
+
includeAudit: z$1.boolean(),
|
|
950
|
+
auditLimit: z$1.number().int().min(0)
|
|
951
951
|
}),
|
|
952
|
-
sections: z.array(sectionHeaderSchema),
|
|
953
|
-
digest: z.string().regex(/^sha256:[0-9a-f]{64}$/)
|
|
952
|
+
sections: z$1.array(sectionHeaderSchema),
|
|
953
|
+
digest: z$1.string().regex(/^sha256:[0-9a-f]{64}$/)
|
|
954
954
|
});
|
|
955
|
-
const signatureSchema = z.object({
|
|
956
|
-
algorithm: z.literal("ed25519"),
|
|
957
|
-
publicKey: z.string().regex(/^[0-9a-f]{64}$/),
|
|
958
|
-
keyId: z.string().regex(/^[0-9a-f]{16}$/),
|
|
959
|
-
value: z.string().regex(/^[0-9a-f]{128}$/)
|
|
955
|
+
const signatureSchema = z$1.object({
|
|
956
|
+
algorithm: z$1.literal("ed25519"),
|
|
957
|
+
publicKey: z$1.string().regex(/^[0-9a-f]{64}$/),
|
|
958
|
+
keyId: z$1.string().regex(/^[0-9a-f]{16}$/),
|
|
959
|
+
value: z$1.string().regex(/^[0-9a-f]{128}$/)
|
|
960
960
|
});
|
|
961
961
|
/**
|
|
962
962
|
* Envelope schema. `data` is validated only as an object: a v1 verifier must
|
|
@@ -965,11 +965,11 @@ const signatureSchema = z.object({
|
|
|
965
965
|
* `verifyArchive`'s undeclared/declared cross-check rather than by rejecting
|
|
966
966
|
* the file outright.
|
|
967
967
|
*/
|
|
968
|
-
const archiveSchema = z.object({
|
|
969
|
-
format: z.literal(ARCHIVE_FORMAT),
|
|
968
|
+
const archiveSchema = z$1.object({
|
|
969
|
+
format: z$1.literal(ARCHIVE_FORMAT),
|
|
970
970
|
manifest: manifestSchema,
|
|
971
971
|
signature: signatureSchema.nullable(),
|
|
972
|
-
data: z.record(z.string(), z.unknown())
|
|
972
|
+
data: z$1.record(z$1.string(), z$1.unknown())
|
|
973
973
|
});
|
|
974
974
|
/** Parse untrusted JSON text into an archive. Throws with a readable message. */
|
|
975
975
|
function parseArchive(text) {
|
|
@@ -1238,15 +1238,15 @@ function describeChange(kind, name, restoreFrom) {
|
|
|
1238
1238
|
}
|
|
1239
1239
|
// ../../packages/core/src/connect.ts
|
|
1240
1240
|
/** Workload authentication is separate from secret-decryption principals. */
|
|
1241
|
-
const workloadPublicKeySchema = z.object({
|
|
1242
|
-
kty: z.literal("EC"),
|
|
1243
|
-
crv: z.literal("P-256"),
|
|
1244
|
-
x: z.string().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
1245
|
-
y: z.string().regex(/^[A-Za-z0-9_-]{43}$/)
|
|
1241
|
+
const workloadPublicKeySchema = z$1.object({
|
|
1242
|
+
kty: z$1.literal("EC"),
|
|
1243
|
+
crv: z$1.literal("P-256"),
|
|
1244
|
+
x: z$1.string().regex(/^[A-Za-z0-9_-]{43}$/),
|
|
1245
|
+
y: z$1.string().regex(/^[A-Za-z0-9_-]{43}$/)
|
|
1246
1246
|
}).strict();
|
|
1247
|
-
const audienceSchema = z.string().min(1).max(256).regex(/^[^\s]+$/);
|
|
1247
|
+
const audienceSchema = z$1.string().min(1).max(256).regex(/^[^\s]+$/);
|
|
1248
1248
|
/** HTTPS endpoint syntax. Resolved-address safety is enforced by the server runtime. */
|
|
1249
|
-
const connectUrlSchema = z.string().max(2048).url().refine((value) => {
|
|
1249
|
+
const connectUrlSchema = z$1.string().max(2048).url().refine((value) => {
|
|
1250
1250
|
try {
|
|
1251
1251
|
const u = new URL(value);
|
|
1252
1252
|
return u.protocol === "https:" && !u.username && !u.password && !u.hash && !u.search && !u.port && u.hostname.length <= 253 && u.hostname.split(".").every((label) => /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label)) && u.hostname.includes(".") && !/^[0-9.]+$/.test(u.hostname) && !u.hostname.endsWith(".") && !/(^|\.)(localhost|local|localdomain|internal|invalid|home|home\.arpa|onion)$/.test(u.hostname);
|
|
@@ -1254,12 +1254,12 @@ const connectUrlSchema = z.string().max(2048).url().refine((value) => {
|
|
|
1254
1254
|
return false;
|
|
1255
1255
|
}
|
|
1256
1256
|
}, "use a public HTTPS hostname without credentials, query, fragment, or port");
|
|
1257
|
-
const federationPolicySchema = z.object({
|
|
1257
|
+
const federationPolicySchema = z$1.object({
|
|
1258
1258
|
issuer: connectUrlSchema,
|
|
1259
1259
|
jwksUri: connectUrlSchema,
|
|
1260
1260
|
audience: audienceSchema,
|
|
1261
|
-
subject: z.string().min(1).max(512),
|
|
1262
|
-
claims: z.record(z.string().regex(/^[a-zA-Z][a-zA-Z0-9_]{0,63}$/), z.string().min(1).max(512)).refine((claims) => Object.keys(claims).length <= 20 && Object.keys(claims).every((key) => ![
|
|
1261
|
+
subject: z$1.string().min(1).max(512),
|
|
1262
|
+
claims: z$1.record(z$1.string().regex(/^[a-zA-Z][a-zA-Z0-9_]{0,63}$/), z$1.string().min(1).max(512)).refine((claims) => Object.keys(claims).length <= 20 && Object.keys(claims).every((key) => ![
|
|
1263
1263
|
"iss",
|
|
1264
1264
|
"aud",
|
|
1265
1265
|
"sub",
|
|
@@ -1268,29 +1268,29 @@ const federationPolicySchema = z.object({
|
|
|
1268
1268
|
"nbf"
|
|
1269
1269
|
].includes(key))).default({})
|
|
1270
1270
|
}).strict();
|
|
1271
|
-
z.object({
|
|
1272
|
-
name: z.string().trim().min(1).max(100),
|
|
1273
|
-
environmentId: z.string().min(1).max(128),
|
|
1271
|
+
z$1.object({
|
|
1272
|
+
name: z$1.string().trim().min(1).max(100),
|
|
1273
|
+
environmentId: z$1.string().min(1).max(128),
|
|
1274
1274
|
publicKey: workloadPublicKeySchema.optional(),
|
|
1275
1275
|
federation: federationPolicySchema.optional(),
|
|
1276
|
-
audiences: z.array(audienceSchema).max(20).default([]),
|
|
1277
|
-
maxTtlSeconds: z.number().int().min(60).max(900).default(300)
|
|
1276
|
+
audiences: z$1.array(audienceSchema).max(20).default([]),
|
|
1277
|
+
maxTtlSeconds: z$1.number().int().min(60).max(900).default(300)
|
|
1278
1278
|
}).strict().refine((v) => !!v.publicKey !== !!v.federation, "choose a public key or a federation policy");
|
|
1279
|
-
z.object({
|
|
1280
|
-
enabled: z.boolean().optional(),
|
|
1279
|
+
z$1.object({
|
|
1280
|
+
enabled: z$1.boolean().optional(),
|
|
1281
1281
|
publicKey: workloadPublicKeySchema.optional()
|
|
1282
1282
|
}).strict().refine((v) => v.enabled !== void 0 || v.publicKey !== void 0);
|
|
1283
|
-
const githubPermissionsSchema = z.record(z.string().regex(/^[a-z_]{1,64}$/), z.enum(["read", "write"])).refine((p) => Object.keys(p).length > 0 && Object.keys(p).length <= 40, "specify between 1 and 40 permissions");
|
|
1284
|
-
const githubScopeSchema = z.object({
|
|
1285
|
-
repositoryIds: z.array(z.number().int().positive().max(Number.MAX_SAFE_INTEGER)).min(1).max(100),
|
|
1283
|
+
const githubPermissionsSchema = z$1.record(z$1.string().regex(/^[a-z_]{1,64}$/), z$1.enum(["read", "write"])).refine((p) => Object.keys(p).length > 0 && Object.keys(p).length <= 40, "specify between 1 and 40 permissions");
|
|
1284
|
+
const githubScopeSchema = z$1.object({
|
|
1285
|
+
repositoryIds: z$1.array(z$1.number().int().positive().max(Number.MAX_SAFE_INTEGER)).min(1).max(100),
|
|
1286
1286
|
permissions: githubPermissionsSchema
|
|
1287
1287
|
}).strict();
|
|
1288
1288
|
/** Exact paths or explicitly allowed descendants, without URL decoding ambiguity. */
|
|
1289
|
-
const connectPathSchema = z.string().min(1).max(1024).refine((path) => /^\/[A-Za-z0-9_./~-]*$/.test(path) && !path.includes("//") && !path.split("/").some((part) => part === "." || part === ".."), "use an absolute API path without encoded characters or dot segments");
|
|
1290
|
-
const proxyRuleSchema = z.object({
|
|
1289
|
+
const connectPathSchema = z$1.string().min(1).max(1024).refine((path) => /^\/[A-Za-z0-9_./~-]*$/.test(path) && !path.includes("//") && !path.split("/").some((part) => part === "." || part === ".."), "use an absolute API path without encoded characters or dot segments");
|
|
1290
|
+
const proxyRuleSchema = z$1.object({
|
|
1291
1291
|
path: connectPathSchema,
|
|
1292
|
-
descendants: z.boolean().default(false),
|
|
1293
|
-
methods: z.array(z.enum([
|
|
1292
|
+
descendants: z$1.boolean().default(false),
|
|
1293
|
+
methods: z$1.array(z$1.enum([
|
|
1294
1294
|
"GET",
|
|
1295
1295
|
"POST",
|
|
1296
1296
|
"PUT",
|
|
@@ -1299,18 +1299,18 @@ const proxyRuleSchema = z.object({
|
|
|
1299
1299
|
"HEAD"
|
|
1300
1300
|
])).min(1).max(6)
|
|
1301
1301
|
}).strict();
|
|
1302
|
-
const staticScopeSchema = z.object({ rules: z.array(proxyRuleSchema).min(1).max(30) }).strict();
|
|
1303
|
-
const oauthScopeSchema = z.object({ scopes: z.array(z.string().regex(/^[\x21\x23-\x5b\x5d-\x7e]{1,200}$/)).min(1).max(50).refine((scopes) => new Set(scopes).size === scopes.length, "scopes must be unique") }).strict();
|
|
1302
|
+
const staticScopeSchema = z$1.object({ rules: z$1.array(proxyRuleSchema).min(1).max(30) }).strict();
|
|
1303
|
+
const oauthScopeSchema = z$1.object({ scopes: z$1.array(z$1.string().regex(/^[\x21\x23-\x5b\x5d-\x7e]{1,200}$/)).min(1).max(50).refine((scopes) => new Set(scopes).size === scopes.length, "scopes must be unique") }).strict();
|
|
1304
1304
|
const oauthConfigSchema = oauthScopeSchema.extend({
|
|
1305
|
-
provider: z.literal("oauth"),
|
|
1306
|
-
grantType: z.enum(["client_credentials", "authorization_code"]),
|
|
1307
|
-
clientId: z.string().min(1).max(512),
|
|
1305
|
+
provider: z$1.literal("oauth"),
|
|
1306
|
+
grantType: z$1.enum(["client_credentials", "authorization_code"]),
|
|
1307
|
+
clientId: z$1.string().min(1).max(512),
|
|
1308
1308
|
tokenEndpoint: connectUrlSchema,
|
|
1309
1309
|
authorizationEndpoint: connectUrlSchema.optional(),
|
|
1310
|
-
tokenAuthMethod: z.enum(["client_secret_basic", "client_secret_post"]).default("client_secret_basic")
|
|
1310
|
+
tokenAuthMethod: z$1.enum(["client_secret_basic", "client_secret_post"]).default("client_secret_basic")
|
|
1311
1311
|
});
|
|
1312
1312
|
const customApiConfigSchema = staticScopeSchema.extend({
|
|
1313
|
-
provider: z.literal("custom_api"),
|
|
1313
|
+
provider: z$1.literal("custom_api"),
|
|
1314
1314
|
origin: connectUrlSchema.refine((value) => {
|
|
1315
1315
|
try {
|
|
1316
1316
|
return new URL(value).pathname === "/";
|
|
@@ -1318,46 +1318,46 @@ const customApiConfigSchema = staticScopeSchema.extend({
|
|
|
1318
1318
|
return false;
|
|
1319
1319
|
}
|
|
1320
1320
|
}, "use an origin without a path"),
|
|
1321
|
-
headerName: z.string().regex(/^[A-Za-z][A-Za-z0-9-]{0,63}$/).refine((v) => !/^(accept$|user-agent$|host|cookie|content-|proxy-|forwarded|x-forwarded-|cf-|connection|transfer-encoding|upgrade|trailer|te$)/i.test(v)),
|
|
1322
|
-
headerPrefix: z.string().max(64).regex(/^[\x20-\x7e]*$/).default("Bearer ")
|
|
1321
|
+
headerName: z$1.string().regex(/^[A-Za-z][A-Za-z0-9-]{0,63}$/).refine((v) => !/^(accept$|user-agent$|host|cookie|content-|proxy-|forwarded|x-forwarded-|cf-|connection|transfer-encoding|upgrade|trailer|te$)/i.test(v)),
|
|
1322
|
+
headerPrefix: z$1.string().max(64).regex(/^[\x20-\x7e]*$/).default("Bearer ")
|
|
1323
1323
|
});
|
|
1324
|
-
const connectConfigSchema = z.discriminatedUnion("provider", [
|
|
1324
|
+
const connectConfigSchema = z$1.discriminatedUnion("provider", [
|
|
1325
1325
|
customApiConfigSchema,
|
|
1326
1326
|
oauthConfigSchema,
|
|
1327
1327
|
githubScopeSchema.extend({
|
|
1328
|
-
provider: z.literal("github_app"),
|
|
1329
|
-
appId: z.string().regex(/^[A-Za-z0-9_.-]{1,128}$/),
|
|
1330
|
-
installationId: z.string().regex(/^[1-9][0-9]{0,19}$/)
|
|
1328
|
+
provider: z$1.literal("github_app"),
|
|
1329
|
+
appId: z$1.string().regex(/^[A-Za-z0-9_.-]{1,128}$/),
|
|
1330
|
+
installationId: z$1.string().regex(/^[1-9][0-9]{0,19}$/)
|
|
1331
1331
|
}),
|
|
1332
1332
|
staticScopeSchema.extend({
|
|
1333
|
-
provider: z.literal("static_key"),
|
|
1334
|
-
service: z.literal("resend"),
|
|
1335
|
-
rules: z.array(proxyRuleSchema.refine((rule) => rule.path === "/emails" || rule.path.startsWith("/emails/"), "the Resend connector supports only /emails and its descendants")).min(1).max(30)
|
|
1333
|
+
provider: z$1.literal("static_key"),
|
|
1334
|
+
service: z$1.literal("resend"),
|
|
1335
|
+
rules: z$1.array(proxyRuleSchema.refine((rule) => rule.path === "/emails" || rule.path.startsWith("/emails/"), "the Resend connector supports only /emails and its descendants")).min(1).max(30)
|
|
1336
1336
|
})
|
|
1337
1337
|
]).refine((v) => v.provider !== "oauth" || v.grantType !== "authorization_code" || !!v.authorizationEndpoint, "authorization-code connections require an authorization endpoint");
|
|
1338
|
-
const connectGrantPolicySchema = z.discriminatedUnion("provider", [
|
|
1339
|
-
staticScopeSchema.extend({ provider: z.literal("custom_api") }),
|
|
1340
|
-
oauthScopeSchema.extend({ provider: z.literal("oauth") }),
|
|
1341
|
-
githubScopeSchema.extend({ provider: z.literal("github_app") }),
|
|
1342
|
-
staticScopeSchema.extend({ provider: z.literal("static_key") })
|
|
1338
|
+
const connectGrantPolicySchema = z$1.discriminatedUnion("provider", [
|
|
1339
|
+
staticScopeSchema.extend({ provider: z$1.literal("custom_api") }),
|
|
1340
|
+
oauthScopeSchema.extend({ provider: z$1.literal("oauth") }),
|
|
1341
|
+
githubScopeSchema.extend({ provider: z$1.literal("github_app") }),
|
|
1342
|
+
staticScopeSchema.extend({ provider: z$1.literal("static_key") })
|
|
1343
1343
|
]);
|
|
1344
|
-
z.object({
|
|
1345
|
-
name: z.string().trim().min(1).max(100),
|
|
1344
|
+
z$1.object({
|
|
1345
|
+
name: z$1.string().trim().min(1).max(100),
|
|
1346
1346
|
config: connectConfigSchema,
|
|
1347
|
-
credentialWrapped: z.string().startsWith("wd1.").max(32e3)
|
|
1347
|
+
credentialWrapped: z$1.string().startsWith("wd1.").max(32e3)
|
|
1348
1348
|
}).strict();
|
|
1349
|
-
z.object({
|
|
1350
|
-
enabled: z.boolean().optional(),
|
|
1351
|
-
credentialWrapped: z.string().startsWith("wd1.").max(32e3).optional()
|
|
1349
|
+
z$1.object({
|
|
1350
|
+
enabled: z$1.boolean().optional(),
|
|
1351
|
+
credentialWrapped: z$1.string().startsWith("wd1.").max(32e3).optional()
|
|
1352
1352
|
}).strict().refine((v) => v.enabled !== void 0 || v.credentialWrapped !== void 0);
|
|
1353
|
-
z.object({
|
|
1354
|
-
workloadId: z.string().min(1).max(128),
|
|
1355
|
-
connectionId: z.string().min(1).max(128),
|
|
1353
|
+
z$1.object({
|
|
1354
|
+
workloadId: z$1.string().min(1).max(128),
|
|
1355
|
+
connectionId: z$1.string().min(1).max(128),
|
|
1356
1356
|
policy: connectGrantPolicySchema
|
|
1357
1357
|
}).strict();
|
|
1358
|
-
z.object({
|
|
1358
|
+
z$1.object({
|
|
1359
1359
|
path: connectPathSchema,
|
|
1360
|
-
method: z.enum([
|
|
1360
|
+
method: z$1.enum([
|
|
1361
1361
|
"GET",
|
|
1362
1362
|
"POST",
|
|
1363
1363
|
"PUT",
|
|
@@ -1365,11 +1365,11 @@ z.object({
|
|
|
1365
1365
|
"DELETE",
|
|
1366
1366
|
"HEAD"
|
|
1367
1367
|
]).default("GET"),
|
|
1368
|
-
query: z.record(z.string().min(1).max(100), z.string().max(2048)).default({}),
|
|
1368
|
+
query: z$1.record(z$1.string().min(1).max(100), z$1.string().max(2048)).default({}),
|
|
1369
1369
|
/** JSON only; callers cannot inject authorization or forwarding headers. */
|
|
1370
|
-
body: z.string().max(65536).optional()
|
|
1370
|
+
body: z$1.string().max(65536).optional()
|
|
1371
1371
|
}).strict().refine((v) => !["GET", "HEAD"].includes(v.method) || v.body === void 0, "GET and HEAD cannot carry a body");
|
|
1372
|
-
z.object({ audience: audienceSchema }).strict();
|
|
1372
|
+
z$1.object({ audience: audienceSchema }).strict();
|
|
1373
1373
|
// ../../packages/core/src/dotenv.ts
|
|
1374
1374
|
/**
|
|
1375
1375
|
* `.env` parsing and serialization — the canonical definition of what a `.env`
|
|
@@ -1752,7 +1752,7 @@ function prettyJsonOrRaw(value) {
|
|
|
1752
1752
|
const inspection = inspectJsonValue(value);
|
|
1753
1753
|
return inspection.kind === "json" ? inspection.pretty : value;
|
|
1754
1754
|
}
|
|
1755
|
-
z.enum([
|
|
1755
|
+
z$1.enum([
|
|
1756
1756
|
"postgres",
|
|
1757
1757
|
"mysql",
|
|
1758
1758
|
"ssh",
|
|
@@ -1761,35 +1761,35 @@ z.enum([
|
|
|
1761
1761
|
"gcp",
|
|
1762
1762
|
"mongodb"
|
|
1763
1763
|
]);
|
|
1764
|
-
const executorModeSchema = z.enum(["in_do", "remote"]);
|
|
1764
|
+
const executorModeSchema = z$1.enum(["in_do", "remote"]);
|
|
1765
1765
|
/**
|
|
1766
1766
|
* A Postgres role name we are willing to create. Deliberately strict — this
|
|
1767
1767
|
* value is interpolated into a SQL template, so it must be a bare identifier
|
|
1768
1768
|
* with no way to break out of quoting (no quotes, whitespace, or semicolons).
|
|
1769
1769
|
*/
|
|
1770
|
-
const postgresRoleNameSchema = z.string().regex(/^[a-z_][a-z0-9_]{2,62}$/, "must be 3–63 chars, lowercase letters/digits/underscore, starting with a letter or underscore");
|
|
1770
|
+
const postgresRoleNameSchema = z$1.string().regex(/^[a-z_][a-z0-9_]{2,62}$/, "must be 3–63 chars, lowercase letters/digits/underscore, starting with a letter or underscore");
|
|
1771
1771
|
/**
|
|
1772
1772
|
* A SCRAM-SHA-256 verifier string as produced by @seekrit/crypto. Validated so
|
|
1773
1773
|
* it, too, is safe to interpolate into a quoted SQL literal (the alphabet is
|
|
1774
1774
|
* base64 + the fixed structural characters, none of which is a single quote).
|
|
1775
1775
|
*/
|
|
1776
|
-
const scramVerifierSchema = z.string().regex(/^SCRAM-SHA-256\$\d{3,}:[A-Za-z0-9+/=]+\$[A-Za-z0-9+/=]+:[A-Za-z0-9+/=]+$/, "must be a SCRAM-SHA-256 verifier");
|
|
1776
|
+
const scramVerifierSchema = z$1.string().regex(/^SCRAM-SHA-256\$\d{3,}:[A-Za-z0-9+/=]+\$[A-Za-z0-9+/=]+:[A-Za-z0-9+/=]+$/, "must be a SCRAM-SHA-256 verifier");
|
|
1777
1777
|
/**
|
|
1778
1778
|
* An SSH login principal (a Unix-style username the certificate authorizes).
|
|
1779
1779
|
* Bounded and restricted to a safe charset — principals are SSH-wire-encoded,
|
|
1780
1780
|
* not shell-interpolated, so this is sanity/DoS hardening, not an injection gate.
|
|
1781
1781
|
*/
|
|
1782
|
-
const sshPrincipalSchema = z.string().regex(/^[A-Za-z0-9._-]{1,64}$/, "must be 1–64 chars of letters, digits, dot, dash, underscore");
|
|
1782
|
+
const sshPrincipalSchema = z$1.string().regex(/^[A-Za-z0-9._-]{1,64}$/, "must be 1–64 chars of letters, digits, dot, dash, underscore");
|
|
1783
1783
|
/** An `ssh-ed25519 <base64> [comment]` public key line (deep-validated on sign). */
|
|
1784
|
-
const sshPublicKeySchema = z.string().max(2048).regex(/^ssh-ed25519 [A-Za-z0-9+/=]+( .*)?$/, "must be an ssh-ed25519 public key");
|
|
1784
|
+
const sshPublicKeySchema = z$1.string().max(2048).regex(/^ssh-ed25519 [A-Za-z0-9+/=]+( .*)?$/, "must be an ssh-ed25519 public key");
|
|
1785
1785
|
/** An SSH certificate extension name, e.g. `permit-pty`. */
|
|
1786
|
-
const sshExtensionSchema = z.string().regex(/^[a-z0-9-]{1,64}$/);
|
|
1786
|
+
const sshExtensionSchema = z$1.string().regex(/^[a-z0-9-]{1,64}$/);
|
|
1787
1787
|
/**
|
|
1788
1788
|
* A MySQL/MariaDB user name we are willing to create. Interpolated into a
|
|
1789
1789
|
* quoted SQL literal (`'{{name}}'@'%'`), so it is kept strict — plain
|
|
1790
1790
|
* alphanumerics/underscore, no quotes/whitespace/semicolons to break out.
|
|
1791
1791
|
*/
|
|
1792
|
-
const mysqlUserNameSchema = z.string().regex(/^[A-Za-z0-9_]{3,32}$/, "must be 3–32 chars, letters/digits/underscore");
|
|
1792
|
+
const mysqlUserNameSchema = z$1.string().regex(/^[A-Za-z0-9_]{3,32}$/, "must be 3–32 chars, letters/digits/underscore");
|
|
1793
1793
|
/**
|
|
1794
1794
|
* A `mysql_native_password` authentication string — `*` followed by 40 upper
|
|
1795
1795
|
* hex chars (`UPPER(HEX(SHA1(SHA1(password))))`), as produced by
|
|
@@ -1797,14 +1797,14 @@ const mysqlUserNameSchema = z.string().regex(/^[A-Za-z0-9_]{3,32}$/, "must be 3
|
|
|
1797
1797
|
* `CREATE USER … IDENTIFIED WITH mysql_native_password AS '<str>'`, and its
|
|
1798
1798
|
* alphabet contains no single quote, so it is safe in a quoted SQL literal.
|
|
1799
1799
|
*/
|
|
1800
|
-
const mysqlNativeVerifierSchema = z.string().regex(/^\*[0-9A-F]{40}$/, "must be a mysql_native_password hash (*<40 hex>)");
|
|
1800
|
+
const mysqlNativeVerifierSchema = z$1.string().regex(/^\*[0-9A-F]{40}$/, "must be a mysql_native_password hash (*<40 hex>)");
|
|
1801
1801
|
/**
|
|
1802
1802
|
* A Redis ACL user name we are willing to create. Interpolated into a Redis
|
|
1803
1803
|
* command line as a bare token (`ACL SETUSER <name> …`), so it is kept strict —
|
|
1804
1804
|
* plain alphanumerics/underscore, no whitespace to split the arg or ACL rule
|
|
1805
1805
|
* characters (`~ + @ # & %`) that could be read as a permission.
|
|
1806
1806
|
*/
|
|
1807
|
-
const redisUserNameSchema = z.string().regex(/^[A-Za-z0-9_]{3,32}$/, "must be 3–32 chars, letters/digits/underscore");
|
|
1807
|
+
const redisUserNameSchema = z$1.string().regex(/^[A-Za-z0-9_]{3,32}$/, "must be 3–32 chars, letters/digits/underscore");
|
|
1808
1808
|
/**
|
|
1809
1809
|
* A Redis password verifier — the lowercase-hex SHA-256 of the password, as
|
|
1810
1810
|
* produced by @seekrit/crypto `redisSha256Verifier`. `ACL SETUSER … on #<hex>`
|
|
@@ -1813,22 +1813,22 @@ const redisUserNameSchema = z.string().regex(/^[A-Za-z0-9_]{3,32}$/, "must be 3
|
|
|
1813
1813
|
* is preimage-resistant (the password is high-entropy and machine-generated).
|
|
1814
1814
|
* The alphabet is bare hex, so it is a safe bare command token.
|
|
1815
1815
|
*/
|
|
1816
|
-
const redisSha256VerifierSchema = z.string().regex(/^[0-9a-f]{64}$/, "must be a lowercase-hex SHA-256 digest (64 chars)");
|
|
1816
|
+
const redisSha256VerifierSchema = z$1.string().regex(/^[0-9a-f]{64}$/, "must be a lowercase-hex SHA-256 digest (64 chars)");
|
|
1817
1817
|
/**
|
|
1818
1818
|
* An IAM role ARN the broker is allowed to assume. Bounded and structurally
|
|
1819
1819
|
* validated: `arn:<partition>:iam::<account>:role/<path-and-name>`. Partition
|
|
1820
1820
|
* covers commercial (`aws`), GovCloud (`aws-us-gov`), and China (`aws-cn`).
|
|
1821
1821
|
*/
|
|
1822
|
-
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>)");
|
|
1822
|
+
const awsRoleArnSchema = z$1.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>)");
|
|
1823
1823
|
/** An AWS region id, e.g. `us-east-1`, `eu-west-2`, `us-gov-west-1`. */
|
|
1824
|
-
const awsRegionSchema$1 = z.string().regex(/^[a-z]{2}(?:-[a-z]+)+-\d$/, "must be an AWS region id (e.g. us-east-1)");
|
|
1824
|
+
const awsRegionSchema$1 = z$1.string().regex(/^[a-z]{2}(?:-[a-z]+)+-\d$/, "must be an AWS region id (e.g. us-east-1)");
|
|
1825
1825
|
/**
|
|
1826
1826
|
* An STS external id — the shared string a role's trust policy can require so a
|
|
1827
1827
|
* confused-deputy can't assume it. AWS allows a broad charset; we keep to the
|
|
1828
1828
|
* documented safe set and bound the length.
|
|
1829
1829
|
*/
|
|
1830
|
-
const awsExternalIdSchema = z.string().regex(/^[\w+=,.@:/-]{2,1224}$/, "must be a valid STS external id");
|
|
1831
|
-
z.string().regex(/^[\w+=,.@-]{2,64}$/, "must be 2–64 chars of [A-Za-z0-9_+=,.@-]");
|
|
1830
|
+
const awsExternalIdSchema = z$1.string().regex(/^[\w+=,.@:/-]{2,1224}$/, "must be a valid STS external id");
|
|
1831
|
+
z$1.string().regex(/^[\w+=,.@-]{2,64}$/, "must be 2–64 chars of [A-Za-z0-9_+=,.@-]");
|
|
1832
1832
|
/**
|
|
1833
1833
|
* A GCP service-account email the broker is allowed to impersonate (or that
|
|
1834
1834
|
* appears in a delegation chain). Structurally validated and bounded: it is
|
|
@@ -1837,21 +1837,21 @@ z.string().regex(/^[\w+=,.@-]{2,64}$/, "must be 2–64 chars of [A-Za-z0-9_+=,.@
|
|
|
1837
1837
|
* (`name@<project>.iam.gserviceaccount.com`) and Google-managed
|
|
1838
1838
|
* (`<project-number>-compute@developer.gserviceaccount.com`) forms.
|
|
1839
1839
|
*/
|
|
1840
|
-
const gcpServiceAccountEmailSchema = z.string().max(256).regex(/^[a-z0-9-]+@[a-z0-9.-]+\.gserviceaccount\.com$/, "must be a service-account email (…@….gserviceaccount.com)");
|
|
1840
|
+
const gcpServiceAccountEmailSchema = z$1.string().max(256).regex(/^[a-z0-9-]+@[a-z0-9.-]+\.gserviceaccount\.com$/, "must be a service-account email (…@….gserviceaccount.com)");
|
|
1841
1841
|
/**
|
|
1842
1842
|
* An OAuth 2.0 scope granted to the minted access token, e.g.
|
|
1843
1843
|
* `https://www.googleapis.com/auth/cloud-platform`. Bounded and whitespace-free
|
|
1844
1844
|
* (scopes are space-delimited); passed to the IAM Credentials API in a JSON body
|
|
1845
1845
|
* array, not a URL, so this is sanity/DoS hardening rather than an injection gate.
|
|
1846
1846
|
*/
|
|
1847
|
-
const gcpOauthScopeSchema = z.string().min(1).max(256).regex(/^\S+$/, "must be a single OAuth scope with no whitespace");
|
|
1847
|
+
const gcpOauthScopeSchema = z$1.string().min(1).max(256).regex(/^\S+$/, "must be a single OAuth scope with no whitespace");
|
|
1848
1848
|
/**
|
|
1849
1849
|
* The consumer's ephemeral P-256 public key (JWK-serialized) that a tier-2
|
|
1850
1850
|
* credential is wrapped to before it is returned. Validated structurally here;
|
|
1851
1851
|
* the executor imports it defensively before wrapping. Bounded so a giant blob
|
|
1852
1852
|
* can't be pushed through the control plane.
|
|
1853
1853
|
*/
|
|
1854
|
-
const p256PublicKeyJwkSchema = z.string().max(2048).refine((s) => {
|
|
1854
|
+
const p256PublicKeyJwkSchema = z$1.string().max(2048).refine((s) => {
|
|
1855
1855
|
try {
|
|
1856
1856
|
const jwk = JSON.parse(s);
|
|
1857
1857
|
return jwk.kty === "EC" && jwk.crv === "P-256" && !!jwk.x && !!jwk.y;
|
|
@@ -1859,23 +1859,23 @@ const p256PublicKeyJwkSchema = z.string().max(2048).refine((s) => {
|
|
|
1859
1859
|
return false;
|
|
1860
1860
|
}
|
|
1861
1861
|
}, "must be a JWK-serialized P-256 public key");
|
|
1862
|
-
z.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "must be 1–64 chars of [A-Za-z0-9_-]");
|
|
1862
|
+
z$1.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "must be 1–64 chars of [A-Za-z0-9_-]");
|
|
1863
1863
|
/**
|
|
1864
1864
|
* A MongoDB database name — the db a preset role is granted on, or the
|
|
1865
1865
|
* authentication database a leased user is created in. MongoDB forbids
|
|
1866
1866
|
* `/\. "$*<>:|?` and the empty string in db names; we keep to a safe subset.
|
|
1867
1867
|
*/
|
|
1868
|
-
const mongoDatabaseNameSchema = z.string().regex(/^[A-Za-z0-9_-]{1,63}$/, "must be 1–63 chars of [A-Za-z0-9_-]");
|
|
1868
|
+
const mongoDatabaseNameSchema = z$1.string().regex(/^[A-Za-z0-9_-]{1,63}$/, "must be 1–63 chars of [A-Za-z0-9_-]");
|
|
1869
1869
|
/**
|
|
1870
1870
|
* A single MongoDB role grant `{ role, db }` for a `custom` target — e.g.
|
|
1871
1871
|
* `{ role: "readWrite", db: "app" }` or a user-defined role. Admin-supplied
|
|
1872
1872
|
* trusted input, set once at registration; still bounded structurally.
|
|
1873
1873
|
*/
|
|
1874
|
-
const mongoRoleSchema = z.object({
|
|
1875
|
-
role: z.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "must be a role name"),
|
|
1874
|
+
const mongoRoleSchema = z$1.object({
|
|
1875
|
+
role: z$1.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "must be a role name"),
|
|
1876
1876
|
db: mongoDatabaseNameSchema
|
|
1877
1877
|
});
|
|
1878
|
-
const postgresAccessLevelSchema = z.enum([
|
|
1878
|
+
const postgresAccessLevelSchema = z$1.enum([
|
|
1879
1879
|
"readonly",
|
|
1880
1880
|
"readwrite",
|
|
1881
1881
|
"custom"
|
|
@@ -1885,109 +1885,109 @@ const POSTGRES_GROUP_ROLES = {
|
|
|
1885
1885
|
readonly: "seekrit_readonly",
|
|
1886
1886
|
readwrite: "seekrit_readwrite"
|
|
1887
1887
|
};
|
|
1888
|
-
const mysqlAccessLevelSchema = z.enum([
|
|
1888
|
+
const mysqlAccessLevelSchema = z$1.enum([
|
|
1889
1889
|
"readonly",
|
|
1890
1890
|
"readwrite",
|
|
1891
1891
|
"custom"
|
|
1892
1892
|
]);
|
|
1893
|
-
const redisAccessLevelSchema = z.enum([
|
|
1893
|
+
const redisAccessLevelSchema = z$1.enum([
|
|
1894
1894
|
"readonly",
|
|
1895
1895
|
"readwrite",
|
|
1896
1896
|
"custom"
|
|
1897
1897
|
]);
|
|
1898
|
-
const mongoAccessLevelSchema = z.enum([
|
|
1898
|
+
const mongoAccessLevelSchema = z$1.enum([
|
|
1899
1899
|
"readonly",
|
|
1900
1900
|
"readwrite",
|
|
1901
1901
|
"custom"
|
|
1902
1902
|
]);
|
|
1903
|
-
const connectionSchema = z.object({
|
|
1904
|
-
host: z.string().min(1),
|
|
1905
|
-
port: z.number().int().min(1).max(65535),
|
|
1906
|
-
database: z.string().min(1)
|
|
1903
|
+
const connectionSchema = z$1.object({
|
|
1904
|
+
host: z$1.string().min(1),
|
|
1905
|
+
port: z$1.number().int().min(1).max(65535),
|
|
1906
|
+
database: z$1.string().min(1)
|
|
1907
1907
|
});
|
|
1908
1908
|
/** A `{{name}}`/`{{verifier}}`/`{{valid_until}}` templated SQL statement. */
|
|
1909
|
-
const statementSchema$1 = z.string().min(1).max(4e3);
|
|
1909
|
+
const statementSchema$1 = z$1.string().min(1).max(4e3);
|
|
1910
1910
|
/** A bare SQL identifier (schema name) — no quotes/whitespace/semicolons. */
|
|
1911
|
-
const identifierSchema = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,62}$/, "must be an identifier");
|
|
1912
|
-
const postgresTargetConfigSchema = z.object({
|
|
1913
|
-
provider: z.literal("postgres"),
|
|
1911
|
+
const identifierSchema = z$1.string().regex(/^[A-Za-z_][A-Za-z0-9_]{0,62}$/, "must be an identifier");
|
|
1912
|
+
const postgresTargetConfigSchema = z$1.object({
|
|
1913
|
+
provider: z$1.literal("postgres"),
|
|
1914
1914
|
executor: executorModeSchema,
|
|
1915
1915
|
accessLevel: postgresAccessLevelSchema.optional(),
|
|
1916
1916
|
schema: identifierSchema.optional(),
|
|
1917
1917
|
connection: connectionSchema,
|
|
1918
|
-
provisionerUrl: z.url().optional(),
|
|
1919
|
-
createStatements: z.array(statementSchema$1).max(16).optional(),
|
|
1920
|
-
revokeStatements: z.array(statementSchema$1).max(16).optional()
|
|
1918
|
+
provisionerUrl: z$1.url().optional(),
|
|
1919
|
+
createStatements: z$1.array(statementSchema$1).max(16).optional(),
|
|
1920
|
+
revokeStatements: z$1.array(statementSchema$1).max(16).optional()
|
|
1921
1921
|
});
|
|
1922
1922
|
/** A MySQL account host part (`'name'@'<host>'`) — no quotes/whitespace. */
|
|
1923
|
-
const mysqlHostSchema$1 = z.string().regex(/^[A-Za-z0-9_.%:-]{1,255}$/, "must be a host pattern");
|
|
1924
|
-
const mysqlTargetConfigSchema = z.object({
|
|
1925
|
-
provider: z.literal("mysql"),
|
|
1923
|
+
const mysqlHostSchema$1 = z$1.string().regex(/^[A-Za-z0-9_.%:-]{1,255}$/, "must be a host pattern");
|
|
1924
|
+
const mysqlTargetConfigSchema = z$1.object({
|
|
1925
|
+
provider: z$1.literal("mysql"),
|
|
1926
1926
|
executor: executorModeSchema,
|
|
1927
1927
|
accessLevel: mysqlAccessLevelSchema.optional(),
|
|
1928
1928
|
connection: connectionSchema,
|
|
1929
1929
|
userHost: mysqlHostSchema$1.optional(),
|
|
1930
|
-
provisionerUrl: z.url().optional(),
|
|
1931
|
-
createStatements: z.array(statementSchema$1).max(16).optional(),
|
|
1932
|
-
revokeStatements: z.array(statementSchema$1).max(16).optional()
|
|
1930
|
+
provisionerUrl: z$1.url().optional(),
|
|
1931
|
+
createStatements: z$1.array(statementSchema$1).max(16).optional(),
|
|
1932
|
+
revokeStatements: z$1.array(statementSchema$1).max(16).optional()
|
|
1933
1933
|
});
|
|
1934
|
-
const redisConnectionSchema = z.object({
|
|
1935
|
-
host: z.string().min(1),
|
|
1936
|
-
port: z.number().int().min(1).max(65535),
|
|
1934
|
+
const redisConnectionSchema = z$1.object({
|
|
1935
|
+
host: z$1.string().min(1),
|
|
1936
|
+
port: z$1.number().int().min(1).max(65535),
|
|
1937
1937
|
/** Redis logical database index (the `/<n>` in a connection URL). */
|
|
1938
|
-
db: z.number().int().min(0).max(15).optional()
|
|
1938
|
+
db: z$1.number().int().min(0).max(15).optional()
|
|
1939
1939
|
});
|
|
1940
|
-
const redisTargetConfigSchema = z.object({
|
|
1941
|
-
provider: z.literal("redis"),
|
|
1940
|
+
const redisTargetConfigSchema = z$1.object({
|
|
1941
|
+
provider: z$1.literal("redis"),
|
|
1942
1942
|
executor: executorModeSchema,
|
|
1943
1943
|
accessLevel: redisAccessLevelSchema.optional(),
|
|
1944
1944
|
connection: redisConnectionSchema,
|
|
1945
|
-
provisionerUrl: z.url().optional(),
|
|
1946
|
-
createStatements: z.array(statementSchema$1).max(16).optional(),
|
|
1947
|
-
revokeStatements: z.array(statementSchema$1).max(16).optional()
|
|
1945
|
+
provisionerUrl: z$1.url().optional(),
|
|
1946
|
+
createStatements: z$1.array(statementSchema$1).max(16).optional(),
|
|
1947
|
+
revokeStatements: z$1.array(statementSchema$1).max(16).optional()
|
|
1948
1948
|
});
|
|
1949
|
-
const sshTargetConfigSchema = z.object({
|
|
1950
|
-
provider: z.literal("ssh"),
|
|
1951
|
-
executor: z.literal("in_do"),
|
|
1949
|
+
const sshTargetConfigSchema = z$1.object({
|
|
1950
|
+
provider: z$1.literal("ssh"),
|
|
1951
|
+
executor: z$1.literal("in_do"),
|
|
1952
1952
|
caPublicKey: sshPublicKeySchema,
|
|
1953
|
-
allowedPrincipals: z.array(sshPrincipalSchema).max(64).optional(),
|
|
1954
|
-
extensions: z.array(sshExtensionSchema).max(16).optional(),
|
|
1955
|
-
maxTtlSeconds: z.number().int().min(60).max(604800).optional(),
|
|
1956
|
-
connection: z.object({
|
|
1957
|
-
host: z.string().min(1).optional(),
|
|
1953
|
+
allowedPrincipals: z$1.array(sshPrincipalSchema).max(64).optional(),
|
|
1954
|
+
extensions: z$1.array(sshExtensionSchema).max(16).optional(),
|
|
1955
|
+
maxTtlSeconds: z$1.number().int().min(60).max(604800).optional(),
|
|
1956
|
+
connection: z$1.object({
|
|
1957
|
+
host: z$1.string().min(1).optional(),
|
|
1958
1958
|
user: sshPrincipalSchema.optional()
|
|
1959
1959
|
}).optional()
|
|
1960
1960
|
});
|
|
1961
1961
|
const AWS_MAX_TTL_SECONDS = 43200;
|
|
1962
|
-
const awsTargetConfigSchema = z.object({
|
|
1963
|
-
provider: z.literal("aws"),
|
|
1964
|
-
executor: z.literal("in_do"),
|
|
1962
|
+
const awsTargetConfigSchema = z$1.object({
|
|
1963
|
+
provider: z$1.literal("aws"),
|
|
1964
|
+
executor: z$1.literal("in_do"),
|
|
1965
1965
|
roleArn: awsRoleArnSchema,
|
|
1966
1966
|
region: awsRegionSchema$1,
|
|
1967
1967
|
externalId: awsExternalIdSchema.optional(),
|
|
1968
|
-
sessionPolicy: z.string().min(1).max(4e3).optional(),
|
|
1969
|
-
maxTtlSeconds: z.number().int().min(900).max(AWS_MAX_TTL_SECONDS).optional()
|
|
1968
|
+
sessionPolicy: z$1.string().min(1).max(4e3).optional(),
|
|
1969
|
+
maxTtlSeconds: z$1.number().int().min(900).max(AWS_MAX_TTL_SECONDS).optional()
|
|
1970
1970
|
});
|
|
1971
1971
|
const GCP_MAX_TTL_SECONDS = 43200;
|
|
1972
|
-
const gcpTargetConfigSchema = z.object({
|
|
1973
|
-
provider: z.literal("gcp"),
|
|
1974
|
-
executor: z.literal("in_do"),
|
|
1972
|
+
const gcpTargetConfigSchema = z$1.object({
|
|
1973
|
+
provider: z$1.literal("gcp"),
|
|
1974
|
+
executor: z$1.literal("in_do"),
|
|
1975
1975
|
serviceAccount: gcpServiceAccountEmailSchema,
|
|
1976
|
-
scopes: z.array(gcpOauthScopeSchema).min(1).max(32).optional(),
|
|
1977
|
-
delegates: z.array(gcpServiceAccountEmailSchema).max(8).optional(),
|
|
1978
|
-
maxTtlSeconds: z.number().int().min(60).max(GCP_MAX_TTL_SECONDS).optional()
|
|
1976
|
+
scopes: z$1.array(gcpOauthScopeSchema).min(1).max(32).optional(),
|
|
1977
|
+
delegates: z$1.array(gcpServiceAccountEmailSchema).max(8).optional(),
|
|
1978
|
+
maxTtlSeconds: z$1.number().int().min(60).max(GCP_MAX_TTL_SECONDS).optional()
|
|
1979
1979
|
});
|
|
1980
|
-
const mongoTargetConfigSchema = z.object({
|
|
1981
|
-
provider: z.literal("mongodb"),
|
|
1982
|
-
executor: z.literal("in_do"),
|
|
1980
|
+
const mongoTargetConfigSchema = z$1.object({
|
|
1981
|
+
provider: z$1.literal("mongodb"),
|
|
1982
|
+
executor: z$1.literal("in_do"),
|
|
1983
1983
|
accessLevel: mongoAccessLevelSchema.optional(),
|
|
1984
1984
|
connection: connectionSchema,
|
|
1985
1985
|
authSource: mongoDatabaseNameSchema.optional(),
|
|
1986
|
-
roles: z.array(mongoRoleSchema).min(1).max(32).optional(),
|
|
1987
|
-
tls: z.boolean().optional(),
|
|
1988
|
-
maxTtlSeconds: z.number().int().min(60).max(604800).optional()
|
|
1986
|
+
roles: z$1.array(mongoRoleSchema).min(1).max(32).optional(),
|
|
1987
|
+
tls: z$1.boolean().optional(),
|
|
1988
|
+
maxTtlSeconds: z$1.number().int().min(60).max(604800).optional()
|
|
1989
1989
|
});
|
|
1990
|
-
const leaseTargetConfigSchema = z.discriminatedUnion("provider", [
|
|
1990
|
+
const leaseTargetConfigSchema = z$1.discriminatedUnion("provider", [
|
|
1991
1991
|
postgresTargetConfigSchema,
|
|
1992
1992
|
mysqlTargetConfigSchema,
|
|
1993
1993
|
redisTargetConfigSchema,
|
|
@@ -1996,26 +1996,26 @@ const leaseTargetConfigSchema = z.discriminatedUnion("provider", [
|
|
|
1996
1996
|
gcpTargetConfigSchema,
|
|
1997
1997
|
mongoTargetConfigSchema
|
|
1998
1998
|
]);
|
|
1999
|
-
z.object({
|
|
2000
|
-
name: z.string().trim().min(1).max(128),
|
|
1999
|
+
z$1.object({
|
|
2000
|
+
name: z$1.string().trim().min(1).max(128),
|
|
2001
2001
|
config: leaseTargetConfigSchema,
|
|
2002
2002
|
/**
|
|
2003
2003
|
* The admin/provisioning credential (e.g. a Postgres connection string),
|
|
2004
2004
|
* encrypted client-side to the broker's public key (a `wd1.` wrap). The
|
|
2005
2005
|
* control plane stores only this ciphertext — it never sees the plaintext.
|
|
2006
2006
|
*/
|
|
2007
|
-
wrappedAdminSecret: z.string().min(1)
|
|
2007
|
+
wrappedAdminSecret: z$1.string().min(1)
|
|
2008
2008
|
});
|
|
2009
2009
|
/** Requested lease lifetime, shared by all providers. */
|
|
2010
|
-
const ttlSecondsSchema = z.number().int().min(60).max(604800);
|
|
2010
|
+
const ttlSecondsSchema = z$1.number().int().min(60).max(604800);
|
|
2011
2011
|
/**
|
|
2012
2012
|
* Client → API: mint a Postgres lease. The client generates the password and
|
|
2013
2013
|
* its SCRAM verifier locally and sends only the verifier — the plaintext
|
|
2014
2014
|
* password never leaves the requesting machine.
|
|
2015
2015
|
*/
|
|
2016
|
-
const mintPostgresLeaseSchema = z.object({
|
|
2017
|
-
provider: z.literal("postgres"),
|
|
2018
|
-
targetId: z.string().min(1),
|
|
2016
|
+
const mintPostgresLeaseSchema = z$1.object({
|
|
2017
|
+
provider: z$1.literal("postgres"),
|
|
2018
|
+
targetId: z$1.string().min(1),
|
|
2019
2019
|
roleName: postgresRoleNameSchema,
|
|
2020
2020
|
verifier: scramVerifierSchema,
|
|
2021
2021
|
ttlSeconds: ttlSecondsSchema
|
|
@@ -2025,9 +2025,9 @@ const mintPostgresLeaseSchema = z.object({
|
|
|
2025
2025
|
* and its `mysql_native_password` hash locally and sends only the hash — the
|
|
2026
2026
|
* plaintext password never leaves the requesting machine.
|
|
2027
2027
|
*/
|
|
2028
|
-
const mintMysqlLeaseSchema = z.object({
|
|
2029
|
-
provider: z.literal("mysql"),
|
|
2030
|
-
targetId: z.string().min(1),
|
|
2028
|
+
const mintMysqlLeaseSchema = z$1.object({
|
|
2029
|
+
provider: z$1.literal("mysql"),
|
|
2030
|
+
targetId: z$1.string().min(1),
|
|
2031
2031
|
roleName: mysqlUserNameSchema,
|
|
2032
2032
|
verifier: mysqlNativeVerifierSchema,
|
|
2033
2033
|
ttlSeconds: ttlSecondsSchema
|
|
@@ -2037,9 +2037,9 @@ const mintMysqlLeaseSchema = z.object({
|
|
|
2037
2037
|
* SHA-256 hex digest locally and sends only the digest — the plaintext password
|
|
2038
2038
|
* never leaves the requesting machine.
|
|
2039
2039
|
*/
|
|
2040
|
-
const mintRedisLeaseSchema = z.object({
|
|
2041
|
-
provider: z.literal("redis"),
|
|
2042
|
-
targetId: z.string().min(1),
|
|
2040
|
+
const mintRedisLeaseSchema = z$1.object({
|
|
2041
|
+
provider: z$1.literal("redis"),
|
|
2042
|
+
targetId: z$1.string().min(1),
|
|
2043
2043
|
roleName: redisUserNameSchema,
|
|
2044
2044
|
verifier: redisSha256VerifierSchema,
|
|
2045
2045
|
ttlSeconds: ttlSecondsSchema
|
|
@@ -2049,11 +2049,11 @@ const mintRedisLeaseSchema = z.object({
|
|
|
2049
2049
|
* locally and sends only the public key; the signed certificate comes back in
|
|
2050
2050
|
* the response. The private key never leaves the requesting machine.
|
|
2051
2051
|
*/
|
|
2052
|
-
const mintSshLeaseSchema = z.object({
|
|
2053
|
-
provider: z.literal("ssh"),
|
|
2054
|
-
targetId: z.string().min(1),
|
|
2052
|
+
const mintSshLeaseSchema = z$1.object({
|
|
2053
|
+
provider: z$1.literal("ssh"),
|
|
2054
|
+
targetId: z$1.string().min(1),
|
|
2055
2055
|
publicKey: sshPublicKeySchema,
|
|
2056
|
-
principals: z.array(sshPrincipalSchema).min(1).max(32),
|
|
2056
|
+
principals: z$1.array(sshPrincipalSchema).min(1).max(32),
|
|
2057
2057
|
ttlSeconds: ttlSecondsSchema
|
|
2058
2058
|
});
|
|
2059
2059
|
/**
|
|
@@ -2065,11 +2065,11 @@ const mintSshLeaseSchema = z.object({
|
|
|
2065
2065
|
* TTL bounds are STS's own `DurationSeconds` limits (15 min – 12 h), not the
|
|
2066
2066
|
* generic lease bounds — STS rejects anything below 900 seconds.
|
|
2067
2067
|
*/
|
|
2068
|
-
const mintAwsLeaseSchema = z.object({
|
|
2069
|
-
provider: z.literal("aws"),
|
|
2070
|
-
targetId: z.string().min(1),
|
|
2068
|
+
const mintAwsLeaseSchema = z$1.object({
|
|
2069
|
+
provider: z$1.literal("aws"),
|
|
2070
|
+
targetId: z$1.string().min(1),
|
|
2071
2071
|
recipientPublicKey: p256PublicKeyJwkSchema,
|
|
2072
|
-
ttlSeconds: z.number().int().min(900).max(AWS_MAX_TTL_SECONDS)
|
|
2072
|
+
ttlSeconds: z$1.number().int().min(900).max(AWS_MAX_TTL_SECONDS)
|
|
2073
2073
|
});
|
|
2074
2074
|
/**
|
|
2075
2075
|
* Client → API: mint a GCP lease. Like AWS (tier 2): the client generates an
|
|
@@ -2081,11 +2081,11 @@ const mintAwsLeaseSchema = z.object({
|
|
|
2081
2081
|
* TTL bounds are GCP's `generateAccessToken` limits (1 min – 12 h); tokens over
|
|
2082
2082
|
* 1 h require the credential-lifetime-extension org policy.
|
|
2083
2083
|
*/
|
|
2084
|
-
const mintGcpLeaseSchema = z.object({
|
|
2085
|
-
provider: z.literal("gcp"),
|
|
2086
|
-
targetId: z.string().min(1),
|
|
2084
|
+
const mintGcpLeaseSchema = z$1.object({
|
|
2085
|
+
provider: z$1.literal("gcp"),
|
|
2086
|
+
targetId: z$1.string().min(1),
|
|
2087
2087
|
recipientPublicKey: p256PublicKeyJwkSchema,
|
|
2088
|
-
ttlSeconds: z.number().int().min(60).max(GCP_MAX_TTL_SECONDS)
|
|
2088
|
+
ttlSeconds: z$1.number().int().min(60).max(GCP_MAX_TTL_SECONDS)
|
|
2089
2089
|
});
|
|
2090
2090
|
/**
|
|
2091
2091
|
* Client → API: mint a MongoDB lease. Like AWS (tier 2), the client generates
|
|
@@ -2094,13 +2094,13 @@ const mintGcpLeaseSchema = z.object({
|
|
|
2094
2094
|
* to that key. The private key never leaves the requesting machine, so the
|
|
2095
2095
|
* plaintext credential is only decryptable there.
|
|
2096
2096
|
*/
|
|
2097
|
-
const mintMongoLeaseSchema = z.object({
|
|
2098
|
-
provider: z.literal("mongodb"),
|
|
2099
|
-
targetId: z.string().min(1),
|
|
2097
|
+
const mintMongoLeaseSchema = z$1.object({
|
|
2098
|
+
provider: z$1.literal("mongodb"),
|
|
2099
|
+
targetId: z$1.string().min(1),
|
|
2100
2100
|
recipientPublicKey: p256PublicKeyJwkSchema,
|
|
2101
2101
|
ttlSeconds: ttlSecondsSchema
|
|
2102
2102
|
});
|
|
2103
|
-
z.discriminatedUnion("provider", [
|
|
2103
|
+
z$1.discriminatedUnion("provider", [
|
|
2104
2104
|
mintPostgresLeaseSchema,
|
|
2105
2105
|
mintMysqlLeaseSchema,
|
|
2106
2106
|
mintRedisLeaseSchema,
|
|
@@ -2234,6 +2234,13 @@ function sshHostSetupInstructions(config) {
|
|
|
2234
2234
|
"sudo systemctl reload sshd"
|
|
2235
2235
|
].join("\n");
|
|
2236
2236
|
}
|
|
2237
|
+
// ../../packages/core/src/slug.ts
|
|
2238
|
+
/**
|
|
2239
|
+
* The slug rule, with no dependencies — so a caller that cannot afford zod (the
|
|
2240
|
+
* resolve gateway's bundle, see apps/api/src/gateway) validates with the same
|
|
2241
|
+
* pattern `slugSchema` uses rather than a copy of it.
|
|
2242
|
+
*/
|
|
2243
|
+
const SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
2237
2244
|
// ../../packages/core/src/types.ts
|
|
2238
2245
|
const AUDIT_ACTIONS = [
|
|
2239
2246
|
"workload.created",
|
|
@@ -2468,94 +2475,94 @@ const NOTIFICATION_TYPE_META = {
|
|
|
2468
2475
|
};
|
|
2469
2476
|
// ../../packages/core/src/schemas.ts
|
|
2470
2477
|
/** URL-safe identifier segment: `my-app`, `production`, … */
|
|
2471
|
-
const slugSchema = z.string().min(1).max(64).regex(
|
|
2472
|
-
const nameSchema = z.string().trim().min(1).max(128);
|
|
2478
|
+
const slugSchema = z$1.string().min(1).max(64).regex(SLUG_PATTERN, "must be lowercase alphanumeric with hyphens");
|
|
2479
|
+
const nameSchema = z$1.string().trim().min(1).max(128);
|
|
2473
2480
|
/** An email address, normalized to trimmed lowercase before validation. */
|
|
2474
|
-
const emailSchema = z.string().trim().toLowerCase().pipe(z.email().max(320));
|
|
2481
|
+
const emailSchema = z$1.string().trim().toLowerCase().pipe(z$1.email().max(320));
|
|
2475
2482
|
/** Env-var style secret name: FOO, DATABASE_URL, apiKey2 … */
|
|
2476
|
-
const secretNameSchema = z.string().min(1).max(256).regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "must be a valid environment variable name");
|
|
2477
|
-
const orgRoleSchema = z.enum([
|
|
2483
|
+
const secretNameSchema = z$1.string().min(1).max(256).regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "must be a valid environment variable name");
|
|
2484
|
+
const orgRoleSchema = z$1.enum([
|
|
2478
2485
|
"owner",
|
|
2479
2486
|
"admin",
|
|
2480
2487
|
"member"
|
|
2481
2488
|
]);
|
|
2482
2489
|
/** Role a person may be invited at — never `owner` (ownership isn't invitable). */
|
|
2483
|
-
const inviteRoleSchema = z.enum(["admin", "member"]);
|
|
2484
|
-
const principalTypeSchema = z.enum(["user", "service_token"]);
|
|
2490
|
+
const inviteRoleSchema = z$1.enum(["admin", "member"]);
|
|
2491
|
+
const principalTypeSchema = z$1.enum(["user", "service_token"]);
|
|
2485
2492
|
/** Org-level capability a service token can hold (never `owner`). */
|
|
2486
|
-
const serviceTokenRoleSchema = z.enum(["admin", "member"]);
|
|
2487
|
-
z.object({
|
|
2493
|
+
const serviceTokenRoleSchema = z$1.enum(["admin", "member"]);
|
|
2494
|
+
z$1.object({
|
|
2488
2495
|
/** Include the full append-only ciphertext history of every secret. */
|
|
2489
|
-
includeVersions: z.boolean().optional(),
|
|
2490
|
-
includeAudit: z.boolean().optional(),
|
|
2496
|
+
includeVersions: z$1.boolean().optional(),
|
|
2497
|
+
includeAudit: z$1.boolean().optional(),
|
|
2491
2498
|
/** Newest audit rows to keep. Exceeding the server cap is a 400, not a silent trim. */
|
|
2492
|
-
auditLimit: z.number().int().min(0).optional()
|
|
2499
|
+
auditLimit: z$1.number().int().min(0).optional()
|
|
2493
2500
|
});
|
|
2494
|
-
z.object({
|
|
2501
|
+
z$1.object({
|
|
2495
2502
|
name: nameSchema,
|
|
2496
2503
|
slug: slugSchema
|
|
2497
2504
|
});
|
|
2498
|
-
z.object({
|
|
2505
|
+
z$1.object({
|
|
2499
2506
|
name: nameSchema,
|
|
2500
2507
|
slug: slugSchema
|
|
2501
2508
|
});
|
|
2502
|
-
z.object({ name: nameSchema });
|
|
2503
|
-
z.object({ name: nameSchema });
|
|
2504
|
-
z.object({ name: nameSchema });
|
|
2505
|
-
z.object({ name: nameSchema });
|
|
2506
|
-
z.object({ name: nameSchema });
|
|
2507
|
-
z.object({ required: z.boolean() });
|
|
2508
|
-
z.object({
|
|
2509
|
+
z$1.object({ name: nameSchema });
|
|
2510
|
+
z$1.object({ name: nameSchema });
|
|
2511
|
+
z$1.object({ name: nameSchema });
|
|
2512
|
+
z$1.object({ name: nameSchema });
|
|
2513
|
+
z$1.object({ name: nameSchema });
|
|
2514
|
+
z$1.object({ required: z$1.boolean() });
|
|
2515
|
+
z$1.object({
|
|
2509
2516
|
email: emailSchema,
|
|
2510
2517
|
role: inviteRoleSchema.default("member")
|
|
2511
2518
|
});
|
|
2512
|
-
z.object({
|
|
2519
|
+
z$1.object({
|
|
2513
2520
|
name: nameSchema,
|
|
2514
2521
|
slug: slugSchema
|
|
2515
2522
|
});
|
|
2516
2523
|
/** Compose a group into an application environment (matched by env slug). */
|
|
2517
|
-
const appAccessKeySchema = z.object({
|
|
2518
|
-
environmentId: z.string().min(1),
|
|
2519
|
-
userId: z.string().min(1),
|
|
2520
|
-
wrappedDek: z.string().min(1).max(8192)
|
|
2524
|
+
const appAccessKeySchema = z$1.object({
|
|
2525
|
+
environmentId: z$1.string().min(1),
|
|
2526
|
+
userId: z$1.string().min(1),
|
|
2527
|
+
wrappedDek: z$1.string().min(1).max(8192)
|
|
2521
2528
|
});
|
|
2522
|
-
const appAccessGrantsSchema = z.array(appAccessKeySchema).max(5e3).optional();
|
|
2523
|
-
z.object({
|
|
2524
|
-
userId: z.string().min(1),
|
|
2525
|
-
grants: z.array(appAccessKeySchema).max(5e3)
|
|
2529
|
+
const appAccessGrantsSchema = z$1.array(appAccessKeySchema).max(5e3).optional();
|
|
2530
|
+
z$1.object({
|
|
2531
|
+
userId: z$1.string().min(1),
|
|
2532
|
+
grants: z$1.array(appAccessKeySchema).max(5e3)
|
|
2526
2533
|
});
|
|
2527
|
-
z.object({ role: orgRoleSchema });
|
|
2528
|
-
z.object({
|
|
2534
|
+
z$1.object({ role: orgRoleSchema });
|
|
2535
|
+
z$1.object({
|
|
2529
2536
|
appAccessGrants: appAccessGrantsSchema,
|
|
2530
|
-
groupId: z.string().min(1),
|
|
2537
|
+
groupId: z$1.string().min(1),
|
|
2531
2538
|
/** Precedence among an env's groups (higher wins). Appended if omitted. */
|
|
2532
|
-
position: z.number().int().min(0).optional(),
|
|
2539
|
+
position: z$1.number().int().min(0).optional(),
|
|
2533
2540
|
/** Client-wrapped group DEK for each sync connection newly reading this group. */
|
|
2534
|
-
syncGrants: z.array(z.object({
|
|
2535
|
-
connectionId: z.string().min(1),
|
|
2536
|
-
wrappedDek: z.string().min(1)
|
|
2541
|
+
syncGrants: z$1.array(z$1.object({
|
|
2542
|
+
connectionId: z$1.string().min(1),
|
|
2543
|
+
wrappedDek: z$1.string().min(1)
|
|
2537
2544
|
})).optional()
|
|
2538
2545
|
});
|
|
2539
|
-
z.object({
|
|
2546
|
+
z$1.object({
|
|
2540
2547
|
/** Wrap the new DEK to all persistent app recipients. */
|
|
2541
|
-
userGrants: z.array(z.object({
|
|
2542
|
-
userId: z.string().min(1),
|
|
2543
|
-
wrappedDek: z.string().min(1).max(8192)
|
|
2548
|
+
userGrants: z$1.array(z$1.object({
|
|
2549
|
+
userId: z$1.string().min(1),
|
|
2550
|
+
wrappedDek: z$1.string().min(1).max(8192)
|
|
2544
2551
|
})).max(5e3).optional(),
|
|
2545
2552
|
name: nameSchema,
|
|
2546
2553
|
slug: slugSchema,
|
|
2547
2554
|
/** Environment DEK wrapped to the creator's public key — created client-side. */
|
|
2548
|
-
wrappedDek: z.string().min(1),
|
|
2555
|
+
wrappedDek: z$1.string().min(1),
|
|
2549
2556
|
/**
|
|
2550
2557
|
* When the org has recovery enabled, the same DEK additionally wrapped to the
|
|
2551
2558
|
* org recovery public key, so the environment is recovery-protected from
|
|
2552
2559
|
* creation. Omitted when recovery is off (backfilled later by `recovery sync`).
|
|
2553
2560
|
*/
|
|
2554
|
-
recoveryWrappedDek: z.string().min(1).nullish()
|
|
2561
|
+
recoveryWrappedDek: z$1.string().min(1).nullish()
|
|
2555
2562
|
});
|
|
2556
|
-
z.object({
|
|
2563
|
+
z$1.object({
|
|
2557
2564
|
/** Opaque versioned ciphertext blob from @seekrit/crypto. */
|
|
2558
|
-
ciphertext: z.string().min(1).max(65536),
|
|
2565
|
+
ciphertext: z$1.string().min(1).max(65536),
|
|
2559
2566
|
/**
|
|
2560
2567
|
* Compare-and-swap: write only if the secret is currently at this version
|
|
2561
2568
|
* (`0` = only if it does not exist yet), else 412 `precondition_failed`.
|
|
@@ -2563,77 +2570,77 @@ z.object({
|
|
|
2563
2570
|
* changed, and wrote back — a refreshed agent login, say — find out that
|
|
2564
2571
|
* someone else wrote in between instead of silently erasing their value.
|
|
2565
2572
|
*/
|
|
2566
|
-
expectedVersion: z.number().int().min(0).optional()
|
|
2573
|
+
expectedVersion: z$1.number().int().min(0).optional()
|
|
2567
2574
|
});
|
|
2568
|
-
z.object({ version: z.number().int().positive() });
|
|
2569
|
-
z.object({ limit: z.coerce.number().int().min(1).max(200).default(50) });
|
|
2570
|
-
z.object({
|
|
2571
|
-
requiredApprovals: z.number().int().min(1).max(10).default(1),
|
|
2572
|
-
allowSelfApproval: z.boolean().default(false)
|
|
2575
|
+
z$1.object({ version: z$1.number().int().positive() });
|
|
2576
|
+
z$1.object({ limit: z$1.coerce.number().int().min(1).max(200).default(50) });
|
|
2577
|
+
z$1.object({
|
|
2578
|
+
requiredApprovals: z$1.number().int().min(1).max(10).default(1),
|
|
2579
|
+
allowSelfApproval: z$1.boolean().default(false)
|
|
2573
2580
|
});
|
|
2574
|
-
z.object({ comment: z.string().trim().max(1e3).optional() });
|
|
2575
|
-
z.string().trim().min(1).max(1e3);
|
|
2576
|
-
z.object({
|
|
2577
|
-
status: z.enum(CHANGE_REQUEST_STATUSES).optional(),
|
|
2578
|
-
envId: z.string().min(1).optional(),
|
|
2579
|
-
limit: z.coerce.number().int().min(1).max(200).default(50)
|
|
2581
|
+
z$1.object({ comment: z$1.string().trim().max(1e3).optional() });
|
|
2582
|
+
z$1.string().trim().min(1).max(1e3);
|
|
2583
|
+
z$1.object({
|
|
2584
|
+
status: z$1.enum(CHANGE_REQUEST_STATUSES).optional(),
|
|
2585
|
+
envId: z$1.string().min(1).optional(),
|
|
2586
|
+
limit: z$1.coerce.number().int().min(1).max(200).default(50)
|
|
2580
2587
|
});
|
|
2581
|
-
z.object({
|
|
2582
|
-
publicKeyJwk: z.string().min(1),
|
|
2588
|
+
z$1.object({
|
|
2589
|
+
publicKeyJwk: z$1.string().min(1),
|
|
2583
2590
|
/**
|
|
2584
2591
|
* Private key encrypted with a passphrase-derived KEK; opaque to the
|
|
2585
2592
|
* server. Self-contained blob (embeds KDF salt + iterations).
|
|
2586
2593
|
*/
|
|
2587
|
-
encryptedPrivateKey: z.string().min(1)
|
|
2594
|
+
encryptedPrivateKey: z$1.string().min(1)
|
|
2588
2595
|
});
|
|
2589
|
-
z.object({
|
|
2596
|
+
z$1.object({
|
|
2590
2597
|
/** Base64url WebAuthn credential id — what `allowCredentials` is built from. */
|
|
2591
|
-
credentialId: z.string().min(1).max(512),
|
|
2598
|
+
credentialId: z$1.string().min(1).max(512),
|
|
2592
2599
|
/** Human label for the device, e.g. "MacBook Touch ID". Display only. */
|
|
2593
|
-
label: z.string().min(1).max(64),
|
|
2600
|
+
label: z$1.string().min(1).max(64),
|
|
2594
2601
|
/** Base64url PRF evaluation input for this credential; not a secret. */
|
|
2595
|
-
prfInput: z.string().min(1).max(256),
|
|
2602
|
+
prfInput: z$1.string().min(1).max(256),
|
|
2596
2603
|
/** The private key wrapped to this passkey's PRF output. */
|
|
2597
|
-
encryptedPrivateKey: z.string().min(1).max(8192).refine((v) => v.startsWith("pk2."), { message: "expected a pk2. passkey wrap" })
|
|
2604
|
+
encryptedPrivateKey: z$1.string().min(1).max(8192).refine((v) => v.startsWith("pk2."), { message: "expected a pk2. passkey wrap" })
|
|
2598
2605
|
});
|
|
2599
|
-
const grantEnvironmentKeySchema = z.object({
|
|
2606
|
+
const grantEnvironmentKeySchema = z$1.object({
|
|
2600
2607
|
principalType: principalTypeSchema,
|
|
2601
|
-
principalId: z.string().min(1),
|
|
2602
|
-
wrappedDek: z.string().min(1)
|
|
2608
|
+
principalId: z$1.string().min(1),
|
|
2609
|
+
wrappedDek: z$1.string().min(1)
|
|
2603
2610
|
});
|
|
2604
|
-
z.object({
|
|
2611
|
+
z$1.object({
|
|
2605
2612
|
slug: slugSchema,
|
|
2606
2613
|
/** Display name; defaults to the slug. */
|
|
2607
2614
|
name: nameSchema.optional(),
|
|
2608
|
-
ttlSeconds: z.number().int().min(60).max(MAX_BRANCH_TTL_SECONDS).nullish(),
|
|
2615
|
+
ttlSeconds: z$1.number().int().min(60).max(MAX_BRANCH_TTL_SECONDS).nullish(),
|
|
2609
2616
|
/** The branch's own DEK, wrapped to the creator — generated client-side. */
|
|
2610
|
-
wrappedDek: z.string().min(1),
|
|
2611
|
-
recoveryWrappedDek: z.string().min(1).nullish(),
|
|
2617
|
+
wrappedDek: z$1.string().min(1),
|
|
2618
|
+
recoveryWrappedDek: z$1.string().min(1).nullish(),
|
|
2612
2619
|
/** The same DEK wrapped to each of the parent's existing grant-holders. */
|
|
2613
|
-
grants: z.array(grantEnvironmentKeySchema).max(500).default([])
|
|
2620
|
+
grants: z$1.array(grantEnvironmentKeySchema).max(500).default([])
|
|
2614
2621
|
});
|
|
2615
2622
|
/**
|
|
2616
2623
|
* Token creation is client-driven so the server never sees the token secret:
|
|
2617
2624
|
* the client generates the keypair + token string and submits only the hash
|
|
2618
2625
|
* and public key.
|
|
2619
2626
|
*/
|
|
2620
|
-
const environmentGrantBatchSchema = z.array(z.object({
|
|
2621
|
-
environmentId: z.string().min(1),
|
|
2622
|
-
wrappedDek: z.string().min(1).max(8192)
|
|
2627
|
+
const environmentGrantBatchSchema = z$1.array(z$1.object({
|
|
2628
|
+
environmentId: z$1.string().min(1),
|
|
2629
|
+
wrappedDek: z$1.string().min(1).max(8192)
|
|
2623
2630
|
})).max(5e3);
|
|
2624
|
-
z.object({
|
|
2631
|
+
z$1.object({
|
|
2625
2632
|
principalType: principalTypeSchema,
|
|
2626
|
-
principalId: z.string().min(1),
|
|
2633
|
+
principalId: z$1.string().min(1),
|
|
2627
2634
|
grants: environmentGrantBatchSchema
|
|
2628
2635
|
});
|
|
2629
|
-
z.object({
|
|
2636
|
+
z$1.object({
|
|
2630
2637
|
/** Commit the token and all default resolve keys together, when supplied. */
|
|
2631
2638
|
initialGrants: environmentGrantBatchSchema.optional(),
|
|
2632
2639
|
name: nameSchema,
|
|
2633
|
-
tokenId: z.string().regex(/^skt_[0-9A-Za-z]+$/),
|
|
2640
|
+
tokenId: z$1.string().regex(/^skt_[0-9A-Za-z]+$/),
|
|
2634
2641
|
/** SHA-256 hash (base64url) of the full token string. */
|
|
2635
|
-
tokenHash: z.string().min(1),
|
|
2636
|
-
publicKeyJwk: z.string().min(1),
|
|
2642
|
+
tokenHash: z$1.string().min(1),
|
|
2643
|
+
publicKeyJwk: z$1.string().min(1),
|
|
2637
2644
|
/**
|
|
2638
2645
|
* Org-level capability. Defaults to `member` (a runtime credential); pass
|
|
2639
2646
|
* `admin` to mint a headless provisioning token. Only an admin caller may
|
|
@@ -2645,35 +2652,35 @@ z.object({
|
|
|
2645
2652
|
* Optional so org-admin tokens can exist, but required for runtime tokens
|
|
2646
2653
|
* that resolve secrets via `GET /v1/resolve`.
|
|
2647
2654
|
*/
|
|
2648
|
-
environmentId: z.string().min(1).nullish(),
|
|
2649
|
-
expiresAt: z.iso.datetime().nullish()
|
|
2655
|
+
environmentId: z$1.string().min(1).nullish(),
|
|
2656
|
+
expiresAt: z$1.iso.datetime().nullish()
|
|
2650
2657
|
});
|
|
2651
|
-
z.object({
|
|
2658
|
+
z$1.object({
|
|
2652
2659
|
name: nameSchema,
|
|
2653
|
-
tokenId: z.string().regex(/^skt_[0-9A-Za-z]+$/),
|
|
2660
|
+
tokenId: z$1.string().regex(/^skt_[0-9A-Za-z]+$/),
|
|
2654
2661
|
/** SHA-256 hash (base64url) of the full token string. */
|
|
2655
|
-
tokenHash: z.string().min(1),
|
|
2662
|
+
tokenHash: z$1.string().min(1),
|
|
2656
2663
|
/** Where the decoy was planted, as a reminder for whoever reads the alert. */
|
|
2657
|
-
placement: z.string().max(200).nullish()
|
|
2664
|
+
placement: z$1.string().max(200).nullish()
|
|
2658
2665
|
});
|
|
2659
|
-
const kmsKeyPurposeSchema = z.enum(["encrypt", "sign"]);
|
|
2660
|
-
const kmsKeySpecSchema = z.enum(["aes-256-gcm", "ecdsa-p256"]);
|
|
2666
|
+
const kmsKeyPurposeSchema = z$1.enum(["encrypt", "sign"]);
|
|
2667
|
+
const kmsKeySpecSchema = z$1.enum(["aes-256-gcm", "ecdsa-p256"]);
|
|
2661
2668
|
/** A wrapped key grant supplied by the client (server never sees plaintext material). */
|
|
2662
|
-
const kmsGrantInputSchema = z.object({
|
|
2669
|
+
const kmsGrantInputSchema = z$1.object({
|
|
2663
2670
|
principalType: principalTypeSchema,
|
|
2664
|
-
principalId: z.string().min(1),
|
|
2671
|
+
principalId: z$1.string().min(1),
|
|
2665
2672
|
/** Key material wrapped to the principal's public key (`wd1.` blob). */
|
|
2666
|
-
wrappedKey: z.string().min(1)
|
|
2673
|
+
wrappedKey: z$1.string().min(1)
|
|
2667
2674
|
});
|
|
2668
|
-
z.object({
|
|
2675
|
+
z$1.object({
|
|
2669
2676
|
name: nameSchema,
|
|
2670
2677
|
purpose: kmsKeyPurposeSchema,
|
|
2671
2678
|
spec: kmsKeySpecSchema,
|
|
2672
|
-
applicationId: z.string().min(1).nullish(),
|
|
2673
|
-
groupId: z.string().min(1).nullish(),
|
|
2679
|
+
applicationId: z$1.string().min(1).nullish(),
|
|
2680
|
+
groupId: z$1.string().min(1).nullish(),
|
|
2674
2681
|
/** ECDSA P-256 public key (JWK) for `sign` keys; omit for `encrypt` keys. */
|
|
2675
|
-
publicKeyJwk: z.string().min(1).nullish(),
|
|
2676
|
-
grants: z.array(kmsGrantInputSchema).min(1)
|
|
2682
|
+
publicKeyJwk: z$1.string().min(1).nullish(),
|
|
2683
|
+
grants: z$1.array(kmsGrantInputSchema).min(1)
|
|
2677
2684
|
}).refine((v) => !(v.applicationId && v.groupId), {
|
|
2678
2685
|
message: "a key may be scoped to an application or a group, not both",
|
|
2679
2686
|
path: ["groupId"]
|
|
@@ -2684,15 +2691,15 @@ z.object({
|
|
|
2684
2691
|
message: "sign keys require a publicKeyJwk; encrypt keys must omit it",
|
|
2685
2692
|
path: ["publicKeyJwk"]
|
|
2686
2693
|
});
|
|
2687
|
-
z.object({
|
|
2694
|
+
z$1.object({
|
|
2688
2695
|
principalType: principalTypeSchema,
|
|
2689
|
-
principalId: z.string().min(1),
|
|
2696
|
+
principalId: z$1.string().min(1),
|
|
2690
2697
|
/** Current-version key material wrapped to the principal's public key. */
|
|
2691
|
-
wrappedKey: z.string().min(1)
|
|
2698
|
+
wrappedKey: z$1.string().min(1)
|
|
2692
2699
|
});
|
|
2693
|
-
z.object({
|
|
2694
|
-
publicKeyJwk: z.string().min(1).nullish(),
|
|
2695
|
-
grants: z.array(kmsGrantInputSchema).min(1)
|
|
2700
|
+
z$1.object({
|
|
2701
|
+
publicKeyJwk: z$1.string().min(1).nullish(),
|
|
2702
|
+
grants: z$1.array(kmsGrantInputSchema).min(1)
|
|
2696
2703
|
});
|
|
2697
2704
|
/**
|
|
2698
2705
|
* One custodian's wrapped Shamir share of the org recovery private key. The
|
|
@@ -2700,25 +2707,25 @@ z.object({
|
|
|
2700
2707
|
* wraps each share to a custodian's public key — the server stores only the
|
|
2701
2708
|
* opaque `wrappedShare` and can reconstruct nothing.
|
|
2702
2709
|
*/
|
|
2703
|
-
const recoveryShareInputSchema = z.object({
|
|
2710
|
+
const recoveryShareInputSchema = z$1.object({
|
|
2704
2711
|
principalType: principalTypeSchema,
|
|
2705
|
-
principalId: z.string().min(1),
|
|
2712
|
+
principalId: z$1.string().min(1),
|
|
2706
2713
|
/** Shamir x-coordinate carried by the share (1..255). */
|
|
2707
|
-
shareIndex: z.number().int().min(1).max(255),
|
|
2714
|
+
shareIndex: z$1.number().int().min(1).max(255),
|
|
2708
2715
|
/** The recovery-key share wrapped to the custodian's public key (`wd1.`). */
|
|
2709
|
-
wrappedShare: z.string().min(1)
|
|
2716
|
+
wrappedShare: z$1.string().min(1)
|
|
2710
2717
|
});
|
|
2711
2718
|
/** An environment DEK additionally wrapped to the org recovery public key. */
|
|
2712
|
-
const recoveryEnvGrantSchema = z.object({
|
|
2713
|
-
environmentId: z.string().min(1),
|
|
2719
|
+
const recoveryEnvGrantSchema = z$1.object({
|
|
2720
|
+
environmentId: z$1.string().min(1),
|
|
2714
2721
|
/** The environment's DEK wrapped to the recovery public key (`wd1.`). */
|
|
2715
|
-
wrappedDek: z.string().min(1)
|
|
2722
|
+
wrappedDek: z$1.string().min(1)
|
|
2716
2723
|
});
|
|
2717
|
-
z.object({
|
|
2718
|
-
recoveryPublicKeyJwk: z.string().min(1),
|
|
2719
|
-
threshold: z.number().int().min(1).max(255),
|
|
2720
|
-
shares: z.array(recoveryShareInputSchema).min(1).max(255),
|
|
2721
|
-
grants: z.array(recoveryEnvGrantSchema).default([])
|
|
2724
|
+
z$1.object({
|
|
2725
|
+
recoveryPublicKeyJwk: z$1.string().min(1),
|
|
2726
|
+
threshold: z$1.number().int().min(1).max(255),
|
|
2727
|
+
shares: z$1.array(recoveryShareInputSchema).min(1).max(255),
|
|
2728
|
+
grants: z$1.array(recoveryEnvGrantSchema).default([])
|
|
2722
2729
|
}).refine((v) => v.threshold <= v.shares.length, {
|
|
2723
2730
|
message: "threshold cannot exceed the number of custodians",
|
|
2724
2731
|
path: ["threshold"]
|
|
@@ -2726,104 +2733,104 @@ z.object({
|
|
|
2726
2733
|
message: "custodians must be distinct",
|
|
2727
2734
|
path: ["shares"]
|
|
2728
2735
|
});
|
|
2729
|
-
z.object({ grants: z.array(recoveryEnvGrantSchema).min(1).max(500) });
|
|
2730
|
-
z.object({
|
|
2731
|
-
targetPublicKeyJwk: z.string().min(1),
|
|
2736
|
+
z$1.object({ grants: z$1.array(recoveryEnvGrantSchema).min(1).max(500) });
|
|
2737
|
+
z$1.object({
|
|
2738
|
+
targetPublicKeyJwk: z$1.string().min(1),
|
|
2732
2739
|
targetType: principalTypeSchema.nullish(),
|
|
2733
|
-
targetId: z.string().min(1).nullish(),
|
|
2734
|
-
reason: z.string().max(500).nullish()
|
|
2740
|
+
targetId: z$1.string().min(1).nullish(),
|
|
2741
|
+
reason: z$1.string().max(500).nullish()
|
|
2735
2742
|
});
|
|
2736
|
-
z.object({
|
|
2737
|
-
shareIndex: z.number().int().min(1).max(255),
|
|
2743
|
+
z$1.object({
|
|
2744
|
+
shareIndex: z$1.number().int().min(1).max(255),
|
|
2738
2745
|
/** The custodian's share re-wrapped to the target public key (`wd1.`). */
|
|
2739
|
-
contributedShare: z.string().min(1)
|
|
2746
|
+
contributedShare: z$1.string().min(1)
|
|
2740
2747
|
});
|
|
2741
|
-
z.object({
|
|
2748
|
+
z$1.object({
|
|
2742
2749
|
principalType: principalTypeSchema,
|
|
2743
|
-
principalId: z.string().min(1),
|
|
2744
|
-
grants: z.array(recoveryEnvGrantSchema).min(1).max(500)
|
|
2750
|
+
principalId: z$1.string().min(1),
|
|
2751
|
+
grants: z$1.array(recoveryEnvGrantSchema).min(1).max(500)
|
|
2745
2752
|
});
|
|
2746
|
-
z.object({ prefs: z.partialRecord(z.enum(NOTIFICATION_TYPES), z.boolean()) });
|
|
2747
|
-
z.object({
|
|
2748
|
-
endpoint: z.url().max(2048),
|
|
2749
|
-
headers: z.record(z.string().min(1).max(256), z.string().max(4096)).optional(),
|
|
2750
|
-
enabled: z.boolean().default(true)
|
|
2753
|
+
z$1.object({ prefs: z$1.partialRecord(z$1.enum(NOTIFICATION_TYPES), z$1.boolean()) });
|
|
2754
|
+
z$1.object({
|
|
2755
|
+
endpoint: z$1.url().max(2048),
|
|
2756
|
+
headers: z$1.record(z$1.string().min(1).max(256), z$1.string().max(4096)).optional(),
|
|
2757
|
+
enabled: z$1.boolean().default(true)
|
|
2751
2758
|
});
|
|
2752
|
-
const planFamilySchema = z.enum(PLAN_FAMILY_IDS);
|
|
2753
|
-
const subscriptionStatusSchema = z.enum(SUBSCRIPTION_STATUSES);
|
|
2754
|
-
z.enum(ENTITLEMENT_KEYS);
|
|
2759
|
+
const planFamilySchema = z$1.enum(PLAN_FAMILY_IDS);
|
|
2760
|
+
const subscriptionStatusSchema = z$1.enum(SUBSCRIPTION_STATUSES);
|
|
2761
|
+
z$1.enum(ENTITLEMENT_KEYS);
|
|
2755
2762
|
/** An entitlement value: boolean (features), number or null/unlimited (limits, metered). */
|
|
2756
|
-
const entitlementValueSchema = z.union([
|
|
2757
|
-
z.boolean(),
|
|
2758
|
-
z.number(),
|
|
2759
|
-
z.null()
|
|
2763
|
+
const entitlementValueSchema = z$1.union([
|
|
2764
|
+
z$1.boolean(),
|
|
2765
|
+
z$1.number(),
|
|
2766
|
+
z$1.null()
|
|
2760
2767
|
]);
|
|
2761
|
-
z.object({
|
|
2768
|
+
z$1.object({
|
|
2762
2769
|
family: planFamilySchema,
|
|
2763
|
-
version: z.number().int().positive().optional(),
|
|
2770
|
+
version: z$1.number().int().positive().optional(),
|
|
2764
2771
|
status: subscriptionStatusSchema.default("active")
|
|
2765
2772
|
});
|
|
2766
|
-
z.object({
|
|
2773
|
+
z$1.object({
|
|
2767
2774
|
value: entitlementValueSchema,
|
|
2768
|
-
note: z.string().max(500).nullish(),
|
|
2769
|
-
expiresAt: z.iso.datetime().nullish()
|
|
2775
|
+
note: z$1.string().max(500).nullish(),
|
|
2776
|
+
expiresAt: z$1.iso.datetime().nullish()
|
|
2770
2777
|
});
|
|
2771
|
-
z.object({
|
|
2772
|
-
code: z.string().min(4).max(40),
|
|
2778
|
+
z$1.object({
|
|
2779
|
+
code: z$1.string().min(4).max(40),
|
|
2773
2780
|
family: planFamilySchema,
|
|
2774
|
-
version: z.number().int().positive().optional(),
|
|
2775
|
-
durationDays: z.number().int().positive().max(3650).nullish(),
|
|
2776
|
-
maxRedemptions: z.number().int().positive().nullish(),
|
|
2777
|
-
startsAt: z.iso.datetime().nullish(),
|
|
2778
|
-
endsAt: z.iso.datetime().nullish(),
|
|
2779
|
-
note: z.string().max(500).nullish()
|
|
2781
|
+
version: z$1.number().int().positive().optional(),
|
|
2782
|
+
durationDays: z$1.number().int().positive().max(3650).nullish(),
|
|
2783
|
+
maxRedemptions: z$1.number().int().positive().nullish(),
|
|
2784
|
+
startsAt: z$1.iso.datetime().nullish(),
|
|
2785
|
+
endsAt: z$1.iso.datetime().nullish(),
|
|
2786
|
+
note: z$1.string().max(500).nullish()
|
|
2780
2787
|
});
|
|
2781
|
-
z.object({
|
|
2782
|
-
maxRedemptions: z.number().int().positive().nullish(),
|
|
2783
|
-
startsAt: z.iso.datetime().nullish(),
|
|
2784
|
-
endsAt: z.iso.datetime().nullish(),
|
|
2785
|
-
note: z.string().max(500).nullish(),
|
|
2788
|
+
z$1.object({
|
|
2789
|
+
maxRedemptions: z$1.number().int().positive().nullish(),
|
|
2790
|
+
startsAt: z$1.iso.datetime().nullish(),
|
|
2791
|
+
endsAt: z$1.iso.datetime().nullish(),
|
|
2792
|
+
note: z$1.string().max(500).nullish(),
|
|
2786
2793
|
/** Kill switch. Disabling stops new redemptions; live grants are untouched. */
|
|
2787
|
-
disabled: z.boolean().optional()
|
|
2794
|
+
disabled: z$1.boolean().optional()
|
|
2788
2795
|
});
|
|
2789
|
-
z.object({ code: z.string().min(1).max(40) });
|
|
2790
|
-
z.object({ family: planFamilySchema });
|
|
2791
|
-
z.object({
|
|
2792
|
-
sessionId: z.string().regex(/^skc_[0-9A-Za-z]+$/),
|
|
2796
|
+
z$1.object({ code: z$1.string().min(1).max(40) });
|
|
2797
|
+
z$1.object({ family: planFamilySchema });
|
|
2798
|
+
z$1.object({
|
|
2799
|
+
sessionId: z$1.string().regex(/^skc_[0-9A-Za-z]+$/),
|
|
2793
2800
|
/** SHA-256 hash (base64url) of the full session token string. */
|
|
2794
|
-
tokenHash: z.string().min(1).max(128),
|
|
2801
|
+
tokenHash: z$1.string().min(1).max(128),
|
|
2795
2802
|
/** Display-only, e.g. `miles@studio.local`. */
|
|
2796
|
-
deviceLabel: z.string().trim().min(1).max(120),
|
|
2803
|
+
deviceLabel: z$1.string().trim().min(1).max(120),
|
|
2797
2804
|
/** Display-only, e.g. `cli/0.4.2`. */
|
|
2798
|
-
client: z.string().trim().max(60).optional()
|
|
2805
|
+
client: z$1.string().trim().max(60).optional()
|
|
2799
2806
|
});
|
|
2800
|
-
z.object({ code: z.string().trim().min(1).max(32) });
|
|
2801
|
-
z.object({
|
|
2802
|
-
cursor: z.string().optional(),
|
|
2803
|
-
limit: z.coerce.number().int().min(1).max(200).default(50),
|
|
2804
|
-
action: z.string().optional(),
|
|
2805
|
-
resourceType: z.string().optional()
|
|
2807
|
+
z$1.object({ code: z$1.string().trim().min(1).max(32) });
|
|
2808
|
+
z$1.object({
|
|
2809
|
+
cursor: z$1.string().optional(),
|
|
2810
|
+
limit: z$1.coerce.number().int().min(1).max(200).default(50),
|
|
2811
|
+
action: z$1.string().optional(),
|
|
2812
|
+
resourceType: z$1.string().optional()
|
|
2806
2813
|
});
|
|
2807
|
-
z.enum([
|
|
2814
|
+
z$1.enum([
|
|
2808
2815
|
"generated",
|
|
2809
2816
|
"postgres",
|
|
2810
2817
|
"mysql",
|
|
2811
2818
|
"redis"
|
|
2812
2819
|
]);
|
|
2813
|
-
z.enum([
|
|
2820
|
+
z$1.enum([
|
|
2814
2821
|
"active",
|
|
2815
2822
|
"paused",
|
|
2816
2823
|
"failed"
|
|
2817
2824
|
]);
|
|
2818
2825
|
/** Statuses an admin may set directly (`failed` is only reached by the sweep). */
|
|
2819
|
-
const settableRotationStatusSchema = z.enum(["active", "paused"]);
|
|
2820
|
-
const rotationAlphabetSchema = z.enum([
|
|
2826
|
+
const settableRotationStatusSchema = z$1.enum(["active", "paused"]);
|
|
2827
|
+
const rotationAlphabetSchema = z$1.enum([
|
|
2821
2828
|
"alphanumeric",
|
|
2822
2829
|
"hex",
|
|
2823
2830
|
"base64url",
|
|
2824
2831
|
"printable"
|
|
2825
2832
|
]);
|
|
2826
|
-
const rotationIntervalSchema = z.number().int().min(300).max(31536e3);
|
|
2833
|
+
const rotationIntervalSchema = z$1.number().int().min(300).max(31536e3);
|
|
2827
2834
|
/**
|
|
2828
2835
|
* A database user name we are willing to *re-key*. Deliberately more permissive
|
|
2829
2836
|
* than the lease providers' name schemas — those name accounts seekrit creates,
|
|
@@ -2836,44 +2843,44 @@ const rotationIntervalSchema = z.number().int().min(300).max(31536e3);
|
|
|
2836
2843
|
* `;`, so there is no way out of the surrounding quoting, and no whitespace to
|
|
2837
2844
|
* split one Redis argument into two.
|
|
2838
2845
|
*/
|
|
2839
|
-
const rotationUsernameSchema = z.string().regex(/^[A-Za-z0-9_$.-]{1,63}$/, "must be 1–63 chars of letters, digits, underscore, dollar, dot or dash");
|
|
2846
|
+
const rotationUsernameSchema = z$1.string().regex(/^[A-Za-z0-9_$.-]{1,63}$/, "must be 1–63 chars of letters, digits, underscore, dollar, dot or dash");
|
|
2840
2847
|
/** A `{{name}}`/`{{host}}`/`{{verifier}}` templated statement or command line. */
|
|
2841
|
-
const statementSchema = z.string().min(1).max(4e3);
|
|
2842
|
-
const passwordLengthSchema = z.number().int().min(16).max(256);
|
|
2848
|
+
const statementSchema = z$1.string().min(1).max(4e3);
|
|
2849
|
+
const passwordLengthSchema = z$1.number().int().min(16).max(256);
|
|
2843
2850
|
/** A MySQL account host part (`'name'@'<host>'`) — no quotes/whitespace. */
|
|
2844
|
-
const mysqlHostSchema = z.string().regex(/^[A-Za-z0-9_.%:-]{1,255}$/, "must be a host pattern");
|
|
2845
|
-
const generatedRotationConfigSchema = z.object({
|
|
2846
|
-
kind: z.literal("generated"),
|
|
2851
|
+
const mysqlHostSchema = z$1.string().regex(/^[A-Za-z0-9_.%:-]{1,255}$/, "must be a host pattern");
|
|
2852
|
+
const generatedRotationConfigSchema = z$1.object({
|
|
2853
|
+
kind: z$1.literal("generated"),
|
|
2847
2854
|
length: passwordLengthSchema.optional(),
|
|
2848
2855
|
alphabet: rotationAlphabetSchema.optional()
|
|
2849
2856
|
});
|
|
2850
|
-
const postgresRotationConfigSchema = z.object({
|
|
2851
|
-
kind: z.literal("postgres"),
|
|
2857
|
+
const postgresRotationConfigSchema = z$1.object({
|
|
2858
|
+
kind: z$1.literal("postgres"),
|
|
2852
2859
|
username: rotationUsernameSchema,
|
|
2853
2860
|
passwordLength: passwordLengthSchema.optional(),
|
|
2854
|
-
statements: z.array(statementSchema).max(16).optional()
|
|
2861
|
+
statements: z$1.array(statementSchema).max(16).optional()
|
|
2855
2862
|
});
|
|
2856
|
-
const mysqlRotationConfigSchema = z.object({
|
|
2857
|
-
kind: z.literal("mysql"),
|
|
2863
|
+
const mysqlRotationConfigSchema = z$1.object({
|
|
2864
|
+
kind: z$1.literal("mysql"),
|
|
2858
2865
|
username: rotationUsernameSchema,
|
|
2859
2866
|
userHost: mysqlHostSchema.optional(),
|
|
2860
2867
|
passwordLength: passwordLengthSchema.optional(),
|
|
2861
|
-
statements: z.array(statementSchema).max(16).optional()
|
|
2868
|
+
statements: z$1.array(statementSchema).max(16).optional()
|
|
2862
2869
|
});
|
|
2863
|
-
const redisRotationConfigSchema = z.object({
|
|
2864
|
-
kind: z.literal("redis"),
|
|
2870
|
+
const redisRotationConfigSchema = z$1.object({
|
|
2871
|
+
kind: z$1.literal("redis"),
|
|
2865
2872
|
username: rotationUsernameSchema,
|
|
2866
2873
|
passwordLength: passwordLengthSchema.optional(),
|
|
2867
|
-
statements: z.array(statementSchema).max(16).optional()
|
|
2874
|
+
statements: z$1.array(statementSchema).max(16).optional()
|
|
2868
2875
|
});
|
|
2869
|
-
const rotationConfigSchema = z.discriminatedUnion("kind", [
|
|
2876
|
+
const rotationConfigSchema = z$1.discriminatedUnion("kind", [
|
|
2870
2877
|
generatedRotationConfigSchema,
|
|
2871
2878
|
postgresRotationConfigSchema,
|
|
2872
2879
|
mysqlRotationConfigSchema,
|
|
2873
2880
|
redisRotationConfigSchema
|
|
2874
2881
|
]);
|
|
2875
|
-
z.object({
|
|
2876
|
-
environmentId: z.string().min(1),
|
|
2882
|
+
z$1.object({
|
|
2883
|
+
environmentId: z$1.string().min(1),
|
|
2877
2884
|
/** The secret whose value rotates. It must already exist. */
|
|
2878
2885
|
secretName: secretNameSchema,
|
|
2879
2886
|
config: rotationConfigSchema,
|
|
@@ -2882,13 +2889,13 @@ z.object({
|
|
|
2882
2889
|
* The registered lease target supplying the connection, executor mode, and
|
|
2883
2890
|
* wrapped admin credential. Required for every kind but `generated`.
|
|
2884
2891
|
*/
|
|
2885
|
-
targetId: z.string().min(1).optional(),
|
|
2892
|
+
targetId: z$1.string().min(1).optional(),
|
|
2886
2893
|
/** Environment DEK wrapped to the rotator public key (`wd1.` blob). */
|
|
2887
|
-
wrappedDek: z.string().min(1).optional(),
|
|
2894
|
+
wrappedDek: z$1.string().min(1).optional(),
|
|
2888
2895
|
/** Rotate once immediately instead of waiting for the first interval. */
|
|
2889
|
-
rotateNow: z.boolean().optional()
|
|
2896
|
+
rotateNow: z$1.boolean().optional()
|
|
2890
2897
|
});
|
|
2891
|
-
z.object({
|
|
2898
|
+
z$1.object({
|
|
2892
2899
|
intervalSeconds: rotationIntervalSchema.optional(),
|
|
2893
2900
|
config: rotationConfigSchema.optional(),
|
|
2894
2901
|
/**
|
|
@@ -2948,7 +2955,7 @@ const SYNC_PROVIDER_KINDS = [
|
|
|
2948
2955
|
"openai-vault",
|
|
2949
2956
|
"claude-managed-agents"
|
|
2950
2957
|
];
|
|
2951
|
-
z.enum(SYNC_PROVIDER_KINDS);
|
|
2958
|
+
z$1.enum(SYNC_PROVIDER_KINDS);
|
|
2952
2959
|
/**
|
|
2953
2960
|
* Vercel account scope. The API token itself is never here — it is wrapped to
|
|
2954
2961
|
* the connection's public key and stored as ciphertext.
|
|
@@ -2957,10 +2964,10 @@ z.enum(SYNC_PROVIDER_KINDS);
|
|
|
2957
2964
|
* tokens omit it. Vercel rejects team-owned project calls that lack it with a
|
|
2958
2965
|
* bare 403, so we pass it through as `?teamId=` on every request.
|
|
2959
2966
|
*/
|
|
2960
|
-
const vercelConnectionConfigSchema = z.object({
|
|
2961
|
-
provider: z.literal("vercel"),
|
|
2967
|
+
const vercelConnectionConfigSchema = z$1.object({
|
|
2968
|
+
provider: z$1.literal("vercel"),
|
|
2962
2969
|
/** Vercel Team id (`team_…`). Omit for a personal account. */
|
|
2963
|
-
teamId: z.string().trim().min(1).max(128).optional()
|
|
2970
|
+
teamId: z$1.string().trim().min(1).max(128).optional()
|
|
2964
2971
|
});
|
|
2965
2972
|
/**
|
|
2966
2973
|
* A Cloudflare account id — 32 lowercase hex characters, found in the sidebar
|
|
@@ -2971,7 +2978,7 @@ const vercelConnectionConfigSchema = z.object({
|
|
|
2971
2978
|
* hours later inside an alarm, with nobody watching. It does not catch pasting
|
|
2972
2979
|
* a *zone* id, which has the same shape — only the API can tell those apart.
|
|
2973
2980
|
*/
|
|
2974
|
-
const cloudflareAccountIdSchema = z.string().trim().regex(/^[0-9a-f]{32}$/, "must be a 32-character Cloudflare account ID (lowercase hex)");
|
|
2981
|
+
const cloudflareAccountIdSchema = z$1.string().trim().regex(/^[0-9a-f]{32}$/, "must be a 32-character Cloudflare account ID (lowercase hex)");
|
|
2975
2982
|
/**
|
|
2976
2983
|
* Cloudflare account scope, shared by all three Cloudflare providers. The API
|
|
2977
2984
|
* token is never here — it is wrapped to the connection's public key and
|
|
@@ -2982,16 +2989,16 @@ const cloudflareAccountIdSchema = z.string().trim().regex(/^[0-9a-f]{32}$/, "mus
|
|
|
2982
2989
|
* destinations, and fail in different ways. Splitting them keeps the
|
|
2983
2990
|
* exhaustiveness guard in `connectorFor` meaningful.
|
|
2984
2991
|
*/
|
|
2985
|
-
const cloudflareWorkersConnectionConfigSchema = z.object({
|
|
2986
|
-
provider: z.literal("cloudflare-workers"),
|
|
2992
|
+
const cloudflareWorkersConnectionConfigSchema = z$1.object({
|
|
2993
|
+
provider: z$1.literal("cloudflare-workers"),
|
|
2987
2994
|
accountId: cloudflareAccountIdSchema
|
|
2988
2995
|
});
|
|
2989
|
-
const cloudflarePagesConnectionConfigSchema = z.object({
|
|
2990
|
-
provider: z.literal("cloudflare-pages"),
|
|
2996
|
+
const cloudflarePagesConnectionConfigSchema = z$1.object({
|
|
2997
|
+
provider: z$1.literal("cloudflare-pages"),
|
|
2991
2998
|
accountId: cloudflareAccountIdSchema
|
|
2992
2999
|
});
|
|
2993
|
-
const cloudflareSecretsStoreConnectionConfigSchema = z.object({
|
|
2994
|
-
provider: z.literal("cloudflare-secrets-store"),
|
|
3000
|
+
const cloudflareSecretsStoreConnectionConfigSchema = z$1.object({
|
|
3001
|
+
provider: z$1.literal("cloudflare-secrets-store"),
|
|
2995
3002
|
accountId: cloudflareAccountIdSchema
|
|
2996
3003
|
});
|
|
2997
3004
|
/**
|
|
@@ -3000,7 +3007,7 @@ const cloudflareSecretsStoreConnectionConfigSchema = z.object({
|
|
|
3000
3007
|
* bare GraphQL "Problem processing request" hours later inside an alarm, with
|
|
3001
3008
|
* nobody watching.
|
|
3002
3009
|
*/
|
|
3003
|
-
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");
|
|
3010
|
+
const railwayIdSchema = z$1.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");
|
|
3004
3011
|
/**
|
|
3005
3012
|
* Which kind of Railway API token the connection holds. This is connection
|
|
3006
3013
|
* scope rather than a destination detail because it decides the *header* the
|
|
@@ -3024,9 +3031,9 @@ const RAILWAY_TOKEN_KINDS = ["account", "project"];
|
|
|
3024
3031
|
* a destination names its project outright, so the token plus the destination
|
|
3025
3032
|
* is the whole address.
|
|
3026
3033
|
*/
|
|
3027
|
-
const railwayConnectionConfigSchema = z.object({
|
|
3028
|
-
provider: z.literal("railway"),
|
|
3029
|
-
tokenKind: z.enum(RAILWAY_TOKEN_KINDS).default("account")
|
|
3034
|
+
const railwayConnectionConfigSchema = z$1.object({
|
|
3035
|
+
provider: z$1.literal("railway"),
|
|
3036
|
+
tokenKind: z$1.enum(RAILWAY_TOKEN_KINDS).default("account")
|
|
3030
3037
|
});
|
|
3031
3038
|
/**
|
|
3032
3039
|
* An AWS region id (`us-east-1`, `eu-central-1`, `us-gov-west-1`).
|
|
@@ -3036,7 +3043,7 @@ const railwayConnectionConfigSchema = z.object({
|
|
|
3036
3043
|
* would otherwise surface as a DNS failure inside an alarm with nobody
|
|
3037
3044
|
* watching — which is a much worse place to learn about it than this form.
|
|
3038
3045
|
*/
|
|
3039
|
-
const awsRegionSchema = z.string().trim().regex(/^[a-z]{2}(-[a-z]+)+-\d$/, "must be an AWS region ID, e.g. us-east-1");
|
|
3046
|
+
const awsRegionSchema = z$1.string().trim().regex(/^[a-z]{2}(-[a-z]+)+-\d$/, "must be an AWS region ID, e.g. us-east-1");
|
|
3040
3047
|
/**
|
|
3041
3048
|
* The IAM access key id seekrit signs with.
|
|
3042
3049
|
*
|
|
@@ -3051,7 +3058,7 @@ const awsRegionSchema = z.string().trim().regex(/^[a-z]{2}(-[a-z]+)+-\d$/, "must
|
|
|
3051
3058
|
* Long-lived IAM user keys only. `ASIA…` session credentials from STS expire
|
|
3052
3059
|
* within hours, and a sync connection has to keep working unattended.
|
|
3053
3060
|
*/
|
|
3054
|
-
const awsAccessKeyIdSchema = z.string().trim().regex(/^[A-Z0-9]{16,128}$/, "must be an AWS access key ID, e.g. AKIAIOSFODNN7EXAMPLE");
|
|
3061
|
+
const awsAccessKeyIdSchema = z$1.string().trim().regex(/^[A-Z0-9]{16,128}$/, "must be an AWS access key ID, e.g. AKIAIOSFODNN7EXAMPLE");
|
|
3055
3062
|
/**
|
|
3056
3063
|
* AWS account scope, shared by both AWS providers: which region to call and
|
|
3057
3064
|
* which key to sign with. There is no account id — every endpoint seekrit calls
|
|
@@ -3062,13 +3069,13 @@ const awsAccessKeyIdSchema = z.string().trim().regex(/^[A-Z0-9]{16,128}$/, "must
|
|
|
3062
3069
|
* the three Cloudflare kinds are separate: different APIs, different
|
|
3063
3070
|
* destinations, different IAM actions.
|
|
3064
3071
|
*/
|
|
3065
|
-
const awsSecretsManagerConnectionConfigSchema = z.object({
|
|
3066
|
-
provider: z.literal("aws-secrets-manager"),
|
|
3072
|
+
const awsSecretsManagerConnectionConfigSchema = z$1.object({
|
|
3073
|
+
provider: z$1.literal("aws-secrets-manager"),
|
|
3067
3074
|
region: awsRegionSchema,
|
|
3068
3075
|
accessKeyId: awsAccessKeyIdSchema
|
|
3069
3076
|
});
|
|
3070
|
-
const awsParameterStoreConnectionConfigSchema = z.object({
|
|
3071
|
-
provider: z.literal("aws-parameter-store"),
|
|
3077
|
+
const awsParameterStoreConnectionConfigSchema = z$1.object({
|
|
3078
|
+
provider: z$1.literal("aws-parameter-store"),
|
|
3072
3079
|
region: awsRegionSchema,
|
|
3073
3080
|
accessKeyId: awsAccessKeyIdSchema
|
|
3074
3081
|
});
|
|
@@ -3082,7 +3089,7 @@ const awsParameterStoreConnectionConfigSchema = z.object({
|
|
|
3082
3089
|
* nothing is stored. The connection's `name` is what tells an operator which Render
|
|
3083
3090
|
* workspace it belongs to.
|
|
3084
3091
|
*/
|
|
3085
|
-
const renderConnectionConfigSchema = z.object({ provider: z.literal("render") });
|
|
3092
|
+
const renderConnectionConfigSchema = z$1.object({ provider: z$1.literal("render") });
|
|
3086
3093
|
/**
|
|
3087
3094
|
* Fly.io account scope — empty, as Render's is.
|
|
3088
3095
|
*
|
|
@@ -3092,7 +3099,7 @@ const renderConnectionConfigSchema = z.object({ provider: z.literal("render") })
|
|
|
3092
3099
|
* is — Fly's two token shapes do take different auth schemes, but
|
|
3093
3100
|
* `flyAuthorization` in the connector reads which one from the token itself.
|
|
3094
3101
|
*/
|
|
3095
|
-
const flyConnectionConfigSchema = z.object({ provider: z.literal("fly") });
|
|
3102
|
+
const flyConnectionConfigSchema = z$1.object({ provider: z$1.literal("fly") });
|
|
3096
3103
|
/**
|
|
3097
3104
|
* A Northflank id — projects and secret groups are both slugs derived from the
|
|
3098
3105
|
* name they were created with (`default-project`, `example-secret-group`), and
|
|
@@ -3100,7 +3107,7 @@ const flyConnectionConfigSchema = z.object({ provider: z.literal("fly") });
|
|
|
3100
3107
|
* so the common slip — pasting the *display name*, spaces and all — fails here
|
|
3101
3108
|
* rather than as a bare 404 inside an alarm with nobody watching.
|
|
3102
3109
|
*/
|
|
3103
|
-
const northflankIdSchema = z.string().trim().min(3).max(100).regex(/^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$/, "must be a Northflank ID — the slug in the resource's URL, not its display name");
|
|
3110
|
+
const northflankIdSchema = z$1.string().trim().min(3).max(100).regex(/^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$/, "must be a Northflank ID — the slug in the resource's URL, not its display name");
|
|
3104
3111
|
/**
|
|
3105
3112
|
* Northflank account scope — deliberately empty.
|
|
3106
3113
|
*
|
|
@@ -3110,7 +3117,7 @@ const northflankIdSchema = z.string().trim().min(3).max(100).regex(/^[a-zA-Z0-9]
|
|
|
3110
3117
|
* Cloudflare does: the token plus the destination's project is the whole
|
|
3111
3118
|
* address. The kind exists so the discriminated union stays uniform.
|
|
3112
3119
|
*/
|
|
3113
|
-
const northflankConnectionConfigSchema = z.object({ provider: z.literal("northflank") });
|
|
3120
|
+
const northflankConnectionConfigSchema = z$1.object({ provider: z$1.literal("northflank") });
|
|
3114
3121
|
/**
|
|
3115
3122
|
* DigitalOcean account scope — deliberately empty, as Render's and Fly's are.
|
|
3116
3123
|
*
|
|
@@ -3120,7 +3127,7 @@ const northflankConnectionConfigSchema = z.object({ provider: z.literal("northfl
|
|
|
3120
3127
|
* disambiguate the way Vercel needs one: the token plus the destination's app
|
|
3121
3128
|
* id is the whole address.
|
|
3122
3129
|
*/
|
|
3123
|
-
const digitalOceanConnectionConfigSchema = z.object({ provider: z.literal("digitalocean") });
|
|
3130
|
+
const digitalOceanConnectionConfigSchema = z$1.object({ provider: z$1.literal("digitalocean") });
|
|
3124
3131
|
/**
|
|
3125
3132
|
* Heroku account scope — empty, as Fly's and Render's are.
|
|
3126
3133
|
*
|
|
@@ -3131,7 +3138,7 @@ const digitalOceanConnectionConfigSchema = z.object({ provider: z.literal("digit
|
|
|
3131
3138
|
* `/apps/{app_id_or_name}` against everything the token can see, team-owned or
|
|
3132
3139
|
* not.
|
|
3133
3140
|
*/
|
|
3134
|
-
const herokuConnectionConfigSchema = z.object({ provider: z.literal("heroku") });
|
|
3141
|
+
const herokuConnectionConfigSchema = z$1.object({ provider: z$1.literal("heroku") });
|
|
3135
3142
|
/**
|
|
3136
3143
|
* Netlify team scope — the one thing a Netlify token cannot tell us itself.
|
|
3137
3144
|
*
|
|
@@ -3146,10 +3153,10 @@ const herokuConnectionConfigSchema = z.object({ provider: z.literal("heroku") })
|
|
|
3146
3153
|
* can find without an API call — it is in the dashboard URL
|
|
3147
3154
|
* (`app.netlify.com/teams/<slug>`) and under Team settings → General.
|
|
3148
3155
|
*/
|
|
3149
|
-
const netlifyConnectionConfigSchema = z.object({
|
|
3150
|
-
provider: z.literal("netlify"),
|
|
3156
|
+
const netlifyConnectionConfigSchema = z$1.object({
|
|
3157
|
+
provider: z$1.literal("netlify"),
|
|
3151
3158
|
/** Netlify team slug (`acme`) or account id — `{account_id}` accepts either. */
|
|
3152
|
-
accountId: z.string().trim().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "must be a Netlify team slug or account ID — no slashes or spaces")
|
|
3159
|
+
accountId: z$1.string().trim().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "must be a Netlify team slug or account ID — no slashes or spaces")
|
|
3153
3160
|
});
|
|
3154
3161
|
/**
|
|
3155
3162
|
* Bunnyshell account scope — empty, as Fly's, Heroku's, and Northflank's are.
|
|
@@ -3163,7 +3170,7 @@ const netlifyConnectionConfigSchema = z.object({
|
|
|
3163
3170
|
* id is the whole address. The API offers an `organization` filter, but it
|
|
3164
3171
|
* narrows a listing — it is not part of an address.
|
|
3165
3172
|
*/
|
|
3166
|
-
const bunnyshellConnectionConfigSchema = z.object({ provider: z.literal("bunnyshell") });
|
|
3173
|
+
const bunnyshellConnectionConfigSchema = z$1.object({ provider: z$1.literal("bunnyshell") });
|
|
3167
3174
|
/**
|
|
3168
3175
|
* GitHub account scope — empty for github.com, which is the whole point.
|
|
3169
3176
|
*
|
|
@@ -3177,13 +3184,13 @@ const bunnyshellConnectionConfigSchema = z.object({ provider: z.literal("bunnysh
|
|
|
3177
3184
|
* with a different plan behind it). Set only for a self-hosted GHES appliance,
|
|
3178
3185
|
* where the REST API is at `https://<host>/api/v3`.
|
|
3179
3186
|
*/
|
|
3180
|
-
const githubActionsConnectionConfigSchema = z.object({
|
|
3181
|
-
provider: z.literal("github-actions"),
|
|
3187
|
+
const githubActionsConnectionConfigSchema = z$1.object({
|
|
3188
|
+
provider: z$1.literal("github-actions"),
|
|
3182
3189
|
/**
|
|
3183
3190
|
* GitHub Enterprise Server API root, e.g. `https://github.acme.com/api/v3`.
|
|
3184
3191
|
* Omit for github.com. Must be `https:` — this URL carries the token.
|
|
3185
3192
|
*/
|
|
3186
|
-
baseUrl: z.string().trim().max(300).refine((value) => {
|
|
3193
|
+
baseUrl: z$1.string().trim().max(300).refine((value) => {
|
|
3187
3194
|
let parsed;
|
|
3188
3195
|
try {
|
|
3189
3196
|
parsed = new URL(value);
|
|
@@ -3203,7 +3210,7 @@ const githubActionsConnectionConfigSchema = z.object({
|
|
|
3203
3210
|
* Manager URL is built from this string, and a typo would otherwise surface as
|
|
3204
3211
|
* a 403 from Google hours later inside an alarm, with nobody watching.
|
|
3205
3212
|
*/
|
|
3206
|
-
const gcpProjectSchema = z.string().trim().refine((value) => /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/.test(value) || /^\d{1,20}$/.test(value), "must be a Google Cloud project ID (e.g. acme-prod) or project number");
|
|
3213
|
+
const gcpProjectSchema = z$1.string().trim().refine((value) => /^[a-z][a-z0-9-]{4,28}[a-z0-9]$/.test(value) || /^\d{1,20}$/.test(value), "must be a Google Cloud project ID (e.g. acme-prod) or project number");
|
|
3207
3214
|
/**
|
|
3208
3215
|
* Google Cloud project scope — which project's Secret Manager to write.
|
|
3209
3216
|
*
|
|
@@ -3222,8 +3229,8 @@ const gcpProjectSchema = z.string().trim().refine((value) => /^[a-z][a-z0-9-]{4,
|
|
|
3222
3229
|
* regional secrets live behind. Data residency is expressed instead through the
|
|
3223
3230
|
* destination's user-managed replication.
|
|
3224
3231
|
*/
|
|
3225
|
-
const gcpSecretManagerConnectionConfigSchema = z.object({
|
|
3226
|
-
provider: z.literal("gcp-secret-manager"),
|
|
3232
|
+
const gcpSecretManagerConnectionConfigSchema = z$1.object({
|
|
3233
|
+
provider: z$1.literal("gcp-secret-manager"),
|
|
3227
3234
|
/** Project ID (`acme-prod`) or project number. */
|
|
3228
3235
|
projectId: gcpProjectSchema
|
|
3229
3236
|
});
|
|
@@ -3263,16 +3270,16 @@ const LANGGRAPH_PLATFORM_REGIONS = [
|
|
|
3263
3270
|
* Vercel's `teamId` sets — so it is passed through as `X-Tenant-Id` whenever
|
|
3264
3271
|
* it is present.
|
|
3265
3272
|
*/
|
|
3266
|
-
const langgraphPlatformConnectionConfigSchema = z.object({
|
|
3267
|
-
provider: z.literal("langgraph-platform"),
|
|
3273
|
+
const langgraphPlatformConnectionConfigSchema = z$1.object({
|
|
3274
|
+
provider: z$1.literal("langgraph-platform"),
|
|
3268
3275
|
/** Control-plane region. Omit for `us`. Mutually exclusive with `baseUrl`. */
|
|
3269
|
-
region: z.enum(LANGGRAPH_PLATFORM_REGIONS).optional(),
|
|
3276
|
+
region: z$1.enum(LANGGRAPH_PLATFORM_REGIONS).optional(),
|
|
3270
3277
|
/**
|
|
3271
3278
|
* Self-hosted LangSmith control-plane root, e.g.
|
|
3272
3279
|
* `https://langsmith.acme.com/api-host`. Omit for LangChain's own hosts.
|
|
3273
3280
|
* Must be `https:` — this URL carries the API key.
|
|
3274
3281
|
*/
|
|
3275
|
-
baseUrl: z.string().trim().max(300).refine((value) => {
|
|
3282
|
+
baseUrl: z$1.string().trim().max(300).refine((value) => {
|
|
3276
3283
|
let parsed;
|
|
3277
3284
|
try {
|
|
3278
3285
|
parsed = new URL(value);
|
|
@@ -3282,7 +3289,7 @@ const langgraphPlatformConnectionConfigSchema = z.object({
|
|
|
3282
3289
|
return parsed.protocol === "https:" && !parsed.username && !parsed.password;
|
|
3283
3290
|
}, "must be an https:// URL — the self-hosted control-plane root, e.g. https://langsmith.acme.com/api-host").optional(),
|
|
3284
3291
|
/** LangSmith workspace (tenant) UUID, sent as `X-Tenant-Id`. */
|
|
3285
|
-
tenantId: 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 LangSmith workspace UUID").optional()
|
|
3292
|
+
tenantId: z$1.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 LangSmith workspace UUID").optional()
|
|
3286
3293
|
}).refine((c) => !(c.baseUrl !== void 0 && c.region !== void 0), {
|
|
3287
3294
|
message: "set region for a LangChain-hosted account or baseUrl for a self-hosted one, not both",
|
|
3288
3295
|
path: ["baseUrl"]
|
|
@@ -3309,7 +3316,7 @@ const AZURE_CLOUDS = [
|
|
|
3309
3316
|
* keeps one rule — a tenant's GUID is on the same admin-center page as the
|
|
3310
3317
|
* client id it pairs with, so nothing is harder to find.
|
|
3311
3318
|
*/
|
|
3312
|
-
const azureGuidSchema = 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 GUID, as the Entra admin center shows it");
|
|
3319
|
+
const azureGuidSchema = z$1.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 GUID, as the Entra admin center shows it");
|
|
3313
3320
|
/**
|
|
3314
3321
|
* Azure account scope: which directory to authenticate against, as which app.
|
|
3315
3322
|
*
|
|
@@ -3326,14 +3333,14 @@ const azureGuidSchema = z.string().trim().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-
|
|
|
3326
3333
|
* has nowhere to keep, and Entra caps a client secret at 24 months, which is a
|
|
3327
3334
|
* rotation the connection's `lastError` will make loud.
|
|
3328
3335
|
*/
|
|
3329
|
-
const azureKeyVaultConnectionConfigSchema = z.object({
|
|
3330
|
-
provider: z.literal("azure-key-vault"),
|
|
3336
|
+
const azureKeyVaultConnectionConfigSchema = z$1.object({
|
|
3337
|
+
provider: z$1.literal("azure-key-vault"),
|
|
3331
3338
|
/** Entra directory (tenant) ID. */
|
|
3332
3339
|
tenantId: azureGuidSchema,
|
|
3333
3340
|
/** Application (client) ID of the service principal seekrit signs in as. */
|
|
3334
3341
|
clientId: azureGuidSchema,
|
|
3335
3342
|
/** Which Azure cloud the tenant and its vaults live in. */
|
|
3336
|
-
cloud: z.enum(AZURE_CLOUDS).default("public")
|
|
3343
|
+
cloud: z$1.enum(AZURE_CLOUDS).default("public")
|
|
3337
3344
|
});
|
|
3338
3345
|
/**
|
|
3339
3346
|
* Hugging Face account scope — empty, as Render's and Fly's are.
|
|
@@ -3349,9 +3356,9 @@ const azureKeyVaultConnectionConfigSchema = z.object({
|
|
|
3349
3356
|
* not something a mirror is expected to accept a write on. Adding the field
|
|
3350
3357
|
* would invite pointing a connection at a host that silently swallows secrets.
|
|
3351
3358
|
*/
|
|
3352
|
-
const huggingfaceSpacesConnectionConfigSchema = z.object({ provider: z.literal("huggingface-spaces") });
|
|
3359
|
+
const huggingfaceSpacesConnectionConfigSchema = z$1.object({ provider: z$1.literal("huggingface-spaces") });
|
|
3353
3360
|
/** The wrapped credential is an OpenAI project API key with vault read/write access. */
|
|
3354
|
-
const openaiVaultConnectionConfigSchema = z.object({ provider: z.literal("openai-vault") });
|
|
3361
|
+
const openaiVaultConnectionConfigSchema = z$1.object({ provider: z$1.literal("openai-vault") });
|
|
3355
3362
|
/**
|
|
3356
3363
|
* Claude Managed Agents account scope — empty, for Hugging Face's reason.
|
|
3357
3364
|
*
|
|
@@ -3368,8 +3375,8 @@ const openaiVaultConnectionConfigSchema = z.object({ provider: z.literal("openai
|
|
|
3368
3375
|
* credential shape and a different connector (one that would reuse
|
|
3369
3376
|
* `lib/leases/aws-sigv4.ts`), not a different host for this one.
|
|
3370
3377
|
*/
|
|
3371
|
-
const claudeManagedAgentsConnectionConfigSchema = z.object({ provider: z.literal("claude-managed-agents") });
|
|
3372
|
-
const syncConnectionConfigSchema = z.discriminatedUnion("provider", [
|
|
3378
|
+
const claudeManagedAgentsConnectionConfigSchema = z$1.object({ provider: z$1.literal("claude-managed-agents") });
|
|
3379
|
+
const syncConnectionConfigSchema = z$1.discriminatedUnion("provider", [
|
|
3373
3380
|
vercelConnectionConfigSchema,
|
|
3374
3381
|
cloudflareWorkersConnectionConfigSchema,
|
|
3375
3382
|
cloudflarePagesConnectionConfigSchema,
|
|
@@ -3398,17 +3405,17 @@ const VERCEL_TARGETS = [
|
|
|
3398
3405
|
"preview",
|
|
3399
3406
|
"development"
|
|
3400
3407
|
];
|
|
3401
|
-
const vercelDestinationSchema = z.object({
|
|
3402
|
-
provider: z.literal("vercel"),
|
|
3408
|
+
const vercelDestinationSchema = z$1.object({
|
|
3409
|
+
provider: z$1.literal("vercel"),
|
|
3403
3410
|
/** Vercel project id (`prj_…`) or project name. */
|
|
3404
|
-
projectId: z.string().trim().min(1).max(128),
|
|
3411
|
+
projectId: z$1.string().trim().min(1).max(128),
|
|
3405
3412
|
/** Which deployment targets receive these values. At least one. */
|
|
3406
|
-
targets: z.array(z.enum(VERCEL_TARGETS)).min(1),
|
|
3413
|
+
targets: z$1.array(z$1.enum(VERCEL_TARGETS)).min(1),
|
|
3407
3414
|
/**
|
|
3408
3415
|
* Restrict `preview` writes to one git branch. Vercel only honors this when
|
|
3409
3416
|
* `targets` includes `preview`; ignored otherwise.
|
|
3410
3417
|
*/
|
|
3411
|
-
gitBranch: z.string().trim().min(1).max(255).optional()
|
|
3418
|
+
gitBranch: z$1.string().trim().min(1).max(255).optional()
|
|
3412
3419
|
});
|
|
3413
3420
|
/**
|
|
3414
3421
|
* The Worker whose secrets a binding owns. Wrangler *environments* are not a
|
|
@@ -3416,19 +3423,19 @@ const vercelDestinationSchema = z.object({
|
|
|
3416
3423
|
* `my-api` with `--env staging` creates a Worker literally named
|
|
3417
3424
|
* `my-api-staging`, so pointing at an environment means naming that script.
|
|
3418
3425
|
*/
|
|
3419
|
-
const cloudflareWorkersDestinationSchema = z.object({
|
|
3420
|
-
provider: z.literal("cloudflare-workers"),
|
|
3426
|
+
const cloudflareWorkersDestinationSchema = z$1.object({
|
|
3427
|
+
provider: z$1.literal("cloudflare-workers"),
|
|
3421
3428
|
/** Worker script name, as shown in the dashboard (`my-api`). */
|
|
3422
|
-
scriptName: z.string().trim().min(1).max(63).regex(/^[A-Za-z0-9_][A-Za-z0-9_-]*$/, "must be a Worker script name")
|
|
3429
|
+
scriptName: z$1.string().trim().min(1).max(63).regex(/^[A-Za-z0-9_][A-Za-z0-9_-]*$/, "must be a Worker script name")
|
|
3423
3430
|
});
|
|
3424
3431
|
/** The two deployment configs a Pages project keeps env vars under. */
|
|
3425
3432
|
const CLOUDFLARE_PAGES_ENVIRONMENTS = ["production", "preview"];
|
|
3426
|
-
const cloudflarePagesDestinationSchema = z.object({
|
|
3427
|
-
provider: z.literal("cloudflare-pages"),
|
|
3433
|
+
const cloudflarePagesDestinationSchema = z$1.object({
|
|
3434
|
+
provider: z$1.literal("cloudflare-pages"),
|
|
3428
3435
|
/** Pages project name (`my-site`) — Pages has no separate project id. */
|
|
3429
|
-
projectName: z.string().trim().min(1).max(58).regex(/^[A-Za-z0-9][A-Za-z0-9-]*$/, "must be a Pages project name"),
|
|
3436
|
+
projectName: z$1.string().trim().min(1).max(58).regex(/^[A-Za-z0-9][A-Za-z0-9-]*$/, "must be a Pages project name"),
|
|
3430
3437
|
/** Which deployment configs receive these values. At least one. */
|
|
3431
|
-
environments: z.array(z.enum(CLOUDFLARE_PAGES_ENVIRONMENTS)).min(1)
|
|
3438
|
+
environments: z$1.array(z$1.enum(CLOUDFLARE_PAGES_ENVIRONMENTS)).min(1)
|
|
3432
3439
|
});
|
|
3433
3440
|
/**
|
|
3434
3441
|
* Scopes a Secrets Store secret may be used by. Cloudflare requires at least
|
|
@@ -3442,12 +3449,12 @@ const CLOUDFLARE_SECRETS_STORE_SCOPES = [
|
|
|
3442
3449
|
"containers",
|
|
3443
3450
|
"websearch"
|
|
3444
3451
|
];
|
|
3445
|
-
const cloudflareSecretsStoreDestinationSchema = z.object({
|
|
3446
|
-
provider: z.literal("cloudflare-secrets-store"),
|
|
3452
|
+
const cloudflareSecretsStoreDestinationSchema = z$1.object({
|
|
3453
|
+
provider: z$1.literal("cloudflare-secrets-store"),
|
|
3447
3454
|
/** Store id (32 hex). An account has exactly one store today. */
|
|
3448
|
-
storeId: z.string().trim().regex(/^[0-9a-f]{32}$/, "must be a 32-character Secrets Store ID (lowercase hex)"),
|
|
3455
|
+
storeId: z$1.string().trim().regex(/^[0-9a-f]{32}$/, "must be a 32-character Secrets Store ID (lowercase hex)"),
|
|
3449
3456
|
/** Scopes applied to secrets this binding creates. At least one. */
|
|
3450
|
-
scopes: z.array(z.enum(CLOUDFLARE_SECRETS_STORE_SCOPES)).min(1)
|
|
3457
|
+
scopes: z$1.array(z$1.enum(CLOUDFLARE_SECRETS_STORE_SCOPES)).min(1)
|
|
3451
3458
|
});
|
|
3452
3459
|
/**
|
|
3453
3460
|
* Where inside Railway a binding writes.
|
|
@@ -3462,8 +3469,8 @@ const cloudflareSecretsStoreDestinationSchema = z.object({
|
|
|
3462
3469
|
* genuinely different destination from any one service's variables, so it is an
|
|
3463
3470
|
* absent field rather than a sentinel.
|
|
3464
3471
|
*/
|
|
3465
|
-
const railwayDestinationSchema = z.object({
|
|
3466
|
-
provider: z.literal("railway"),
|
|
3472
|
+
const railwayDestinationSchema = z$1.object({
|
|
3473
|
+
provider: z$1.literal("railway"),
|
|
3467
3474
|
/** Railway project id (a UUID, from the project's Settings page or URL). */
|
|
3468
3475
|
projectId: railwayIdSchema,
|
|
3469
3476
|
/** Railway environment id (a UUID) — the deployment environment to write. */
|
|
@@ -3479,7 +3486,7 @@ const railwayDestinationSchema = z.object({
|
|
|
3479
3486
|
* gated behind a release process and a secrets push must not start one; the
|
|
3480
3487
|
* values then sit staged until the next deploy.
|
|
3481
3488
|
*/
|
|
3482
|
-
skipDeploys: z.boolean().optional()
|
|
3489
|
+
skipDeploys: z$1.boolean().optional()
|
|
3483
3490
|
});
|
|
3484
3491
|
/**
|
|
3485
3492
|
* A customer-managed KMS key to encrypt with, as a key id, ARN, or alias
|
|
@@ -3490,7 +3497,7 @@ const railwayDestinationSchema = z.object({
|
|
|
3490
3497
|
* cross-account ARNs, and rejecting a valid one here would be worse than
|
|
3491
3498
|
* letting KMS give its own (very clear) error.
|
|
3492
3499
|
*/
|
|
3493
|
-
const awsKmsKeyIdSchema = z.string().trim().min(1).max(2048);
|
|
3500
|
+
const awsKmsKeyIdSchema = z$1.string().trim().min(1).max(2048);
|
|
3494
3501
|
/**
|
|
3495
3502
|
* How a binding lays its secrets out in Secrets Manager.
|
|
3496
3503
|
*
|
|
@@ -3510,18 +3517,18 @@ const AWS_SECRETS_MANAGER_LAYOUTS = ["secret-per-name", "json-bundle"];
|
|
|
3510
3517
|
* *variable name* (`[A-Za-z0-9_]`, no slashes), while this produces a
|
|
3511
3518
|
* *namespace* — `prod/storefront/` — and slashes are the whole point of it.
|
|
3512
3519
|
*/
|
|
3513
|
-
const awsSecretsManagerDestinationSchema = z.object({
|
|
3514
|
-
provider: z.literal("aws-secrets-manager"),
|
|
3515
|
-
layout: z.enum(AWS_SECRETS_MANAGER_LAYOUTS).default("secret-per-name"),
|
|
3520
|
+
const awsSecretsManagerDestinationSchema = z$1.object({
|
|
3521
|
+
provider: z$1.literal("aws-secrets-manager"),
|
|
3522
|
+
layout: z$1.enum(AWS_SECRETS_MANAGER_LAYOUTS).default("secret-per-name"),
|
|
3516
3523
|
/**
|
|
3517
3524
|
* `secret-per-name` only: prepended to every secret's name, e.g.
|
|
3518
3525
|
* `prod/storefront/`. Optional, but strongly advised in an account that
|
|
3519
3526
|
* holds anything else — without it a binding writes at the root of a
|
|
3520
3527
|
* namespace it does not own.
|
|
3521
3528
|
*/
|
|
3522
|
-
pathPrefix: z.string().trim().max(400).regex(/^[A-Za-z0-9/_+=.@-]*$/, "may contain letters, digits, and / _ + = . @ -").optional(),
|
|
3529
|
+
pathPrefix: z$1.string().trim().max(400).regex(/^[A-Za-z0-9/_+=.@-]*$/, "may contain letters, digits, and / _ + = . @ -").optional(),
|
|
3523
3530
|
/** `json-bundle` only: the one secret that holds every value, e.g. `prod/storefront/env`. */
|
|
3524
|
-
secretName: z.string().trim().min(1).max(512).regex(/^[A-Za-z0-9/_+=.@-]+$/, "may contain letters, digits, and / _ + = . @ -").optional(),
|
|
3531
|
+
secretName: z$1.string().trim().min(1).max(512).regex(/^[A-Za-z0-9/_+=.@-]+$/, "may contain letters, digits, and / _ + = . @ -").optional(),
|
|
3525
3532
|
kmsKeyId: awsKmsKeyIdSchema.optional()
|
|
3526
3533
|
}).refine((d) => d.layout !== "json-bundle" || d.secretName !== void 0, {
|
|
3527
3534
|
message: "a json-bundle destination needs the name of the secret to write",
|
|
@@ -3544,17 +3551,17 @@ const AWS_PARAMETER_TIERS = [
|
|
|
3544
3551
|
* and trailing slashes are required so the binding's names concatenate
|
|
3545
3552
|
* unambiguously — `/prod/storefront/` + `DB_URL`.
|
|
3546
3553
|
*/
|
|
3547
|
-
const awsParameterStoreDestinationSchema = z.object({
|
|
3548
|
-
provider: z.literal("aws-parameter-store"),
|
|
3554
|
+
const awsParameterStoreDestinationSchema = z$1.object({
|
|
3555
|
+
provider: z$1.literal("aws-parameter-store"),
|
|
3549
3556
|
/** Must start and end with `/`. `aws`/`ssm` are reserved by AWS as the first segment. */
|
|
3550
|
-
path: z.string().trim().max(1011).regex(/^\/([A-Za-z0-9_.-]+\/)*$/, "must be a parameter path like /prod/storefront/"),
|
|
3551
|
-
type: z.enum(AWS_PARAMETER_TYPES).default("SecureString"),
|
|
3557
|
+
path: z$1.string().trim().max(1011).regex(/^\/([A-Za-z0-9_.-]+\/)*$/, "must be a parameter path like /prod/storefront/"),
|
|
3558
|
+
type: z$1.enum(AWS_PARAMETER_TYPES).default("SecureString"),
|
|
3552
3559
|
/**
|
|
3553
3560
|
* Standard caps a value at 4KB and costs nothing; Advanced raises that to 8KB
|
|
3554
3561
|
* and is billed per parameter per month. `Intelligent-Tiering` lets AWS pick,
|
|
3555
3562
|
* upgrading only the parameters that need it.
|
|
3556
3563
|
*/
|
|
3557
|
-
tier: z.enum(AWS_PARAMETER_TIERS).default("Standard"),
|
|
3564
|
+
tier: z$1.enum(AWS_PARAMETER_TIERS).default("Standard"),
|
|
3558
3565
|
kmsKeyId: awsKmsKeyIdSchema.optional()
|
|
3559
3566
|
});
|
|
3560
3567
|
/**
|
|
@@ -3569,12 +3576,12 @@ const awsParameterStoreDestinationSchema = z.object({
|
|
|
3569
3576
|
* the day Render introduces a new resource prefix, which is not our call to
|
|
3570
3577
|
* make.
|
|
3571
3578
|
*/
|
|
3572
|
-
const renderServiceIdSchema = z.string().trim().regex(/^(?!evg-)[A-Za-z0-9_-]{1,64}$/, "must be a Render service ID (`srv-…` or `crn-…`), not an environment group");
|
|
3573
|
-
const renderEnvGroupIdSchema = z.string().trim().regex(/^(?!srv-|crn-)[A-Za-z0-9_-]{1,64}$/, "must be a Render environment group ID (`evg-…`), not a service");
|
|
3579
|
+
const renderServiceIdSchema = z$1.string().trim().regex(/^(?!evg-)[A-Za-z0-9_-]{1,64}$/, "must be a Render service ID (`srv-…` or `crn-…`), not an environment group");
|
|
3580
|
+
const renderEnvGroupIdSchema = z$1.string().trim().regex(/^(?!srv-|crn-)[A-Za-z0-9_-]{1,64}$/, "must be a Render environment group ID (`evg-…`), not a service");
|
|
3574
3581
|
/** Environment variables set directly on one service. */
|
|
3575
|
-
const renderServiceDestinationSchema = z.object({
|
|
3576
|
-
provider: z.literal("render"),
|
|
3577
|
-
kind: z.literal("service"),
|
|
3582
|
+
const renderServiceDestinationSchema = z$1.object({
|
|
3583
|
+
provider: z$1.literal("render"),
|
|
3584
|
+
kind: z$1.literal("service"),
|
|
3578
3585
|
/** Service id (`srv-…`, or `crn-…` for a cron job), from its dashboard URL. */
|
|
3579
3586
|
serviceId: renderServiceIdSchema
|
|
3580
3587
|
});
|
|
@@ -3584,13 +3591,13 @@ const renderServiceDestinationSchema = z.object({
|
|
|
3584
3591
|
* worth thinking about twice: its blast radius is the link list, not one
|
|
3585
3592
|
* service.
|
|
3586
3593
|
*/
|
|
3587
|
-
const renderEnvGroupDestinationSchema = z.object({
|
|
3588
|
-
provider: z.literal("render"),
|
|
3589
|
-
kind: z.literal("env-group"),
|
|
3594
|
+
const renderEnvGroupDestinationSchema = z$1.object({
|
|
3595
|
+
provider: z$1.literal("render"),
|
|
3596
|
+
kind: z$1.literal("env-group"),
|
|
3590
3597
|
/** Environment group id (`evg-…`), from its dashboard URL. */
|
|
3591
3598
|
envGroupId: renderEnvGroupIdSchema
|
|
3592
3599
|
});
|
|
3593
|
-
const renderDestinationSchema = z.discriminatedUnion("kind", [renderServiceDestinationSchema, renderEnvGroupDestinationSchema]);
|
|
3600
|
+
const renderDestinationSchema = z$1.discriminatedUnion("kind", [renderServiceDestinationSchema, renderEnvGroupDestinationSchema]);
|
|
3594
3601
|
/**
|
|
3595
3602
|
* The Fly app whose secret set a binding owns.
|
|
3596
3603
|
*
|
|
@@ -3607,10 +3614,10 @@ const renderDestinationSchema = z.discriminatedUnion("kind", [renderServiceDesti
|
|
|
3607
3614
|
* after the push get them straight away. The connector deliberately restarts
|
|
3608
3615
|
* nothing — see the note in `apps/api/src/lib/sync/connectors/fly.ts`.
|
|
3609
3616
|
*/
|
|
3610
|
-
const flyDestinationSchema = z.object({
|
|
3611
|
-
provider: z.literal("fly"),
|
|
3617
|
+
const flyDestinationSchema = z$1.object({
|
|
3618
|
+
provider: z$1.literal("fly"),
|
|
3612
3619
|
/** Fly app name, as `fly apps list` prints it. */
|
|
3613
|
-
appName: z.string().trim().min(1).max(63).regex(/^[a-z0-9][a-z0-9-]*$/, "must be a Fly app name (lowercase letters, numbers, and dashes)")
|
|
3620
|
+
appName: z$1.string().trim().min(1).max(63).regex(/^[a-z0-9][a-z0-9-]*$/, "must be a Fly app name (lowercase letters, numbers, and dashes)")
|
|
3614
3621
|
});
|
|
3615
3622
|
/**
|
|
3616
3623
|
* Where inside Northflank a binding writes: one **secret group** in one
|
|
@@ -3627,8 +3634,8 @@ const flyDestinationSchema = z.object({
|
|
|
3627
3634
|
* separate environments are separate projects (or separate groups restricted to
|
|
3628
3635
|
* a stage), so the binding's seekrit environment maps to a group, one to one.
|
|
3629
3636
|
*/
|
|
3630
|
-
const northflankDestinationSchema = z.object({
|
|
3631
|
-
provider: z.literal("northflank"),
|
|
3637
|
+
const northflankDestinationSchema = z$1.object({
|
|
3638
|
+
provider: z$1.literal("northflank"),
|
|
3632
3639
|
/** Project id — the slug in the project URL (`default-project`). */
|
|
3633
3640
|
projectId: northflankIdSchema,
|
|
3634
3641
|
/** Secret group id — the slug in the group's URL (`example-secret-group`). */
|
|
@@ -3660,13 +3667,13 @@ const DIGITALOCEAN_ENV_SCOPES = [
|
|
|
3660
3667
|
* from a typo: pasting the app's *name* (`storefront`), which `GET /v2/apps/{id}`
|
|
3661
3668
|
* answers with a flat 404 hours later inside an alarm, with nobody watching.
|
|
3662
3669
|
*/
|
|
3663
|
-
const digitalOceanAppIdSchema = 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 DigitalOcean app ID (the UUID in the app's URL), not its name");
|
|
3670
|
+
const digitalOceanAppIdSchema = z$1.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 DigitalOcean app ID (the UUID in the app's URL), not its name");
|
|
3664
3671
|
/**
|
|
3665
3672
|
* A component name, matching App Platform's own pattern for one. Names are
|
|
3666
3673
|
* unique within an app, which is what makes a name — rather than an index into
|
|
3667
3674
|
* `services` — the stable way to address a component's variables.
|
|
3668
3675
|
*/
|
|
3669
|
-
const digitalOceanComponentNameSchema = z.string().trim().regex(/^[a-z][a-z0-9-]{0,30}[a-z0-9]$/, "must be an App Platform component name (lowercase letters, numbers, and dashes)");
|
|
3676
|
+
const digitalOceanComponentNameSchema = z$1.string().trim().regex(/^[a-z][a-z0-9-]{0,30}[a-z0-9]$/, "must be an App Platform component name (lowercase letters, numbers, and dashes)");
|
|
3670
3677
|
/**
|
|
3671
3678
|
* Where inside a DigitalOcean app a binding writes.
|
|
3672
3679
|
*
|
|
@@ -3693,27 +3700,27 @@ const digitalOceanComponentNameSchema = z.string().trim().regex(/^[a-z][a-z0-9-]
|
|
|
3693
3700
|
* about to write is already there — see
|
|
3694
3701
|
* `apps/api/src/lib/sync/connectors/digitalocean.ts`.
|
|
3695
3702
|
*/
|
|
3696
|
-
const digitalOceanAppDestinationSchema = z.object({
|
|
3697
|
-
provider: z.literal("digitalocean"),
|
|
3698
|
-
kind: z.literal("app"),
|
|
3703
|
+
const digitalOceanAppDestinationSchema = z$1.object({
|
|
3704
|
+
provider: z$1.literal("digitalocean"),
|
|
3705
|
+
kind: z$1.literal("app"),
|
|
3699
3706
|
/** App id — the UUID in `cloud.digitalocean.com/apps/<id>`. */
|
|
3700
3707
|
appId: digitalOceanAppIdSchema,
|
|
3701
|
-
scope: z.enum(DIGITALOCEAN_ENV_SCOPES).default("RUN_TIME")
|
|
3708
|
+
scope: z$1.enum(DIGITALOCEAN_ENV_SCOPES).default("RUN_TIME")
|
|
3702
3709
|
});
|
|
3703
3710
|
/**
|
|
3704
3711
|
* One component's own environment variables. Narrower than the app-level list:
|
|
3705
3712
|
* only this service, worker, job, static site, or function sees them, and a key
|
|
3706
3713
|
* here wins over the same key at app level.
|
|
3707
3714
|
*/
|
|
3708
|
-
const digitalOceanComponentDestinationSchema = z.object({
|
|
3709
|
-
provider: z.literal("digitalocean"),
|
|
3710
|
-
kind: z.literal("component"),
|
|
3715
|
+
const digitalOceanComponentDestinationSchema = z$1.object({
|
|
3716
|
+
provider: z$1.literal("digitalocean"),
|
|
3717
|
+
kind: z$1.literal("component"),
|
|
3711
3718
|
appId: digitalOceanAppIdSchema,
|
|
3712
3719
|
/** Component name, as it appears in the app spec — not its type. */
|
|
3713
3720
|
componentName: digitalOceanComponentNameSchema,
|
|
3714
|
-
scope: z.enum(DIGITALOCEAN_ENV_SCOPES).default("RUN_TIME")
|
|
3721
|
+
scope: z$1.enum(DIGITALOCEAN_ENV_SCOPES).default("RUN_TIME")
|
|
3715
3722
|
});
|
|
3716
|
-
const digitalOceanDestinationSchema = z.discriminatedUnion("kind", [digitalOceanAppDestinationSchema, digitalOceanComponentDestinationSchema]);
|
|
3723
|
+
const digitalOceanDestinationSchema = z$1.discriminatedUnion("kind", [digitalOceanAppDestinationSchema, digitalOceanComponentDestinationSchema]);
|
|
3717
3724
|
/**
|
|
3718
3725
|
* A Heroku app, named the way `/apps/{app_id_or_name}` names one: either the
|
|
3719
3726
|
* app name or its UUID id. Both are accepted because both work, and the id is
|
|
@@ -3726,7 +3733,7 @@ const digitalOceanDestinationSchema = z.discriminatedUnion("kind", [digitalOcean
|
|
|
3726
3733
|
* capitals — into a message at the form rather than a bare 404 from an alarm
|
|
3727
3734
|
* with nobody watching.
|
|
3728
3735
|
*/
|
|
3729
|
-
const herokuAppSchema = z.string().trim().refine((value) => /^[a-z][a-z0-9-]{1,28}[a-z0-9]$/.test(value) || /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value), "must be a Heroku app name (lowercase letters, numbers, and dashes) or its UUID");
|
|
3736
|
+
const herokuAppSchema = z$1.string().trim().refine((value) => /^[a-z][a-z0-9-]{1,28}[a-z0-9]$/.test(value) || /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value), "must be a Heroku app name (lowercase letters, numbers, and dashes) or its UUID");
|
|
3730
3737
|
/**
|
|
3731
3738
|
* The Heroku app whose config vars a binding owns.
|
|
3732
3739
|
*
|
|
@@ -3740,8 +3747,8 @@ const herokuAppSchema = z.string().trim().refine((value) => /^[a-z][a-z0-9-]{1,2
|
|
|
3740
3747
|
* release and restarts the app's dynos, which is why a run sends exactly one
|
|
3741
3748
|
* request — see the note in `apps/api/src/lib/sync/connectors/heroku.ts`.
|
|
3742
3749
|
*/
|
|
3743
|
-
const herokuDestinationSchema = z.object({
|
|
3744
|
-
provider: z.literal("heroku"),
|
|
3750
|
+
const herokuDestinationSchema = z$1.object({
|
|
3751
|
+
provider: z$1.literal("heroku"),
|
|
3745
3752
|
/** App name as `heroku apps` prints it, or the app's UUID. */
|
|
3746
3753
|
app: herokuAppSchema
|
|
3747
3754
|
});
|
|
@@ -3779,7 +3786,7 @@ const NETLIFY_CONTEXTS = [
|
|
|
3779
3786
|
* by every site in it. Refusing anything but the UUID keeps a slip from turning
|
|
3780
3787
|
* into a much wider blast radius than the operator asked for.
|
|
3781
3788
|
*/
|
|
3782
|
-
const netlifySiteIdSchema = 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 the site's API ID (a UUID), not its name or URL");
|
|
3789
|
+
const netlifySiteIdSchema = z$1.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 the site's API ID (a UUID), not its name or URL");
|
|
3783
3790
|
/**
|
|
3784
3791
|
* The Netlify site, and which of its deploy contexts a binding owns.
|
|
3785
3792
|
*
|
|
@@ -3801,16 +3808,16 @@ const netlifySiteIdSchema = z.string().trim().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0
|
|
|
3801
3808
|
* existing variable, or removed from one ever — and it needs a plan that
|
|
3802
3809
|
* includes Secrets Controller.
|
|
3803
3810
|
*/
|
|
3804
|
-
const netlifyDestinationSchema = z.object({
|
|
3805
|
-
provider: z.literal("netlify"),
|
|
3811
|
+
const netlifyDestinationSchema = z$1.object({
|
|
3812
|
+
provider: z$1.literal("netlify"),
|
|
3806
3813
|
/** Site API ID (a UUID), from Project configuration → General. */
|
|
3807
3814
|
siteId: netlifySiteIdSchema,
|
|
3808
3815
|
/** Which deploy contexts receive these values. At least one. */
|
|
3809
|
-
contexts: z.array(z.enum(NETLIFY_CONTEXTS)).min(1),
|
|
3816
|
+
contexts: z$1.array(z$1.enum(NETLIFY_CONTEXTS)).min(1),
|
|
3810
3817
|
/** Branch name, required when `contexts` includes `branch`; ignored otherwise. */
|
|
3811
|
-
branch: z.string().trim().min(1).max(255).optional(),
|
|
3818
|
+
branch: z$1.string().trim().min(1).max(255).optional(),
|
|
3812
3819
|
/** Create variables as Netlify secrets (default true). */
|
|
3813
|
-
secret: z.boolean().optional()
|
|
3820
|
+
secret: z$1.boolean().optional()
|
|
3814
3821
|
}).refine((d) => !d.contexts.includes("branch") || d.branch !== void 0, {
|
|
3815
3822
|
message: "a branch context needs the branch name it applies to",
|
|
3816
3823
|
path: ["branch"]
|
|
@@ -3832,7 +3839,7 @@ const netlifyDestinationSchema = z.object({
|
|
|
3832
3839
|
* before it touches it, so a filter that failed to bite cannot turn into an
|
|
3833
3840
|
* edit of a neighbouring environment's variables.
|
|
3834
3841
|
*/
|
|
3835
|
-
const bunnyshellIdSchema = z.string().trim().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "must be a Bunnyshell ID — no slashes or spaces");
|
|
3842
|
+
const bunnyshellIdSchema = z$1.string().trim().min(1).max(64).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "must be a Bunnyshell ID — no slashes or spaces");
|
|
3836
3843
|
/**
|
|
3837
3844
|
* Variables on one Bunnyshell **environment** — the set every component in it
|
|
3838
3845
|
* inherits, and the closest match to a seekrit environment.
|
|
@@ -3840,13 +3847,13 @@ const bunnyshellIdSchema = z.string().trim().min(1).max(64).regex(/^[A-Za-z0-9][
|
|
|
3840
3847
|
* This is the destination for an environment that already exists and stays
|
|
3841
3848
|
* around: a primary environment, or a long-lived ephemeral one.
|
|
3842
3849
|
*/
|
|
3843
|
-
const bunnyshellEnvironmentDestinationSchema = z.object({
|
|
3844
|
-
provider: z.literal("bunnyshell"),
|
|
3845
|
-
kind: z.literal("environment"),
|
|
3850
|
+
const bunnyshellEnvironmentDestinationSchema = z$1.object({
|
|
3851
|
+
provider: z$1.literal("bunnyshell"),
|
|
3852
|
+
kind: z$1.literal("environment"),
|
|
3846
3853
|
/** Environment ID, as `bns environments list` prints it. */
|
|
3847
3854
|
environmentId: bunnyshellIdSchema,
|
|
3848
3855
|
/** Mark what seekrit creates as a Bunnyshell secret (default true). */
|
|
3849
|
-
secret: z.boolean().optional()
|
|
3856
|
+
secret: z$1.boolean().optional()
|
|
3850
3857
|
});
|
|
3851
3858
|
/**
|
|
3852
3859
|
* Variables on a Bunnyshell **project** — inherited by every environment
|
|
@@ -3863,13 +3870,13 @@ const bunnyshellEnvironmentDestinationSchema = z.object({
|
|
|
3863
3870
|
* overridden at its own scope — so a project binding does not fight an
|
|
3864
3871
|
* environment binding pointed at the same name, it loses to it.
|
|
3865
3872
|
*/
|
|
3866
|
-
const bunnyshellProjectDestinationSchema = z.object({
|
|
3867
|
-
provider: z.literal("bunnyshell"),
|
|
3868
|
-
kind: z.literal("project"),
|
|
3873
|
+
const bunnyshellProjectDestinationSchema = z$1.object({
|
|
3874
|
+
provider: z$1.literal("bunnyshell"),
|
|
3875
|
+
kind: z$1.literal("project"),
|
|
3869
3876
|
/** Project ID, as `bns projects list` prints it. */
|
|
3870
3877
|
projectId: bunnyshellIdSchema,
|
|
3871
3878
|
/** Mark what seekrit creates as a Bunnyshell secret (default true). */
|
|
3872
|
-
secret: z.boolean().optional()
|
|
3879
|
+
secret: z$1.boolean().optional()
|
|
3873
3880
|
});
|
|
3874
3881
|
/**
|
|
3875
3882
|
* Where in Bunnyshell a binding writes.
|
|
@@ -3889,7 +3896,7 @@ const bunnyshellProjectDestinationSchema = z.object({
|
|
|
3889
3896
|
* variables seekrit **creates**: an update never sends the flag, so a variable
|
|
3890
3897
|
* an operator deliberately un-secreted stays that way.
|
|
3891
3898
|
*/
|
|
3892
|
-
const bunnyshellDestinationSchema = z.discriminatedUnion("kind", [bunnyshellEnvironmentDestinationSchema, bunnyshellProjectDestinationSchema]);
|
|
3899
|
+
const bunnyshellDestinationSchema = z$1.discriminatedUnion("kind", [bunnyshellEnvironmentDestinationSchema, bunnyshellProjectDestinationSchema]);
|
|
3893
3900
|
/**
|
|
3894
3901
|
* Which repositories in an organization can read an org-level secret.
|
|
3895
3902
|
*
|
|
@@ -3912,14 +3919,14 @@ const GITHUB_ACTIONS_VISIBILITIES = [
|
|
|
3912
3919
|
* owner field — fails at the form rather than as a 404 from an alarm with nobody
|
|
3913
3920
|
* watching.
|
|
3914
3921
|
*/
|
|
3915
|
-
const githubOwnerSchema = z.string().trim().min(1).max(39).regex(/^[A-Za-z0-9](?:-?[A-Za-z0-9])*$/, "must be a GitHub user or organization login — not a URL or an owner/repo pair");
|
|
3922
|
+
const githubOwnerSchema = z$1.string().trim().min(1).max(39).regex(/^[A-Za-z0-9](?:-?[A-Za-z0-9])*$/, "must be a GitHub user or organization login — not a URL or an owner/repo pair");
|
|
3916
3923
|
/**
|
|
3917
3924
|
* A repository name. GitHub's rules are looser than an owner's: letters,
|
|
3918
3925
|
* numbers, hyphens, underscores, and periods, up to 100 characters. `.` and `..`
|
|
3919
3926
|
* are refused outright — they would traverse the API path rather than name a
|
|
3920
3927
|
* repository.
|
|
3921
3928
|
*/
|
|
3922
|
-
const githubRepoSchema = z.string().trim().min(1).max(100).regex(/^[A-Za-z0-9._-]+$/, "must be a repository name alone (`api`), not `owner/repo` or a URL").refine((value) => value !== "." && value !== "..", "is not a repository name");
|
|
3929
|
+
const githubRepoSchema = z$1.string().trim().min(1).max(100).regex(/^[A-Za-z0-9._-]+$/, "must be a repository name alone (`api`), not `owner/repo` or a URL").refine((value) => value !== "." && value !== "..", "is not a repository name");
|
|
3923
3930
|
/**
|
|
3924
3931
|
* A deployment environment name.
|
|
3925
3932
|
*
|
|
@@ -3928,7 +3935,7 @@ const githubRepoSchema = z.string().trim().min(1).max(100).regex(/^[A-Za-z0-9._-
|
|
|
3928
3935
|
* break the request are refused — an empty name, and the path separators that
|
|
3929
3936
|
* would let a name escape its URL segment. Everything else is GitHub's to reject.
|
|
3930
3937
|
*/
|
|
3931
|
-
const githubEnvironmentSchema = z.string().trim().min(1).max(255).refine((value) => !value.includes("/") && !value.includes("\\"), "cannot contain a slash — that is a path separator, not part of an environment name");
|
|
3938
|
+
const githubEnvironmentSchema = z$1.string().trim().min(1).max(255).refine((value) => !value.includes("/") && !value.includes("\\"), "cannot contain a slash — that is a path separator, not part of an environment name");
|
|
3932
3939
|
/**
|
|
3933
3940
|
* One repository's Actions secrets.
|
|
3934
3941
|
*
|
|
@@ -3937,9 +3944,9 @@ const githubEnvironmentSchema = z.string().trim().min(1).max(255).refine((value)
|
|
|
3937
3944
|
* choice this connector makes — but it is the reason `environment` exists below,
|
|
3938
3945
|
* and the reason to prefer it for anything that touches production.
|
|
3939
3946
|
*/
|
|
3940
|
-
const githubActionsRepoDestinationSchema = z.object({
|
|
3941
|
-
provider: z.literal("github-actions"),
|
|
3942
|
-
kind: z.literal("repo"),
|
|
3947
|
+
const githubActionsRepoDestinationSchema = z$1.object({
|
|
3948
|
+
provider: z$1.literal("github-actions"),
|
|
3949
|
+
kind: z$1.literal("repo"),
|
|
3943
3950
|
/** Repository owner — a user or organization login. */
|
|
3944
3951
|
owner: githubOwnerSchema,
|
|
3945
3952
|
/** Repository name, without the owner. */
|
|
@@ -3959,9 +3966,9 @@ const githubActionsRepoDestinationSchema = z.object({
|
|
|
3959
3966
|
* because a name was misspelled would quietly remove the protection the operator
|
|
3960
3967
|
* was relying on.
|
|
3961
3968
|
*/
|
|
3962
|
-
const githubActionsEnvironmentDestinationSchema = z.object({
|
|
3963
|
-
provider: z.literal("github-actions"),
|
|
3964
|
-
kind: z.literal("environment"),
|
|
3969
|
+
const githubActionsEnvironmentDestinationSchema = z$1.object({
|
|
3970
|
+
provider: z$1.literal("github-actions"),
|
|
3971
|
+
kind: z$1.literal("environment"),
|
|
3965
3972
|
owner: githubOwnerSchema,
|
|
3966
3973
|
repo: githubRepoSchema,
|
|
3967
3974
|
/** Environment name, exactly as the repository's Settings → Environments shows it. */
|
|
@@ -3981,15 +3988,15 @@ const githubActionsEnvironmentDestinationSchema = z.object({
|
|
|
3981
3988
|
* resolution would mean this connector picking which repository an ambiguous
|
|
3982
3989
|
* name meant, and getting that wrong widens a secret's reach silently.
|
|
3983
3990
|
*/
|
|
3984
|
-
const githubActionsOrgDestinationSchema = z.object({
|
|
3985
|
-
provider: z.literal("github-actions"),
|
|
3986
|
-
kind: z.literal("org"),
|
|
3991
|
+
const githubActionsOrgDestinationSchema = z$1.object({
|
|
3992
|
+
provider: z$1.literal("github-actions"),
|
|
3993
|
+
kind: z$1.literal("org"),
|
|
3987
3994
|
/** Organization login. */
|
|
3988
3995
|
org: githubOwnerSchema,
|
|
3989
3996
|
/** Which repositories may read these secrets. Stated, never defaulted. */
|
|
3990
|
-
visibility: z.enum(GITHUB_ACTIONS_VISIBILITIES),
|
|
3997
|
+
visibility: z$1.enum(GITHUB_ACTIONS_VISIBILITIES),
|
|
3991
3998
|
/** Numeric repository ids, required when `visibility` is `selected`. */
|
|
3992
|
-
selectedRepositoryIds: z.array(z.number().int().positive()).max(500).optional()
|
|
3999
|
+
selectedRepositoryIds: z$1.array(z$1.number().int().positive()).max(500).optional()
|
|
3993
4000
|
}).refine((dest) => dest.visibility !== "selected" || dest.selectedRepositoryIds !== void 0 && dest.selectedRepositoryIds.length > 0, {
|
|
3994
4001
|
message: "selected visibility needs at least one repository id",
|
|
3995
4002
|
path: ["selectedRepositoryIds"]
|
|
@@ -3997,7 +4004,7 @@ const githubActionsOrgDestinationSchema = z.object({
|
|
|
3997
4004
|
message: "repository ids only apply to selected visibility — remove them, or select it",
|
|
3998
4005
|
path: ["selectedRepositoryIds"]
|
|
3999
4006
|
});
|
|
4000
|
-
const githubActionsDestinationSchema = z.discriminatedUnion("kind", [
|
|
4007
|
+
const githubActionsDestinationSchema = z$1.discriminatedUnion("kind", [
|
|
4001
4008
|
githubActionsRepoDestinationSchema,
|
|
4002
4009
|
githubActionsEnvironmentDestinationSchema,
|
|
4003
4010
|
githubActionsOrgDestinationSchema
|
|
@@ -4036,14 +4043,14 @@ const GCP_REPLICATION_POLICIES = ["automatic", "user-managed"];
|
|
|
4036
4043
|
* Manager namespace is spelled `prod-storefront-DB_URL`, not
|
|
4037
4044
|
* `prod/storefront/DB_URL`.
|
|
4038
4045
|
*/
|
|
4039
|
-
const gcpSecretIdSchema = z.string().trim().min(1).max(255).regex(/^[A-Za-z0-9_-]+$/, "may contain letters, digits, hyphens, and underscores");
|
|
4046
|
+
const gcpSecretIdSchema = z$1.string().trim().min(1).max(255).regex(/^[A-Za-z0-9_-]+$/, "may contain letters, digits, hyphens, and underscores");
|
|
4040
4047
|
/**
|
|
4041
4048
|
* A GCP region for a user-managed replica (`us-east1`, `europe-west4`,
|
|
4042
4049
|
* `northamerica-northeast1`). Validated by shape rather than against a list,
|
|
4043
4050
|
* because Google adds regions faster than we ship — a name Secret Manager does
|
|
4044
4051
|
* not know is refused by Google with a clear message at creation.
|
|
4045
4052
|
*/
|
|
4046
|
-
const gcpLocationSchema = z.string().trim().regex(/^[a-z]+-[a-z]+\d+$/, "must be a GCP region ID, e.g. us-east1");
|
|
4053
|
+
const gcpLocationSchema = z$1.string().trim().regex(/^[a-z]+-[a-z]+\d+$/, "must be a GCP region ID, e.g. us-east1");
|
|
4047
4054
|
/**
|
|
4048
4055
|
* A Cloud KMS key, as its full resource name — the only form the API accepts:
|
|
4049
4056
|
* `projects/p/locations/l/keyRings/r/cryptoKeys/k`.
|
|
@@ -4053,7 +4060,7 @@ const gcpLocationSchema = z.string().trim().regex(/^[a-z]+-[a-z]+\d+$/, "must be
|
|
|
4053
4060
|
* rejected at creation: an automatic-replication secret needs a `global` key,
|
|
4054
4061
|
* and a user-managed replica needs one in its own region.
|
|
4055
4062
|
*/
|
|
4056
|
-
const gcpKmsKeyNameSchema = z.string().trim().max(1024).regex(/^projects\/[^/]+\/locations\/[^/]+\/keyRings\/[^/]+\/cryptoKeys\/[^/]+$/, "must be a full Cloud KMS key name (projects/…/locations/…/keyRings/…/cryptoKeys/…)");
|
|
4063
|
+
const gcpKmsKeyNameSchema = z$1.string().trim().max(1024).regex(/^projects\/[^/]+\/locations\/[^/]+\/keyRings\/[^/]+\/cryptoKeys\/[^/]+$/, "must be a full Cloud KMS key name (projects/…/locations/…/keyRings/…/cryptoKeys/…)");
|
|
4057
4064
|
/**
|
|
4058
4065
|
* Where inside a project's Secret Manager a binding writes.
|
|
4059
4066
|
*
|
|
@@ -4072,25 +4079,25 @@ const gcpKmsKeyNameSchema = z.string().trim().max(1024).regex(/^projects\/[^/]+\
|
|
|
4072
4079
|
* on, the version a push supersedes is destroyed as soon as the new one lands,
|
|
4073
4080
|
* so a secret keeps exactly one active version.
|
|
4074
4081
|
*/
|
|
4075
|
-
const gcpSecretManagerDestinationSchema = z.object({
|
|
4076
|
-
provider: z.literal("gcp-secret-manager"),
|
|
4077
|
-
layout: z.enum(GCP_SECRET_MANAGER_LAYOUTS).default("secret-per-name"),
|
|
4082
|
+
const gcpSecretManagerDestinationSchema = z$1.object({
|
|
4083
|
+
provider: z$1.literal("gcp-secret-manager"),
|
|
4084
|
+
layout: z$1.enum(GCP_SECRET_MANAGER_LAYOUTS).default("secret-per-name"),
|
|
4078
4085
|
/**
|
|
4079
4086
|
* `secret-per-name` only: prepended to every secret ID, e.g.
|
|
4080
4087
|
* `prod-storefront-`. Optional, but strongly advised in a project that holds
|
|
4081
4088
|
* anything else — without it a binding writes at the root of a namespace it
|
|
4082
4089
|
* does not own, and Secret Manager has no folders to hide behind.
|
|
4083
4090
|
*/
|
|
4084
|
-
idPrefix: z.string().trim().max(200).regex(/^[A-Za-z0-9_-]*$/, "may contain letters, digits, hyphens, and underscores").optional(),
|
|
4091
|
+
idPrefix: z$1.string().trim().max(200).regex(/^[A-Za-z0-9_-]*$/, "may contain letters, digits, hyphens, and underscores").optional(),
|
|
4085
4092
|
/** `json-bundle` only: the one secret that holds every value, e.g. `prod-storefront-env`. */
|
|
4086
4093
|
secretId: gcpSecretIdSchema.optional(),
|
|
4087
|
-
replication: z.enum(GCP_REPLICATION_POLICIES).default("automatic"),
|
|
4094
|
+
replication: z$1.enum(GCP_REPLICATION_POLICIES).default("automatic"),
|
|
4088
4095
|
/** `user-managed` only: the regions to replicate to. At least one. */
|
|
4089
|
-
locations: z.array(gcpLocationSchema).min(1).max(16).optional(),
|
|
4096
|
+
locations: z$1.array(gcpLocationSchema).min(1).max(16).optional(),
|
|
4090
4097
|
/** Customer-managed encryption key. Omitted means Google-managed keys. */
|
|
4091
4098
|
kmsKeyName: gcpKmsKeyNameSchema.optional(),
|
|
4092
4099
|
/** Destroy the version each push supersedes, keeping one active version. */
|
|
4093
|
-
pruneVersions: z.boolean().optional()
|
|
4100
|
+
pruneVersions: z$1.boolean().optional()
|
|
4094
4101
|
}).refine((d) => d.layout !== "json-bundle" || d.secretId !== void 0, {
|
|
4095
4102
|
message: "a json-bundle destination needs the ID of the secret to write",
|
|
4096
4103
|
path: ["secretId"]
|
|
@@ -4117,10 +4124,10 @@ const gcpSecretManagerDestinationSchema = z.object({
|
|
|
4117
4124
|
* the `id` from `GET /v2/deployments`, and the UUID in the deployment's
|
|
4118
4125
|
* dashboard URL.
|
|
4119
4126
|
*/
|
|
4120
|
-
const langgraphPlatformDestinationSchema = z.object({
|
|
4121
|
-
provider: z.literal("langgraph-platform"),
|
|
4127
|
+
const langgraphPlatformDestinationSchema = z$1.object({
|
|
4128
|
+
provider: z$1.literal("langgraph-platform"),
|
|
4122
4129
|
/** Deployment UUID, from the dashboard URL or `GET /v2/deployments`. */
|
|
4123
|
-
deploymentId: 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 LangGraph Platform deployment UUID")
|
|
4130
|
+
deploymentId: z$1.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 LangGraph Platform deployment UUID")
|
|
4124
4131
|
});
|
|
4125
4132
|
/**
|
|
4126
4133
|
* A Key Vault's name — the leftmost label of its DNS name, so the vault
|
|
@@ -4136,7 +4143,7 @@ const langgraphPlatformDestinationSchema = z.object({
|
|
|
4136
4143
|
* any host, and the whole address seekrit needs is this label plus the cloud
|
|
4137
4144
|
* already named on the connection.
|
|
4138
4145
|
*/
|
|
4139
|
-
const azureVaultNameSchema = z.string().trim().regex(/^[A-Za-z](?!.*--)[A-Za-z0-9-]{1,22}[A-Za-z0-9]$/, "must be a Key Vault name: 3–24 letters, digits, and single hyphens, starting with a letter");
|
|
4146
|
+
const azureVaultNameSchema = z$1.string().trim().regex(/^[A-Za-z](?!.*--)[A-Za-z0-9-]{1,22}[A-Za-z0-9]$/, "must be a Key Vault name: 3–24 letters, digits, and single hyphens, starting with a letter");
|
|
4140
4147
|
/**
|
|
4141
4148
|
* What to do with a name Key Vault cannot store.
|
|
4142
4149
|
*
|
|
@@ -4167,8 +4174,8 @@ const AZURE_KEY_VAULT_NAME_MODES = ["dash", "reject"];
|
|
|
4167
4174
|
* charges per *operation*, so fifty names cost the same stored fifty ways. One
|
|
4168
4175
|
* secret per name is simply correct here.
|
|
4169
4176
|
*/
|
|
4170
|
-
const azureKeyVaultDestinationSchema = z.object({
|
|
4171
|
-
provider: z.literal("azure-key-vault"),
|
|
4177
|
+
const azureKeyVaultDestinationSchema = z$1.object({
|
|
4178
|
+
provider: z$1.literal("azure-key-vault"),
|
|
4172
4179
|
/** Vault name, e.g. `acme-prod` for `acme-prod.vault.azure.net`. */
|
|
4173
4180
|
vault: azureVaultNameSchema,
|
|
4174
4181
|
/**
|
|
@@ -4177,9 +4184,9 @@ const azureKeyVaultDestinationSchema = z.object({
|
|
|
4177
4184
|
* accepts — so unlike Parameter Store's `path` this is a naming convention
|
|
4178
4185
|
* and nothing more. Worth setting in a vault that holds anything else.
|
|
4179
4186
|
*/
|
|
4180
|
-
prefix: z.string().trim().max(64).regex(/^[A-Za-z0-9-]*$/, "may contain letters, digits, and hyphens").optional(),
|
|
4187
|
+
prefix: z$1.string().trim().max(64).regex(/^[A-Za-z0-9-]*$/, "may contain letters, digits, and hyphens").optional(),
|
|
4181
4188
|
/** What to do with a name Key Vault cannot store — see {@link AZURE_KEY_VAULT_NAME_MODES}. */
|
|
4182
|
-
nameMode: z.enum(AZURE_KEY_VAULT_NAME_MODES).default("dash")
|
|
4189
|
+
nameMode: z$1.enum(AZURE_KEY_VAULT_NAME_MODES).default("dash")
|
|
4183
4190
|
});
|
|
4184
4191
|
/**
|
|
4185
4192
|
* The Space whose secrets a binding owns, addressed the way the Hub addresses
|
|
@@ -4198,7 +4205,7 @@ const azureKeyVaultDestinationSchema = z.object({
|
|
|
4198
4205
|
* leading character is held to alphanumeric, which is also what rejects the
|
|
4199
4206
|
* `.` and `..` that no repository may be called.
|
|
4200
4207
|
*/
|
|
4201
|
-
const huggingfaceRepoIdSchema = z.string().trim().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,95}\/[A-Za-z0-9][A-Za-z0-9._-]{0,95}$/, "must be a Space ID as owner/name, e.g. acme/support-demo — not a URL");
|
|
4208
|
+
const huggingfaceRepoIdSchema = z$1.string().trim().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,95}\/[A-Za-z0-9][A-Za-z0-9._-]{0,95}$/, "must be a Space ID as owner/name, e.g. acme/support-demo — not a URL");
|
|
4202
4209
|
/**
|
|
4203
4210
|
* One Hugging Face **Space**, whose secrets arrive in the app's container as
|
|
4204
4211
|
* environment variables.
|
|
@@ -4210,16 +4217,16 @@ const huggingfaceRepoIdSchema = z.string().trim().regex(/^[A-Za-z0-9][A-Za-z0-9.
|
|
|
4210
4217
|
* it, so this connector has no variables mode — a binding that wants one is
|
|
4211
4218
|
* asking for the wrong thing.
|
|
4212
4219
|
*/
|
|
4213
|
-
const huggingfaceSpacesDestinationSchema = z.object({
|
|
4214
|
-
provider: z.literal("huggingface-spaces"),
|
|
4220
|
+
const huggingfaceSpacesDestinationSchema = z$1.object({
|
|
4221
|
+
provider: z$1.literal("huggingface-spaces"),
|
|
4215
4222
|
/** Space ID as `owner/name`, e.g. `acme/support-demo`. */
|
|
4216
4223
|
repoId: huggingfaceRepoIdSchema
|
|
4217
4224
|
});
|
|
4218
4225
|
/** One existing OpenAI vault; every credential in this binding gets the same host boundary. */
|
|
4219
|
-
const openaiVaultDestinationSchema = z.object({
|
|
4220
|
-
provider: z.literal("openai-vault"),
|
|
4221
|
-
vaultId: z.string().trim().regex(/^vault_[A-Za-z0-9_-]+$/, "must be an OpenAI vault ID"),
|
|
4222
|
-
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")
|
|
4226
|
+
const openaiVaultDestinationSchema = z$1.object({
|
|
4227
|
+
provider: z$1.literal("openai-vault"),
|
|
4228
|
+
vaultId: z$1.string().trim().regex(/^vault_[A-Za-z0-9_-]+$/, "must be an OpenAI vault ID"),
|
|
4229
|
+
allowedHosts: z$1.array(z$1.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")
|
|
4223
4230
|
});
|
|
4224
4231
|
/**
|
|
4225
4232
|
* A vault ID as the Claude Console and API show it: `vlt_` and an opaque
|
|
@@ -4227,7 +4234,7 @@ const openaiVaultDestinationSchema = z.object({
|
|
|
4227
4234
|
* vault's display name — is a 404 from inside an alarm, which says nothing
|
|
4228
4235
|
* about which half of the address was wrong.
|
|
4229
4236
|
*/
|
|
4230
|
-
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");
|
|
4237
|
+
const claudeVaultIdSchema = z$1.string().trim().regex(/^vlt_[A-Za-z0-9]{1,128}$/, "must be a vault ID, e.g. vlt_01ABC… — not its display name");
|
|
4231
4238
|
/** Ceiling on per-name host rules; a vault itself holds at most 20 credentials. */
|
|
4232
4239
|
const CLAUDE_VAULT_MAX_HOST_RULES = 100;
|
|
4233
4240
|
/** A hostname of two or more labels whose last label starts with a letter. */
|
|
@@ -4244,14 +4251,14 @@ const CLAUDE_VAULT_IPV4 = /^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2
|
|
|
4244
4251
|
* `stripe.com` **but not `stripe.com` itself**, so a service reached at its apex
|
|
4245
4252
|
* needs both entries. A bare TLD wildcard (`*.com`) is refused by shape.
|
|
4246
4253
|
*/
|
|
4247
|
-
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");
|
|
4254
|
+
const claudeVaultHostSchema = z$1.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");
|
|
4248
4255
|
/**
|
|
4249
4256
|
* Where one secret's value may be substituted: a list of hosts, or the literal
|
|
4250
4257
|
* `"unrestricted"`, which lets it reach any host the agent's *environment*
|
|
4251
4258
|
* permits. The literal is spelled out rather than implied by an empty list, so
|
|
4252
4259
|
* the widest setting is never something a binding arrives at by omission.
|
|
4253
4260
|
*/
|
|
4254
|
-
const claudeVaultHostRuleSchema = z.union([z.literal("unrestricted"), z.array(claudeVaultHostSchema).min(1).max(16)]);
|
|
4261
|
+
const claudeVaultHostRuleSchema = z$1.union([z$1.literal("unrestricted"), z$1.array(claudeVaultHostSchema).min(1).max(16)]);
|
|
4255
4262
|
/**
|
|
4256
4263
|
* Which parts of an outbound request a secret's placeholder is swapped in. The
|
|
4257
4264
|
* API's own default when the field is omitted is both; seekrit's is `header`,
|
|
@@ -4284,15 +4291,15 @@ const CLAUDE_VAULT_INJECTION_LOCATIONS = [
|
|
|
4284
4291
|
* flow. Both levels apply beside the agent environment's own network policy,
|
|
4285
4292
|
* which must also let the host through; this shape cannot see that policy.
|
|
4286
4293
|
*/
|
|
4287
|
-
const claudeManagedAgentsDestinationSchema = z.object({
|
|
4288
|
-
provider: z.literal("claude-managed-agents"),
|
|
4294
|
+
const claudeManagedAgentsDestinationSchema = z$1.object({
|
|
4295
|
+
provider: z$1.literal("claude-managed-agents"),
|
|
4289
4296
|
/** The vault's ID, e.g. `vlt_01ABC…`. */
|
|
4290
4297
|
vaultId: claudeVaultIdSchema,
|
|
4291
4298
|
/** Hosts for every name without its own entry in `allowedHostsByName`. */
|
|
4292
4299
|
allowedHosts: claudeVaultHostRuleSchema.optional(),
|
|
4293
4300
|
/** Destination name → the hosts that one value may be substituted on. */
|
|
4294
|
-
allowedHostsByName: z.record(secretNameSchema, claudeVaultHostRuleSchema).optional(),
|
|
4295
|
-
injectionLocation: z.enum(CLAUDE_VAULT_INJECTION_LOCATIONS).default("header")
|
|
4301
|
+
allowedHostsByName: z$1.record(secretNameSchema, claudeVaultHostRuleSchema).optional(),
|
|
4302
|
+
injectionLocation: z$1.enum(CLAUDE_VAULT_INJECTION_LOCATIONS).default("header")
|
|
4296
4303
|
}).refine((d) => d.allowedHosts !== void 0 || Object.keys(d.allowedHostsByName ?? {}).length > 0, {
|
|
4297
4304
|
message: "name the hosts each secret may be used on — allowedHosts for all of them, allowedHostsByName per name, or \"unrestricted\"",
|
|
4298
4305
|
path: ["allowedHosts"]
|
|
@@ -4300,7 +4307,7 @@ const claudeManagedAgentsDestinationSchema = z.object({
|
|
|
4300
4307
|
message: `at most ${CLAUDE_VAULT_MAX_HOST_RULES} per-name host rules`,
|
|
4301
4308
|
path: ["allowedHostsByName"]
|
|
4302
4309
|
});
|
|
4303
|
-
const syncDestinationSchema = z.discriminatedUnion("provider", [
|
|
4310
|
+
const syncDestinationSchema = z$1.discriminatedUnion("provider", [
|
|
4304
4311
|
vercelDestinationSchema,
|
|
4305
4312
|
cloudflareWorkersDestinationSchema,
|
|
4306
4313
|
cloudflarePagesDestinationSchema,
|
|
@@ -4327,59 +4334,59 @@ const syncDestinationSchema = z.discriminatedUnion("provider", [
|
|
|
4327
4334
|
* How seekrit secret names become destination key names. Applied in order:
|
|
4328
4335
|
* explicit `rename` (wins outright), then `prefix`/`suffix`, then `case`.
|
|
4329
4336
|
*/
|
|
4330
|
-
const nameTransformSchema = z.object({
|
|
4331
|
-
prefix: z.string().max(64).regex(/^[A-Za-z0-9_]*$/, "must be alphanumeric or underscore").optional(),
|
|
4332
|
-
suffix: z.string().max(64).regex(/^[A-Za-z0-9_]*$/, "must be alphanumeric or underscore").optional(),
|
|
4333
|
-
case: z.enum([
|
|
4337
|
+
const nameTransformSchema = z$1.object({
|
|
4338
|
+
prefix: z$1.string().max(64).regex(/^[A-Za-z0-9_]*$/, "must be alphanumeric or underscore").optional(),
|
|
4339
|
+
suffix: z$1.string().max(64).regex(/^[A-Za-z0-9_]*$/, "must be alphanumeric or underscore").optional(),
|
|
4340
|
+
case: z$1.enum([
|
|
4334
4341
|
"preserve",
|
|
4335
4342
|
"upper",
|
|
4336
4343
|
"lower"
|
|
4337
4344
|
]).optional(),
|
|
4338
4345
|
/** Exact per-secret overrides, seekrit name → destination name. */
|
|
4339
|
-
rename: z.record(secretNameSchema, secretNameSchema).optional()
|
|
4346
|
+
rename: z$1.record(secretNameSchema, secretNameSchema).optional()
|
|
4340
4347
|
});
|
|
4341
|
-
z.object({
|
|
4348
|
+
z$1.object({
|
|
4342
4349
|
/**
|
|
4343
4350
|
* The id the client already fetched a public key for. Connections are a
|
|
4344
4351
|
* two-step dance — mint the key, wrap the credential to it, then create the
|
|
4345
4352
|
* row — so the id has to be chosen before the row exists. Omit it and the
|
|
4346
4353
|
* server generates one (only useful when there is no credential to wrap yet).
|
|
4347
4354
|
*/
|
|
4348
|
-
id: z.string().regex(/^syc_[A-Za-z0-9]{24}$/, "must be a connection id from the public-key call").optional(),
|
|
4349
|
-
name: z.string().trim().min(1).max(128),
|
|
4355
|
+
id: z$1.string().regex(/^syc_[A-Za-z0-9]{24}$/, "must be a connection id from the public-key call").optional(),
|
|
4356
|
+
name: z$1.string().trim().min(1).max(128),
|
|
4350
4357
|
config: syncConnectionConfigSchema,
|
|
4351
4358
|
/**
|
|
4352
4359
|
* The destination's API credential (a Vercel token), encrypted client-side to
|
|
4353
4360
|
* the connection's public key (a `wd1.` wrap). The control plane stores only
|
|
4354
4361
|
* this ciphertext; it is unwrapped transiently inside the sync engine DO.
|
|
4355
4362
|
*/
|
|
4356
|
-
wrappedCredential: z.string().min(1)
|
|
4363
|
+
wrappedCredential: z$1.string().min(1)
|
|
4357
4364
|
});
|
|
4358
|
-
z.object({ destination: syncDestinationSchema });
|
|
4359
|
-
const globListSchema = z.array(z.string().trim().min(1).max(256)).max(100);
|
|
4360
|
-
z.object({
|
|
4361
|
-
connectionId: z.string().min(1),
|
|
4362
|
-
environmentId: z.string().min(1),
|
|
4365
|
+
z$1.object({ destination: syncDestinationSchema });
|
|
4366
|
+
const globListSchema = z$1.array(z$1.string().trim().min(1).max(256)).max(100);
|
|
4367
|
+
z$1.object({
|
|
4368
|
+
connectionId: z$1.string().min(1),
|
|
4369
|
+
environmentId: z$1.string().min(1),
|
|
4363
4370
|
destination: syncDestinationSchema,
|
|
4364
4371
|
nameTransform: nameTransformSchema.optional(),
|
|
4365
4372
|
include: globListSchema.optional(),
|
|
4366
4373
|
exclude: globListSchema.optional(),
|
|
4367
|
-
onDelete: z.enum(["delete", "retain"]).default("delete"),
|
|
4368
|
-
mode: z.enum(["auto", "manual"]).default("auto"),
|
|
4369
|
-
wrappedDeks: z.array(z.object({
|
|
4370
|
-
environmentId: z.string().min(1),
|
|
4371
|
-
wrappedDek: z.string().min(1)
|
|
4374
|
+
onDelete: z$1.enum(["delete", "retain"]).default("delete"),
|
|
4375
|
+
mode: z$1.enum(["auto", "manual"]).default("auto"),
|
|
4376
|
+
wrappedDeks: z$1.array(z$1.object({
|
|
4377
|
+
environmentId: z$1.string().min(1),
|
|
4378
|
+
wrappedDek: z$1.string().min(1)
|
|
4372
4379
|
})).min(1),
|
|
4373
|
-
acknowledgedDecryption: z.literal(true)
|
|
4380
|
+
acknowledgedDecryption: z$1.literal(true)
|
|
4374
4381
|
});
|
|
4375
|
-
z.object({
|
|
4382
|
+
z$1.object({
|
|
4376
4383
|
destination: syncDestinationSchema.optional(),
|
|
4377
4384
|
nameTransform: nameTransformSchema.nullable().optional(),
|
|
4378
4385
|
include: globListSchema.nullable().optional(),
|
|
4379
4386
|
exclude: globListSchema.nullable().optional(),
|
|
4380
|
-
onDelete: z.enum(["delete", "retain"]).optional(),
|
|
4381
|
-
mode: z.enum(["auto", "manual"]).optional(),
|
|
4382
|
-
enabled: z.boolean().optional()
|
|
4387
|
+
onDelete: z$1.enum(["delete", "retain"]).optional(),
|
|
4388
|
+
mode: z$1.enum(["auto", "manual"]).optional(),
|
|
4389
|
+
enabled: z$1.boolean().optional()
|
|
4383
4390
|
});
|
|
4384
4391
|
// ../../packages/core/src/vault.ts
|
|
4385
4392
|
/** One encrypted credential within a connection. */
|
|
@@ -4411,73 +4418,73 @@ const VAULT_WEBHOOK_EVENTS = [
|
|
|
4411
4418
|
"connection.reauth_required",
|
|
4412
4419
|
"connection.revoked"
|
|
4413
4420
|
];
|
|
4414
|
-
const base64urlMember = z.string().regex(/^[A-Za-z0-9_-]{43}$/, "not a P-256 coordinate");
|
|
4415
|
-
const vaultPublicJwkSchema = z.object({
|
|
4416
|
-
kty: z.literal("EC"),
|
|
4417
|
-
crv: z.literal("P-256"),
|
|
4421
|
+
const base64urlMember = z$1.string().regex(/^[A-Za-z0-9_-]{43}$/, "not a P-256 coordinate");
|
|
4422
|
+
const vaultPublicJwkSchema = z$1.object({
|
|
4423
|
+
kty: z$1.literal("EC"),
|
|
4424
|
+
crv: z$1.literal("P-256"),
|
|
4418
4425
|
x: base64urlMember,
|
|
4419
4426
|
y: base64urlMember
|
|
4420
4427
|
});
|
|
4421
|
-
const isoInstant = z.iso.datetime({ offset: true });
|
|
4422
|
-
z.object({
|
|
4423
|
-
v: z.literal(1),
|
|
4424
|
-
accessToken: z.string().min(1).max(16384),
|
|
4425
|
-
tokenType: z.literal("Bearer"),
|
|
4428
|
+
const isoInstant = z$1.iso.datetime({ offset: true });
|
|
4429
|
+
z$1.object({
|
|
4430
|
+
v: z$1.literal(1),
|
|
4431
|
+
accessToken: z$1.string().min(1).max(16384),
|
|
4432
|
+
tokenType: z$1.literal("Bearer"),
|
|
4426
4433
|
accessTokenExpiresAt: isoInstant.optional(),
|
|
4427
|
-
refreshToken: z.string().min(1).max(16384).optional(),
|
|
4434
|
+
refreshToken: z$1.string().min(1).max(16384).optional(),
|
|
4428
4435
|
refreshTokenExpiresAt: isoInstant.optional(),
|
|
4429
|
-
scope: z.array(z.string().max(512)).max(128),
|
|
4436
|
+
scope: z$1.array(z$1.string().max(512)).max(128),
|
|
4430
4437
|
obtainedAt: isoInstant
|
|
4431
4438
|
});
|
|
4432
|
-
z.object({
|
|
4433
|
-
v: z.literal(1),
|
|
4434
|
-
key: z.string().min(1).max(8192)
|
|
4439
|
+
z$1.object({
|
|
4440
|
+
v: z$1.literal(1),
|
|
4441
|
+
key: z$1.string().min(1).max(8192)
|
|
4435
4442
|
});
|
|
4436
4443
|
/** RFC 6238 parameters, as an authenticator app would hold them. */
|
|
4437
|
-
const vaultTotpSchema = z.object({
|
|
4444
|
+
const vaultTotpSchema = z$1.object({
|
|
4438
4445
|
/** Base32 (RFC 4648), upper case, no padding, no separators. */
|
|
4439
|
-
secret: z.string().regex(/^[A-Z2-7]{16,128}$/, "TOTP secret must be base32"),
|
|
4440
|
-
digits: z.union([z.literal(6), z.literal(8)]),
|
|
4441
|
-
period: z.number().int().min(15).max(120),
|
|
4442
|
-
algorithm: z.enum([
|
|
4446
|
+
secret: z$1.string().regex(/^[A-Z2-7]{16,128}$/, "TOTP secret must be base32"),
|
|
4447
|
+
digits: z$1.union([z$1.literal(6), z$1.literal(8)]),
|
|
4448
|
+
period: z$1.number().int().min(15).max(120),
|
|
4449
|
+
algorithm: z$1.enum([
|
|
4443
4450
|
"SHA1",
|
|
4444
4451
|
"SHA256",
|
|
4445
4452
|
"SHA512"
|
|
4446
4453
|
])
|
|
4447
4454
|
});
|
|
4448
|
-
z.object({
|
|
4449
|
-
v: z.literal(1),
|
|
4450
|
-
username: z.string().min(1).max(512),
|
|
4451
|
-
password: z.string().min(1).max(4096),
|
|
4455
|
+
z$1.object({
|
|
4456
|
+
v: z$1.literal(1),
|
|
4457
|
+
username: z$1.string().min(1).max(512),
|
|
4458
|
+
password: z$1.string().min(1).max(4096),
|
|
4452
4459
|
totp: vaultTotpSchema.optional()
|
|
4453
4460
|
});
|
|
4454
|
-
const vaultSessionCookieSchema = z.object({
|
|
4455
|
-
name: z.string().min(1).max(4096),
|
|
4456
|
-
value: z.string().max(16384),
|
|
4461
|
+
const vaultSessionCookieSchema = z$1.object({
|
|
4462
|
+
name: z$1.string().min(1).max(4096),
|
|
4463
|
+
value: z$1.string().max(16384),
|
|
4457
4464
|
/** Kept exactly as captured — parent-domain cookies restore to their parent. */
|
|
4458
|
-
domain: z.string().min(1).max(253),
|
|
4459
|
-
path: z.string().min(1).max(4096),
|
|
4465
|
+
domain: z$1.string().min(1).max(253),
|
|
4466
|
+
path: z$1.string().min(1).max(4096),
|
|
4460
4467
|
/** Unix seconds; -1 ⇒ a session cookie. */
|
|
4461
|
-
expires: z.number(),
|
|
4462
|
-
httpOnly: z.boolean(),
|
|
4463
|
-
secure: z.boolean(),
|
|
4464
|
-
sameSite: z.enum([
|
|
4468
|
+
expires: z$1.number(),
|
|
4469
|
+
httpOnly: z$1.boolean(),
|
|
4470
|
+
secure: z$1.boolean(),
|
|
4471
|
+
sameSite: z$1.enum([
|
|
4465
4472
|
"Strict",
|
|
4466
4473
|
"Lax",
|
|
4467
4474
|
"None"
|
|
4468
4475
|
]).optional(),
|
|
4469
|
-
partitionKey: z.unknown().optional()
|
|
4476
|
+
partitionKey: z$1.unknown().optional()
|
|
4470
4477
|
});
|
|
4471
|
-
z.object({
|
|
4472
|
-
v: z.literal(1),
|
|
4473
|
-
siteId: z.string().min(1).max(64),
|
|
4478
|
+
z$1.object({
|
|
4479
|
+
v: z$1.literal(1),
|
|
4480
|
+
siteId: z$1.string().min(1).max(64),
|
|
4474
4481
|
capturedAt: isoInstant,
|
|
4475
|
-
userAgent: z.string().max(1024),
|
|
4476
|
-
cookies: z.array(vaultSessionCookieSchema).max(4096),
|
|
4482
|
+
userAgent: z$1.string().max(1024),
|
|
4483
|
+
cookies: z$1.array(vaultSessionCookieSchema).max(4096),
|
|
4477
4484
|
/** origin → key → value. */
|
|
4478
|
-
localStorage: z.record(z.string(), z.record(z.string(), z.string()))
|
|
4485
|
+
localStorage: z$1.record(z$1.string(), z$1.record(z$1.string(), z$1.string()))
|
|
4479
4486
|
});
|
|
4480
|
-
const vaultProviderIdSchema = z.string().trim().min(1).max(64).regex(/^[a-z0-9][a-z0-9._-]*$/, "provider ids are lowercase letters, digits, dots and dashes");
|
|
4487
|
+
const vaultProviderIdSchema = z$1.string().trim().min(1).max(64).regex(/^[a-z0-9][a-z0-9._-]*$/, "provider ids are lowercase letters, digits, dots and dashes");
|
|
4481
4488
|
/**
|
|
4482
4489
|
* What the dashboard, Connect and the SDK may know about a provider: its name,
|
|
4483
4490
|
* its method, and — for the consent screen — a plain sentence about what the
|
|
@@ -4485,90 +4492,90 @@ const vaultProviderIdSchema = z.string().trim().min(1).max(64).regex(/^[a-z0-9][
|
|
|
4485
4492
|
* those are compiled into the executor and the API must not be able to serve a
|
|
4486
4493
|
* different version of them.
|
|
4487
4494
|
*/
|
|
4488
|
-
const vaultCatalogProviderSchema = z.object({
|
|
4495
|
+
const vaultCatalogProviderSchema = z$1.object({
|
|
4489
4496
|
id: vaultProviderIdSchema,
|
|
4490
|
-
displayName: z.string().trim().min(1).max(80),
|
|
4491
|
-
method: z.enum(["oauth2", "api_key"]),
|
|
4497
|
+
displayName: z$1.string().trim().min(1).max(80),
|
|
4498
|
+
method: z$1.enum(["oauth2", "api_key"]),
|
|
4492
4499
|
/** The one sentence Connect shows: "read and send email as you". */
|
|
4493
|
-
consentSummary: z.string().trim().min(1).max(240),
|
|
4500
|
+
consentSummary: z$1.string().trim().min(1).max(240),
|
|
4494
4501
|
/** Hosts the executor will forward to, for display. */
|
|
4495
|
-
hosts: z.array(z.string().trim().min(1).max(253)).max(64).default([]),
|
|
4502
|
+
hosts: z$1.array(z$1.string().trim().min(1).max(253)).max(64).default([]),
|
|
4496
4503
|
/** Where the end user finds the key (api_key providers). */
|
|
4497
|
-
helpUrl: z.url().max(2048).optional(),
|
|
4504
|
+
helpUrl: z$1.url().max(2048).optional(),
|
|
4498
4505
|
/** Label for the key field (api_key providers), e.g. "API key". */
|
|
4499
|
-
keyLabel: z.string().trim().min(1).max(60).optional(),
|
|
4506
|
+
keyLabel: z$1.string().trim().min(1).max(60).optional(),
|
|
4500
4507
|
/** OAuth scopes the provider will be asked for (oauth2 providers), for display. */
|
|
4501
|
-
scopes: z.array(z.string().trim().min(1).max(512)).max(64).optional()
|
|
4508
|
+
scopes: z$1.array(z$1.string().trim().min(1).max(512)).max(64).optional()
|
|
4502
4509
|
});
|
|
4503
|
-
const vaultCatalogSiteSchema = z.object({
|
|
4510
|
+
const vaultCatalogSiteSchema = z$1.object({
|
|
4504
4511
|
id: vaultProviderIdSchema,
|
|
4505
|
-
displayName: z.string().trim().min(1).max(80),
|
|
4506
|
-
consentSummary: z.string().trim().min(1).max(240),
|
|
4512
|
+
displayName: z$1.string().trim().min(1).max(80),
|
|
4513
|
+
consentSummary: z$1.string().trim().min(1).max(240),
|
|
4507
4514
|
/** Origins the login form must live on (shown so the person can check). */
|
|
4508
|
-
loginOrigins: z.array(z.url().max(2048)).max(16).default([]),
|
|
4515
|
+
loginOrigins: z$1.array(z$1.url().max(2048)).max(16).default([]),
|
|
4509
4516
|
/** Where a restored session lands, for display. */
|
|
4510
|
-
startUrl: z.url().max(2048).optional()
|
|
4517
|
+
startUrl: z$1.url().max(2048).optional()
|
|
4511
4518
|
});
|
|
4512
|
-
const vaultCatalogSchema = z.object({
|
|
4513
|
-
v: z.literal(1),
|
|
4514
|
-
providers: z.array(vaultCatalogProviderSchema).max(200).default([]),
|
|
4515
|
-
sites: z.array(vaultCatalogSiteSchema).max(200).default([])
|
|
4519
|
+
const vaultCatalogSchema = z$1.object({
|
|
4520
|
+
v: z$1.literal(1),
|
|
4521
|
+
providers: z$1.array(vaultCatalogProviderSchema).max(200).default([]),
|
|
4522
|
+
sites: z$1.array(vaultCatalogSiteSchema).max(200).default([])
|
|
4516
4523
|
});
|
|
4517
|
-
const vaultDisplaySchema = z.object({
|
|
4524
|
+
const vaultDisplaySchema = z$1.object({
|
|
4518
4525
|
/** The platform's name as its users know it: "Instinct", not "instinct-prod". */
|
|
4519
|
-
name: z.string().trim().min(1).max(80),
|
|
4520
|
-
logoUrl: z.url().max(2048).startsWith("https://").optional(),
|
|
4526
|
+
name: z$1.string().trim().min(1).max(80),
|
|
4527
|
+
logoUrl: z$1.url().max(2048).startsWith("https://").optional(),
|
|
4521
4528
|
/** `#rrggbb`. */
|
|
4522
|
-
accent: z.string().regex(/^#[0-9a-fA-F]{6}$/, "accent must be a #rrggbb colour").optional(),
|
|
4529
|
+
accent: z$1.string().regex(/^#[0-9a-fA-F]{6}$/, "accent must be a #rrggbb colour").optional(),
|
|
4523
4530
|
/** Origins a `returnUrl` may point at. Exact-match, scheme + host (+ port). */
|
|
4524
|
-
returnOrigins: z.array(z.url().max(2048)).max(16).default([])
|
|
4531
|
+
returnOrigins: z$1.array(z$1.url().max(2048)).max(16).default([])
|
|
4525
4532
|
});
|
|
4526
|
-
z.object({
|
|
4533
|
+
z$1.object({
|
|
4527
4534
|
name: nameSchema,
|
|
4528
4535
|
display: vaultDisplaySchema.optional()
|
|
4529
4536
|
});
|
|
4530
|
-
z.object({
|
|
4537
|
+
z$1.object({
|
|
4531
4538
|
name: nameSchema.optional(),
|
|
4532
4539
|
display: vaultDisplaySchema.optional(),
|
|
4533
|
-
status: z.enum(["active", "disabled"]).optional()
|
|
4540
|
+
status: z$1.enum(["active", "disabled"]).optional()
|
|
4534
4541
|
});
|
|
4535
|
-
z.object({ name: nameSchema });
|
|
4542
|
+
z$1.object({ name: nameSchema });
|
|
4536
4543
|
/**
|
|
4537
4544
|
* The platform's opaque identifier for its end user. Stored as given and
|
|
4538
4545
|
* returned as given. It must not be an email address or anything else that
|
|
4539
4546
|
* identifies the person outside the platform: the person has no seekrit
|
|
4540
4547
|
* account, and this is the one field about them Vault holds in the clear.
|
|
4541
4548
|
*/
|
|
4542
|
-
const vaultUserRefSchema = z.string().trim().min(1).max(200).refine((v) => !/\s/.test(v), { message: "userRef must not contain whitespace" }).refine((v) => !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v), { message: "userRef must be an opaque identifier, not an email address" });
|
|
4543
|
-
z.object({
|
|
4549
|
+
const vaultUserRefSchema = z$1.string().trim().min(1).max(200).refine((v) => !/\s/.test(v), { message: "userRef must not contain whitespace" }).refine((v) => !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v), { message: "userRef must be an opaque identifier, not an email address" });
|
|
4550
|
+
z$1.object({
|
|
4544
4551
|
userRef: vaultUserRefSchema,
|
|
4545
4552
|
providerId: vaultProviderIdSchema,
|
|
4546
|
-
purpose: z.enum(["connect", "reconnect"]).default("connect"),
|
|
4553
|
+
purpose: z$1.enum(["connect", "reconnect"]).default("connect"),
|
|
4547
4554
|
/** Required for `reconnect`: the connection being repaired. */
|
|
4548
|
-
connectionId: z.string().trim().min(1).max(64).optional(),
|
|
4555
|
+
connectionId: z$1.string().trim().min(1).max(64).optional(),
|
|
4549
4556
|
/** Where the done screen may send the person. Must match a registered origin. */
|
|
4550
|
-
returnUrl: z.url().max(2048).optional(),
|
|
4551
|
-
expiresInSeconds: z.number().int().min(60).max(VAULT_CONNECT_SESSION_MAX_TTL_SECONDS).optional()
|
|
4557
|
+
returnUrl: z$1.url().max(2048).optional(),
|
|
4558
|
+
expiresInSeconds: z$1.number().int().min(60).max(VAULT_CONNECT_SESSION_MAX_TTL_SECONDS).optional()
|
|
4552
4559
|
});
|
|
4553
|
-
z.object({
|
|
4560
|
+
z$1.object({
|
|
4554
4561
|
userRef: vaultUserRefSchema,
|
|
4555
|
-
returnUrl: z.url().max(2048).optional(),
|
|
4556
|
-
expiresInSeconds: z.number().int().min(60).max(VAULT_CONNECT_SESSION_MAX_TTL_SECONDS).optional()
|
|
4562
|
+
returnUrl: z$1.url().max(2048).optional(),
|
|
4563
|
+
expiresInSeconds: z$1.number().int().min(60).max(VAULT_CONNECT_SESSION_MAX_TTL_SECONDS).optional()
|
|
4557
4564
|
});
|
|
4558
|
-
z.object({
|
|
4559
|
-
url: z.url().max(2048).refine((u) => u.startsWith("https://"), "webhook URLs must be https"),
|
|
4565
|
+
z$1.object({
|
|
4566
|
+
url: z$1.url().max(2048).refine((u) => u.startsWith("https://"), "webhook URLs must be https"),
|
|
4560
4567
|
/** Defaults to every event. */
|
|
4561
|
-
events: z.array(z.enum(VAULT_WEBHOOK_EVENTS)).min(1).max(VAULT_WEBHOOK_EVENTS.length).optional(),
|
|
4562
|
-
description: z.string().trim().max(200).optional()
|
|
4568
|
+
events: z$1.array(z$1.enum(VAULT_WEBHOOK_EVENTS)).min(1).max(VAULT_WEBHOOK_EVENTS.length).optional(),
|
|
4569
|
+
description: z$1.string().trim().max(200).optional()
|
|
4563
4570
|
});
|
|
4564
|
-
z.object({
|
|
4571
|
+
z$1.object({
|
|
4565
4572
|
userRef: vaultUserRefSchema.optional(),
|
|
4566
|
-
status: z.enum(VAULT_CONNECTION_STATUSES).optional(),
|
|
4567
|
-
cursor: z.string().max(200).optional(),
|
|
4568
|
-
limit: z.coerce.number().int().min(1).max(200).default(50)
|
|
4573
|
+
status: z$1.enum(VAULT_CONNECTION_STATUSES).optional(),
|
|
4574
|
+
cursor: z$1.string().max(200).optional(),
|
|
4575
|
+
limit: z$1.coerce.number().int().min(1).max(200).default(50)
|
|
4569
4576
|
});
|
|
4570
4577
|
/** An `https://host[:port]` origin with nothing after it; `http://localhost` in dev. */
|
|
4571
|
-
const vaultOriginSchema = z.string().trim().max(2048).refine((v) => {
|
|
4578
|
+
const vaultOriginSchema = z$1.string().trim().max(2048).refine((v) => {
|
|
4572
4579
|
let url;
|
|
4573
4580
|
try {
|
|
4574
4581
|
url = new URL(v);
|
|
@@ -4583,85 +4590,85 @@ const vaultOriginSchema = z.string().trim().max(2048).refine((v) => {
|
|
|
4583
4590
|
"[::1]"
|
|
4584
4591
|
].includes(url.hostname);
|
|
4585
4592
|
}, { message: "must be an https origin (scheme + host, nothing else)" });
|
|
4586
|
-
const vaultWrappingKeySchema = z.object({
|
|
4593
|
+
const vaultWrappingKeySchema = z$1.object({
|
|
4587
4594
|
jwk: vaultPublicJwkSchema,
|
|
4588
|
-
thumbprint: z.string().regex(/^[A-Za-z0-9_-]{43}$/)
|
|
4595
|
+
thumbprint: z$1.string().regex(/^[A-Za-z0-9_-]{43}$/)
|
|
4589
4596
|
});
|
|
4590
|
-
z.object({
|
|
4591
|
-
code: z.string().trim().min(16).max(200),
|
|
4597
|
+
z$1.object({
|
|
4598
|
+
code: z$1.string().trim().min(16).max(200),
|
|
4592
4599
|
origin: vaultOriginSchema,
|
|
4593
4600
|
wrappingKey: vaultWrappingKeySchema,
|
|
4594
4601
|
catalog: vaultCatalogSchema,
|
|
4595
|
-
sdkVersion: z.string().trim().min(1).max(40)
|
|
4602
|
+
sdkVersion: z$1.string().trim().min(1).max(40)
|
|
4596
4603
|
});
|
|
4597
|
-
z.object({
|
|
4604
|
+
z$1.object({
|
|
4598
4605
|
catalog: vaultCatalogSchema,
|
|
4599
|
-
sdkVersion: z.string().trim().min(1).max(40)
|
|
4606
|
+
sdkVersion: z$1.string().trim().min(1).max(40)
|
|
4600
4607
|
});
|
|
4601
|
-
z.object({ wrappingKey: vaultWrappingKeySchema });
|
|
4608
|
+
z$1.object({ wrappingKey: vaultWrappingKeySchema });
|
|
4602
4609
|
/** One encrypted slot, as produced by Connect or the executor. */
|
|
4603
|
-
const vaultEncryptedSlotSchema = z.object({
|
|
4604
|
-
slot: z.enum(VAULT_SLOTS),
|
|
4605
|
-
revision: z.number().int().min(1),
|
|
4606
|
-
ciphertext: z.string().startsWith("sc1.").max(VAULT_CIPHERTEXT_MAX_CHARS),
|
|
4607
|
-
wrappedDek: z.string().startsWith("wd1.").max(2048),
|
|
4608
|
-
keyThumbprint: z.string().regex(/^[A-Za-z0-9_-]{43}$/)
|
|
4610
|
+
const vaultEncryptedSlotSchema = z$1.object({
|
|
4611
|
+
slot: z$1.enum(VAULT_SLOTS),
|
|
4612
|
+
revision: z$1.number().int().min(1),
|
|
4613
|
+
ciphertext: z$1.string().startsWith("sc1.").max(VAULT_CIPHERTEXT_MAX_CHARS),
|
|
4614
|
+
wrappedDek: z$1.string().startsWith("wd1.").max(2048),
|
|
4615
|
+
keyThumbprint: z$1.string().regex(/^[A-Za-z0-9_-]{43}$/)
|
|
4609
4616
|
});
|
|
4610
|
-
z.object({
|
|
4611
|
-
slots: z.array(vaultEncryptedSlotSchema).min(1).max(2),
|
|
4617
|
+
z$1.object({
|
|
4618
|
+
slots: z$1.array(vaultEncryptedSlotSchema).min(1).max(2),
|
|
4612
4619
|
/** "ava@example.com" / "@ava" — shown to the platform and the org, never used for auth. */
|
|
4613
|
-
accountLabel: z.string().trim().min(1).max(200).optional()
|
|
4620
|
+
accountLabel: z$1.string().trim().min(1).max(200).optional()
|
|
4614
4621
|
});
|
|
4615
|
-
z.object({
|
|
4622
|
+
z$1.object({
|
|
4616
4623
|
/** The revision the writer read; the write lands as `expectedRevision + 1`. */
|
|
4617
|
-
expectedRevision: z.number().int().min(0),
|
|
4618
|
-
ciphertext: z.string().startsWith("sc1.").max(VAULT_CIPHERTEXT_MAX_CHARS),
|
|
4619
|
-
wrappedDek: z.string().startsWith("wd1.").max(2048),
|
|
4620
|
-
keyThumbprint: z.string().regex(/^[A-Za-z0-9_-]{43}$/)
|
|
4624
|
+
expectedRevision: z$1.number().int().min(0),
|
|
4625
|
+
ciphertext: z$1.string().startsWith("sc1.").max(VAULT_CIPHERTEXT_MAX_CHARS),
|
|
4626
|
+
wrappedDek: z$1.string().startsWith("wd1.").max(2048),
|
|
4627
|
+
keyThumbprint: z$1.string().regex(/^[A-Za-z0-9_-]{43}$/)
|
|
4621
4628
|
});
|
|
4622
|
-
z.discriminatedUnion("status", [z.object({
|
|
4623
|
-
status: z.literal("reauth_required"),
|
|
4624
|
-
reason: z.string().trim().min(1).max(120)
|
|
4625
|
-
}), z.object({ status: z.literal("active") })]);
|
|
4626
|
-
z.object({ claimToken: z.string().min(16).max(200) });
|
|
4627
|
-
z.object({ token: z.string().min(16).max(200) });
|
|
4628
|
-
z.object({ consentVersion: z.literal(1) });
|
|
4629
|
-
z.object({ connectionId: z.string().trim().min(1).max(64) });
|
|
4630
|
-
z.object({
|
|
4629
|
+
z$1.discriminatedUnion("status", [z$1.object({
|
|
4630
|
+
status: z$1.literal("reauth_required"),
|
|
4631
|
+
reason: z$1.string().trim().min(1).max(120)
|
|
4632
|
+
}), z$1.object({ status: z$1.literal("active") })]);
|
|
4633
|
+
z$1.object({ claimToken: z$1.string().min(16).max(200) });
|
|
4634
|
+
z$1.object({ token: z$1.string().min(16).max(200) });
|
|
4635
|
+
z$1.object({ consentVersion: z$1.literal(1) });
|
|
4636
|
+
z$1.object({ connectionId: z$1.string().trim().min(1).max(64) });
|
|
4637
|
+
z$1.object({
|
|
4631
4638
|
login: vaultEncryptedSlotSchema.refine((slot) => slot.slot === "login", "must be the login slot").optional(),
|
|
4632
|
-
accountLabel: z.string().trim().min(1).max(200).optional()
|
|
4639
|
+
accountLabel: z$1.string().trim().min(1).max(200).optional()
|
|
4633
4640
|
});
|
|
4634
|
-
z.object({
|
|
4641
|
+
z$1.object({
|
|
4635
4642
|
userRef: vaultUserRefSchema,
|
|
4636
|
-
connectionId: z.string().trim().min(1).max(64),
|
|
4637
|
-
request: z.object({
|
|
4638
|
-
method: z.string().trim().toUpperCase().regex(/^[A-Z]{3,10}$/),
|
|
4639
|
-
url: z.url().max(8192),
|
|
4640
|
-
headers: z.record(z.string().max(256), z.string().max(16384)).optional(),
|
|
4643
|
+
connectionId: z$1.string().trim().min(1).max(64),
|
|
4644
|
+
request: z$1.object({
|
|
4645
|
+
method: z$1.string().trim().toUpperCase().regex(/^[A-Z]{3,10}$/),
|
|
4646
|
+
url: z$1.url().max(8192),
|
|
4647
|
+
headers: z$1.record(z$1.string().max(256), z$1.string().max(16384)).optional(),
|
|
4641
4648
|
/** UTF-8 text. Binary bodies are out of scope for V1. */
|
|
4642
|
-
body: z.string().max(4194304).optional()
|
|
4649
|
+
body: z$1.string().max(4194304).optional()
|
|
4643
4650
|
})
|
|
4644
4651
|
});
|
|
4645
|
-
const connectionIdSchema = z.string().trim().min(1).max(64);
|
|
4646
|
-
const leaseIdSchema = z.string().trim().min(1).max(64);
|
|
4647
|
-
z.object({
|
|
4652
|
+
const connectionIdSchema = z$1.string().trim().min(1).max(64);
|
|
4653
|
+
const leaseIdSchema = z$1.string().trim().min(1).max(64);
|
|
4654
|
+
z$1.object({
|
|
4648
4655
|
userRef: vaultUserRefSchema,
|
|
4649
4656
|
connectionId: connectionIdSchema,
|
|
4650
4657
|
/** How long the agent may hold the browser. Clamped to the site's ceiling. */
|
|
4651
|
-
keepAliveMs: z.number().int().min(VAULT_LEASE_MIN_MS).max(VAULT_LEASE_MAX_MS).optional()
|
|
4658
|
+
keepAliveMs: z$1.number().int().min(VAULT_LEASE_MIN_MS).max(VAULT_LEASE_MAX_MS).optional()
|
|
4652
4659
|
});
|
|
4653
|
-
z.object({
|
|
4660
|
+
z$1.object({
|
|
4654
4661
|
userRef: vaultUserRefSchema,
|
|
4655
4662
|
leaseId: leaseIdSchema,
|
|
4656
|
-
field: z.enum(VAULT_FILL_FIELDS)
|
|
4663
|
+
field: z$1.enum(VAULT_FILL_FIELDS)
|
|
4657
4664
|
});
|
|
4658
|
-
z.object({
|
|
4665
|
+
z$1.object({
|
|
4659
4666
|
userRef: vaultUserRefSchema,
|
|
4660
4667
|
leaseId: leaseIdSchema,
|
|
4661
4668
|
/** Write the rotated session back (default). `false` drops the browser without saving. */
|
|
4662
|
-
save: z.boolean().default(true)
|
|
4669
|
+
save: z$1.boolean().default(true)
|
|
4663
4670
|
});
|
|
4664
|
-
z.object({
|
|
4671
|
+
z$1.object({
|
|
4665
4672
|
userRef: vaultUserRefSchema,
|
|
4666
4673
|
leaseId: leaseIdSchema
|
|
4667
4674
|
});
|
|
@@ -5887,7 +5894,7 @@ async function createAgentTaskToken() {
|
|
|
5887
5894
|
};
|
|
5888
5895
|
}
|
|
5889
5896
|
// package.json
|
|
5890
|
-
var version = "2.
|
|
5897
|
+
var version = "2.6.0";
|
|
5891
5898
|
// ../../packages/api-client/src/index.ts
|
|
5892
5899
|
/**
|
|
5893
5900
|
* The proposer's note travels as a header, not a body field: an unprotected
|
|
@@ -7052,13 +7059,13 @@ const CLI_CLIENT = `cli/${version}`;
|
|
|
7052
7059
|
* bearer of nothing: a 401 where the honest answer is "you have no
|
|
7053
7060
|
* credentials", pointing at the API instead of at the missing variable.
|
|
7054
7061
|
*/
|
|
7055
|
-
function present(value) {
|
|
7062
|
+
function present$1(value) {
|
|
7056
7063
|
const trimmed = value?.trim();
|
|
7057
7064
|
return trimmed ? trimmed : void 0;
|
|
7058
7065
|
}
|
|
7059
7066
|
function tryBuildContext(dotenvVars = {}) {
|
|
7060
7067
|
const config = readGlobalConfig();
|
|
7061
|
-
const fromEnv = (key) => present(process.env[key]) ?? present(dotenvVars[key]);
|
|
7068
|
+
const fromEnv = (key) => present$1(process.env[key]) ?? present$1(dotenvVars[key]);
|
|
7062
7069
|
const apiUrl = fromEnv("SEEKRIT_API_URL") ?? config.apiUrl ?? "https://api.seekrit.dev";
|
|
7063
7070
|
const token = fromEnv("SEEKRIT_TOKEN") ?? config.token ?? config.sessionToken;
|
|
7064
7071
|
const devUser = fromEnv("SEEKRIT_DEV_USER") ?? config.devUser;
|
|
@@ -7103,7 +7110,7 @@ function describeError(err) {
|
|
|
7103
7110
|
if (!(err instanceof Error)) return String(err);
|
|
7104
7111
|
if (err.message !== "fetch failed") return err.message;
|
|
7105
7112
|
const config = readGlobalConfig();
|
|
7106
|
-
const apiUrl = present(process.env.SEEKRIT_API_URL) ?? config.apiUrl ?? "https://api.seekrit.dev";
|
|
7113
|
+
const apiUrl = present$1(process.env.SEEKRIT_API_URL) ?? config.apiUrl ?? "https://api.seekrit.dev";
|
|
7107
7114
|
const cause = err.cause;
|
|
7108
7115
|
const reason = cause instanceof Error ? cause.code ?? cause.message : void 0;
|
|
7109
7116
|
return `cannot reach the seekrit API at ${apiUrl}${reason ? ` (${reason})` : ""} — check SEEKRIT_API_URL, your network, and that the API is up`;
|
|
@@ -7658,7 +7665,7 @@ function defaultIsAlive(pid) {
|
|
|
7658
7665
|
}
|
|
7659
7666
|
}
|
|
7660
7667
|
/** Write a file so a reader sees the old bytes or the new ones, never half of each. */
|
|
7661
|
-
function writeAtomic(path, content) {
|
|
7668
|
+
function writeAtomic$1(path, content) {
|
|
7662
7669
|
mkdirSync(dirname(path), {
|
|
7663
7670
|
recursive: true,
|
|
7664
7671
|
mode: 448
|
|
@@ -7725,7 +7732,7 @@ var AgentLoginSession = class {
|
|
|
7725
7732
|
await this.recoverLeftover();
|
|
7726
7733
|
await this.acquire();
|
|
7727
7734
|
this.registerHolder();
|
|
7728
|
-
writeAtomic(this.o.path, this.synced);
|
|
7735
|
+
writeAtomic$1(this.o.path, this.synced);
|
|
7729
7736
|
this.placed = true;
|
|
7730
7737
|
this.lastRemotePoll = this.o.now();
|
|
7731
7738
|
this.timer = setInterval(() => void this.tick(), this.o.localPollMs);
|
|
@@ -7899,7 +7906,7 @@ var AgentLoginSession = class {
|
|
|
7899
7906
|
this.version = snapshot.version;
|
|
7900
7907
|
continue;
|
|
7901
7908
|
}
|
|
7902
|
-
writeAtomic(this.o.path, remote.content);
|
|
7909
|
+
writeAtomic$1(this.o.path, remote.content);
|
|
7903
7910
|
this.adopt(snapshot, remote);
|
|
7904
7911
|
this.o.log(`another sandbox refreshed the ${this.o.adapter.label} login at the same moment; switched to its copy. If ${this.o.adapter.label} reports a sign-in error, run \`seekrit agent-login ${this.o.adapter.id}\` again.`);
|
|
7905
7912
|
return;
|
|
@@ -7925,7 +7932,7 @@ var AgentLoginSession = class {
|
|
|
7925
7932
|
const local = readIfExists(this.o.path);
|
|
7926
7933
|
if (local !== null && local !== this.synced) return;
|
|
7927
7934
|
if (remote.content !== this.synced && !this.fresher(this.synced, remote.content)) {
|
|
7928
|
-
writeAtomic(this.o.path, remote.content);
|
|
7935
|
+
writeAtomic$1(this.o.path, remote.content);
|
|
7929
7936
|
this.o.log(`picked up a ${this.o.adapter.label} login another sandbox refreshed`);
|
|
7930
7937
|
}
|
|
7931
7938
|
if (remote.claim?.holder !== this.o.holder) this.claimUntil = null;
|
|
@@ -7976,7 +7983,7 @@ var AgentLoginSession = class {
|
|
|
7976
7983
|
secret: this.o.secretName,
|
|
7977
7984
|
holders
|
|
7978
7985
|
};
|
|
7979
|
-
writeAtomic(this.markerPath, `${JSON.stringify(marker, null, 2)}\n`);
|
|
7986
|
+
writeAtomic$1(this.markerPath, `${JSON.stringify(marker, null, 2)}\n`);
|
|
7980
7987
|
}
|
|
7981
7988
|
/** Several runs on one machine can share a home; the last one out cleans up. */
|
|
7982
7989
|
registerHolder() {
|
|
@@ -9560,7 +9567,7 @@ async function syncGrantsForGroupLink(ctx, orgId, envId, envSlug, groupId) {
|
|
|
9560
9567
|
}
|
|
9561
9568
|
// src/kms.ts
|
|
9562
9569
|
/** Collect a repeatable option into a list. */
|
|
9563
|
-
function collect$
|
|
9570
|
+
function collect$10(value, acc = []) {
|
|
9564
9571
|
acc.push(value);
|
|
9565
9572
|
return acc;
|
|
9566
9573
|
}
|
|
@@ -9628,7 +9635,7 @@ async function kmsRecoverMaterial(ctx, orgId, keyId, version) {
|
|
|
9628
9635
|
}
|
|
9629
9636
|
function registerKmsCommands(program) {
|
|
9630
9637
|
const kms = program.command("kms").description("managed keys for application-layer encryption & signing (client-side)");
|
|
9631
|
-
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$
|
|
9638
|
+
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$10, []).option("--grant-token <tokenId>", "also grant a service token (repeatable)", collect$10, []).action(async (options) => {
|
|
9632
9639
|
if (options.purpose !== "encrypt" && options.purpose !== "sign") fail("--purpose must be encrypt or sign");
|
|
9633
9640
|
if (options.app && options.group) fail("pass at most one of --app or --group");
|
|
9634
9641
|
const ctx = buildContext();
|
|
@@ -9849,7 +9856,7 @@ function registerKmsCommands(program) {
|
|
|
9849
9856
|
}
|
|
9850
9857
|
// src/recovery.ts
|
|
9851
9858
|
/** Collect a repeatable option into a list. */
|
|
9852
|
-
function collect$
|
|
9859
|
+
function collect$9(value, acc = []) {
|
|
9853
9860
|
acc.push(value);
|
|
9854
9861
|
return acc;
|
|
9855
9862
|
}
|
|
@@ -9946,7 +9953,7 @@ function registerRecoveryCommands(program) {
|
|
|
9946
9953
|
for (const cst of status.custodians) console.log(` - ${cst.label ?? cst.principalId} (${cst.principalType}, share #${cst.shareIndex})`);
|
|
9947
9954
|
if (status.coverage.unprotectedEnvIds.length > 0) console.log(`${status.coverage.unprotectedEnvIds.length} environment(s) not yet protected — run \`seekrit recovery sync\``);
|
|
9948
9955
|
});
|
|
9949
|
-
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$
|
|
9956
|
+
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$9, []).option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
9950
9957
|
const ctx = buildContext();
|
|
9951
9958
|
const org = await resolveOrg(ctx, options.org);
|
|
9952
9959
|
const config = await buildRecoveryConfig(ctx, org.id, options.threshold, options.custodian);
|
|
@@ -9964,7 +9971,7 @@ function registerRecoveryCommands(program) {
|
|
|
9964
9971
|
const { wrapped, skipped } = await syncRecoveryGrants(ctx, (await resolveOrg(ctx, options.org)).id);
|
|
9965
9972
|
console.error(`recovery-protected ${wrapped} environment(s); skipped ${skipped} you cannot decrypt`);
|
|
9966
9973
|
});
|
|
9967
|
-
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$
|
|
9974
|
+
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$9, []).option("--org <slug>", "organization slug (defaults to seekrit.json, or your only org)").action(async (options) => {
|
|
9968
9975
|
const ctx = buildContext();
|
|
9969
9976
|
const org = await resolveOrg(ctx, options.org);
|
|
9970
9977
|
const config = await buildRecoveryConfig(ctx, org.id, options.threshold, options.custodian);
|
|
@@ -11103,7 +11110,7 @@ function formatSecrets(values, format) {
|
|
|
11103
11110
|
* `buildContext()` in any of them silently breaks the promise the feature makes.
|
|
11104
11111
|
*/
|
|
11105
11112
|
/** Collect a repeatable option into a list. */
|
|
11106
|
-
function collect$
|
|
11113
|
+
function collect$8(value, acc = []) {
|
|
11107
11114
|
acc.push(value);
|
|
11108
11115
|
return acc;
|
|
11109
11116
|
}
|
|
@@ -11380,7 +11387,7 @@ function registerArchiveCommands(program) {
|
|
|
11380
11387
|
} catch {}
|
|
11381
11388
|
fail("none of the recovery shares in this archive unwrap with that key");
|
|
11382
11389
|
});
|
|
11383
|
-
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$
|
|
11390
|
+
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$8).option("-y, --yes", "skip the confirmation when printing plaintext to stdout").action(async (file, options) => {
|
|
11384
11391
|
if (!options.out && !options.stdout) fail("choose a destination: --out <dir> to write files, or --stdout to print plaintext");
|
|
11385
11392
|
if (![
|
|
11386
11393
|
"dotenv",
|
|
@@ -13123,6 +13130,644 @@ function registerHoneyTokenCommands(program) {
|
|
|
13123
13130
|
console.error(`${honeyTokenId} deleted`);
|
|
13124
13131
|
});
|
|
13125
13132
|
}
|
|
13133
|
+
// src/run-command.ts
|
|
13134
|
+
/** Keep signal handlers installed from provisioning through asynchronous cleanup. */
|
|
13135
|
+
async function runCommand(cmd, args, prepare, cleanup) {
|
|
13136
|
+
let child;
|
|
13137
|
+
let forwarded;
|
|
13138
|
+
let result = { code: 1 };
|
|
13139
|
+
const stragglers = /* @__PURE__ */ new Set();
|
|
13140
|
+
const collect = () => {
|
|
13141
|
+
if (process.platform === "win32" || !child?.pid) return;
|
|
13142
|
+
for (const pid of teardownTargets(child.pid)) stragglers.add(pid);
|
|
13143
|
+
};
|
|
13144
|
+
const forward = (signal) => {
|
|
13145
|
+
forwarded ??= signal;
|
|
13146
|
+
if (!child || child.exitCode !== null || child.signalCode !== null) return;
|
|
13147
|
+
collect();
|
|
13148
|
+
child.kill(signal);
|
|
13149
|
+
for (const pid of stragglers) try {
|
|
13150
|
+
process.kill(pid, signal);
|
|
13151
|
+
} catch {}
|
|
13152
|
+
};
|
|
13153
|
+
const signals = [
|
|
13154
|
+
"SIGINT",
|
|
13155
|
+
"SIGTERM",
|
|
13156
|
+
"SIGHUP",
|
|
13157
|
+
"SIGQUIT"
|
|
13158
|
+
];
|
|
13159
|
+
for (const signal of signals) process.on(signal, forward);
|
|
13160
|
+
try {
|
|
13161
|
+
const env = await prepare(() => forwarded !== void 0);
|
|
13162
|
+
if (!forwarded) {
|
|
13163
|
+
const running = spawn(cmd, args, {
|
|
13164
|
+
stdio: "inherit",
|
|
13165
|
+
env
|
|
13166
|
+
});
|
|
13167
|
+
child = running;
|
|
13168
|
+
result = await new Promise((resolve, reject) => {
|
|
13169
|
+
running.once("exit", (code, signal) => resolve({
|
|
13170
|
+
code: code ?? 1,
|
|
13171
|
+
signal: signal ?? void 0
|
|
13172
|
+
}));
|
|
13173
|
+
running.once("error", (err) => reject(/* @__PURE__ */ new Error(`failed to start ${cmd}: ${err.message}`)));
|
|
13174
|
+
});
|
|
13175
|
+
}
|
|
13176
|
+
} finally {
|
|
13177
|
+
try {
|
|
13178
|
+
if (forwarded) {
|
|
13179
|
+
collect();
|
|
13180
|
+
await reapStragglers(stragglers, forwarded);
|
|
13181
|
+
}
|
|
13182
|
+
} finally {
|
|
13183
|
+
try {
|
|
13184
|
+
if (!await cleanup() && result.code === 0) result.code = 1;
|
|
13185
|
+
} finally {
|
|
13186
|
+
for (const signal of signals) process.off(signal, forward);
|
|
13187
|
+
}
|
|
13188
|
+
}
|
|
13189
|
+
}
|
|
13190
|
+
if (!child && forwarded) result.signal = forwarded;
|
|
13191
|
+
return result;
|
|
13192
|
+
}
|
|
13193
|
+
/**
|
|
13194
|
+
* Every live process `seekrit run` is responsible for tearing down, from a
|
|
13195
|
+
* single `ps(1)` snapshot: each descendant of `root`, plus — when we lead our
|
|
13196
|
+
* own process group — every other member of that group.
|
|
13197
|
+
*
|
|
13198
|
+
* Two mechanisms, because each covers the other's blind spot:
|
|
13199
|
+
*
|
|
13200
|
+
* - The parent-chain walk reaches a process that left our group via
|
|
13201
|
+
* setsid(2)/setpgid(2), which no group-wide kill and no tty-generated signal
|
|
13202
|
+
* can touch. But it loses anything whose chain up to `root` has already
|
|
13203
|
+
* broken — teardown kills the middle layers first, and an orphan reparents to
|
|
13204
|
+
* init, out of reach of any walk down from `root`.
|
|
13205
|
+
* - Group membership survives exactly that: a pgid outlives the parent that
|
|
13206
|
+
* passed it down. But it misses the ones that left the group.
|
|
13207
|
+
*
|
|
13208
|
+
* The group half applies only when our pid *is* our pgid, which is the shell-job
|
|
13209
|
+
* case: the shell put this command in a new group, so every other member of it
|
|
13210
|
+
* descends from us. Nested inside a shell script or a CI runner we are not the
|
|
13211
|
+
* leader and the group holds processes we did not start, so there we use the
|
|
13212
|
+
* walk alone.
|
|
13213
|
+
*
|
|
13214
|
+
* Zombies are skipped: they are already dead and waiting to be reaped, so
|
|
13215
|
+
* counting them would stall teardown for the full grace period and then report
|
|
13216
|
+
* a kill that did nothing. Returns nothing if `ps` is unavailable; callers fall
|
|
13217
|
+
* back to signalling the immediate child.
|
|
13218
|
+
*/
|
|
13219
|
+
function teardownTargets(root) {
|
|
13220
|
+
const ps = spawnSync("ps", [
|
|
13221
|
+
"-A",
|
|
13222
|
+
"-o",
|
|
13223
|
+
"pid=,ppid=,pgid=,stat="
|
|
13224
|
+
], { encoding: "utf8" });
|
|
13225
|
+
if (ps.status !== 0 || typeof ps.stdout !== "string") return [];
|
|
13226
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
13227
|
+
const groupOf = /* @__PURE__ */ new Map();
|
|
13228
|
+
const isId = (value) => value !== void 0 && Number.isInteger(value);
|
|
13229
|
+
for (const line of ps.stdout.split("\n")) {
|
|
13230
|
+
const [pid, ppid, pgid, stat] = line.trim().split(/\s+/, 4);
|
|
13231
|
+
const [id, parent, group] = [
|
|
13232
|
+
pid,
|
|
13233
|
+
ppid,
|
|
13234
|
+
pgid
|
|
13235
|
+
].map(Number);
|
|
13236
|
+
if (!isId(id) || !isId(parent) || !isId(group)) continue;
|
|
13237
|
+
if (stat?.startsWith("Z")) continue;
|
|
13238
|
+
const siblings = childrenOf.get(parent);
|
|
13239
|
+
if (siblings) siblings.push(id);
|
|
13240
|
+
else childrenOf.set(parent, [id]);
|
|
13241
|
+
groupOf.set(id, group);
|
|
13242
|
+
}
|
|
13243
|
+
const found = /* @__PURE__ */ new Set();
|
|
13244
|
+
const queue = [root];
|
|
13245
|
+
const seen = new Set(queue);
|
|
13246
|
+
for (let pid = queue.shift(); pid !== void 0; pid = queue.shift()) for (const kid of childrenOf.get(pid) ?? []) {
|
|
13247
|
+
if (seen.has(kid)) continue;
|
|
13248
|
+
seen.add(kid);
|
|
13249
|
+
found.add(kid);
|
|
13250
|
+
queue.push(kid);
|
|
13251
|
+
}
|
|
13252
|
+
if (groupOf.get(process.pid) === process.pid) {
|
|
13253
|
+
for (const [pid, group] of groupOf) if (group === process.pid) found.add(pid);
|
|
13254
|
+
}
|
|
13255
|
+
found.delete(process.pid);
|
|
13256
|
+
found.delete(root);
|
|
13257
|
+
return [...found];
|
|
13258
|
+
}
|
|
13259
|
+
/** Whether `pid` still exists — signal 0 tests reachability, delivers nothing. */
|
|
13260
|
+
function isAlive(pid) {
|
|
13261
|
+
try {
|
|
13262
|
+
process.kill(pid, 0);
|
|
13263
|
+
return true;
|
|
13264
|
+
} catch {
|
|
13265
|
+
return false;
|
|
13266
|
+
}
|
|
13267
|
+
}
|
|
13268
|
+
/** How long a straggler gets to finish shutting down before SIGKILL. */
|
|
13269
|
+
const STRAGGLER_GRACE_MS = 2e3;
|
|
13270
|
+
/**
|
|
13271
|
+
* Once the command has exited, make sure nothing it started outlives us.
|
|
13272
|
+
*
|
|
13273
|
+
* Anything still alive here has ignored the signal we forwarded *and* lost the
|
|
13274
|
+
* parent that launched it, and we are the last process that knows its pid:
|
|
13275
|
+
* the tty won't hang it up (it is no longer in the foreground group, and the
|
|
13276
|
+
* shell has moved on), so it survives until the machine reboots. That is how
|
|
13277
|
+
* `tsx` and the server under it stacked up for days.
|
|
13278
|
+
*
|
|
13279
|
+
* So escalate rather than trust. SIGTERM first — it costs no extra time and is
|
|
13280
|
+
* a rung the SIGINT-only handlers common in dev servers don't catch — then
|
|
13281
|
+
* SIGKILL whatever is left after the grace period. Nothing is killed unless it
|
|
13282
|
+
* was asked to stop first and declined, and this runs only when we forwarded a
|
|
13283
|
+
* termination signal: a command that exits on its own having deliberately left a
|
|
13284
|
+
* daemon behind keeps working.
|
|
13285
|
+
*/
|
|
13286
|
+
async function reapStragglers(pids, signal) {
|
|
13287
|
+
let live = [...pids].filter(isAlive);
|
|
13288
|
+
if (live.length === 0) return;
|
|
13289
|
+
for (const pid of live) try {
|
|
13290
|
+
process.kill(pid, "SIGTERM");
|
|
13291
|
+
} catch {}
|
|
13292
|
+
const deadline = Date.now() + STRAGGLER_GRACE_MS;
|
|
13293
|
+
while (live.length > 0 && Date.now() < deadline) {
|
|
13294
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
13295
|
+
live = live.filter(isAlive);
|
|
13296
|
+
}
|
|
13297
|
+
if (live.length === 0) return;
|
|
13298
|
+
console.error(`seekrit: force-killed ${live.length} leftover process(es) that ignored ${signal} and SIGTERM (${live.join(", ")})`);
|
|
13299
|
+
for (const pid of live) try {
|
|
13300
|
+
process.kill(pid, "SIGKILL");
|
|
13301
|
+
} catch {}
|
|
13302
|
+
}
|
|
13303
|
+
// src/identity.ts
|
|
13304
|
+
/**
|
|
13305
|
+
* `seekrit identity` — keyless access to AWS, Google Cloud, OpenAI and Anthropic
|
|
13306
|
+
* from a seekrit workload identity, the way a hosting platform's OIDC
|
|
13307
|
+
* integration works.
|
|
13308
|
+
*
|
|
13309
|
+
* The workload authenticates to seekrit (an enrolled P-256 key, or a platform
|
|
13310
|
+
* JWT it already has), asks for a short-lived identity token addressed to each
|
|
13311
|
+
* provider, and writes it to the token file that provider's own SDK already
|
|
13312
|
+
* knows how to read. The provider verifies the token against the org's issuer
|
|
13313
|
+
* (`https://oidc.seekrit.dev/<orgId>`) and its own trust policy, and mints its own short-lived
|
|
13314
|
+
* credential. No provider key is stored anywhere — not here, not in seekrit.
|
|
13315
|
+
*
|
|
13316
|
+
* Identity, not secrets: nothing on this path decrypts anything, and the API
|
|
13317
|
+
* signs these tokens with its own issuer key, so the zero-knowledge invariant is
|
|
13318
|
+
* untouched.
|
|
13319
|
+
*/
|
|
13320
|
+
const DEFAULT_ISSUER = "https://api.seekrit.dev";
|
|
13321
|
+
const CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
13322
|
+
const TOKEN_EXCHANGE_GRANT = "urn:ietf:params:oauth:grant-type:token-exchange";
|
|
13323
|
+
const JWT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:jwt";
|
|
13324
|
+
function present(value) {
|
|
13325
|
+
return value?.trim() ? value.trim() : void 0;
|
|
13326
|
+
}
|
|
13327
|
+
function httpsOrigin(value, what) {
|
|
13328
|
+
let url;
|
|
13329
|
+
try {
|
|
13330
|
+
url = new URL(value);
|
|
13331
|
+
} catch {
|
|
13332
|
+
throw new Error(`${what} must be an HTTPS origin, got "${value}"`);
|
|
13333
|
+
}
|
|
13334
|
+
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) throw new Error(`${what} must be an HTTPS origin, got "${value}"`);
|
|
13335
|
+
return url.origin;
|
|
13336
|
+
}
|
|
13337
|
+
/**
|
|
13338
|
+
* Where the workload's credential comes from: the file the dashboard downloads
|
|
13339
|
+
* at enrollment (`SEEKRIT_WORKLOAD_FILE`), or — for a platform-federated
|
|
13340
|
+
* workload — its id plus the platform token's path.
|
|
13341
|
+
*/
|
|
13342
|
+
function loadWorkloadCredentials(opts, env = process.env) {
|
|
13343
|
+
const file = present(opts.workloadFile) ?? present(env.SEEKRIT_WORKLOAD_FILE);
|
|
13344
|
+
let fromFile = {};
|
|
13345
|
+
if (file) {
|
|
13346
|
+
let parsed;
|
|
13347
|
+
try {
|
|
13348
|
+
parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
13349
|
+
} catch (err) {
|
|
13350
|
+
throw new Error(`cannot read workload credentials from ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
13351
|
+
}
|
|
13352
|
+
if (!parsed || typeof parsed !== "object") throw new Error(`${file} is not a workload credential file`);
|
|
13353
|
+
fromFile = parsed;
|
|
13354
|
+
}
|
|
13355
|
+
const workloadId = present(opts.workloadId) ?? present(fromFile.workloadId) ?? present(env.SEEKRIT_WORKLOAD_ID);
|
|
13356
|
+
if (!workloadId) throw new Error("no workload identity: set SEEKRIT_WORKLOAD_FILE (or --workload-file) to the credentials downloaded at enrollment, or --workload-id with --platform-token-file");
|
|
13357
|
+
if (!/^wli_[A-Za-z0-9_-]{1,128}$/.test(workloadId)) throw new Error(`"${workloadId}" is not a workload id (expected wli_…)`);
|
|
13358
|
+
const issuer = httpsOrigin(present(opts.issuer) ?? present(fromFile.issuer) ?? present(env.SEEKRIT_ISSUER) ?? DEFAULT_ISSUER, "the seekrit issuer");
|
|
13359
|
+
const platformTokenFile = present(opts.platformTokenFile) ?? present(fromFile.platformTokenFile) ?? present(env.SEEKRIT_PLATFORM_TOKEN_FILE);
|
|
13360
|
+
const signingKey = fromFile.signingKey;
|
|
13361
|
+
if (!!signingKey === !!platformTokenFile) throw new Error(signingKey ? "the workload file holds an enrollment key; do not also pass a platform token" : "this workload needs an enrollment key (in its credential file) or --platform-token-file");
|
|
13362
|
+
if (signingKey && (signingKey.kty !== "EC" || signingKey.crv !== "P-256" || !signingKey.d || !signingKey.x)) throw new Error("the workload file's signingKey is not a private P-256 key");
|
|
13363
|
+
return {
|
|
13364
|
+
workloadId,
|
|
13365
|
+
issuer,
|
|
13366
|
+
signingKey,
|
|
13367
|
+
platformTokenFile
|
|
13368
|
+
};
|
|
13369
|
+
}
|
|
13370
|
+
function base64url(bytes) {
|
|
13371
|
+
return Buffer.from(bytes).toString("base64url");
|
|
13372
|
+
}
|
|
13373
|
+
/** A one-use, 60-second ES256 client assertion, the shape `/oauth/token` verifies. */
|
|
13374
|
+
async function signClientAssertion(creds, key, nowSeconds) {
|
|
13375
|
+
const unsigned = `${base64url(new TextEncoder().encode(JSON.stringify({
|
|
13376
|
+
alg: "ES256",
|
|
13377
|
+
typ: "JWT"
|
|
13378
|
+
})))}.${base64url(new TextEncoder().encode(JSON.stringify({
|
|
13379
|
+
iss: creds.workloadId,
|
|
13380
|
+
sub: creds.workloadId,
|
|
13381
|
+
aud: `${creds.issuer}/oauth/token`,
|
|
13382
|
+
iat: nowSeconds,
|
|
13383
|
+
exp: nowSeconds + 60,
|
|
13384
|
+
jti: crypto.randomUUID()
|
|
13385
|
+
})))}`;
|
|
13386
|
+
const signature = await crypto.subtle.sign({
|
|
13387
|
+
name: "ECDSA",
|
|
13388
|
+
hash: "SHA-256"
|
|
13389
|
+
}, key, new TextEncoder().encode(unsigned));
|
|
13390
|
+
return `${unsigned}.${base64url(new Uint8Array(signature))}`;
|
|
13391
|
+
}
|
|
13392
|
+
async function errorText(response) {
|
|
13393
|
+
try {
|
|
13394
|
+
const body = await response.json();
|
|
13395
|
+
if (body.error_description) return body.error_description;
|
|
13396
|
+
if (typeof body.error === "object" && body.error?.message) return body.error.message;
|
|
13397
|
+
if (typeof body.error === "string") return body.error;
|
|
13398
|
+
} catch {}
|
|
13399
|
+
return `HTTP ${response.status}`;
|
|
13400
|
+
}
|
|
13401
|
+
/** Session exchange + identity-token requests against one seekrit deployment. */
|
|
13402
|
+
var WorkloadClient = class {
|
|
13403
|
+
creds;
|
|
13404
|
+
fetcher;
|
|
13405
|
+
now;
|
|
13406
|
+
session;
|
|
13407
|
+
key;
|
|
13408
|
+
constructor(creds, fetcher = (input, init) => fetch(input, init), now = Date.now) {
|
|
13409
|
+
this.creds = creds;
|
|
13410
|
+
this.fetcher = fetcher;
|
|
13411
|
+
this.now = now;
|
|
13412
|
+
}
|
|
13413
|
+
signingKey() {
|
|
13414
|
+
this.key ??= crypto.subtle.importKey("jwk", this.creds.signingKey, {
|
|
13415
|
+
name: "ECDSA",
|
|
13416
|
+
namedCurve: "P-256"
|
|
13417
|
+
}, false, ["sign"]);
|
|
13418
|
+
return this.key;
|
|
13419
|
+
}
|
|
13420
|
+
async exchange() {
|
|
13421
|
+
const form = new URLSearchParams({
|
|
13422
|
+
client_id: this.creds.workloadId,
|
|
13423
|
+
scope: "connect"
|
|
13424
|
+
});
|
|
13425
|
+
if (this.creds.platformTokenFile) {
|
|
13426
|
+
const platformToken = readFileSync(this.creds.platformTokenFile, "utf8").trim();
|
|
13427
|
+
form.set("grant_type", TOKEN_EXCHANGE_GRANT);
|
|
13428
|
+
form.set("subject_token_type", JWT_TOKEN_TYPE);
|
|
13429
|
+
form.set("subject_token", platformToken);
|
|
13430
|
+
} else {
|
|
13431
|
+
form.set("grant_type", "client_credentials");
|
|
13432
|
+
form.set("client_assertion_type", CLIENT_ASSERTION_TYPE);
|
|
13433
|
+
form.set("client_assertion", await signClientAssertion(this.creds, await this.signingKey(), Math.floor(this.now() / 1e3)));
|
|
13434
|
+
}
|
|
13435
|
+
const response = await this.fetcher(`${this.creds.issuer}/oauth/token`, {
|
|
13436
|
+
method: "POST",
|
|
13437
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
13438
|
+
body: form
|
|
13439
|
+
});
|
|
13440
|
+
if (!response.ok) throw new Error(`workload sign-in failed: ${await errorText(response)}`);
|
|
13441
|
+
const body = await response.json();
|
|
13442
|
+
this.session = {
|
|
13443
|
+
token: body.access_token,
|
|
13444
|
+
expiresAt: this.now() + body.expires_in * 1e3
|
|
13445
|
+
};
|
|
13446
|
+
return body.access_token;
|
|
13447
|
+
}
|
|
13448
|
+
async accessToken() {
|
|
13449
|
+
if (this.session && this.session.expiresAt - 3e4 > this.now()) return this.session.token;
|
|
13450
|
+
return this.exchange();
|
|
13451
|
+
}
|
|
13452
|
+
/** An identity token for one enrolled audience. */
|
|
13453
|
+
async identityToken(audience) {
|
|
13454
|
+
for (let attempt = 0;; attempt++) {
|
|
13455
|
+
const response = await this.fetcher(`${this.creds.issuer}/v1/connect/identity-token`, {
|
|
13456
|
+
method: "POST",
|
|
13457
|
+
headers: {
|
|
13458
|
+
authorization: `Bearer ${await this.accessToken()}`,
|
|
13459
|
+
"content-type": "application/json"
|
|
13460
|
+
},
|
|
13461
|
+
body: JSON.stringify({ audience })
|
|
13462
|
+
});
|
|
13463
|
+
if (response.status === 401 && attempt === 0) {
|
|
13464
|
+
this.session = void 0;
|
|
13465
|
+
continue;
|
|
13466
|
+
}
|
|
13467
|
+
if (!response.ok) throw new Error(`identity token for "${audience}" refused: ${await errorText(response)}`);
|
|
13468
|
+
return await response.json();
|
|
13469
|
+
}
|
|
13470
|
+
}
|
|
13471
|
+
};
|
|
13472
|
+
/** The audience each provider's own docs use; any enrolled audience can override it. */
|
|
13473
|
+
const DEFAULT_AUDIENCES = {
|
|
13474
|
+
aws: "sts.amazonaws.com",
|
|
13475
|
+
openai: "https://api.openai.com/v1",
|
|
13476
|
+
anthropic: "https://api.anthropic.com"
|
|
13477
|
+
};
|
|
13478
|
+
/**
|
|
13479
|
+
* Credentials that outrank web identity in each provider's default chain. An
|
|
13480
|
+
* explicit `--aws`/`--anthropic`/… means "use the federated identity", so a
|
|
13481
|
+
* long-lived key left in the shell must not quietly win — the same rule as
|
|
13482
|
+
* `seekrit run --lease`.
|
|
13483
|
+
*/
|
|
13484
|
+
const SHADOWING = {
|
|
13485
|
+
aws: [
|
|
13486
|
+
"AWS_ACCESS_KEY_ID",
|
|
13487
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
13488
|
+
"AWS_SESSION_TOKEN",
|
|
13489
|
+
"AWS_PROFILE"
|
|
13490
|
+
],
|
|
13491
|
+
gcp: [],
|
|
13492
|
+
openai: ["OPENAI_API_KEY"],
|
|
13493
|
+
anthropic: [
|
|
13494
|
+
"ANTHROPIC_API_KEY",
|
|
13495
|
+
"ANTHROPIC_AUTH_TOKEN",
|
|
13496
|
+
"ANTHROPIC_PROFILE",
|
|
13497
|
+
"ANTHROPIC_IDENTITY_TOKEN"
|
|
13498
|
+
]
|
|
13499
|
+
};
|
|
13500
|
+
/** `projects/N/locations/global/workloadIdentityPools/P/providers/R`, from any of its spellings. */
|
|
13501
|
+
function normalizeGcpProvider(input) {
|
|
13502
|
+
const resource = input.trim().replace(/^https:\/\/iam\.googleapis\.com\//, "").replace(/^\/\/iam\.googleapis\.com\//, "");
|
|
13503
|
+
if (!/^projects\/\d+\/locations\/global\/workloadIdentityPools\/[a-z0-9-]{4,32}\/providers\/[a-z0-9-]{4,32}$/.test(resource)) throw new Error(`--gcp must name a workload identity pool provider by project number: projects/<number>/locations/global/workloadIdentityPools/<pool>/providers/<provider> (got "${input}")`);
|
|
13504
|
+
return resource;
|
|
13505
|
+
}
|
|
13506
|
+
function parseAudienceOverrides(values = []) {
|
|
13507
|
+
const out = {};
|
|
13508
|
+
for (const value of values) {
|
|
13509
|
+
const at = value.indexOf("=");
|
|
13510
|
+
const id = value.slice(0, at);
|
|
13511
|
+
const audience = value.slice(at + 1).trim();
|
|
13512
|
+
if (at < 1 || ![
|
|
13513
|
+
"aws",
|
|
13514
|
+
"gcp",
|
|
13515
|
+
"openai",
|
|
13516
|
+
"anthropic"
|
|
13517
|
+
].includes(id) || !audience) throw new Error(`--audience takes provider=audience, e.g. anthropic=api.anthropic.com`);
|
|
13518
|
+
if (out[id]) throw new Error(`--audience given twice for ${id}`);
|
|
13519
|
+
out[id] = audience;
|
|
13520
|
+
}
|
|
13521
|
+
return out;
|
|
13522
|
+
}
|
|
13523
|
+
/**
|
|
13524
|
+
* What to write and export for each requested provider. Pure: paths are inside
|
|
13525
|
+
* `dir`, which the caller creates (mode 0700) and removes.
|
|
13526
|
+
*/
|
|
13527
|
+
function planProviders(opts, dir, workloadId, env = process.env) {
|
|
13528
|
+
const audiences = parseAudienceOverrides(opts.audience);
|
|
13529
|
+
const plans = [];
|
|
13530
|
+
if (opts.aws !== void 0) {
|
|
13531
|
+
if (!/^arn:aws[a-z-]*:iam::\d{12}:role\/[\w+=,.@/-]+$/.test(opts.aws.trim())) throw new Error(`--aws must be an IAM role ARN, got "${opts.aws}"`);
|
|
13532
|
+
const tokenFile = join(dir, "aws-token");
|
|
13533
|
+
plans.push({
|
|
13534
|
+
id: "aws",
|
|
13535
|
+
audience: audiences.aws ?? DEFAULT_AUDIENCES.aws,
|
|
13536
|
+
tokenFile,
|
|
13537
|
+
env: {
|
|
13538
|
+
AWS_ROLE_ARN: opts.aws.trim(),
|
|
13539
|
+
AWS_WEB_IDENTITY_TOKEN_FILE: tokenFile,
|
|
13540
|
+
AWS_ROLE_SESSION_NAME: present(env.AWS_ROLE_SESSION_NAME) ?? `seekrit-${workloadId}`.slice(0, 64)
|
|
13541
|
+
},
|
|
13542
|
+
unset: SHADOWING.aws,
|
|
13543
|
+
files: []
|
|
13544
|
+
});
|
|
13545
|
+
}
|
|
13546
|
+
if (opts.gcp !== void 0) {
|
|
13547
|
+
const resource = normalizeGcpProvider(opts.gcp);
|
|
13548
|
+
const tokenFile = join(dir, "gcp-token");
|
|
13549
|
+
const configFile = join(dir, "gcp-credentials.json");
|
|
13550
|
+
const sa = present(opts.gcpServiceAccount);
|
|
13551
|
+
if (sa && !/^[a-z0-9-]+@[a-z0-9.-]+\.gserviceaccount\.com$/.test(sa)) throw new Error(`--gcp-service-account must be a service account email, got "${sa}"`);
|
|
13552
|
+
const config = {
|
|
13553
|
+
type: "external_account",
|
|
13554
|
+
audience: `//iam.googleapis.com/${resource}`,
|
|
13555
|
+
subject_token_type: JWT_TOKEN_TYPE,
|
|
13556
|
+
token_url: "https://sts.googleapis.com/v1/token",
|
|
13557
|
+
credential_source: {
|
|
13558
|
+
file: tokenFile,
|
|
13559
|
+
format: { type: "text" }
|
|
13560
|
+
},
|
|
13561
|
+
...sa ? { service_account_impersonation_url: `https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${sa}:generateAccessToken` } : {}
|
|
13562
|
+
};
|
|
13563
|
+
plans.push({
|
|
13564
|
+
id: "gcp",
|
|
13565
|
+
audience: audiences.gcp ?? `https://iam.googleapis.com/${resource}`,
|
|
13566
|
+
tokenFile,
|
|
13567
|
+
env: {
|
|
13568
|
+
GOOGLE_APPLICATION_CREDENTIALS: configFile,
|
|
13569
|
+
CLOUDSDK_AUTH_CREDENTIAL_FILE_OVERRIDE: configFile
|
|
13570
|
+
},
|
|
13571
|
+
unset: SHADOWING.gcp,
|
|
13572
|
+
files: [{
|
|
13573
|
+
path: configFile,
|
|
13574
|
+
content: `${JSON.stringify(config, null, 2)}\n`
|
|
13575
|
+
}]
|
|
13576
|
+
});
|
|
13577
|
+
} else if (opts.gcpServiceAccount) throw new Error("--gcp-service-account needs --gcp");
|
|
13578
|
+
if (opts.openai) {
|
|
13579
|
+
const tokenFile = join(dir, "openai-token");
|
|
13580
|
+
plans.push({
|
|
13581
|
+
id: "openai",
|
|
13582
|
+
audience: audiences.openai ?? DEFAULT_AUDIENCES.openai,
|
|
13583
|
+
tokenFile,
|
|
13584
|
+
env: { OPENAI_IDENTITY_TOKEN_FILE: tokenFile },
|
|
13585
|
+
unset: SHADOWING.openai,
|
|
13586
|
+
files: []
|
|
13587
|
+
});
|
|
13588
|
+
}
|
|
13589
|
+
if (opts.anthropic) {
|
|
13590
|
+
const tokenFile = join(dir, "anthropic-token");
|
|
13591
|
+
plans.push({
|
|
13592
|
+
id: "anthropic",
|
|
13593
|
+
audience: audiences.anthropic ?? DEFAULT_AUDIENCES.anthropic,
|
|
13594
|
+
tokenFile,
|
|
13595
|
+
env: { ANTHROPIC_IDENTITY_TOKEN_FILE: tokenFile },
|
|
13596
|
+
unset: SHADOWING.anthropic,
|
|
13597
|
+
files: []
|
|
13598
|
+
});
|
|
13599
|
+
}
|
|
13600
|
+
for (const id of Object.keys(audiences)) if (!plans.some((p) => p.id === id)) throw new Error(`--audience ${id}=… without --${id}`);
|
|
13601
|
+
if (plans.length === 0) throw new Error("name at least one provider: --aws <role-arn>, --gcp <provider>, --openai, --anthropic");
|
|
13602
|
+
return plans;
|
|
13603
|
+
}
|
|
13604
|
+
/**
|
|
13605
|
+
* The child's environment: shadowing credentials removed, provider variables
|
|
13606
|
+
* set. Returns the names it removed (never values) so the caller can say so.
|
|
13607
|
+
*/
|
|
13608
|
+
function childEnvironment(base, plans) {
|
|
13609
|
+
const env = { ...base };
|
|
13610
|
+
const removed = [];
|
|
13611
|
+
for (const plan of plans) for (const name of plan.unset) if (env[name] !== void 0) {
|
|
13612
|
+
delete env[name];
|
|
13613
|
+
removed.push(name);
|
|
13614
|
+
}
|
|
13615
|
+
for (const plan of plans) Object.assign(env, plan.env);
|
|
13616
|
+
return {
|
|
13617
|
+
env,
|
|
13618
|
+
removed
|
|
13619
|
+
};
|
|
13620
|
+
}
|
|
13621
|
+
/** Variables a provider SDK needs from the operator before it will federate at all. */
|
|
13622
|
+
function missingProviderSettings(plans, env) {
|
|
13623
|
+
const missing = [];
|
|
13624
|
+
if (plans.some((p) => p.id === "anthropic")) {
|
|
13625
|
+
for (const name of ["ANTHROPIC_FEDERATION_RULE_ID", "ANTHROPIC_ORGANIZATION_ID"]) if (!present(env[name])) missing.push(name);
|
|
13626
|
+
}
|
|
13627
|
+
return missing;
|
|
13628
|
+
}
|
|
13629
|
+
/** Write-then-rename, so a reader never sees half a token. */
|
|
13630
|
+
function writeAtomic(path, content) {
|
|
13631
|
+
const tmp = `${path}.${crypto.randomUUID()}.tmp`;
|
|
13632
|
+
writeFileSync(tmp, content, { mode: 384 });
|
|
13633
|
+
renameSync(tmp, path);
|
|
13634
|
+
}
|
|
13635
|
+
/**
|
|
13636
|
+
* Keeps each provider's token file current. Refreshes at half the token's
|
|
13637
|
+
* lifetime, so a provider SDK rereading the file always finds a token with
|
|
13638
|
+
* plenty of life left — and, for Anthropic, which accepts each token's `jti`
|
|
13639
|
+
* only once, a fresh one by the time it next exchanges.
|
|
13640
|
+
*/
|
|
13641
|
+
var TokenFileRefresher = class {
|
|
13642
|
+
client;
|
|
13643
|
+
plans;
|
|
13644
|
+
timers = /* @__PURE__ */ new Map();
|
|
13645
|
+
expiries = /* @__PURE__ */ new Map();
|
|
13646
|
+
stopped = false;
|
|
13647
|
+
log;
|
|
13648
|
+
minIntervalMs;
|
|
13649
|
+
setTimer;
|
|
13650
|
+
clearTimer;
|
|
13651
|
+
now;
|
|
13652
|
+
constructor(client, plans, opts = {}) {
|
|
13653
|
+
this.client = client;
|
|
13654
|
+
this.plans = plans;
|
|
13655
|
+
this.log = opts.log ?? ((m) => console.error(m));
|
|
13656
|
+
this.minIntervalMs = opts.minIntervalMs ?? 3e4;
|
|
13657
|
+
this.setTimer = opts.setTimer ?? ((fn, ms) => setTimeout(fn, ms));
|
|
13658
|
+
this.clearTimer = opts.clearTimer ?? ((t) => clearTimeout(t));
|
|
13659
|
+
this.now = opts.now ?? Date.now;
|
|
13660
|
+
}
|
|
13661
|
+
/** Fetch every token once. Throws if any provider's first token cannot be had. */
|
|
13662
|
+
async start() {
|
|
13663
|
+
for (const plan of this.plans) for (const file of plan.files) writeAtomic(file.path, file.content);
|
|
13664
|
+
await Promise.all(this.plans.map((plan) => this.refresh(plan, true)));
|
|
13665
|
+
}
|
|
13666
|
+
async refresh(plan, first = false) {
|
|
13667
|
+
if (this.stopped) return;
|
|
13668
|
+
try {
|
|
13669
|
+
const issued = await this.client.identityToken(plan.audience);
|
|
13670
|
+
if (this.stopped) return;
|
|
13671
|
+
writeAtomic(plan.tokenFile, issued.token);
|
|
13672
|
+
this.expiries.set(plan.id, this.now() + issued.expiresIn * 1e3);
|
|
13673
|
+
this.schedule(plan, Math.max(this.minIntervalMs, issued.expiresIn * 1e3 / 2));
|
|
13674
|
+
} catch (err) {
|
|
13675
|
+
if (first) throw err;
|
|
13676
|
+
const left = (this.expiries.get(plan.id) ?? 0) - this.now();
|
|
13677
|
+
this.log(`seekrit: could not refresh the ${plan.id} identity token (${err instanceof Error ? err.message : String(err)}); ${left > 0 ? `the current one is valid for ${Math.round(left / 1e3)}s more, retrying` : "the current one has expired, retrying"}`);
|
|
13678
|
+
this.schedule(plan, Math.min(this.minIntervalMs, Math.max(1e3, left / 4)));
|
|
13679
|
+
}
|
|
13680
|
+
}
|
|
13681
|
+
schedule(plan, ms) {
|
|
13682
|
+
if (this.stopped) return;
|
|
13683
|
+
const timer = this.setTimer(() => void this.refresh(plan), ms);
|
|
13684
|
+
timer.unref?.();
|
|
13685
|
+
this.timers.set(plan.id, timer);
|
|
13686
|
+
}
|
|
13687
|
+
stop() {
|
|
13688
|
+
this.stopped = true;
|
|
13689
|
+
for (const timer of this.timers.values()) this.clearTimer(timer);
|
|
13690
|
+
this.timers.clear();
|
|
13691
|
+
}
|
|
13692
|
+
};
|
|
13693
|
+
/**
|
|
13694
|
+
* Run `cmd` with provider token files kept fresh for as long as it lives. The
|
|
13695
|
+
* token directory is private (0700, files 0600) and removed afterwards.
|
|
13696
|
+
*/
|
|
13697
|
+
async function runWithIdentity(cmd, args, client, opts) {
|
|
13698
|
+
const log = opts.log ?? ((m) => console.error(m));
|
|
13699
|
+
const dir = mkdtempSync(join(tmpdir(), "seekrit-identity-"));
|
|
13700
|
+
let refresher;
|
|
13701
|
+
try {
|
|
13702
|
+
const plans = planProviders(opts, dir, opts.workloadId);
|
|
13703
|
+
for (const name of missingProviderSettings(plans, process.env)) log(`seekrit: ${name} is not set; the provider SDK will not use the federated token without it`);
|
|
13704
|
+
return await runCommand(cmd, args, async () => {
|
|
13705
|
+
refresher = new TokenFileRefresher(client, plans, { log });
|
|
13706
|
+
await refresher.start();
|
|
13707
|
+
const { env, removed } = childEnvironment(process.env, plans);
|
|
13708
|
+
if (removed.length > 0) log(`seekrit: unset ${removed.join(", ")} for the command so the federated identity is used`);
|
|
13709
|
+
return env;
|
|
13710
|
+
}, async () => {
|
|
13711
|
+
refresher?.stop();
|
|
13712
|
+
rmSync(dir, {
|
|
13713
|
+
recursive: true,
|
|
13714
|
+
force: true
|
|
13715
|
+
});
|
|
13716
|
+
return true;
|
|
13717
|
+
});
|
|
13718
|
+
} catch (err) {
|
|
13719
|
+
rmSync(dir, {
|
|
13720
|
+
recursive: true,
|
|
13721
|
+
force: true
|
|
13722
|
+
});
|
|
13723
|
+
throw err;
|
|
13724
|
+
}
|
|
13725
|
+
}
|
|
13726
|
+
function collect$7(value, previous = []) {
|
|
13727
|
+
return [...previous, value];
|
|
13728
|
+
}
|
|
13729
|
+
function clientFrom(options) {
|
|
13730
|
+
try {
|
|
13731
|
+
const creds = loadWorkloadCredentials(options);
|
|
13732
|
+
return {
|
|
13733
|
+
client: new WorkloadClient(creds),
|
|
13734
|
+
workloadId: creds.workloadId
|
|
13735
|
+
};
|
|
13736
|
+
} catch (err) {
|
|
13737
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
13738
|
+
}
|
|
13739
|
+
}
|
|
13740
|
+
function credentialFlags(command) {
|
|
13741
|
+
return command.option("--workload-file <path>", "credentials downloaded at enrollment (default: $SEEKRIT_WORKLOAD_FILE)").option("--workload-id <id>", "workload id, for a platform-federated workload").option("--platform-token-file <path>", "platform JWT to exchange, reread on every sign-in (default: $SEEKRIT_PLATFORM_TOKEN_FILE)").option("--issuer <origin>", `seekrit API origin (default: the credential file's, or ${DEFAULT_ISSUER})`);
|
|
13742
|
+
}
|
|
13743
|
+
function registerIdentityCommands(program) {
|
|
13744
|
+
const identity = program.command("identity").description("keyless AWS, Google Cloud, OpenAI and Anthropic access from a seekrit workload identity");
|
|
13745
|
+
credentialFlags(identity.command("token").description("print a signed identity token for one enrolled audience").requiredOption("--audience <aud>", "an audience enrolled on the workload").option("--json", "print {token, expiresIn, issuer, subject} instead of the bare token")).action(async (options) => {
|
|
13746
|
+
const { client } = clientFrom(options);
|
|
13747
|
+
try {
|
|
13748
|
+
const issued = await client.identityToken(options.audience);
|
|
13749
|
+
console.log(options.json ? JSON.stringify(issued, null, 2) : issued.token);
|
|
13750
|
+
} catch (err) {
|
|
13751
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
13752
|
+
}
|
|
13753
|
+
});
|
|
13754
|
+
credentialFlags(identity.command("run").description("run a command with provider identity-token files kept fresh and the provider SDK variables set").passThroughOptions().option("--aws <role-arn>", "assume this IAM role via web identity").option("--gcp <provider>", "workload identity pool provider: projects/<number>/locations/global/workloadIdentityPools/<pool>/providers/<id>").option("--gcp-service-account <email>", "impersonate this service account after federating").option("--openai", "write OPENAI_IDENTITY_TOKEN_FILE").option("--anthropic", "write ANTHROPIC_IDENTITY_TOKEN_FILE").option("--audience <provider=aud>", "override a provider's default audience (repeatable)", collect$7).argument("<command...>", "command to run (prefix with -- to pass flags)")).action(async (commandParts, options) => {
|
|
13755
|
+
const [cmd, ...args] = commandParts;
|
|
13756
|
+
if (!cmd) fail("no command given");
|
|
13757
|
+
const { client, workloadId } = clientFrom(options);
|
|
13758
|
+
let result;
|
|
13759
|
+
try {
|
|
13760
|
+
result = await runWithIdentity(cmd, args, client, {
|
|
13761
|
+
...options,
|
|
13762
|
+
workloadId
|
|
13763
|
+
});
|
|
13764
|
+
} catch (err) {
|
|
13765
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
13766
|
+
}
|
|
13767
|
+
if (result.signal) process.kill(process.pid, result.signal);
|
|
13768
|
+
else process.exit(result.code);
|
|
13769
|
+
});
|
|
13770
|
+
}
|
|
13126
13771
|
// src/m2m.ts
|
|
13127
13772
|
/**
|
|
13128
13773
|
* Resolve M2M client credentials from (in order) the process environment, a
|
|
@@ -16171,176 +16816,6 @@ function registerRotationCommands(program) {
|
|
|
16171
16816
|
console.error(`disabled rotation of ${r.secretName}${rotatorRevoked ? " — rotator key access revoked for this environment" : ""}`);
|
|
16172
16817
|
});
|
|
16173
16818
|
}
|
|
16174
|
-
// src/run-command.ts
|
|
16175
|
-
/** Keep signal handlers installed from provisioning through asynchronous cleanup. */
|
|
16176
|
-
async function runCommand(cmd, args, prepare, cleanup) {
|
|
16177
|
-
let child;
|
|
16178
|
-
let forwarded;
|
|
16179
|
-
let result = { code: 1 };
|
|
16180
|
-
const stragglers = /* @__PURE__ */ new Set();
|
|
16181
|
-
const collect = () => {
|
|
16182
|
-
if (process.platform === "win32" || !child?.pid) return;
|
|
16183
|
-
for (const pid of teardownTargets(child.pid)) stragglers.add(pid);
|
|
16184
|
-
};
|
|
16185
|
-
const forward = (signal) => {
|
|
16186
|
-
forwarded ??= signal;
|
|
16187
|
-
if (!child || child.exitCode !== null || child.signalCode !== null) return;
|
|
16188
|
-
collect();
|
|
16189
|
-
child.kill(signal);
|
|
16190
|
-
for (const pid of stragglers) try {
|
|
16191
|
-
process.kill(pid, signal);
|
|
16192
|
-
} catch {}
|
|
16193
|
-
};
|
|
16194
|
-
const signals = [
|
|
16195
|
-
"SIGINT",
|
|
16196
|
-
"SIGTERM",
|
|
16197
|
-
"SIGHUP",
|
|
16198
|
-
"SIGQUIT"
|
|
16199
|
-
];
|
|
16200
|
-
for (const signal of signals) process.on(signal, forward);
|
|
16201
|
-
try {
|
|
16202
|
-
const env = await prepare(() => forwarded !== void 0);
|
|
16203
|
-
if (!forwarded) {
|
|
16204
|
-
const running = spawn(cmd, args, {
|
|
16205
|
-
stdio: "inherit",
|
|
16206
|
-
env
|
|
16207
|
-
});
|
|
16208
|
-
child = running;
|
|
16209
|
-
result = await new Promise((resolve, reject) => {
|
|
16210
|
-
running.once("exit", (code, signal) => resolve({
|
|
16211
|
-
code: code ?? 1,
|
|
16212
|
-
signal: signal ?? void 0
|
|
16213
|
-
}));
|
|
16214
|
-
running.once("error", (err) => reject(/* @__PURE__ */ new Error(`failed to start ${cmd}: ${err.message}`)));
|
|
16215
|
-
});
|
|
16216
|
-
}
|
|
16217
|
-
} finally {
|
|
16218
|
-
try {
|
|
16219
|
-
if (forwarded) {
|
|
16220
|
-
collect();
|
|
16221
|
-
await reapStragglers(stragglers, forwarded);
|
|
16222
|
-
}
|
|
16223
|
-
} finally {
|
|
16224
|
-
try {
|
|
16225
|
-
if (!await cleanup() && result.code === 0) result.code = 1;
|
|
16226
|
-
} finally {
|
|
16227
|
-
for (const signal of signals) process.off(signal, forward);
|
|
16228
|
-
}
|
|
16229
|
-
}
|
|
16230
|
-
}
|
|
16231
|
-
if (!child && forwarded) result.signal = forwarded;
|
|
16232
|
-
return result;
|
|
16233
|
-
}
|
|
16234
|
-
/**
|
|
16235
|
-
* Every live process `seekrit run` is responsible for tearing down, from a
|
|
16236
|
-
* single `ps(1)` snapshot: each descendant of `root`, plus — when we lead our
|
|
16237
|
-
* own process group — every other member of that group.
|
|
16238
|
-
*
|
|
16239
|
-
* Two mechanisms, because each covers the other's blind spot:
|
|
16240
|
-
*
|
|
16241
|
-
* - The parent-chain walk reaches a process that left our group via
|
|
16242
|
-
* setsid(2)/setpgid(2), which no group-wide kill and no tty-generated signal
|
|
16243
|
-
* can touch. But it loses anything whose chain up to `root` has already
|
|
16244
|
-
* broken — teardown kills the middle layers first, and an orphan reparents to
|
|
16245
|
-
* init, out of reach of any walk down from `root`.
|
|
16246
|
-
* - Group membership survives exactly that: a pgid outlives the parent that
|
|
16247
|
-
* passed it down. But it misses the ones that left the group.
|
|
16248
|
-
*
|
|
16249
|
-
* The group half applies only when our pid *is* our pgid, which is the shell-job
|
|
16250
|
-
* case: the shell put this command in a new group, so every other member of it
|
|
16251
|
-
* descends from us. Nested inside a shell script or a CI runner we are not the
|
|
16252
|
-
* leader and the group holds processes we did not start, so there we use the
|
|
16253
|
-
* walk alone.
|
|
16254
|
-
*
|
|
16255
|
-
* Zombies are skipped: they are already dead and waiting to be reaped, so
|
|
16256
|
-
* counting them would stall teardown for the full grace period and then report
|
|
16257
|
-
* a kill that did nothing. Returns nothing if `ps` is unavailable; callers fall
|
|
16258
|
-
* back to signalling the immediate child.
|
|
16259
|
-
*/
|
|
16260
|
-
function teardownTargets(root) {
|
|
16261
|
-
const ps = spawnSync("ps", [
|
|
16262
|
-
"-A",
|
|
16263
|
-
"-o",
|
|
16264
|
-
"pid=,ppid=,pgid=,stat="
|
|
16265
|
-
], { encoding: "utf8" });
|
|
16266
|
-
if (ps.status !== 0 || typeof ps.stdout !== "string") return [];
|
|
16267
|
-
const childrenOf = /* @__PURE__ */ new Map();
|
|
16268
|
-
const groupOf = /* @__PURE__ */ new Map();
|
|
16269
|
-
const isId = (value) => value !== void 0 && Number.isInteger(value);
|
|
16270
|
-
for (const line of ps.stdout.split("\n")) {
|
|
16271
|
-
const [pid, ppid, pgid, stat] = line.trim().split(/\s+/, 4);
|
|
16272
|
-
const [id, parent, group] = [
|
|
16273
|
-
pid,
|
|
16274
|
-
ppid,
|
|
16275
|
-
pgid
|
|
16276
|
-
].map(Number);
|
|
16277
|
-
if (!isId(id) || !isId(parent) || !isId(group)) continue;
|
|
16278
|
-
if (stat?.startsWith("Z")) continue;
|
|
16279
|
-
const siblings = childrenOf.get(parent);
|
|
16280
|
-
if (siblings) siblings.push(id);
|
|
16281
|
-
else childrenOf.set(parent, [id]);
|
|
16282
|
-
groupOf.set(id, group);
|
|
16283
|
-
}
|
|
16284
|
-
const found = /* @__PURE__ */ new Set();
|
|
16285
|
-
const queue = [root];
|
|
16286
|
-
const seen = new Set(queue);
|
|
16287
|
-
for (let pid = queue.shift(); pid !== void 0; pid = queue.shift()) for (const kid of childrenOf.get(pid) ?? []) {
|
|
16288
|
-
if (seen.has(kid)) continue;
|
|
16289
|
-
seen.add(kid);
|
|
16290
|
-
found.add(kid);
|
|
16291
|
-
queue.push(kid);
|
|
16292
|
-
}
|
|
16293
|
-
if (groupOf.get(process.pid) === process.pid) {
|
|
16294
|
-
for (const [pid, group] of groupOf) if (group === process.pid) found.add(pid);
|
|
16295
|
-
}
|
|
16296
|
-
found.delete(process.pid);
|
|
16297
|
-
found.delete(root);
|
|
16298
|
-
return [...found];
|
|
16299
|
-
}
|
|
16300
|
-
/** Whether `pid` still exists — signal 0 tests reachability, delivers nothing. */
|
|
16301
|
-
function isAlive(pid) {
|
|
16302
|
-
try {
|
|
16303
|
-
process.kill(pid, 0);
|
|
16304
|
-
return true;
|
|
16305
|
-
} catch {
|
|
16306
|
-
return false;
|
|
16307
|
-
}
|
|
16308
|
-
}
|
|
16309
|
-
/** How long a straggler gets to finish shutting down before SIGKILL. */
|
|
16310
|
-
const STRAGGLER_GRACE_MS = 2e3;
|
|
16311
|
-
/**
|
|
16312
|
-
* Once the command has exited, make sure nothing it started outlives us.
|
|
16313
|
-
*
|
|
16314
|
-
* Anything still alive here has ignored the signal we forwarded *and* lost the
|
|
16315
|
-
* parent that launched it, and we are the last process that knows its pid:
|
|
16316
|
-
* the tty won't hang it up (it is no longer in the foreground group, and the
|
|
16317
|
-
* shell has moved on), so it survives until the machine reboots. That is how
|
|
16318
|
-
* `tsx` and the server under it stacked up for days.
|
|
16319
|
-
*
|
|
16320
|
-
* So escalate rather than trust. SIGTERM first — it costs no extra time and is
|
|
16321
|
-
* a rung the SIGINT-only handlers common in dev servers don't catch — then
|
|
16322
|
-
* SIGKILL whatever is left after the grace period. Nothing is killed unless it
|
|
16323
|
-
* was asked to stop first and declined, and this runs only when we forwarded a
|
|
16324
|
-
* termination signal: a command that exits on its own having deliberately left a
|
|
16325
|
-
* daemon behind keeps working.
|
|
16326
|
-
*/
|
|
16327
|
-
async function reapStragglers(pids, signal) {
|
|
16328
|
-
let live = [...pids].filter(isAlive);
|
|
16329
|
-
if (live.length === 0) return;
|
|
16330
|
-
for (const pid of live) try {
|
|
16331
|
-
process.kill(pid, "SIGTERM");
|
|
16332
|
-
} catch {}
|
|
16333
|
-
const deadline = Date.now() + STRAGGLER_GRACE_MS;
|
|
16334
|
-
while (live.length > 0 && Date.now() < deadline) {
|
|
16335
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
16336
|
-
live = live.filter(isAlive);
|
|
16337
|
-
}
|
|
16338
|
-
if (live.length === 0) return;
|
|
16339
|
-
console.error(`seekrit: force-killed ${live.length} leftover process(es) that ignored ${signal} and SIGTERM (${live.join(", ")})`);
|
|
16340
|
-
for (const pid of live) try {
|
|
16341
|
-
process.kill(pid, "SIGKILL");
|
|
16342
|
-
} catch {}
|
|
16343
|
-
}
|
|
16344
16819
|
// src/run-leases.ts
|
|
16345
16820
|
/** Validate all flags before contacting the API or creating any database roles. */
|
|
16346
16821
|
function parseRunLeaseOptions(bindings = [], ttl) {
|
|
@@ -18000,6 +18475,7 @@ registerHermesCommands(program);
|
|
|
18000
18475
|
registerSshCommands(program);
|
|
18001
18476
|
registerAwsCommands(program);
|
|
18002
18477
|
registerGcpCommands(program);
|
|
18478
|
+
registerIdentityCommands(program);
|
|
18003
18479
|
registerMongoCommands(program);
|
|
18004
18480
|
registerKmsCommands(program);
|
|
18005
18481
|
registerRotationCommands(program);
|