primitive-admin 1.1.0-alpha.78 → 1.1.0-alpha.79

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.
Files changed (105) hide show
  1. package/README.md +38 -17
  2. package/assets/skill/skills/primitive-platform/SKILL.md +39 -52
  3. package/dist/bin/primitive.js +14 -25
  4. package/dist/bin/primitive.js.map +1 -1
  5. package/dist/src/commands/admins.js +8 -18
  6. package/dist/src/commands/admins.js.map +1 -1
  7. package/dist/src/commands/analytics.js +46 -5
  8. package/dist/src/commands/analytics.js.map +1 -1
  9. package/dist/src/commands/auth.js +16 -1
  10. package/dist/src/commands/auth.js.map +1 -1
  11. package/dist/src/commands/collection-type-configs.js +1 -9
  12. package/dist/src/commands/collection-type-configs.js.map +1 -1
  13. package/dist/src/commands/config.js +11 -29
  14. package/dist/src/commands/config.js.map +1 -1
  15. package/dist/src/commands/database-type-configs.js +1 -9
  16. package/dist/src/commands/database-type-configs.js.map +1 -1
  17. package/dist/src/commands/databases.js +8 -38
  18. package/dist/src/commands/databases.js.map +1 -1
  19. package/dist/src/commands/env.js +27 -32
  20. package/dist/src/commands/env.js.map +1 -1
  21. package/dist/src/commands/functions.js +197 -23
  22. package/dist/src/commands/functions.js.map +1 -1
  23. package/dist/src/commands/groups.js +1 -9
  24. package/dist/src/commands/groups.js.map +1 -1
  25. package/dist/src/commands/init.js +6 -0
  26. package/dist/src/commands/init.js.map +1 -1
  27. package/dist/src/commands/rule-sets.js +1 -9
  28. package/dist/src/commands/rule-sets.js.map +1 -1
  29. package/dist/src/commands/scripts.js +12 -28
  30. package/dist/src/commands/scripts.js.map +1 -1
  31. package/dist/src/commands/sync.d.ts +119 -0
  32. package/dist/src/commands/sync.js +387 -196
  33. package/dist/src/commands/sync.js.map +1 -1
  34. package/dist/src/commands/workflows.js +2 -14
  35. package/dist/src/commands/workflows.js.map +1 -1
  36. package/dist/src/lib/api-client.d.ts +46 -11
  37. package/dist/src/lib/api-client.js +21 -11
  38. package/dist/src/lib/api-client.js.map +1 -1
  39. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.d.ts +24 -57
  40. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js +30 -204
  41. package/dist/src/lib/codegen-shared/resolveCodegenSourceDir.js.map +1 -1
  42. package/dist/src/lib/config-json-field.d.ts +28 -0
  43. package/dist/src/lib/config-json-field.js +56 -0
  44. package/dist/src/lib/config-json-field.js.map +1 -0
  45. package/dist/src/lib/config-object-descriptor.js +4 -3
  46. package/dist/src/lib/config-object-descriptor.js.map +1 -1
  47. package/dist/src/lib/config-surface.js +1 -1
  48. package/dist/src/lib/config-surface.js.map +1 -1
  49. package/dist/src/lib/config.d.ts +21 -14
  50. package/dist/src/lib/config.js +25 -44
  51. package/dist/src/lib/config.js.map +1 -1
  52. package/dist/src/lib/credentials-store.d.ts +31 -8
  53. package/dist/src/lib/credentials-store.js +52 -13
  54. package/dist/src/lib/credentials-store.js.map +1 -1
  55. package/dist/src/lib/env-resolver-core.d.ts +18 -0
  56. package/dist/src/lib/env-resolver-core.js +26 -3
  57. package/dist/src/lib/env-resolver-core.js.map +1 -1
  58. package/dist/src/lib/env-resolver.d.ts +15 -0
  59. package/dist/src/lib/env-resolver.js +21 -1
  60. package/dist/src/lib/env-resolver.js.map +1 -1
  61. package/dist/src/lib/function-collect.d.ts +1 -1
  62. package/dist/src/lib/function-collect.js +138 -7
  63. package/dist/src/lib/function-collect.js.map +1 -1
  64. package/dist/src/lib/function-db-types.d.ts +49 -11
  65. package/dist/src/lib/function-db-types.js +234 -39
  66. package/dist/src/lib/function-db-types.js.map +1 -1
  67. package/dist/src/lib/function-document-types.d.ts +50 -0
  68. package/dist/src/lib/function-document-types.js +163 -0
  69. package/dist/src/lib/function-document-types.js.map +1 -0
  70. package/dist/src/lib/function-log-tail.d.ts +83 -0
  71. package/dist/src/lib/function-log-tail.js +115 -0
  72. package/dist/src/lib/function-log-tail.js.map +1 -0
  73. package/dist/src/lib/function-schema-codegen.d.ts +118 -0
  74. package/dist/src/lib/function-schema-codegen.js +402 -0
  75. package/dist/src/lib/function-schema-codegen.js.map +1 -0
  76. package/dist/src/lib/function-sync.d.ts +85 -2
  77. package/dist/src/lib/function-sync.js +196 -27
  78. package/dist/src/lib/function-sync.js.map +1 -1
  79. package/dist/src/lib/function-triggers.d.ts +9 -3
  80. package/dist/src/lib/function-triggers.js +12 -9
  81. package/dist/src/lib/function-triggers.js.map +1 -1
  82. package/dist/src/lib/function-typecheck.d.ts +86 -0
  83. package/dist/src/lib/function-typecheck.js +370 -0
  84. package/dist/src/lib/function-typecheck.js.map +1 -0
  85. package/dist/src/lib/generated-allowlist.js +4 -0
  86. package/dist/src/lib/generated-allowlist.js.map +1 -1
  87. package/dist/src/lib/generated-config-surfaces.d.ts +422 -0
  88. package/dist/src/lib/generated-config-surfaces.js +838 -9
  89. package/dist/src/lib/generated-config-surfaces.js.map +1 -1
  90. package/dist/src/lib/generated-sdk-types.d.ts +1 -1
  91. package/dist/src/lib/generated-sdk-types.js +1 -1
  92. package/dist/src/lib/generated-sdk-types.js.map +1 -1
  93. package/dist/src/lib/log-inspection.d.ts +116 -4
  94. package/dist/src/lib/log-inspection.js +147 -2
  95. package/dist/src/lib/log-inspection.js.map +1 -1
  96. package/dist/src/lib/snapshots.d.ts +6 -7
  97. package/dist/src/lib/snapshots.js +18 -81
  98. package/dist/src/lib/snapshots.js.map +1 -1
  99. package/dist/src/lib/sync-paths.d.ts +29 -53
  100. package/dist/src/lib/sync-paths.js +46 -97
  101. package/dist/src/lib/sync-paths.js.map +1 -1
  102. package/dist/src/lib/workflow-toml-validator.d.ts +8 -3
  103. package/dist/src/lib/workflow-toml-validator.js +8 -3
  104. package/dist/src/lib/workflow-toml-validator.js.map +1 -1
  105. package/package.json +3 -3
@@ -871,10 +871,38 @@ export declare const MAX_MANIFEST_MODELS = 200;
871
871
  export declare const MAX_MANIFEST_FAMILIES = 40;
872
872
  export interface ManifestParam {
873
873
  type?: string;
874
+ /** `type: "array"` — the element type, when declared (#3281). */
875
+ items?: {
876
+ type: string;
877
+ };
874
878
  caller?: boolean;
875
879
  optional?: boolean;
876
880
  default?: unknown;
877
881
  }
882
+ /** The scalar parameter types a registration may declare, and an array's element types. */
883
+ export declare const QUERY_PARAM_SCALAR_TYPES: readonly ["string", "number", "boolean", "any"];
884
+ /**
885
+ * Coerce one value to a declared parameter type the way the SDK coerces a
886
+ * SUPPLIED value: a digit string becomes a number, `"true"`/`"false"` a
887
+ * boolean, an array's elements each by the `items` type. Applied to a
888
+ * declared `default` at registration (D3281-SO-005) so what `run` receives is
889
+ * what the declaration promises — here at push (the collect stub and this
890
+ * grammar) and in the SDK at the first load. ONE rule, spelled in the SDK's
891
+ * source a second time because that module is baked text; the tests hold the
892
+ * two together.
893
+ */
894
+ export declare function coerceParamDefault(spec: {
895
+ type?: string;
896
+ items?: {
897
+ type?: string;
898
+ };
899
+ }, value: unknown): {
900
+ ok: true;
901
+ value: unknown;
902
+ } | {
903
+ ok: false;
904
+ reason: string;
905
+ };
878
906
  export interface ManifestQuery {
879
907
  name: string;
880
908
  kind: "query" | "mutation";
@@ -918,6 +946,400 @@ export interface ParsedManifest {
918
946
  * and whether the author meant any of it are questions this cannot answer.
919
947
  */
920
948
  export declare function parseFunctionManifest(value: unknown): ParsedManifest;
949
+ /**
950
+ * The function MODE vocabulary, and the one place its two spellings meet —
951
+ * #3281, project `server-functions` phase 4.
952
+ *
953
+ * `mode = "request" | "task"` is what an author writes (the intent, "Mode
954
+ * vocabulary?", sponsor decision 2026-09-09): a request function runs inside
955
+ * the call and returns its result; a task function returns a run id and
956
+ * survives restarts. `durable = true | false` is the key the first releases
957
+ * taught, and it stays ACCEPTED as an alias through the transition — a tree
958
+ * pushed with it keeps pushing, and pulls back byte for byte — while `durable`
959
+ * stays the adjective for the property itself.
960
+ *
961
+ * ── Why one module ───────────────────────────────────────────────────────
962
+ *
963
+ * Two spellings for one fact is a place two readers can disagree. So the
964
+ * resolution is written once, here, in the dependency-free tree the CLI
965
+ * vendors (`cli/scripts/gen-config-surfaces.mjs`), and every door reads it:
966
+ * the CLI preflight and diff entity, the server's trigger derivation, the
967
+ * admin config-version route (payload `mode` beside payload `durable`), the
968
+ * invoke branch and the trigger fires. A file — or a payload — that carries
969
+ * both keys has to have them AGREE, or it is refused naming both; a silent
970
+ * winner would be the one thing worse than two spellings.
971
+ *
972
+ * The stored column is still `ServerFunctionConfig.durable` (no `models.yaml`
973
+ * change before phase 7's rename sweep), which is why `configMode` exists:
974
+ * reading the row is the other half of the same translation.
975
+ */
976
+ export type FunctionMode = "request" | "task";
977
+ export declare const FUNCTION_MODES: readonly FunctionMode[];
978
+ /** What a declaration resolved to, or why it could not. */
979
+ export type DeclaredModeResolution = {
980
+ ok: true;
981
+ mode: FunctionMode;
982
+ /**
983
+ * Whether the declaration SAID anything — false when neither key was
984
+ * present and the default applied. The admin route's divergence check
985
+ * reads this: an omitted mode is no claim, so it cannot diverge.
986
+ */
987
+ declared: boolean;
988
+ } | {
989
+ ok: false;
990
+ error: string;
991
+ };
992
+ /**
993
+ * Resolve `mode` and its alias `durable` to one mode.
994
+ *
995
+ * Absent both → `request`. `mode` must be exactly `"request"` or `"task"`;
996
+ * `durable` must be a boolean. Both present and disagreeing is refused naming
997
+ * both keys, so an author who half-migrated a file hears which line to fix.
998
+ */
999
+ export declare function resolveDeclaredMode(input: {
1000
+ mode?: unknown;
1001
+ durable?: unknown;
1002
+ }): DeclaredModeResolution;
1003
+ /**
1004
+ * The mode a stored config-version row runs in.
1005
+ *
1006
+ * The column is `durable`; a row written before #3281 carries only that, and a
1007
+ * row written after it carries the same thing, so there is exactly one reading.
1008
+ */
1009
+ export declare function configMode(row: {
1010
+ durable?: unknown;
1011
+ } | null | undefined): FunctionMode;
1012
+ /** Is this mode the durable one? The adjective, for the paths that branch on it. */
1013
+ export declare function isDurableMode(mode: FunctionMode): boolean;
1014
+ /**
1015
+ * The trigger blocks a function declares, derived from its authored TOML
1016
+ * (#3181, project `server-functions`, criterion 4).
1017
+ *
1018
+ * ── The declaration is derived, never supplied ────────────────────────────
1019
+ *
1020
+ * A config-version push carries the authored `functions/<key>.toml` bytes
1021
+ * inside its envelope, and `envelopeHash` covers them. If the push ALSO
1022
+ * carried a parsed `triggers` payload, the two could disagree: a direct API
1023
+ * caller could ship a public webhook trigger the authored file does not
1024
+ * declare, and two pushes with identical envelopes but different trigger
1025
+ * payloads would collapse to one version. So the server parses the envelope's
1026
+ * own bytes here (D3181-005) and there is no payload field to disagree with —
1027
+ * trigger behavior is bound to the version's identity by construction.
1028
+ *
1029
+ * ── Shape only ────────────────────────────────────────────────────────────
1030
+ *
1031
+ * This module decides what the file SAYS: the accepted key set, the required
1032
+ * values, and the rules that need nothing but the document (scheme `none`,
1033
+ * a nameless or duplicated cron entry, an entry count past the per-function
1034
+ * cap, a webhook or database block on a task version). Everything that needs the
1035
+ * app's state — whether a referenced secret exists, whether the
1036
+ * scheme-specific config is valid, whether the app is at its webhook or cron
1037
+ * cap, whether a standalone webhook holds the key — belongs to the push
1038
+ * boundary, which runs the SAME validators the standalone create runs
1039
+ * (D3181-006).
1040
+ *
1041
+ * ── One implementation, two readers (#3320) ───────────────────────────────
1042
+ *
1043
+ * The CLI checks the same rules against the author's own file before a request
1044
+ * is made. It used to do that from a hand-written restatement
1045
+ * (`cli/src/lib/function-triggers.ts`), kept in step only by a paired-fixture
1046
+ * test — so a rule added here with no fixture drifted silently. The rules
1047
+ * therefore live in `src/config-surface/`, which `cli/scripts/
1048
+ * gen-config-surfaces.mjs` vendors verbatim into the committed artifact
1049
+ * `cli/src/lib/generated-config-surfaces.ts`. There is one implementation, and
1050
+ * a change here that is not regenerated fails `--check` (a build failure)
1051
+ * rather than a fixture list that happens not to cover it.
1052
+ *
1053
+ * Reading the authored BYTES is the one thing that stayed server-side:
1054
+ * `deriveTriggerDeclaration` in `src/server-functions/trigger-declaration.ts`
1055
+ * parses them and calls `normalizeTriggerDeclaration` here. This directory
1056
+ * imports nothing but itself (asserted by `config-surface-drift-guard.test.ts`),
1057
+ * and the CLI has already parsed its own file by the time it asks.
1058
+ */
1059
+ /**
1060
+ * The resolved mode the declaration is checked against (#3281).
1061
+ *
1062
+ * The rule is per KIND, and permanent (the intent, "Sync functions write a run
1063
+ * row?": *Webhook and database-change triggers require request mode; HTTP and
1064
+ * cron allow both*). A webhook delivery and a database write are both waiting
1065
+ * inside a request for the function to answer; a cron fire is waiting for
1066
+ * nothing, so it starts a task run when the version is a task (#3334).
1067
+ *
1068
+ * The caller resolves `mode`/`durable` once, through `resolveDeclaredMode`,
1069
+ * and hands the answer here — this module never reads the alias itself.
1070
+ */
1071
+ export interface TriggerModeOptions {
1072
+ mode?: FunctionMode;
1073
+ }
1074
+ /**
1075
+ * Why a webhook cannot drive a task function, and what to do instead (#3334).
1076
+ *
1077
+ * The first clause is verbatim what it has always been, so sibling pins on the
1078
+ * request-mode prefix (#3281's) keep holding; everything after the dash is the
1079
+ * half that was missing — an author who meets this at the push door could not
1080
+ * previously tell whether it was a limitation or a design, nor what to write.
1081
+ */
1082
+ export declare const WEBHOOK_ON_TASK_REFUSAL: string;
1083
+ /** A function's single webhook trigger, normalized. */
1084
+ export interface WebhookTriggerDeclaration {
1085
+ verificationScheme: string;
1086
+ signingSecret?: string;
1087
+ toleranceSeconds?: number;
1088
+ deduplicationEnabled?: boolean;
1089
+ deduplicationWindowMs?: number;
1090
+ maxBodyBytes?: number;
1091
+ secretGracePeriodMs?: number;
1092
+ /** `[function.triggers.webhook.verification]` — the row's `config`. */
1093
+ verification?: Record<string, unknown>;
1094
+ }
1095
+ /** One `[[function.triggers.cron]]` entry, normalized. */
1096
+ export interface CronTriggerDeclaration {
1097
+ name: string;
1098
+ cron: string;
1099
+ timezone?: string;
1100
+ overlapPolicy?: string;
1101
+ rootInput?: unknown;
1102
+ }
1103
+ /** One `[[function.triggers.database]]` entry, normalized (#3184). */
1104
+ export interface DatabaseTriggerDeclaration {
1105
+ /** The database type key whose writes fire this function. */
1106
+ type: string;
1107
+ }
1108
+ export interface TriggerDeclaration {
1109
+ webhook: WebhookTriggerDeclaration | null;
1110
+ cron: CronTriggerDeclaration[];
1111
+ databases: DatabaseTriggerDeclaration[];
1112
+ }
1113
+ export declare const EMPTY_TRIGGER_DECLARATION: TriggerDeclaration;
1114
+ /**
1115
+ * The most cron entries one function may declare.
1116
+ *
1117
+ * The per-app cap (50) is a resource ceiling; this is a per-OBJECT one, so a
1118
+ * single function cannot take most of an app's budget by itself. Ten is the
1119
+ * same order as the schedules a real function needs and leaves the app's
1120
+ * remaining slots for the other 40+ objects the cap is sized for.
1121
+ */
1122
+ export declare const MAX_CRON_TRIGGERS_PER_FUNCTION = 10;
1123
+ /**
1124
+ * The most database types one function may watch (#3184, D-07).
1125
+ *
1126
+ * Per-OBJECT, like the cron cap above: a function watching more than a handful
1127
+ * of types is describing a fan-in the platform cannot bound per write, and
1128
+ * every extra type is another discovery read on somebody's write path.
1129
+ */
1130
+ export declare const MAX_DATABASE_TRIGGERS_PER_FUNCTION = 5;
1131
+ export declare class TriggerDeclarationError extends Error {
1132
+ }
1133
+ /** Is anything at all declared? Used to skip work on the common case. */
1134
+ export declare function hasDeclaredTriggers(declaration: TriggerDeclaration): boolean;
1135
+ /**
1136
+ * Normalize the `triggers` table of an already-parsed `[function]` table.
1137
+ * Throws `TriggerDeclarationError` naming the rule that was broken.
1138
+ *
1139
+ * Split out from the parse (`deriveTriggerDeclaration`, which owns the bytes)
1140
+ * so the CLI's preflight can check the document it has already read, and so the
1141
+ * push boundary can normalize a declaration it read from a stored row without
1142
+ * re-parsing bytes.
1143
+ */
1144
+ export declare function normalizeTriggerDeclaration(functionTable: any, options?: TriggerModeOptions): TriggerDeclaration;
1145
+ /** The stored JSON for `ServerFunctionConfig.triggers`, or null when empty. */
1146
+ export declare function serializeTriggerDeclaration(declaration: TriggerDeclaration): string | null;
1147
+ /** Read a stored `ServerFunctionConfig.triggers` value back. */
1148
+ export declare function parseStoredTriggerDeclaration(stored: unknown): TriggerDeclaration;
1149
+ /** The `CronTrigger.triggerKey` an entry owns. */
1150
+ export declare function cronTriggerKeyFor(functionKey: string, name: string): string;
1151
+ /** The entry name a `<functionKey>:<name>` trigger key carries. */
1152
+ export declare function cronTriggerNameFrom(functionKey: string, triggerKey: string): string;
1153
+ /**
1154
+ * The `{{secrets.KEY}}` reference SHAPE, decided from the value alone.
1155
+ *
1156
+ * Split out of `src/services/secret-templates.ts` by #3320 so the one rule that
1157
+ * says whether a stored value IS a whole secret reference has one
1158
+ * implementation on both sides of a `config push`. The CLI preflight refuses a
1159
+ * webhook trigger whose `signingSecret` is a literal before anything is applied,
1160
+ * and the server refuses the same value at the write boundary, because both run
1161
+ * this code — the CLI through the artifact `cli/scripts/gen-config-surfaces.mjs`
1162
+ * renders, the server by importing this module. A shape-only restatement in the
1163
+ * CLI would have been subtly different: a reference spoiled by an invisible
1164
+ * character (#2297) survives `String.trim()` and would have passed a preflight
1165
+ * the server then failed.
1166
+ *
1167
+ * Pure by construction, which is what lets it live here: no secret store is
1168
+ * consulted, because whether a value is a REFERENCE never depends on which keys
1169
+ * exist. Whether the referenced key exists is the server's question, and stays
1170
+ * in `secret-templates.ts` beside the rest of resolution.
1171
+ *
1172
+ * `secret-templates.ts` re-exports everything here, so every existing caller
1173
+ * keeps its import and the two can never be different functions.
1174
+ */
1175
+ export declare const SECRETS_TEMPLATE_RE: RegExp;
1176
+ /** True when the value carries at least one `{{secrets.KEY}}` reference. */
1177
+ export declare function isSecretTemplate(value: string | null | undefined): boolean;
1178
+ /**
1179
+ * The value with every well-formed `{{secrets.KEY}}` reference removed — the
1180
+ * text that was NOT part of a reference the grammar accepts.
1181
+ *
1182
+ * This is the string the leftover-syntax rule below has to judge, and it is
1183
+ * always derived from the ORIGINAL stored value, never from a substituted
1184
+ * result. Resolution is a single `String.replace` pass that never re-scans
1185
+ * replacement text (`resolveMultiNamespaceTemplate`), so a brace or a
1186
+ * `secrets.` token coming out of a SECRET'S VALUE is inert — it is credential
1187
+ * material the operator stored, not config text, and testing the substituted
1188
+ * output would reject it (a Stripe key suffix stored as `}v2{` in an otherwise
1189
+ * valid `sk_live_{{secrets.SUFFIX}}` value).
1190
+ */
1191
+ export declare function withoutSecretReferences(value: string): string;
1192
+ /**
1193
+ * True when the value holds a well-formed `{{secrets.KEY}}` reference and the
1194
+ * text beside it is not credential material — a reference SPOILED by an
1195
+ * invisible character, rather than a legacy literal that happens to contain one
1196
+ * (#2297, consolidating #2386).
1197
+ *
1198
+ * Such a value is a reference spoiled by an authoring mistake — pasted out of
1199
+ * an editor or a document that carried a zero-width character along with it —
1200
+ * and it must fail closed rather than resolve. Before this rule a value spelled
1201
+ * `<U+200B>{{secrets.KEY}}` was not a whole reference (neither `\s` nor
1202
+ * `String.trim()` matches U+200B), carried no leftover reference SYNTAX once
1203
+ * the template was removed, and so classified as a working legacy literal that
1204
+ * resolved to U+200B followed by the secret: an HMAC key silently one byte
1205
+ * wrong, and a read surface pointing the operator at the wrong remediation.
1206
+ *
1207
+ * The rule judges the REMAINDER — the value with its well-formed references
1208
+ * taken out — against an allowlist, which is what makes the class closed:
1209
+ *
1210
+ * - Remainder empty, or ordinary whitespace only: the value is the reference it
1211
+ * plainly is, padding and all, and keeps resolving (#2191).
1212
+ * - Remainder carries at least one credential character: a genuine mixed
1213
+ * literal-plus-reference value (`sk_live_{{secrets.SUFFIX}}`), which predates
1214
+ * reference-only and keeps resolving as shipped (#2332) — including when the
1215
+ * operator's own bytes contain an invisible character, because that is
1216
+ * credential material rather than a spoiled pointer.
1217
+ * - Anything else: text that is present but that we cannot recognize as
1218
+ * credential material. Fail closed.
1219
+ *
1220
+ * Failing closed rather than stripping the character is the deliberate choice
1221
+ * (sponsor decision on #2297): stripping hides the mistake, where a
1222
+ * `malformed-reference` names it at the moment the value is written.
1223
+ *
1224
+ * A value with NO well-formed reference is not this rule's business, and needs
1225
+ * no separate handling: a pure literal is credential material the platform has
1226
+ * no standing to judge (`whsec_<U+200B>raw`), and a MISTYPED reference —
1227
+ * including one spoiled inside the braces, `{{secrets.<U+200B>KEY}}` — leaves
1228
+ * its syntax in the remainder and is already caught by
1229
+ * `carriesMalformedReferenceSyntax` in `secret-templates.ts`.
1230
+ *
1231
+ * Scoped to the reference-only credential boundary, not to
1232
+ * `resolveSecretTemplate`: an integration proxy header is a template by design
1233
+ * (`Authorization: Bearer {{secrets.TOKEN}}`), so it keeps resolving exactly
1234
+ * what the operator wrote.
1235
+ */
1236
+ export declare function isSpoiledSecretReference(value: string | null | undefined): boolean;
1237
+ /**
1238
+ * Matches a value that is EXACTLY one `{{secrets.KEY}}` reference and nothing
1239
+ * else. Anchored, and deliberately not global — `SECRETS_TEMPLATE_RE` carries
1240
+ * `lastIndex` state between calls.
1241
+ */
1242
+ export declare const WHOLE_SECRET_REFERENCE_RE: RegExp;
1243
+ /**
1244
+ * True when the whole (trimmed) value is a single `{{secrets.KEY}}` reference.
1245
+ *
1246
+ * The stricter sibling of `isSecretTemplate`, for the callers that use "is a
1247
+ * reference" to mean "carries no secret material of its own". `isSecretTemplate`
1248
+ * is a *contains* test, so `"sk_live_abcd{{secrets.SUFFIX}}"` satisfies it —
1249
+ * which is fine where a value is a template to be resolved (an
1250
+ * `Authorization: Bearer {{secrets.TOKEN}}` header is exactly that), and wrong
1251
+ * where the value is a credential that must live entirely in the encrypted
1252
+ * secret store. Used by the webhook `config` credential rule and by the
1253
+ * redaction that backs it: a mixed value would otherwise pass the write rule
1254
+ * AND skip redaction, storing and echoing most of a working credential in
1255
+ * cleartext.
1256
+ *
1257
+ * Trimmed, so `" {{secrets.KEY}} "` is accepted as the reference it plainly is;
1258
+ * two references, or a reference with any literal text beside it, are not.
1259
+ *
1260
+ * Invisible text beside the reference disqualifies the value before the trim
1261
+ * (#2297): U+FEFF is stripped by `String.trim()` and matched by `\s`, so
1262
+ * without the check a byte-order mark beside the braces would be silently
1263
+ * accepted while its zero-width siblings were not. Rejecting here is what makes
1264
+ * the write gates built on this predicate (`validateWholeSecretReference`, the
1265
+ * webhook `config` credential rule) refuse such a value at configuration time
1266
+ * rather than storing a row that can only fail at use time.
1267
+ */
1268
+ export declare function isWholeSecretReference(value: string | null | undefined): boolean;
1269
+ /**
1270
+ * The `signingSecret` rule a webhook's verification scheme carries, decided
1271
+ * from the declaration alone (#3320).
1272
+ *
1273
+ * `signingSecret` is reference-only (#2254): a whole `{{secrets.KEY}}`
1274
+ * reference naming an app secret, and nothing else. Which schemes need one,
1275
+ * whether one was supplied, and whether the supplied value is a reference are
1276
+ * all answerable from the file — so they belong in the `config push` preflight,
1277
+ * which aborts before anything is applied, rather than in the apply loop where
1278
+ * a refusal lands after sibling entities are already written.
1279
+ *
1280
+ * That is the whole reason this module exists here rather than beside the rest
1281
+ * of the webhook write pipeline: `src/config-surface/` is vendored verbatim
1282
+ * into `cli/src/lib/generated-config-surfaces.ts`, so the CLI runs the server's
1283
+ * rule instead of a restatement of it, and a change that is not regenerated
1284
+ * fails `gen-config-surfaces.mjs --check`.
1285
+ *
1286
+ * ── What is deliberately NOT here ─────────────────────────────────────────
1287
+ *
1288
+ * Everything that needs the app's state: whether the referenced secret exists,
1289
+ * whether the scheme's `config` is valid under this environment's JWKS policy,
1290
+ * the per-app row cap, a key a standalone webhook already holds. Those stay in
1291
+ * `src/app-api/services/webhook-settings-validation.ts`, which has `Env`. A
1292
+ * local copy of them would be a preflight that gives a false all-clear — and
1293
+ * the residue is then app-state-dependent by construction rather than by
1294
+ * accident of where a rule happened to live.
1295
+ */
1296
+ /**
1297
+ * Schemes that don't carry an HMAC `signingSecret`. These either store no key
1298
+ * material at all (`none`) or store it under `AppWebhook.config` instead:
1299
+ * `discord` puts the application public key in `config.publicKey`, `jwt` puts
1300
+ * a JWKS in `config.jwt.jwks`, and `plaid` fetches keys from Plaid using the
1301
+ * API credentials referenced in `config.plaid`.
1302
+ */
1303
+ export declare const SCHEMES_WITHOUT_SIGNING_SECRET: readonly string[];
1304
+ export declare function requiresSigningSecret(scheme: unknown): boolean;
1305
+ /** The coded refusals, so a client can branch without matching prose. */
1306
+ export declare const SIGNING_SECRET_MUST_BE_SECRET_REF = "SIGNING_SECRET_MUST_BE_SECRET_REF";
1307
+ export declare const SIGNING_SECRET_NOT_SUPPORTED_FOR_SCHEME = "SIGNING_SECRET_NOT_SUPPORTED_FOR_SCHEME";
1308
+ /**
1309
+ * A refusal from this rule. `plain` marks the ones the admin webhook create
1310
+ * answered as a bare text body rather than coded JSON — an observable
1311
+ * distinction, so it travels with the error rather than being flattened.
1312
+ */
1313
+ export interface SigningSecretRuleError {
1314
+ message: string;
1315
+ code?: string;
1316
+ plain?: boolean;
1317
+ }
1318
+ /** The reference-only message, shared by every field that carries one. */
1319
+ export declare function wholeSecretReferenceMessage(label: string): string;
1320
+ /**
1321
+ * Is this value a whole `{{secrets.KEY}}` reference? Returns the refusal a
1322
+ * caller renders, or null.
1323
+ *
1324
+ * `isWholeSecretReference`, never `isSecretTemplate`: a *contains* test admits
1325
+ * `sk_live_abcd{{secrets.SUFFIX}}`, which stores most of a working credential
1326
+ * in cleartext.
1327
+ */
1328
+ export declare function validateWholeSecretReference(label: string, value: unknown, code: string): SigningSecretRuleError | null;
1329
+ /**
1330
+ * The whole file-decidable `signingSecret` rule, in the order the standalone
1331
+ * webhook create ran it: a required secret must be present, a present one must
1332
+ * be a whole reference, and a scheme that carries no secret must not be given
1333
+ * one.
1334
+ *
1335
+ * Returns null when the declaration is acceptable — which does NOT mean the
1336
+ * write will succeed: the referenced key still has to exist, and that is the
1337
+ * server's question.
1338
+ */
1339
+ export declare function validateSigningSecretDeclaration(input: {
1340
+ verificationScheme: string;
1341
+ signingSecret?: unknown;
1342
+ }): SigningSecretRuleError | null;
921
1343
  export declare const WORKFLOW_SURFACE: ConfigObjectSurface;
922
1344
  export declare const PROMPT_SURFACE: ConfigObjectSurface;
923
1345
  export declare const INTEGRATION_SURFACE: ConfigObjectSurface;