@kici-dev/engine 0.4.0 → 0.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/audit/access-log-policy.js +1 -0
- package/dist/audit/retention-policy.js +2 -0
- package/dist/context/held-run-job-id.d.ts +26 -10
- package/dist/context/held-run-job-id.js +30 -11
- package/dist/context/host-match.js +2 -1
- package/dist/context/index.d.ts +1 -1
- package/dist/context/index.js +3 -3
- package/dist/context/types.d.ts +10 -1
- package/dist/context/types.js +10 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +17 -12
- package/dist/labels/compile.d.ts +2 -7
- package/dist/labels/compile.js +1 -10
- package/dist/labels.d.ts +74 -12
- package/dist/labels.js +82 -17
- package/dist/mcp/held-run-resolve.d.ts +40 -0
- package/dist/mcp/held-run-resolve.js +68 -15
- package/dist/mcp/tool-schemas.d.ts +4 -0
- package/dist/mcp/tool-schemas.js +13 -1
- package/dist/metrics/catalog-policy.d.ts +17 -3
- package/dist/metrics/catalog-policy.js +44 -13
- package/dist/metrics/metric-catalog.generated.d.ts +140 -0
- package/dist/metrics/metric-catalog.generated.js +168 -0
- package/dist/protocol/dashboard-global-workflows.js +2 -2
- package/dist/protocol/event-log-payload.js +1 -1
- package/dist/protocol/messages/access-log.d.ts +5 -0
- package/dist/protocol/messages/access-log.js +1 -0
- package/dist/protocol/messages/actor.d.ts +13 -2
- package/dist/protocol/messages/actor.js +16 -5
- package/dist/protocol/messages/common.js +1 -1
- package/dist/protocol/messages/dashboard-global-workflows.d.ts +29 -3
- package/dist/protocol/messages/dashboard-global-workflows.js +48 -4
- package/dist/protocol/messages/dashboard.d.ts +48 -7
- package/dist/protocol/messages/dashboard.js +62 -6
- package/dist/protocol/messages/execution-status.d.ts +44 -0
- package/dist/protocol/messages/execution-status.js +69 -2
- package/dist/protocol/messages/git-credential-relay.d.ts +78 -0
- package/dist/protocol/messages/git-credential-relay.js +86 -0
- package/dist/protocol/messages/orchestrator-agent.d.ts +138 -0
- package/dist/protocol/messages/orchestrator-agent.js +131 -3
- package/dist/protocol/messages/peer.d.ts +7 -0
- package/dist/protocol/messages/peer.js +18 -1
- package/dist/protocol/messages/platform-orchestrator.d.ts +155 -2
- package/dist/protocol/messages/platform-orchestrator.js +178 -19
- package/dist/protocol/version.d.ts +19 -2
- package/dist/protocol/version.js +20 -3
- package/dist/provenance/verify.js +11 -10
- package/dist/provider/check-status-poster.d.ts +37 -2
- package/dist/provider/contributor-resolver.d.ts +11 -3
- package/dist/provider/file-contents-fetcher.d.ts +39 -0
- package/dist/provider/file-contents-fetcher.js +2 -0
- package/dist/provider/git-credential.d.ts +77 -0
- package/dist/provider/git-credential.js +10 -0
- package/dist/provider/index.d.ts +4 -0
- package/dist/provider/index.js +2 -1
- package/dist/provider/webhook-normalizer.d.ts +12 -12
- package/dist/repo/pattern-negation.d.ts +73 -0
- package/dist/repo/pattern-negation.js +86 -0
- package/dist/safe-regex.d.ts +16 -0
- package/dist/safe-regex.js +24 -0
- package/dist/scaler/registry-auth.d.ts +18 -0
- package/dist/scaler/registry-auth.js +28 -0
- package/dist/scaler/scaler-backend-type.d.ts +35 -0
- package/dist/scaler/scaler-backend-type.js +39 -2
- package/dist/scaler/scaler-events.d.ts +79 -0
- package/dist/scaler/scaler-events.js +87 -0
- package/dist/trigger/compiled-matchers.d.ts +21 -1
- package/dist/trigger/compiled-matchers.js +30 -3
- package/dist/trigger/content-requirements.d.ts +31 -0
- package/dist/trigger/content-requirements.js +125 -0
- package/dist/trigger/decision-trace.d.ts +168 -0
- package/dist/trigger/decision-trace.js +207 -4
- package/dist/trigger/jsonpath-matcher.js +5 -1
- package/dist/trigger/matcher.js +61 -10
- package/dist/trigger/text-match.d.ts +27 -0
- package/dist/trigger/text-match.js +86 -0
- package/dist/trigger/types.d.ts +265 -23
- package/dist/trigger/types.js +37 -5
- package/dist/ws/rate-limiter.js +3 -3
- package/package.json +20 -3
- package/sbom.spdx.json +40 -15
|
@@ -24,25 +24,133 @@ import { z } from "zod";
|
|
|
24
24
|
* number cannot drift between the two packages that enforce it.
|
|
25
25
|
*/
|
|
26
26
|
const DEFAULT_APPROVAL_EXPIRY_HOURS = 72;
|
|
27
|
+
/** Seconds in an hour — the one conversion between the two expiry spellings. */
|
|
28
|
+
const SECONDS_PER_HOUR = 3600;
|
|
29
|
+
/**
|
|
30
|
+
* The same documented default as {@link DEFAULT_APPROVAL_EXPIRY_HOURS}, in the
|
|
31
|
+
* granularity the policy actually stores.
|
|
32
|
+
*/
|
|
33
|
+
const DEFAULT_APPROVAL_EXPIRY_SECONDS = 72 * SECONDS_PER_HOUR;
|
|
34
|
+
/**
|
|
35
|
+
* The shortest expressible security-hold window.
|
|
36
|
+
*
|
|
37
|
+
* One second, not a rounder-looking number: the floor exists to stop a zero or
|
|
38
|
+
* negative value minting an already-expired hold, and any strictly positive
|
|
39
|
+
* integer discharges that. A larger floor would be an invented usability
|
|
40
|
+
* opinion that also puts the window back out of reach of a test.
|
|
41
|
+
*/
|
|
42
|
+
const MIN_APPROVAL_EXPIRY_SECONDS = 1;
|
|
43
|
+
/** The longest window the Platform accepts: one year, the bound already applied to hours. */
|
|
44
|
+
const MAX_APPROVAL_EXPIRY_HOURS = 8760;
|
|
45
|
+
/** {@link MAX_APPROVAL_EXPIRY_HOURS} in seconds, so neither spelling outranges the other. */
|
|
46
|
+
const MAX_APPROVAL_EXPIRY_SECONDS = MAX_APPROVAL_EXPIRY_HOURS * SECONDS_PER_HOUR;
|
|
47
|
+
/**
|
|
48
|
+
* The window a policy actually means, in seconds.
|
|
49
|
+
*
|
|
50
|
+
* `approvalExpirySeconds` is the authority and `approvalExpiryHours` its coarse
|
|
51
|
+
* spelling, so the more specific field wins whenever both are present — the
|
|
52
|
+
* only rule that never discards what an operator asked for. A policy carrying
|
|
53
|
+
* neither (an older peer that sent no window at all) falls back to the
|
|
54
|
+
* documented default.
|
|
55
|
+
*/
|
|
56
|
+
function approvalExpirySecondsOf(policy) {
|
|
57
|
+
if (policy.approvalExpirySeconds != null) return policy.approvalExpirySeconds;
|
|
58
|
+
if (policy.approvalExpiryHours != null) return policy.approvalExpiryHours * SECONDS_PER_HOUR;
|
|
59
|
+
return DEFAULT_APPROVAL_EXPIRY_SECONDS;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The coarse `approvalExpiryHours` view of a window stored in seconds.
|
|
63
|
+
*
|
|
64
|
+
* Rounds UP, and never below one hour. A sub-hour window has no exact hours
|
|
65
|
+
* spelling, and the peer reading this field is one that cannot express the real
|
|
66
|
+
* value anyway — so the choice is which way to be wrong. Rounding up yields a
|
|
67
|
+
* longer hold than asked for, which stays approvable; rounding down yields zero,
|
|
68
|
+
* which is the already-expired hold `MIN_APPROVAL_EXPIRY_SECONDS` exists to
|
|
69
|
+
* prevent.
|
|
70
|
+
*/
|
|
71
|
+
function approvalExpiryHoursOf(seconds) {
|
|
72
|
+
return Math.max(1, Math.ceil(seconds / SECONDS_PER_HOUR));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The org-level fork switch: the setting an org's trust policy carries for how
|
|
76
|
+
* fork pull requests are treated. Each value names what the operator is
|
|
77
|
+
* choosing; which values the orchestrator's trust-policy gate honours, and the
|
|
78
|
+
* mechanism it uses, are owned there rather than here.
|
|
79
|
+
*
|
|
80
|
+
* - `ignore` — the operator declines fork pull requests.
|
|
81
|
+
* - `hold` — the operator requires approval first; the policy's approval expiry
|
|
82
|
+
* bounds how long that approval stays open.
|
|
83
|
+
* - `allow` — the operator permits fork pull requests to run with reduced
|
|
84
|
+
* privilege.
|
|
85
|
+
* - `reject` — deprecated in favour of `ignore`; removed at v1.0.0.
|
|
86
|
+
*/
|
|
87
|
+
const ForkPolicy = z.enum([
|
|
88
|
+
"ignore",
|
|
89
|
+
"hold",
|
|
90
|
+
"reject",
|
|
91
|
+
"allow"
|
|
92
|
+
]);
|
|
93
|
+
/**
|
|
94
|
+
* How much CI authority one org member holds.
|
|
95
|
+
*
|
|
96
|
+
* The level the `/kici approve` comment path reads once it has resolved a
|
|
97
|
+
* commenter to a KiCI user id: `write` or `admin` may release a security hold,
|
|
98
|
+
* `read` and `none` may not. Named here rather than spelled inline so the wire
|
|
99
|
+
* schema, the orchestrator's admin route, and `kici-admin` all offer exactly
|
|
100
|
+
* the same four values.
|
|
101
|
+
*/
|
|
102
|
+
const CiTrustLevel = z.enum([
|
|
103
|
+
"none",
|
|
104
|
+
"read",
|
|
105
|
+
"write",
|
|
106
|
+
"admin"
|
|
107
|
+
]);
|
|
27
108
|
const trustPolicySchema = z.object({
|
|
28
|
-
forkPolicy:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
109
|
+
forkPolicy: ForkPolicy,
|
|
110
|
+
/**
|
|
111
|
+
* @deprecated Accepted for wire compatibility and still stored and echoed
|
|
112
|
+
* back, so an orchestrator or CLI on an older build keeps seeing the value it
|
|
113
|
+
* expects. The orchestrator's trust-policy gate does not read it, so it
|
|
114
|
+
* changes no dispatch outcome. Removed at v1.0.0.
|
|
115
|
+
*/
|
|
33
116
|
unknownContributorPolicy: z.enum(["hold", "reject"]),
|
|
117
|
+
/**
|
|
118
|
+
* @deprecated Accepted for wire compatibility and still stored and echoed
|
|
119
|
+
* back, so an orchestrator or CLI on an older build keeps seeing the value it
|
|
120
|
+
* expects. The orchestrator's trust-policy gate does not read it, so it
|
|
121
|
+
* changes no dispatch outcome. Removed at v1.0.0.
|
|
122
|
+
*/
|
|
34
123
|
workflowChangePolicy: z.enum([
|
|
35
124
|
"hold",
|
|
36
125
|
"reject",
|
|
37
126
|
"allow"
|
|
38
127
|
]),
|
|
39
128
|
/**
|
|
40
|
-
* Hours a security hold stays open
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
129
|
+
* Hours a security hold stays open — the coarse spelling of
|
|
130
|
+
* `approvalExpirySeconds`, kept required so an orchestrator or CLI on an
|
|
131
|
+
* older build still receives a window it can read. Not deprecated: it is read,
|
|
132
|
+
* enforced whenever no seconds value accompanies it, and remains the
|
|
133
|
+
* ergonomic way to say "72 hours".
|
|
134
|
+
*
|
|
135
|
+
* Integer and positive: the column is INTEGER NOT NULL, so a fractional value
|
|
136
|
+
* throws inside the fire-and-forget persist and the policy is then silently
|
|
137
|
+
* never stored, and a zero or negative value mints an already-expired hold.
|
|
138
|
+
*/
|
|
139
|
+
approvalExpiryHours: z.number().int().min(1),
|
|
140
|
+
/**
|
|
141
|
+
* Seconds a security hold stays open — the authoritative window, and the one
|
|
142
|
+
* granularity that can express a sub-hour hold.
|
|
143
|
+
*
|
|
144
|
+
* Optional because an older Platform sends only the hours field; a frame
|
|
145
|
+
* without it resolves through {@link approvalExpirySecondsOf}, which falls
|
|
146
|
+
* back to `approvalExpiryHours * SECONDS_PER_HOUR`. When both are present this
|
|
147
|
+
* one wins, at every layer.
|
|
148
|
+
*
|
|
149
|
+
* Integer and at least {@link MIN_APPROVAL_EXPIRY_SECONDS} for the same two
|
|
150
|
+
* reasons the hours field is: the column is INTEGER, and a non-positive window
|
|
151
|
+
* mints an already-expired hold.
|
|
44
152
|
*/
|
|
45
|
-
|
|
153
|
+
approvalExpirySeconds: z.number().int().min(1).optional()
|
|
46
154
|
});
|
|
47
155
|
/** Trust policy update pushed from Platform to orchestrator when policy or identity links change. */
|
|
48
156
|
const trustPolicyUpdateSchema = z.object({
|
|
@@ -62,12 +170,7 @@ const trustPolicyUpdateSchema = z.object({
|
|
|
62
170
|
*/
|
|
63
171
|
providerUserId: z.string().nullish()
|
|
64
172
|
})),
|
|
65
|
-
memberCiTrustLevels: z.record(z.string(),
|
|
66
|
-
"none",
|
|
67
|
-
"read",
|
|
68
|
-
"write",
|
|
69
|
-
"admin"
|
|
70
|
-
])),
|
|
173
|
+
memberCiTrustLevels: z.record(z.string(), CiTrustLevel),
|
|
71
174
|
/**
|
|
72
175
|
* Operator-defined teams and their member user ids. The orchestrator has no
|
|
73
176
|
* identity store, so team membership is delivered here (next to
|
|
@@ -141,13 +244,13 @@ const WebhookRelayResult = z.enum([
|
|
|
141
244
|
* 25 MiB matches GitHub's own webhook payload cap. Senders that need higher must
|
|
142
245
|
* connect their orchestrator directly (bypassing Platform).
|
|
143
246
|
*/
|
|
144
|
-
const WEBHOOK_RELAY_MAX_BODY_BYTES =
|
|
247
|
+
const WEBHOOK_RELAY_MAX_BODY_BYTES = 26214400;
|
|
145
248
|
/**
|
|
146
249
|
* Recommended raw chunk size for the chunked relay protocol. 64 KiB raw becomes
|
|
147
250
|
* ~85 KiB base64 in JSON; permessage-deflate compresses it well. Sender (Platform)
|
|
148
251
|
* picks the actual size; receiver (orchestrator) just enforces totalSize and chunkCount.
|
|
149
252
|
*/
|
|
150
|
-
const WEBHOOK_RELAY_CHUNK_SIZE =
|
|
253
|
+
const WEBHOOK_RELAY_CHUNK_SIZE = 65536;
|
|
151
254
|
/**
|
|
152
255
|
* Start frame of the chunked webhook relay protocol (Platform -> orchestrator).
|
|
153
256
|
* Carries metadata + signature inputs only; body bytes follow in subsequent
|
|
@@ -239,6 +342,20 @@ const staleCheckrunCleanupSchema = z.object({
|
|
|
239
342
|
repoIdentifier: z.string(),
|
|
240
343
|
sha: z.string(),
|
|
241
344
|
workflowName: z.string(),
|
|
345
|
+
/**
|
|
346
|
+
* The repository that DEFINES the workflow, when that is not the
|
|
347
|
+
* repository the run acted on — an organization-wide workflow dispatched
|
|
348
|
+
* against another repository. The orchestrator qualifies the check-run
|
|
349
|
+
* names it looks for with it, so a cleanup cannot time out the acted-on
|
|
350
|
+
* repository's own same-named check; see the orchestrator's
|
|
351
|
+
* `workflowLabel`.
|
|
352
|
+
*
|
|
353
|
+
* Additive and optional. The Platform sends it on exactly the condition
|
|
354
|
+
* that writes the mirror column — only when the two repositories differ —
|
|
355
|
+
* so an absent field and a value equal to `repoIdentifier` mean the same
|
|
356
|
+
* thing and both name the unqualified check.
|
|
357
|
+
*/
|
|
358
|
+
workflowRepoIdentifier: z.string().max(256).optional(),
|
|
242
359
|
jobNames: z.array(z.string())
|
|
243
360
|
}))
|
|
244
361
|
});
|
|
@@ -292,6 +409,46 @@ const orchMetricsSchema = z.object({
|
|
|
292
409
|
})).max(2e3),
|
|
293
410
|
timestamp: z.number()
|
|
294
411
|
});
|
|
412
|
+
/** Largest worker-peer snapshot accepted on one frame. */
|
|
413
|
+
const CLUSTER_MEMBERSHIP_MAX_WORKERS = 512;
|
|
414
|
+
/**
|
|
415
|
+
* Worker-peer membership snapshot, sent by a coordinator to the Platform.
|
|
416
|
+
*
|
|
417
|
+
* A snapshot rather than a delta: a dropped or reordered frame self-heals on
|
|
418
|
+
* the next send instead of corrupting a running tally. The Platform stores the
|
|
419
|
+
* reported size on `platform_connections` and aggregates it into the org's
|
|
420
|
+
* combined orchestrator count.
|
|
421
|
+
*
|
|
422
|
+
* The array bound is the first line of defence, for the same reason
|
|
423
|
+
* `orchMetricsSchema` bounds its own arrays: a malformed or hostile push must
|
|
424
|
+
* fail Zod parse at the WS edge and never reach the aggregate.
|
|
425
|
+
*/
|
|
426
|
+
const clusterMembershipSchema = z.object({
|
|
427
|
+
type: z.literal("cluster.membership"),
|
|
428
|
+
workers: z.array(z.object({ instanceId: z.string().min(1).max(128) })).max(512),
|
|
429
|
+
timestamp: z.number()
|
|
430
|
+
});
|
|
431
|
+
/**
|
|
432
|
+
* Per-coordinator orchestrator ceiling, pushed by the Platform.
|
|
433
|
+
*
|
|
434
|
+
* `maxWorkerPeers` is an ABSOLUTE ceiling on this coordinator's connected
|
|
435
|
+
* worker peers, not a remaining allowance. The Platform computes it by
|
|
436
|
+
* excluding this connection's own workers from the org total, so it does not
|
|
437
|
+
* move when a local worker joins or leaves — the coordinator can enforce
|
|
438
|
+
* against its live peer count with no round trip and no staleness window.
|
|
439
|
+
*
|
|
440
|
+
* `orgLimit` and `orgTotal` are informational: they let the coordinator's
|
|
441
|
+
* rejection reason say why, rather than closing opaquely. `evictExcess` is set
|
|
442
|
+
* once the org has been over its limit for longer than the grace window, and
|
|
443
|
+
* asks the coordinator to drain its newest workers down to the ceiling.
|
|
444
|
+
*/
|
|
445
|
+
const planHeadroomSchema = z.object({
|
|
446
|
+
type: z.literal("plan.headroom"),
|
|
447
|
+
maxWorkerPeers: z.number().int().min(0),
|
|
448
|
+
orgLimit: z.number().int().min(0),
|
|
449
|
+
orgTotal: z.number().int().min(0),
|
|
450
|
+
evictExcess: z.boolean()
|
|
451
|
+
});
|
|
295
452
|
/**
|
|
296
453
|
* Acknowledgment that a webhook was received and processing started.
|
|
297
454
|
*
|
|
@@ -421,6 +578,7 @@ const platformToOrchestratorMessageSchema = z.discriminatedUnion("type", [
|
|
|
421
578
|
staleCheckrunCleanupSchema,
|
|
422
579
|
oidcMintResponseSchema,
|
|
423
580
|
platformCapabilitiesMessageSchema,
|
|
581
|
+
planHeadroomSchema,
|
|
424
582
|
nackSchema,
|
|
425
583
|
...dashboardPlatformToOrchSchema.options
|
|
426
584
|
]);
|
|
@@ -450,6 +608,7 @@ const orchestratorToPlatformMessageSchema = z.discriminatedUnion("type", [
|
|
|
450
608
|
jobContextMessageSchema,
|
|
451
609
|
orchMetricsSchema,
|
|
452
610
|
oidcMintRequestSchema,
|
|
611
|
+
clusterMembershipSchema,
|
|
453
612
|
nackSchema
|
|
454
613
|
]);
|
|
455
614
|
/**
|
|
@@ -479,6 +638,6 @@ const ORCH_TO_PLATFORM_RECOGNIZED_TYPES = new Set(collectDiscriminatorTypes(orch
|
|
|
479
638
|
*/
|
|
480
639
|
const PLATFORM_TO_ORCH_RECOGNIZED_TYPES = new Set(collectDiscriminatorTypes(platformToOrchestratorMessageSchema));
|
|
481
640
|
//#endregion
|
|
482
|
-
export { DEFAULT_APPROVAL_EXPIRY_HOURS, ORCH_TO_PLATFORM_RECOGNIZED_TYPES, OrchLogPhase, PLATFORM_TO_ORCH_RECOGNIZED_TYPES, WEBHOOK_RELAY_CHUNK_SIZE, WEBHOOK_RELAY_MAX_BODY_BYTES, WebhookRelayResult, cacheStatsSchema, collectDiscriminatorTypes, executionEventSchema, logChunkSchema, orchCapabilitiesUpdateSchema, orchLogChunkSchema, orchMetricsSchema, orchestratorToPlatformMessageSchema, peerDiscoverSchema, peerUpdateSchema, platformCapabilitiesMessageSchema, platformToOrchestratorMessageSchema, staleCheckrunCleanupSchema, trustPolicySchema, trustPolicyUpdateSchema, webhookAckSchema, webhookRelayChunkSchema, webhookRelaySchema, webhookRelayStartSchema };
|
|
641
|
+
export { CLUSTER_MEMBERSHIP_MAX_WORKERS, CiTrustLevel, DEFAULT_APPROVAL_EXPIRY_HOURS, DEFAULT_APPROVAL_EXPIRY_SECONDS, ForkPolicy, MAX_APPROVAL_EXPIRY_HOURS, MAX_APPROVAL_EXPIRY_SECONDS, MIN_APPROVAL_EXPIRY_SECONDS, ORCH_TO_PLATFORM_RECOGNIZED_TYPES, OrchLogPhase, PLATFORM_TO_ORCH_RECOGNIZED_TYPES, SECONDS_PER_HOUR, WEBHOOK_RELAY_CHUNK_SIZE, WEBHOOK_RELAY_MAX_BODY_BYTES, WebhookRelayResult, approvalExpiryHoursOf, approvalExpirySecondsOf, cacheStatsSchema, clusterMembershipSchema, collectDiscriminatorTypes, executionEventSchema, logChunkSchema, orchCapabilitiesUpdateSchema, orchLogChunkSchema, orchMetricsSchema, orchestratorToPlatformMessageSchema, peerDiscoverSchema, peerUpdateSchema, planHeadroomSchema, platformCapabilitiesMessageSchema, platformToOrchestratorMessageSchema, staleCheckrunCleanupSchema, trustPolicySchema, trustPolicyUpdateSchema, webhookAckSchema, webhookRelayChunkSchema, webhookRelaySchema, webhookRelayStartSchema };
|
|
483
642
|
|
|
484
643
|
//# sourceMappingURL=platform-orchestrator.js.map
|
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Protocol version. Sent during WebSocket handshake.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Increment when a message schema gains something an older peer cannot parse,
|
|
5
|
+
* and pair the bump with a named floor (below) so a sender can gate on the
|
|
6
|
+
* version a peer negotiated instead of guessing.
|
|
4
7
|
*/
|
|
5
|
-
export declare const PROTOCOL_VERSION =
|
|
8
|
+
export declare const PROTOCOL_VERSION = 2;
|
|
6
9
|
/**
|
|
7
10
|
* Minimum protocol version accepted.
|
|
8
11
|
* Connections below this are rejected.
|
|
9
12
|
* Capabilities handle per-feature negotiation above this baseline.
|
|
10
13
|
*/
|
|
11
14
|
export declare const MIN_PROTOCOL_VERSION = 1;
|
|
15
|
+
/**
|
|
16
|
+
* First protocol version whose `trust_policy.update` reader accepts
|
|
17
|
+
* `forkPolicy: 'ignore'`.
|
|
18
|
+
*
|
|
19
|
+
* A peer below this version may validate the pushed policy against a
|
|
20
|
+
* `forkPolicy` enum with no `ignore` member. `trust_policy.update` is a member
|
|
21
|
+
* of a discriminated union, so that value fails the WHOLE frame rather than one
|
|
22
|
+
* field: the org's identity links, member CI trust levels and team memberships
|
|
23
|
+
* are dropped with it. The Platform therefore rewrites `ignore` to the
|
|
24
|
+
* deprecated `reject` before sending to such a peer — the value that peer's
|
|
25
|
+
* enum does carry, that denies dispatch the same way, and that this build's own
|
|
26
|
+
* fork switch already resolves through the same arm as `ignore`.
|
|
27
|
+
*/
|
|
28
|
+
export declare const FORK_POLICY_IGNORE_MIN_PROTOCOL_VERSION = 2;
|
|
12
29
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/protocol/version.js
CHANGED
|
@@ -2,16 +2,33 @@ import "../rolldown-runtime-ClRpJifh.js";
|
|
|
2
2
|
//#region src/protocol/version.ts
|
|
3
3
|
/**
|
|
4
4
|
* Protocol version. Sent during WebSocket handshake.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
|
+
* Increment when a message schema gains something an older peer cannot parse,
|
|
7
|
+
* and pair the bump with a named floor (below) so a sender can gate on the
|
|
8
|
+
* version a peer negotiated instead of guessing.
|
|
6
9
|
*/
|
|
7
|
-
const PROTOCOL_VERSION =
|
|
10
|
+
const PROTOCOL_VERSION = 2;
|
|
8
11
|
/**
|
|
9
12
|
* Minimum protocol version accepted.
|
|
10
13
|
* Connections below this are rejected.
|
|
11
14
|
* Capabilities handle per-feature negotiation above this baseline.
|
|
12
15
|
*/
|
|
13
16
|
const MIN_PROTOCOL_VERSION = 1;
|
|
17
|
+
/**
|
|
18
|
+
* First protocol version whose `trust_policy.update` reader accepts
|
|
19
|
+
* `forkPolicy: 'ignore'`.
|
|
20
|
+
*
|
|
21
|
+
* A peer below this version may validate the pushed policy against a
|
|
22
|
+
* `forkPolicy` enum with no `ignore` member. `trust_policy.update` is a member
|
|
23
|
+
* of a discriminated union, so that value fails the WHOLE frame rather than one
|
|
24
|
+
* field: the org's identity links, member CI trust levels and team memberships
|
|
25
|
+
* are dropped with it. The Platform therefore rewrites `ignore` to the
|
|
26
|
+
* deprecated `reject` before sending to such a peer — the value that peer's
|
|
27
|
+
* enum does carry, that denies dispatch the same way, and that this build's own
|
|
28
|
+
* fork switch already resolves through the same arm as `ignore`.
|
|
29
|
+
*/
|
|
30
|
+
const FORK_POLICY_IGNORE_MIN_PROTOCOL_VERSION = 2;
|
|
14
31
|
//#endregion
|
|
15
|
-
export { MIN_PROTOCOL_VERSION, PROTOCOL_VERSION };
|
|
32
|
+
export { FORK_POLICY_IGNORE_MIN_PROTOCOL_VERSION, MIN_PROTOCOL_VERSION, PROTOCOL_VERSION };
|
|
16
33
|
|
|
17
34
|
//# sourceMappingURL=version.js.map
|
|
@@ -107,17 +107,18 @@ async function verifyKiciBundle(opts) {
|
|
|
107
107
|
failures.push("dsse_signature_invalid");
|
|
108
108
|
}
|
|
109
109
|
const attestationOrigin = resolveAttestationOrigin(claims, statement);
|
|
110
|
-
if (statement && claims && statementBytes)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
if (statement && claims && statementBytes) {
|
|
111
|
+
if (attestationOrigin === AttestationOrigin.enum.live) {
|
|
112
|
+
const ok = crossCheckBuildContext(statement, claims);
|
|
113
|
+
checks.buildContext = ok ? "pass" : "fail";
|
|
114
|
+
if (!ok) failures.push("build_context_mismatch");
|
|
115
|
+
} else {
|
|
116
|
+
const actual = await computeStatementHash(statementBytes);
|
|
117
|
+
const ok = typeof claims.statement_hash === "string" && claims.statement_hash === actual;
|
|
118
|
+
checks.buildContext = ok ? "pass" : "fail";
|
|
119
|
+
if (!ok) failures.push("statement_hash_mismatch");
|
|
120
|
+
}
|
|
114
121
|
} else {
|
|
115
|
-
const actual = await computeStatementHash(statementBytes);
|
|
116
|
-
const ok = typeof claims.statement_hash === "string" && claims.statement_hash === actual;
|
|
117
|
-
checks.buildContext = ok ? "pass" : "fail";
|
|
118
|
-
if (!ok) failures.push("statement_hash_mismatch");
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
122
|
checks.buildContext = "fail";
|
|
122
123
|
failures.push("build_context_uncheckable");
|
|
123
124
|
}
|
|
@@ -4,9 +4,20 @@
|
|
|
4
4
|
* Used by the CI security system to post approval/hold status checks
|
|
5
5
|
* on PRs, enabling visibility into trust-tier gating decisions.
|
|
6
6
|
*/
|
|
7
|
+
import type { CheckRunConclusion } from './check-run-conclusion.js';
|
|
7
8
|
import type { ProviderType } from './types.js';
|
|
8
|
-
/**
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Status values for a check run: still running, or one of the terminal
|
|
11
|
+
* conclusions.
|
|
12
|
+
*
|
|
13
|
+
* The terminal half is `CheckRunConclusion` — the same vocabulary the workflow
|
|
14
|
+
* and per-job `kici/…` check runs conclude with — plus `neutral`, which those
|
|
15
|
+
* runs never use and the informational security checks do. One vocabulary means
|
|
16
|
+
* a hold that ends is reported the same way on the security check and on the
|
|
17
|
+
* `kici/…` checks of the same event: `cancelled` for a rejection, `timed_out`
|
|
18
|
+
* for an elapsed approval window.
|
|
19
|
+
*/
|
|
20
|
+
export type CheckStatus = 'pending' | 'neutral' | CheckRunConclusion;
|
|
10
21
|
/** A single workflow-file change detected between base and head lock files. */
|
|
11
22
|
export interface WorkflowModificationInfo {
|
|
12
23
|
changeType: string;
|
|
@@ -34,5 +45,29 @@ export interface CheckStatusPoster {
|
|
|
34
45
|
* unblocking a branch protection rule that requires the security check.
|
|
35
46
|
*/
|
|
36
47
|
postGlobalWorkflowsSkippedCheck(repoIdentifier: string, commitSha: string, summary: string, credentials: unknown): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Post a failing check recording that the pre-run evaluation of the
|
|
50
|
+
* organization's global workflows could not be completed, so none of the
|
|
51
|
+
* workflows it was deciding on ran for this commit.
|
|
52
|
+
*
|
|
53
|
+
* Its own check name, for the same reason as the two notices above: the
|
|
54
|
+
* security-hold check is a single named run per commit, so posting this
|
|
55
|
+
* through `postCheckStatus` would UPDATE that run and replace a pending
|
|
56
|
+
* "Held for approval" state with a completed conclusion.
|
|
57
|
+
*
|
|
58
|
+
* Optional so a provider bundle that has no notion of commit checks — or a
|
|
59
|
+
* hand-built one — is silent rather than failing the delivery.
|
|
60
|
+
*/
|
|
61
|
+
postGlobalEvalFailedCheck?(repoIdentifier: string, commitSha: string, summary: string, credentials: unknown): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Post the success conclusion on the organization-workflow-evaluation check —
|
|
64
|
+
* the same check name {@link postGlobalEvalFailedCheck} writes — after a
|
|
65
|
+
* re-run of the failed round completes cleanly, so a bot gating on all-green
|
|
66
|
+
* is unblocked without a new commit.
|
|
67
|
+
*
|
|
68
|
+
* Optional for the same reason as the failure poster: a bundle with no notion
|
|
69
|
+
* of commit checks is silent rather than failing the re-run.
|
|
70
|
+
*/
|
|
71
|
+
postGlobalEvalSucceededCheck?(repoIdentifier: string, commitSha: string, summary: string, credentials: unknown): Promise<void>;
|
|
37
72
|
}
|
|
38
73
|
//# sourceMappingURL=check-status-poster.d.ts.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ContributorResolver interface for determining contributor permissions.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Deprecated in favour of ref-based trust: the whole interface, including the
|
|
5
|
+
* `ContributorPermission` / `ContributorInfo` shapes it carries, is retained
|
|
6
|
+
* for compatibility and has no caller in this repo.
|
|
6
7
|
*/
|
|
7
8
|
import type { ProviderType } from './types.js';
|
|
8
9
|
/** Contributor's permission level on a repository. */
|
|
@@ -14,7 +15,14 @@ export interface ContributorInfo {
|
|
|
14
15
|
/** Whether the PR comes from a fork */
|
|
15
16
|
isForkPR: boolean;
|
|
16
17
|
}
|
|
17
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Resolves contributor information from a git hosting provider.
|
|
20
|
+
*
|
|
21
|
+
* @deprecated Trust is derived from the git ref, not from the contributor's
|
|
22
|
+
* permission level, so nothing in the webhook pipeline calls a resolver. The
|
|
23
|
+
* type stays exported for wire compatibility with external implementations and
|
|
24
|
+
* is removed at v1.0.0.
|
|
25
|
+
*/
|
|
18
26
|
export interface ContributorResolver {
|
|
19
27
|
readonly provider: ProviderType;
|
|
20
28
|
resolveContributor(repoIdentifier: string, username: string, credentials: unknown): Promise<ContributorInfo>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileContentsFetcher interface.
|
|
3
|
+
*
|
|
4
|
+
* Fetches the contents of an arbitrary repository file at a specific ref via a
|
|
5
|
+
* provider's repository API. Used by the orchestrator to evaluate content-match
|
|
6
|
+
* triggers without cloning the repo.
|
|
7
|
+
*/
|
|
8
|
+
import type { ProviderType } from './types.js';
|
|
9
|
+
export interface FileContentsFetcher {
|
|
10
|
+
readonly provider: ProviderType;
|
|
11
|
+
/**
|
|
12
|
+
* Fetch the contents of a file in a repository at a specific ref.
|
|
13
|
+
*
|
|
14
|
+
* Result shape:
|
|
15
|
+
* - `{ present: false }` -- the file does not exist (a 404 from the provider,
|
|
16
|
+
* or the path resolves to a directory rather than a file).
|
|
17
|
+
* - `{ present: true, bytes }` -- the file exists and the provider returned
|
|
18
|
+
* its inline content, decoded to a string.
|
|
19
|
+
* - `{ present: true }` (no `bytes`) -- the file exists but the provider did
|
|
20
|
+
* not return inline content (e.g. GitHub's Contents API omits content for
|
|
21
|
+
* files larger than 1 MiB). A caller MUST NOT read a missing `bytes` as an
|
|
22
|
+
* absent file.
|
|
23
|
+
*
|
|
24
|
+
* Implementations do not cap the returned content -- any size limit is the
|
|
25
|
+
* caller's concern (the orchestrator's content matcher enforces a
|
|
26
|
+
* post-materialization cap). Implementations MUST NOT fall back to a raw
|
|
27
|
+
* blob download to defeat a provider's inline-content size limit.
|
|
28
|
+
*
|
|
29
|
+
* @param owner - Repository owner (e.g. "my-org")
|
|
30
|
+
* @param repo - Repository name (e.g. "my-app")
|
|
31
|
+
* @param path - Repo-relative file path (e.g. ".kici/workflows/ci.ts")
|
|
32
|
+
* @param ref - Git ref (branch name, tag, or SHA)
|
|
33
|
+
*/
|
|
34
|
+
getFileContents(owner: string, repo: string, path: string, ref: string): Promise<{
|
|
35
|
+
present: boolean;
|
|
36
|
+
bytes?: string;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=file-contents-fetcher.d.ts.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git credential types shared by the orchestrator broker, the agent helper,
|
|
3
|
+
* and the SDK surface.
|
|
4
|
+
*
|
|
5
|
+
* Browser-safe by construction: these types sit on the `provider/` barrel that
|
|
6
|
+
* the dashboard transitively imports, so this module declares plain TypeScript
|
|
7
|
+
* and imports no Zod. The validating mirror lives in
|
|
8
|
+
* `protocol/messages/git-credential-relay.ts`.
|
|
9
|
+
*/
|
|
10
|
+
import type { ProviderType } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* A forge that can back a git credential. Derived from `ProviderType` rather
|
|
13
|
+
* than declared alongside it so the two cannot drift. `'local'` is excluded —
|
|
14
|
+
* it is an execution mode, not a forge.
|
|
15
|
+
*/
|
|
16
|
+
export type ForgeName = Exclude<ProviderType, 'local'>;
|
|
17
|
+
/**
|
|
18
|
+
* One half of a credential field pair. Exactly one form is set: a qualified
|
|
19
|
+
* `<context>:<secret-name>` reference resolved from the secrets backend, or
|
|
20
|
+
* material supplied at runtime.
|
|
21
|
+
*
|
|
22
|
+
* The field NAME is the discriminator, so neither a reader nor the broker ever
|
|
23
|
+
* has to guess whether a value is a key or a credential. This follows the
|
|
24
|
+
* convention `packages/sdk/src/workflow.ts` already sets with
|
|
25
|
+
* `registries[].tokenSecret` and `isQualifiedSecretRef`.
|
|
26
|
+
*/
|
|
27
|
+
export type Sourced<Name extends string> = {
|
|
28
|
+
[K in `${Name}Secret`]: string;
|
|
29
|
+
} | {
|
|
30
|
+
[K in `${Name}Value`]: string;
|
|
31
|
+
};
|
|
32
|
+
/** Where credential material comes from, for each supported credential shape. */
|
|
33
|
+
export type GitCredentialRef = ({
|
|
34
|
+
kind: 'app';
|
|
35
|
+
} & Sourced<'appId'> & Sourced<'installationId'> & Sourced<'privateKey'>) | ({
|
|
36
|
+
kind: 'token';
|
|
37
|
+
user?: string;
|
|
38
|
+
} & Sourced<'token'>) | ({
|
|
39
|
+
kind: 'ssh';
|
|
40
|
+
} & Sourced<'privateKey'>);
|
|
41
|
+
/**
|
|
42
|
+
* What a credential may actually do. Reported by the broker; never an echo of
|
|
43
|
+
* what the caller requested.
|
|
44
|
+
*
|
|
45
|
+
* `scoped: false` means the credential could not be narrowed at all — every
|
|
46
|
+
* static credential, where the key is read-write or it is not. `scoped: true`
|
|
47
|
+
* carries the permission map the forge actually granted.
|
|
48
|
+
*/
|
|
49
|
+
export type GitCredentialGrant = {
|
|
50
|
+
scoped: false;
|
|
51
|
+
} | {
|
|
52
|
+
scoped: true;
|
|
53
|
+
permissions: Readonly<Record<string, string>>;
|
|
54
|
+
};
|
|
55
|
+
/** A broker request: which repo, on whose behalf, with what asked for. */
|
|
56
|
+
export interface GitCredentialRequest {
|
|
57
|
+
/** Repository identifier, e.g. `'kici-dev/kici-forge-app-token-tester'`. */
|
|
58
|
+
repository: string;
|
|
59
|
+
/** Omit for the source-scoped default credential. */
|
|
60
|
+
ref?: GitCredentialRef;
|
|
61
|
+
/**
|
|
62
|
+
* Requested permissions. Meaningful only for a minted shape; ignored for a
|
|
63
|
+
* static one, which reports `scoped: false`.
|
|
64
|
+
*/
|
|
65
|
+
permissions?: Readonly<Record<string, string>>;
|
|
66
|
+
}
|
|
67
|
+
/** What the broker returns. `expiresAt` is null for a credential that does not expire. */
|
|
68
|
+
export interface GitCredentialResult {
|
|
69
|
+
kind: 'basic' | 'ssh';
|
|
70
|
+
user?: string;
|
|
71
|
+
secret: string;
|
|
72
|
+
grant: GitCredentialGrant;
|
|
73
|
+
expiresAt: string | null;
|
|
74
|
+
}
|
|
75
|
+
/** True when the ref requires minting (and therefore per-operation refresh). */
|
|
76
|
+
export declare function isMintedRef(ref: GitCredentialRef): boolean;
|
|
77
|
+
//# sourceMappingURL=git-credential.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
//#region src/provider/git-credential.ts
|
|
3
|
+
/** True when the ref requires minting (and therefore per-operation refresh). */
|
|
4
|
+
function isMintedRef(ref) {
|
|
5
|
+
return ref.kind === "app";
|
|
6
|
+
}
|
|
7
|
+
//#endregion
|
|
8
|
+
export { isMintedRef };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=git-credential.js.map
|
package/dist/provider/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* - WebhookNormalizer: Webhook ingestion and event normalization
|
|
9
9
|
* - LockFileFetcher: Repository API access for lock files
|
|
10
10
|
* - ChangedFilesFetcher: Repository API access for changed files
|
|
11
|
+
* - FileContentsFetcher: Repository API access for arbitrary file contents
|
|
11
12
|
* - CloneTokenProvider: Auth token generation for agent clone
|
|
12
13
|
* - RepoUrlBuilder: Provider-specific URL construction
|
|
13
14
|
*/
|
|
@@ -16,7 +17,10 @@ export type { WebhookNormalizer, AccessCacheInvalidation } from './webhook-norma
|
|
|
16
17
|
export type { LockFileFetcher } from './lock-file-fetcher.js';
|
|
17
18
|
export { LockFileParseError } from './lock-file-parse-error.js';
|
|
18
19
|
export type { ChangedFilesFetcher, ChangedFilesResult } from './changed-files-fetcher.js';
|
|
20
|
+
export type { FileContentsFetcher } from './file-contents-fetcher.js';
|
|
19
21
|
export type { CloneTokenProvider, ProviderGitAuth } from './clone-token-provider.js';
|
|
22
|
+
export type { ForgeName, Sourced, GitCredentialRef, GitCredentialGrant, GitCredentialRequest, GitCredentialResult, } from './git-credential.js';
|
|
23
|
+
export { isMintedRef } from './git-credential.js';
|
|
20
24
|
export type { RepoUrlBuilder } from './repo-url-builder.js';
|
|
21
25
|
export type { ContributorResolver, ContributorInfo, ContributorPermission, } from './contributor-resolver.js';
|
|
22
26
|
export type { CheckStatusPoster, CheckStatus, WorkflowModificationInfo, } from './check-status-poster.js';
|
package/dist/provider/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
2
|
import { LockFileParseError } from "./lock-file-parse-error.js";
|
|
3
|
+
import { isMintedRef } from "./git-credential.js";
|
|
3
4
|
import { CheckRunConclusion } from "./check-run-conclusion.js";
|
|
4
|
-
export { CheckRunConclusion, LockFileParseError };
|
|
5
|
+
export { CheckRunConclusion, LockFileParseError, isMintedRef };
|
|
@@ -8,8 +8,13 @@
|
|
|
8
8
|
import type { SimulatedEvent } from '../trigger/types.js';
|
|
9
9
|
import type { ProviderType } from './types.js';
|
|
10
10
|
/**
|
|
11
|
-
* Discriminated union describing which
|
|
12
|
-
* event invalidates. Returned by
|
|
11
|
+
* Discriminated union describing which cached provider-permission entries a
|
|
12
|
+
* webhook event invalidates. Returned by
|
|
13
|
+
* `WebhookNormalizer.getAccessCacheInvalidations`.
|
|
14
|
+
*
|
|
15
|
+
* @deprecated Trust is derived from the git ref, so the orchestrator holds no
|
|
16
|
+
* contributor-permission cache and nothing consumes these entries. Removed at
|
|
17
|
+
* v1.0.0.
|
|
13
18
|
*
|
|
14
19
|
* Three scopes:
|
|
15
20
|
* - `repo-user`: a single `{repo, user}` permission changed (e.g. GitHub
|
|
@@ -142,17 +147,12 @@ export interface WebhookNormalizer {
|
|
|
142
147
|
*/
|
|
143
148
|
extractDefaultBranch?(payload: unknown): string | null;
|
|
144
149
|
/**
|
|
145
|
-
* Map a webhook event to the
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* `normalizeEvent` and invalidates every returned entry so the next
|
|
149
|
-
* permission check hits the provider API instead of relying on stale
|
|
150
|
-
* cached data. Events that do not imply a permission shift (most events,
|
|
151
|
-
* including `push` / `pull_request` / etc.) should return `[]`.
|
|
150
|
+
* Map a webhook event to the cached provider-permission entries it implies a
|
|
151
|
+
* shift in. Events that do not imply a permission shift (most events,
|
|
152
|
+
* including `push` / `pull_request` / etc.) return `[]`.
|
|
152
153
|
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
* provider argument.
|
|
154
|
+
* @deprecated Trust is derived from the git ref, so the orchestrator holds no
|
|
155
|
+
* contributor-permission cache and never calls this. Removed at v1.0.0.
|
|
156
156
|
*
|
|
157
157
|
* @param eventType Provider-specific event type (from extractEventType).
|
|
158
158
|
* @param action Event action/sub-type, if applicable.
|