@kici-dev/engine 0.1.27 → 0.2.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/approval/types.d.ts +8 -1
- package/dist/approval/types.js +8 -1
- package/dist/artifacts/name.d.ts +60 -0
- package/dist/artifacts/name.js +71 -0
- package/dist/audit/access-log-policy.d.ts +1 -1
- package/dist/audit/access-log-policy.js +8 -0
- package/dist/audit/activity.d.ts +12 -12
- package/dist/audit/retention-policy.js +16 -0
- package/dist/billing/plan-type.d.ts +53 -0
- package/dist/billing/plan-type.js +70 -0
- package/dist/bundler/rolldown-config.d.ts +3 -3
- package/dist/check-mode.d.ts +1 -1
- package/dist/context/concurrency-strategy.d.ts +34 -0
- package/dist/context/concurrency-strategy.js +35 -0
- package/dist/context/held-run-job-id.d.ts +45 -0
- package/dist/context/held-run-job-id.js +56 -0
- package/dist/context/held-run-status.d.ts +33 -0
- package/dist/context/held-run-status.js +37 -0
- package/dist/context/hold-expiry.d.ts +20 -0
- package/dist/context/hold-expiry.js +25 -0
- package/dist/context/hold-reason.d.ts +33 -0
- package/dist/context/hold-reason.js +23 -0
- package/dist/context/hold-type.d.ts +40 -0
- package/dist/context/hold-type.js +61 -0
- package/dist/context/index.d.ts +9 -1
- package/dist/context/index.js +9 -2
- package/dist/context/multi-context.d.ts +3 -3
- package/dist/context/scope-name.d.ts +29 -0
- package/dist/context/scope-name.js +49 -0
- package/dist/context/scope-resolver.d.ts +33 -0
- package/dist/context/scope-resolver.js +45 -15
- package/dist/context/types.d.ts +8 -5
- package/dist/dev-ops/operations.d.ts +8 -0
- package/dist/dev-ops/operations.js +36 -19
- package/dist/diagnostics/infra-alert.d.ts +71 -0
- package/dist/diagnostics/infra-alert.js +60 -0
- package/dist/env/environment-allowlist.d.ts +4 -4
- package/dist/fanout/materialize.d.ts +16 -0
- package/dist/fanout/materialize.js +32 -3
- package/dist/index.d.ts +15 -6
- package/dist/index.js +34 -19
- package/dist/inputs/descriptor.d.ts +9 -9
- package/dist/inventory.d.ts +4 -4
- package/dist/labels.d.ts +82 -2
- package/dist/labels.js +119 -1
- package/dist/matrix/expand.d.ts +49 -2
- package/dist/matrix/expand.js +102 -13
- package/dist/matrix/format.d.ts +6 -1
- package/dist/matrix/format.js +6 -1
- package/dist/mcp/held-run-resolve.d.ts +7 -10
- package/dist/mcp/held-run-resolve.js +33 -7
- package/dist/metrics/catalog-policy.js +4 -4
- package/dist/metrics/metric-catalog.generated.d.ts +954 -739
- package/dist/metrics/metric-catalog.generated.js +278 -20
- package/dist/metrics/metric-kind-compat.d.ts +26 -0
- package/dist/metrics/metric-kind-compat.js +33 -0
- package/dist/protocol/analytics-events.d.ts +6 -6
- package/dist/protocol/dashboard-api-errors.d.ts +1 -0
- package/dist/protocol/dashboard-api-errors.js +5 -1
- package/dist/protocol/dashboard-write-operations.d.ts +143 -86
- package/dist/protocol/dashboard-write-operations.js +91 -17
- package/dist/protocol/messages/access-log.d.ts +437 -392
- package/dist/protocol/messages/access-log.js +11 -2
- package/dist/protocol/messages/actor.d.ts +2 -2
- package/dist/protocol/messages/agent-dev-ops.d.ts +4 -2
- package/dist/protocol/messages/agent-dev-ops.js +2 -5
- package/dist/protocol/messages/agent-run-result.d.ts +72 -62
- package/dist/protocol/messages/auth.d.ts +35 -23
- package/dist/protocol/messages/browser.d.ts +81 -5
- package/dist/protocol/messages/browser.js +53 -2
- package/dist/protocol/messages/capabilities.d.ts +106 -31
- package/dist/protocol/messages/capabilities.js +94 -10
- package/dist/protocol/messages/common.d.ts +45 -2
- package/dist/protocol/messages/common.js +64 -3
- package/dist/protocol/messages/dashboard-sealed-write.d.ts +52 -0
- package/dist/protocol/messages/dashboard-sealed-write.js +57 -0
- package/dist/protocol/messages/dashboard.d.ts +1022 -498
- package/dist/protocol/messages/dashboard.js +198 -31
- package/dist/protocol/messages/deployment-identity.d.ts +37 -10
- package/dist/protocol/messages/deployment-identity.js +30 -3
- package/dist/protocol/messages/event-log.d.ts +5 -5
- package/dist/protocol/messages/execution-status.d.ts +189 -53
- package/dist/protocol/messages/execution-status.js +108 -40
- package/dist/protocol/messages/heartbeat-health.d.ts +83 -0
- package/dist/protocol/messages/heartbeat-health.js +87 -0
- package/dist/protocol/messages/log-stream.d.ts +18 -0
- package/dist/protocol/messages/log-stream.js +19 -0
- package/dist/protocol/messages/oidc-mint.d.ts +7 -0
- package/dist/protocol/messages/oidc-mint.js +7 -0
- package/dist/protocol/messages/orchestrator-agent.d.ts +264 -67
- package/dist/protocol/messages/orchestrator-agent.js +170 -5
- package/dist/protocol/messages/pat-kind.d.ts +1 -1
- package/dist/protocol/messages/peer.d.ts +122 -58
- package/dist/protocol/messages/peer.js +37 -2
- package/dist/protocol/messages/platform-orchestrator.d.ts +561 -217
- package/dist/protocol/messages/platform-orchestrator.js +131 -21
- package/dist/protocol/messages/run-events.d.ts +5 -5
- package/dist/protocol/messages/scaler-event.d.ts +3 -3
- package/dist/protocol/messages/source-registration.d.ts +56 -13
- package/dist/protocol/messages/source-registration.js +49 -6
- package/dist/protocol/source-origin.d.ts +1 -1
- package/dist/provenance/attestation-origin.d.ts +1 -1
- package/dist/provenance/schema.d.ts +11 -11
- package/dist/provenance/verify.js +2 -1
- package/dist/provider/changed-files-fetcher.d.ts +21 -2
- package/dist/provider/check-run-conclusion.d.ts +2 -2
- package/dist/provider/check-status-poster.d.ts +23 -0
- package/dist/provider/index.d.ts +2 -2
- package/dist/registration/registerable-trigger-type.d.ts +14 -13
- package/dist/registration/registerable-trigger-type.js +1 -0
- package/dist/sandbox/capabilities.d.ts +6 -0
- package/dist/sandbox/capabilities.js +57 -0
- package/dist/scaler/scaler-backend-type.d.ts +1 -1
- package/dist/status/presentation.d.ts +89 -0
- package/dist/status/presentation.js +163 -0
- package/dist/trigger/compiled-matchers.d.ts +13 -0
- package/dist/trigger/compiled-matchers.js +44 -0
- package/dist/trigger/event-buckets.d.ts +44 -0
- package/dist/trigger/event-buckets.js +93 -0
- package/dist/trigger/matcher.d.ts +7 -3
- package/dist/trigger/matcher.js +53 -15
- package/dist/trigger/schedule-key.d.ts +14 -0
- package/dist/trigger/schedule-key.js +21 -0
- package/dist/trigger/trigger-event-type.d.ts +1 -1
- package/dist/trigger/trigger-event-type.js +2 -0
- package/dist/trigger/types.d.ts +173 -43
- package/dist/trigger/types.js +82 -25
- package/dist/webhook/event-types.d.ts +56 -0
- package/dist/webhook/event-types.js +59 -0
- package/dist/ws/ws-like.d.ts +13 -0
- package/package.json +12 -4
- package/sbom.spdx.json +10 -10
- package/dist/environment/host-match.d.ts +0 -25
- package/dist/environment/index.d.ts +0 -6
- package/dist/environment/multi-env.d.ts +0 -30
- package/dist/environment/scope-resolver.d.ts +0 -46
- package/dist/environment/scope-template.d.ts +0 -18
- package/dist/environment/types.d.ts +0 -119
- package/dist/package-manager.d.ts +0 -20
- package/dist/protocol/messages/observe.d.ts +0 -76
- package/dist/protocol/messages/saas-orchestrator.d.ts +0 -2179
- package/dist/protocol/messages/test-run.d.ts +0 -59
- package/dist/state-machine/index.d.ts +0 -2
- package/dist/state-machine/index.js +0 -3
- package/dist/state-machine/machine.d.ts +0 -34
- package/dist/state-machine/machine.js +0 -103
- package/dist/state-machine/types.d.ts +0 -34
- package/dist/state-machine/types.js +0 -15
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
//#region src/context/held-run-job-id.ts
|
|
3
|
+
/**
|
|
4
|
+
* Run-wide sentinel values for `held_runs.job_id`.
|
|
5
|
+
*
|
|
6
|
+
* The column normally carries the held job's expanded name (a matrix child
|
|
7
|
+
* keeps its own `build (18)` form), which is what the dashboard approval queue
|
|
8
|
+
* renders and what `kici approve --job <name>` resolves. Some holds are not
|
|
9
|
+
* scoped to a single job and use a sentinel instead: the workflow install gate,
|
|
10
|
+
* and the PR-wide security holds the org trust policy raises.
|
|
11
|
+
*
|
|
12
|
+
* The orchestrator writes these values and the dashboard reads them back to
|
|
13
|
+
* render a human-meaningful row label, so they live here rather than as a
|
|
14
|
+
* literal on each side — a rename on the writer must not silently stop the
|
|
15
|
+
* reader from recognising the sentinel.
|
|
16
|
+
*/
|
|
17
|
+
/** Sentinel `job_id` for the PR-wide workflow-modification security hold. */
|
|
18
|
+
const WORKFLOW_MODIFICATION_JOB_ID = "__workflow_modification__";
|
|
19
|
+
/**
|
|
20
|
+
* Sentinel `job_id` per PR-wide security-hold reason, written when the org
|
|
21
|
+
* trust policy holds a run.
|
|
22
|
+
*
|
|
23
|
+
* `workflow_modification` keeps its historical value so holds created before
|
|
24
|
+
* the policy was enforced still resolve by job id.
|
|
25
|
+
*
|
|
26
|
+
* Covers the org trust policy's own three arms ONLY. `context_trust` is
|
|
27
|
+
* deliberately absent: that hold comes from the per-context minimum-trust gate,
|
|
28
|
+
* which writes the real expanded job name (a matrix child keeps its `build (18)`
|
|
29
|
+
* form) so the operator can approve that job specifically. A sentinel for it
|
|
30
|
+
* would be a value nothing ever writes.
|
|
31
|
+
*
|
|
32
|
+
* Keyed by the orchestrator's `TrustPolicyHoldReason`; the key set is asserted
|
|
33
|
+
* against that type in the orchestrator's own test rather than importing it
|
|
34
|
+
* here (the engine must not depend on the orchestrator).
|
|
35
|
+
*/
|
|
36
|
+
const SECURITY_HOLD_JOB_IDS = {
|
|
37
|
+
workflow_modification: WORKFLOW_MODIFICATION_JOB_ID,
|
|
38
|
+
fork_pr: "__fork_pr__",
|
|
39
|
+
unknown_contributor: "__unknown_contributor__"
|
|
40
|
+
};
|
|
41
|
+
/** Human-meaningful label per security-hold sentinel, rendered by the dashboard. */
|
|
42
|
+
const SECURITY_HOLD_JOB_LABELS = {
|
|
43
|
+
[SECURITY_HOLD_JOB_IDS.workflow_modification]: "workflow modification",
|
|
44
|
+
[SECURITY_HOLD_JOB_IDS.fork_pr]: "fork pull request",
|
|
45
|
+
[SECURITY_HOLD_JOB_IDS.unknown_contributor]: "unknown contributor"
|
|
46
|
+
};
|
|
47
|
+
/** Sentinel `job_id` prefix for the workflow install gate, suffixed by the workflow name. */
|
|
48
|
+
const INSTALL_JOB_ID_PREFIX = "__install__";
|
|
49
|
+
/** The install-gate sentinel `job_id` for a workflow. */
|
|
50
|
+
function installGateJobId(workflowName) {
|
|
51
|
+
return `${INSTALL_JOB_ID_PREFIX}${workflowName}`;
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { INSTALL_JOB_ID_PREFIX, SECURITY_HOLD_JOB_IDS, SECURITY_HOLD_JOB_LABELS, WORKFLOW_MODIFICATION_JOB_ID, installGateJobId };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=held-run-job-id.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Held-run status vocabulary — the single definition of what a held run's
|
|
3
|
+
* `status` can be.
|
|
4
|
+
*
|
|
5
|
+
* Lives beside `hold-type.ts` and `concurrency-strategy.ts` (the other shared
|
|
6
|
+
* context vocabularies) rather than in the protocol module, so both the domain
|
|
7
|
+
* types here and the wire schema in `protocol/messages/dashboard.ts` name the
|
|
8
|
+
* same enum instead of each carrying its own copy. A duplicated copy is exactly
|
|
9
|
+
* how `released` came to be persisted by the orchestrator but absent from the
|
|
10
|
+
* wire schema.
|
|
11
|
+
*
|
|
12
|
+
* This is the *known* vocabulary — the set the dashboard renders a labelled
|
|
13
|
+
* badge and a queue tab for, and the set a client may filter a list by. It is
|
|
14
|
+
* deliberately NOT the wire type of the response field: `held_runs.status` is a
|
|
15
|
+
* plain-text column owned by a customer-deployed orchestrator, so the response
|
|
16
|
+
* carries `z.string()`.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
/**
|
|
20
|
+
* Known held-run statuses.
|
|
21
|
+
*
|
|
22
|
+
* `released` is written by the orchestrator's `releaseDueWaitHolds()` when a
|
|
23
|
+
* workflow-scope wait timer elapses and the run proceeds.
|
|
24
|
+
*/
|
|
25
|
+
export declare const HeldRunStatus: z.ZodEnum<{
|
|
26
|
+
approved: "approved";
|
|
27
|
+
expired: "expired";
|
|
28
|
+
pending: "pending";
|
|
29
|
+
rejected: "rejected";
|
|
30
|
+
released: "released";
|
|
31
|
+
}>;
|
|
32
|
+
export type HeldRunStatus = z.infer<typeof HeldRunStatus>;
|
|
33
|
+
//# sourceMappingURL=held-run-status.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/context/held-run-status.ts
|
|
4
|
+
/**
|
|
5
|
+
* Held-run status vocabulary — the single definition of what a held run's
|
|
6
|
+
* `status` can be.
|
|
7
|
+
*
|
|
8
|
+
* Lives beside `hold-type.ts` and `concurrency-strategy.ts` (the other shared
|
|
9
|
+
* context vocabularies) rather than in the protocol module, so both the domain
|
|
10
|
+
* types here and the wire schema in `protocol/messages/dashboard.ts` name the
|
|
11
|
+
* same enum instead of each carrying its own copy. A duplicated copy is exactly
|
|
12
|
+
* how `released` came to be persisted by the orchestrator but absent from the
|
|
13
|
+
* wire schema.
|
|
14
|
+
*
|
|
15
|
+
* This is the *known* vocabulary — the set the dashboard renders a labelled
|
|
16
|
+
* badge and a queue tab for, and the set a client may filter a list by. It is
|
|
17
|
+
* deliberately NOT the wire type of the response field: `held_runs.status` is a
|
|
18
|
+
* plain-text column owned by a customer-deployed orchestrator, so the response
|
|
19
|
+
* carries `z.string()`.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Known held-run statuses.
|
|
23
|
+
*
|
|
24
|
+
* `released` is written by the orchestrator's `releaseDueWaitHolds()` when a
|
|
25
|
+
* workflow-scope wait timer elapses and the run proceeds.
|
|
26
|
+
*/
|
|
27
|
+
const HeldRunStatus = z.enum([
|
|
28
|
+
"pending",
|
|
29
|
+
"approved",
|
|
30
|
+
"rejected",
|
|
31
|
+
"expired",
|
|
32
|
+
"released"
|
|
33
|
+
]);
|
|
34
|
+
//#endregion
|
|
35
|
+
export { HeldRunStatus };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=held-run-status.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hold window applied when a context carries no explicit hold expiry.
|
|
3
|
+
*
|
|
4
|
+
* `contexts.hold_expiry_seconds` is nullable, and a cleared column means "no
|
|
5
|
+
* explicit expiry" rather than "expire instantly" — without this fallback a
|
|
6
|
+
* cleared value would reach `evaluateReviewerGate` as `null`, whose
|
|
7
|
+
* `null * 1000` puts `holdUntil` at the current instant, so every reviewer hold
|
|
8
|
+
* would be created already overdue and swept to `expired` on the next stale
|
|
9
|
+
* scan, cancelling the job the hold was meant to gate.
|
|
10
|
+
*
|
|
11
|
+
* It lives here, beside `DEFAULT_CONCURRENCY_STRATEGY`, because the same three
|
|
12
|
+
* modules resolve both: the orchestrator's context store, its protection
|
|
13
|
+
* aggregate, and the dispatch path. The column carries no DDL default, so a
|
|
14
|
+
* context created without a hold expiry and one whose expiry was cleared both
|
|
15
|
+
* land on NULL and resolve through this single constant.
|
|
16
|
+
*
|
|
17
|
+
* Plain number, no `node:*` import — safe for the browser-facing engine barrel.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DEFAULT_HOLD_EXPIRY_SECONDS = 3600;
|
|
20
|
+
//# sourceMappingURL=hold-expiry.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
//#region src/context/hold-expiry.ts
|
|
3
|
+
/**
|
|
4
|
+
* Hold window applied when a context carries no explicit hold expiry.
|
|
5
|
+
*
|
|
6
|
+
* `contexts.hold_expiry_seconds` is nullable, and a cleared column means "no
|
|
7
|
+
* explicit expiry" rather than "expire instantly" — without this fallback a
|
|
8
|
+
* cleared value would reach `evaluateReviewerGate` as `null`, whose
|
|
9
|
+
* `null * 1000` puts `holdUntil` at the current instant, so every reviewer hold
|
|
10
|
+
* would be created already overdue and swept to `expired` on the next stale
|
|
11
|
+
* scan, cancelling the job the hold was meant to gate.
|
|
12
|
+
*
|
|
13
|
+
* It lives here, beside `DEFAULT_CONCURRENCY_STRATEGY`, because the same three
|
|
14
|
+
* modules resolve both: the orchestrator's context store, its protection
|
|
15
|
+
* aggregate, and the dispatch path. The column carries no DDL default, so a
|
|
16
|
+
* context created without a hold expiry and one whose expiry was cleared both
|
|
17
|
+
* land on NULL and resolve through this single constant.
|
|
18
|
+
*
|
|
19
|
+
* Plain number, no `node:*` import — safe for the browser-facing engine barrel.
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_HOLD_EXPIRY_SECONDS = 3600;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { DEFAULT_HOLD_EXPIRY_SECONDS };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=hold-expiry.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protection-gate hold-reason templates — the free-text sentence the trust gate
|
|
3
|
+
* persists into `held_runs.reason` when a contributor is below a context's
|
|
4
|
+
* `minimumTrust`.
|
|
5
|
+
*
|
|
6
|
+
* Single source of truth for the two sentences: the orchestrator's trust gate
|
|
7
|
+
* emits them, the ci-security DB fixture seeds them, and the unit and E2E
|
|
8
|
+
* assertions compare against them. One copy means a copy edit moves every site
|
|
9
|
+
* at once (`.claude/rules/code-style.md` § "Enums over hardcoded strings").
|
|
10
|
+
*
|
|
11
|
+
* `held_runs.reason` is a free-text column, so these are functions rather than
|
|
12
|
+
* an enum: the sentence carries the context name, which is per-hold data.
|
|
13
|
+
*
|
|
14
|
+
* Carries no runtime import on purpose — this module reaches the browser
|
|
15
|
+
* through the engine barrel, which must pull in no Node built-ins
|
|
16
|
+
* (`.claude/rules/engine.md` § "Browser-safe barrel export"). The one import
|
|
17
|
+
* below is type-only and is erased at compile time.
|
|
18
|
+
*/
|
|
19
|
+
import type { TrustTier } from './types.js';
|
|
20
|
+
/**
|
|
21
|
+
* Reason a run is held because the context requires `minimumTrust: 'known'`
|
|
22
|
+
* and the contributor's tier is `unknown`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function unknownContributorHoldReason(contextName: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Reason a run is held because the context requires `minimumTrust: 'trusted'`
|
|
27
|
+
* and the contributor's tier is below that. `trustTier` is the contributor's
|
|
28
|
+
* actual tier, which the gate has already narrowed to a tier below `trusted` —
|
|
29
|
+
* so the type excludes `'trusted'`, whose sentence would read as a
|
|
30
|
+
* contradiction.
|
|
31
|
+
*/
|
|
32
|
+
export declare function trustedContributorHoldReason(contextName: string, trustTier: Exclude<TrustTier, 'trusted'>): string;
|
|
33
|
+
//# sourceMappingURL=hold-reason.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
//#region src/context/hold-reason.ts
|
|
3
|
+
/**
|
|
4
|
+
* Reason a run is held because the context requires `minimumTrust: 'known'`
|
|
5
|
+
* and the contributor's tier is `unknown`.
|
|
6
|
+
*/
|
|
7
|
+
function unknownContributorHoldReason(contextName) {
|
|
8
|
+
return `Context '${contextName}' requires known contributors (contributor is unknown)`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Reason a run is held because the context requires `minimumTrust: 'trusted'`
|
|
12
|
+
* and the contributor's tier is below that. `trustTier` is the contributor's
|
|
13
|
+
* actual tier, which the gate has already narrowed to a tier below `trusted` —
|
|
14
|
+
* so the type excludes `'trusted'`, whose sentence would read as a
|
|
15
|
+
* contradiction.
|
|
16
|
+
*/
|
|
17
|
+
function trustedContributorHoldReason(contextName, trustTier) {
|
|
18
|
+
return `Context '${contextName}' requires trusted contributors (contributor is ${trustTier})`;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { trustedContributorHoldReason, unknownContributorHoldReason };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=hold-reason.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Protection-gate hold-type vocabulary — the reason a run is paused by a
|
|
4
|
+
* context protection gate. Single source of truth for the four gate hold
|
|
5
|
+
* types shared across the engine domain types, the orchestrator gates, and the
|
|
6
|
+
* dashboard held-run UI.
|
|
7
|
+
*
|
|
8
|
+
* The orchestrator persists these verbatim into `held_runs.hold_type`, so the
|
|
9
|
+
* column, the wire and the dashboard all speak one vocabulary. The column and
|
|
10
|
+
* the wire field it rides on stay typed as `string` (see the held-runs list
|
|
11
|
+
* response schema) so an older/newer orchestrator's hold type never rejects the
|
|
12
|
+
* relayed message.
|
|
13
|
+
*/
|
|
14
|
+
export declare const HoldType: z.ZodEnum<{
|
|
15
|
+
concurrency: "concurrency";
|
|
16
|
+
reviewer: "reviewer";
|
|
17
|
+
security: "security";
|
|
18
|
+
timer: "timer";
|
|
19
|
+
}>;
|
|
20
|
+
export type HoldType = z.infer<typeof HoldType>;
|
|
21
|
+
/**
|
|
22
|
+
* Map a persisted hold type onto the gate vocabulary.
|
|
23
|
+
*
|
|
24
|
+
* An unrecognised value passes through unchanged rather than being coerced to a
|
|
25
|
+
* default: the wire field is `z.string()` precisely so a newer orchestrator's
|
|
26
|
+
* hold type survives an older reader, and the dashboard's gray fallback badge
|
|
27
|
+
* is the correct rendering for a genuinely unknown type.
|
|
28
|
+
*/
|
|
29
|
+
export declare function normalizePersistedHoldType(raw: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Every spelling `held_runs.hold_type` may hold for one gate hold type — the
|
|
32
|
+
* current one first, then any legacy alias that normalizes onto it.
|
|
33
|
+
*
|
|
34
|
+
* For queries that must match a hold type in SQL, where the row-by-row
|
|
35
|
+
* `normalizePersistedHoldType` cannot run. Feeding this into an `IN (…)` filter
|
|
36
|
+
* keeps a sweep matching rows an un-upgraded orchestrator wrote, and keeps the
|
|
37
|
+
* legacy spellings out of call sites as bare literals.
|
|
38
|
+
*/
|
|
39
|
+
export declare function persistedHoldTypeSpellings(type: HoldType): string[];
|
|
40
|
+
//# sourceMappingURL=hold-type.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/context/hold-type.ts
|
|
4
|
+
/**
|
|
5
|
+
* Protection-gate hold-type vocabulary — the reason a run is paused by a
|
|
6
|
+
* context protection gate. Single source of truth for the four gate hold
|
|
7
|
+
* types shared across the engine domain types, the orchestrator gates, and the
|
|
8
|
+
* dashboard held-run UI.
|
|
9
|
+
*
|
|
10
|
+
* The orchestrator persists these verbatim into `held_runs.hold_type`, so the
|
|
11
|
+
* column, the wire and the dashboard all speak one vocabulary. The column and
|
|
12
|
+
* the wire field it rides on stay typed as `string` (see the held-runs list
|
|
13
|
+
* response schema) so an older/newer orchestrator's hold type never rejects the
|
|
14
|
+
* relayed message.
|
|
15
|
+
*/
|
|
16
|
+
const HoldType = z.enum([
|
|
17
|
+
"reviewer",
|
|
18
|
+
"timer",
|
|
19
|
+
"concurrency",
|
|
20
|
+
"security"
|
|
21
|
+
]);
|
|
22
|
+
/**
|
|
23
|
+
* Legacy persisted `held_runs.hold_type` spellings → the gate vocabulary.
|
|
24
|
+
*
|
|
25
|
+
* A row written by an un-upgraded orchestrator can carry `approval` for a
|
|
26
|
+
* reviewer hold or `wait_timer` for a workflow-install wait hold. Writers emit
|
|
27
|
+
* the gate vocabulary and a migration backfills the column, so this map exists
|
|
28
|
+
* for those older rows — it is what lets the two sides deploy independently
|
|
29
|
+
* instead of in lockstep.
|
|
30
|
+
*/
|
|
31
|
+
const LEGACY_HOLD_TYPES = Object.freeze({
|
|
32
|
+
approval: HoldType.enum.reviewer,
|
|
33
|
+
wait_timer: HoldType.enum.timer
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* Map a persisted hold type onto the gate vocabulary.
|
|
37
|
+
*
|
|
38
|
+
* An unrecognised value passes through unchanged rather than being coerced to a
|
|
39
|
+
* default: the wire field is `z.string()` precisely so a newer orchestrator's
|
|
40
|
+
* hold type survives an older reader, and the dashboard's gray fallback badge
|
|
41
|
+
* is the correct rendering for a genuinely unknown type.
|
|
42
|
+
*/
|
|
43
|
+
function normalizePersistedHoldType(raw) {
|
|
44
|
+
return Object.hasOwn(LEGACY_HOLD_TYPES, raw) ? LEGACY_HOLD_TYPES[raw] : raw;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Every spelling `held_runs.hold_type` may hold for one gate hold type — the
|
|
48
|
+
* current one first, then any legacy alias that normalizes onto it.
|
|
49
|
+
*
|
|
50
|
+
* For queries that must match a hold type in SQL, where the row-by-row
|
|
51
|
+
* `normalizePersistedHoldType` cannot run. Feeding this into an `IN (…)` filter
|
|
52
|
+
* keeps a sweep matching rows an un-upgraded orchestrator wrote, and keeps the
|
|
53
|
+
* legacy spellings out of call sites as bare literals.
|
|
54
|
+
*/
|
|
55
|
+
function persistedHoldTypeSpellings(type) {
|
|
56
|
+
return [type, ...Object.keys(LEGACY_HOLD_TYPES).filter((spelling) => LEGACY_HOLD_TYPES[spelling] === type)];
|
|
57
|
+
}
|
|
58
|
+
//#endregion
|
|
59
|
+
export { HoldType, normalizePersistedHoldType, persistedHoldTypeSpellings };
|
|
60
|
+
|
|
61
|
+
//# sourceMappingURL=hold-type.js.map
|
package/dist/context/index.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export type { Context, ContextBinding, ContextVariable, ContextSourceOverride, ScopedSecret, HeldRun, ProtectionGateResult, TrustTier, } from './types.js';
|
|
2
2
|
export { TrustTierSchema } from './types.js';
|
|
3
|
-
export {
|
|
3
|
+
export { HoldType, normalizePersistedHoldType, persistedHoldTypeSpellings } from './hold-type.js';
|
|
4
|
+
export { trustedContributorHoldReason, unknownContributorHoldReason } from './hold-reason.js';
|
|
5
|
+
export { HeldRunStatus } from './held-run-status.js';
|
|
6
|
+
export { WORKFLOW_MODIFICATION_JOB_ID, SECURITY_HOLD_JOB_IDS, SECURITY_HOLD_JOB_LABELS, INSTALL_JOB_ID_PREFIX, installGateJobId, } from './held-run-job-id.js';
|
|
7
|
+
export { ConcurrencyStrategy, DEFAULT_CONCURRENCY_STRATEGY } from './concurrency-strategy.js';
|
|
8
|
+
export { DEFAULT_HOLD_EXPIRY_SECONDS } from './hold-expiry.js';
|
|
9
|
+
export { resolveSecretsForContext, resolveSecretsWithProvenance, matchScopePattern, stripScopePrefix, } from './scope-resolver.js';
|
|
10
|
+
export type { ResolvedSecretCandidate } from './scope-resolver.js';
|
|
4
11
|
export type { HostFacts } from './host-match.js';
|
|
5
12
|
export { mergeOrderedMaps, ContextGateRejectReason } from './multi-context.js';
|
|
13
|
+
export { validateScopeName, assertValidScopeName, ScopeNameError, SCOPE_SEGMENT_PATTERN, SCOPE_NAME_MAX_LENGTH, } from './scope-name.js';
|
|
6
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/context/index.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { HeldRunStatus } from "./held-run-status.js";
|
|
2
3
|
import { TrustTierSchema } from "./types.js";
|
|
3
|
-
import {
|
|
4
|
+
import { ConcurrencyStrategy, DEFAULT_CONCURRENCY_STRATEGY } from "./concurrency-strategy.js";
|
|
5
|
+
import { HoldType, normalizePersistedHoldType, persistedHoldTypeSpellings } from "./hold-type.js";
|
|
6
|
+
import { trustedContributorHoldReason, unknownContributorHoldReason } from "./hold-reason.js";
|
|
7
|
+
import { INSTALL_JOB_ID_PREFIX, SECURITY_HOLD_JOB_IDS, SECURITY_HOLD_JOB_LABELS, WORKFLOW_MODIFICATION_JOB_ID, installGateJobId } from "./held-run-job-id.js";
|
|
8
|
+
import { DEFAULT_HOLD_EXPIRY_SECONDS } from "./hold-expiry.js";
|
|
9
|
+
import { matchScopePattern, resolveSecretsForContext, resolveSecretsWithProvenance, stripScopePrefix } from "./scope-resolver.js";
|
|
4
10
|
import { ContextGateRejectReason, mergeOrderedMaps } from "./multi-context.js";
|
|
5
|
-
|
|
11
|
+
import { SCOPE_NAME_MAX_LENGTH, SCOPE_SEGMENT_PATTERN, ScopeNameError, assertValidScopeName, validateScopeName } from "./scope-name.js";
|
|
12
|
+
export { ConcurrencyStrategy, ContextGateRejectReason, DEFAULT_CONCURRENCY_STRATEGY, DEFAULT_HOLD_EXPIRY_SECONDS, HeldRunStatus, HoldType, INSTALL_JOB_ID_PREFIX, SCOPE_NAME_MAX_LENGTH, SCOPE_SEGMENT_PATTERN, SECURITY_HOLD_JOB_IDS, SECURITY_HOLD_JOB_LABELS, ScopeNameError, TrustTierSchema, WORKFLOW_MODIFICATION_JOB_ID, assertValidScopeName, installGateJobId, matchScopePattern, mergeOrderedMaps, normalizePersistedHoldType, persistedHoldTypeSpellings, resolveSecretsForContext, resolveSecretsWithProvenance, stripScopePrefix, trustedContributorHoldReason, unknownContributorHoldReason, validateScopeName };
|
|
@@ -14,11 +14,11 @@ import { z } from 'zod';
|
|
|
14
14
|
*/
|
|
15
15
|
export declare const ContextGateRejectReason: z.ZodEnum<{
|
|
16
16
|
branch_restricted: "branch_restricted";
|
|
17
|
-
trigger_filtered: "trigger_filtered";
|
|
18
|
-
repo_unmatched: "repo_unmatched";
|
|
19
|
-
trust_too_low: "trust_too_low";
|
|
20
17
|
context_disabled: "context_disabled";
|
|
21
18
|
context_not_found: "context_not_found";
|
|
19
|
+
repo_unmatched: "repo_unmatched";
|
|
20
|
+
trigger_filtered: "trigger_filtered";
|
|
21
|
+
trust_too_low: "trust_too_low";
|
|
22
22
|
}>;
|
|
23
23
|
export type ContextGateRejectReason = z.infer<typeof ContextGateRejectReason>;
|
|
24
24
|
/**
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical secret-scope-name validation, shared by the orchestrator write
|
|
3
|
+
* paths and the dashboard scope form. The rule runs on the PATH portion of a
|
|
4
|
+
* scope (after any `backend:` prefix has been stripped): non-empty, length
|
|
5
|
+
* bounded, `/`-separated non-empty segments, each segment restricted to
|
|
6
|
+
* `[A-Za-z0-9_.-]`, and no `.`/`..` segments. Kept dependency-free so the
|
|
7
|
+
* engine barrel stays browser-safe.
|
|
8
|
+
*
|
|
9
|
+
* Callers pass a bare path, so that precondition holds trivially: a writer
|
|
10
|
+
* never supplies a backend qualifier. The `<backend>:` prefix is added by the
|
|
11
|
+
* resolver when it merges secrets across backends at read time, and stripped
|
|
12
|
+
* again before any per-backend call.
|
|
13
|
+
*/
|
|
14
|
+
/** Allowed characters in a single `/`-separated scope segment. */
|
|
15
|
+
export declare const SCOPE_SEGMENT_PATTERN: RegExp;
|
|
16
|
+
/** Maximum total length of a scope path. */
|
|
17
|
+
export declare const SCOPE_NAME_MAX_LENGTH = 512;
|
|
18
|
+
/**
|
|
19
|
+
* Validate the path portion of a scope name. Returns a human-readable error
|
|
20
|
+
* message, or `null` when the name is valid.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateScopeName(path: string): string | null;
|
|
23
|
+
/** Error thrown by {@link assertValidScopeName} for an invalid scope name. */
|
|
24
|
+
export declare class ScopeNameError extends Error {
|
|
25
|
+
constructor(message: string);
|
|
26
|
+
}
|
|
27
|
+
/** Throw {@link ScopeNameError} when `path` is not a valid scope name. */
|
|
28
|
+
export declare function assertValidScopeName(path: string): void;
|
|
29
|
+
//# sourceMappingURL=scope-name.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
//#region src/context/scope-name.ts
|
|
3
|
+
/**
|
|
4
|
+
* Canonical secret-scope-name validation, shared by the orchestrator write
|
|
5
|
+
* paths and the dashboard scope form. The rule runs on the PATH portion of a
|
|
6
|
+
* scope (after any `backend:` prefix has been stripped): non-empty, length
|
|
7
|
+
* bounded, `/`-separated non-empty segments, each segment restricted to
|
|
8
|
+
* `[A-Za-z0-9_.-]`, and no `.`/`..` segments. Kept dependency-free so the
|
|
9
|
+
* engine barrel stays browser-safe.
|
|
10
|
+
*
|
|
11
|
+
* Callers pass a bare path, so that precondition holds trivially: a writer
|
|
12
|
+
* never supplies a backend qualifier. The `<backend>:` prefix is added by the
|
|
13
|
+
* resolver when it merges secrets across backends at read time, and stripped
|
|
14
|
+
* again before any per-backend call.
|
|
15
|
+
*/
|
|
16
|
+
/** Allowed characters in a single `/`-separated scope segment. */
|
|
17
|
+
const SCOPE_SEGMENT_PATTERN = /^[A-Za-z0-9_.-]+$/;
|
|
18
|
+
/** Maximum total length of a scope path. */
|
|
19
|
+
const SCOPE_NAME_MAX_LENGTH = 512;
|
|
20
|
+
/**
|
|
21
|
+
* Validate the path portion of a scope name. Returns a human-readable error
|
|
22
|
+
* message, or `null` when the name is valid.
|
|
23
|
+
*/
|
|
24
|
+
function validateScopeName(path) {
|
|
25
|
+
if (path.length === 0) return "Scope name must not be empty";
|
|
26
|
+
if (path.length > 512) return `Scope name must be at most 512 characters`;
|
|
27
|
+
for (const segment of path.split("/")) {
|
|
28
|
+
if (segment.length === 0) return "Scope name must not contain empty path segments";
|
|
29
|
+
if (segment === "." || segment === "..") return "Scope name segments must not be '.' or '..'";
|
|
30
|
+
if (!SCOPE_SEGMENT_PATTERN.test(segment)) return "Scope name may only contain letters, digits, and _ . - characters, separated by /";
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
/** Error thrown by {@link assertValidScopeName} for an invalid scope name. */
|
|
35
|
+
var ScopeNameError = class extends Error {
|
|
36
|
+
constructor(message) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = "ScopeNameError";
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
/** Throw {@link ScopeNameError} when `path` is not a valid scope name. */
|
|
42
|
+
function assertValidScopeName(path) {
|
|
43
|
+
const error = validateScopeName(path);
|
|
44
|
+
if (error !== null) throw new ScopeNameError(error);
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { SCOPE_NAME_MAX_LENGTH, SCOPE_SEGMENT_PATTERN, ScopeNameError, assertValidScopeName, validateScopeName };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=scope-name.js.map
|
|
@@ -20,6 +20,36 @@ export declare function matchScopePattern(scope: string, pattern: string): boole
|
|
|
20
20
|
* Returns the path after the first colon, or the full scope if no prefix.
|
|
21
21
|
*/
|
|
22
22
|
export declare function stripScopePrefix(scope: string): string;
|
|
23
|
+
/** The winning scoped secret for a key, with the precedence tuple that selected it. */
|
|
24
|
+
export interface ResolvedSecretCandidate {
|
|
25
|
+
secret: ScopedSecret;
|
|
26
|
+
scopeDepth: number;
|
|
27
|
+
hostSpec: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the winning secret per key by matching bindings against scoped secrets,
|
|
31
|
+
* applying the host gate and `(host specificity, scope depth)` precedence — WITHOUT
|
|
32
|
+
* decrypting, keeping this module crypto-free. This is the single source of truth for
|
|
33
|
+
* scope precedence; `resolveSecretsForContext` delegates to it and adds the decrypt step.
|
|
34
|
+
*
|
|
35
|
+
* Takes already-filtered bindings (for the target context). For each binding, finds
|
|
36
|
+
* secrets whose (substituted) scope matches the binding's scopePattern via picomatch.
|
|
37
|
+
* When multiple scopes provide the same key, precedence is the tuple
|
|
38
|
+
* `(host specificity, scope depth)` — a per-host binding (exact host) overrides a
|
|
39
|
+
* fleet-wide one, then longest scope path wins. Scope depth is computed AFTER stripping
|
|
40
|
+
* the backend prefix.
|
|
41
|
+
*
|
|
42
|
+
* When `hostFacts` is supplied (a fan-out child's identity), each binding is gated by
|
|
43
|
+
* its `host_pattern` and its `scope_pattern` is templated per-child
|
|
44
|
+
* (`${agentId}`/`${host}`/`${label:NAME}`). When omitted, only fleet-wide (`'**'`/NULL)
|
|
45
|
+
* non-templated bindings contribute — preserving the workflow-level (no-host) behaviour.
|
|
46
|
+
*
|
|
47
|
+
* @param bindings - Bindings already filtered for the target context
|
|
48
|
+
* @param allSecrets - All scoped secrets in the org
|
|
49
|
+
* @param hostFacts - Optional fan-out child identity for per-host resolution
|
|
50
|
+
* @returns Map of secret key → winning candidate (secret + precedence tuple)
|
|
51
|
+
*/
|
|
52
|
+
export declare function resolveSecretsWithProvenance(bindings: ContextBinding[], allSecrets: ScopedSecret[], hostFacts?: HostFacts): Map<string, ResolvedSecretCandidate>;
|
|
23
53
|
/**
|
|
24
54
|
* Resolve secrets for a context by matching bindings against scoped secrets.
|
|
25
55
|
*
|
|
@@ -36,6 +66,9 @@ export declare function stripScopePrefix(scope: string): string;
|
|
|
36
66
|
* (`'**'`/NULL) non-templated bindings contribute — preserving the workflow-level
|
|
37
67
|
* (no-host) behaviour.
|
|
38
68
|
*
|
|
69
|
+
* Delegates precedence to `resolveSecretsWithProvenance` and applies `decryptFn`
|
|
70
|
+
* to each winning secret.
|
|
71
|
+
*
|
|
39
72
|
* @param bindings - Bindings already filtered for the target context
|
|
40
73
|
* @param allSecrets - All scoped secrets in the org
|
|
41
74
|
* @param decryptFn - Pure decryption function (keeps this module crypto-free)
|
|
@@ -70,28 +70,29 @@ function candidateWins(c, existing) {
|
|
|
70
70
|
return c.scopeDepth > existing.scopeDepth;
|
|
71
71
|
}
|
|
72
72
|
/**
|
|
73
|
-
* Resolve
|
|
73
|
+
* Resolve the winning secret per key by matching bindings against scoped secrets,
|
|
74
|
+
* applying the host gate and `(host specificity, scope depth)` precedence — WITHOUT
|
|
75
|
+
* decrypting, keeping this module crypto-free. This is the single source of truth for
|
|
76
|
+
* scope precedence; `resolveSecretsForContext` delegates to it and adds the decrypt step.
|
|
74
77
|
*
|
|
75
|
-
* Takes already-filtered bindings (for the target context). For each binding,
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
+
* Takes already-filtered bindings (for the target context). For each binding, finds
|
|
79
|
+
* secrets whose (substituted) scope matches the binding's scopePattern via picomatch.
|
|
80
|
+
* When multiple scopes provide the same key, precedence is the tuple
|
|
78
81
|
* `(host specificity, scope depth)` — a per-host binding (exact host) overrides a
|
|
79
|
-
* fleet-wide one, then longest scope path wins. Scope depth is computed AFTER
|
|
80
|
-
*
|
|
82
|
+
* fleet-wide one, then longest scope path wins. Scope depth is computed AFTER stripping
|
|
83
|
+
* the backend prefix.
|
|
81
84
|
*
|
|
82
|
-
* When `hostFacts` is supplied (a fan-out child's identity), each binding is
|
|
83
|
-
*
|
|
84
|
-
* (`${agentId}`/`${host}`/`${label:NAME}`). When omitted, only fleet-wide
|
|
85
|
-
*
|
|
86
|
-
* (no-host) behaviour.
|
|
85
|
+
* When `hostFacts` is supplied (a fan-out child's identity), each binding is gated by
|
|
86
|
+
* its `host_pattern` and its `scope_pattern` is templated per-child
|
|
87
|
+
* (`${agentId}`/`${host}`/`${label:NAME}`). When omitted, only fleet-wide (`'**'`/NULL)
|
|
88
|
+
* non-templated bindings contribute — preserving the workflow-level (no-host) behaviour.
|
|
87
89
|
*
|
|
88
90
|
* @param bindings - Bindings already filtered for the target context
|
|
89
91
|
* @param allSecrets - All scoped secrets in the org
|
|
90
|
-
* @param decryptFn - Pure decryption function (keeps this module crypto-free)
|
|
91
92
|
* @param hostFacts - Optional fan-out child identity for per-host resolution
|
|
92
|
-
* @returns
|
|
93
|
+
* @returns Map of secret key → winning candidate (secret + precedence tuple)
|
|
93
94
|
*/
|
|
94
|
-
function
|
|
95
|
+
function resolveSecretsWithProvenance(bindings, allSecrets, hostFacts) {
|
|
95
96
|
const resolved = /* @__PURE__ */ new Map();
|
|
96
97
|
for (const binding of bindings) {
|
|
97
98
|
const scopePattern = bindingScopeForHost(binding, hostFacts);
|
|
@@ -107,11 +108,40 @@ function resolveSecretsForContext(bindings, allSecrets, decryptFn, hostFacts) {
|
|
|
107
108
|
if (candidateWins(candidate, resolved.get(secret.key))) resolved.set(secret.key, candidate);
|
|
108
109
|
}
|
|
109
110
|
}
|
|
111
|
+
return resolved;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Resolve secrets for a context by matching bindings against scoped secrets.
|
|
115
|
+
*
|
|
116
|
+
* Takes already-filtered bindings (for the target context). For each binding,
|
|
117
|
+
* finds secrets whose (substituted) scope matches the binding's scopePattern via
|
|
118
|
+
* picomatch. When multiple scopes provide the same key, precedence is the tuple
|
|
119
|
+
* `(host specificity, scope depth)` — a per-host binding (exact host) overrides a
|
|
120
|
+
* fleet-wide one, then longest scope path wins. Scope depth is computed AFTER
|
|
121
|
+
* stripping the backend prefix.
|
|
122
|
+
*
|
|
123
|
+
* When `hostFacts` is supplied (a fan-out child's identity), each binding is
|
|
124
|
+
* gated by its `host_pattern` and its `scope_pattern` is templated per-child
|
|
125
|
+
* (`${agentId}`/`${host}`/`${label:NAME}`). When omitted, only fleet-wide
|
|
126
|
+
* (`'**'`/NULL) non-templated bindings contribute — preserving the workflow-level
|
|
127
|
+
* (no-host) behaviour.
|
|
128
|
+
*
|
|
129
|
+
* Delegates precedence to `resolveSecretsWithProvenance` and applies `decryptFn`
|
|
130
|
+
* to each winning secret.
|
|
131
|
+
*
|
|
132
|
+
* @param bindings - Bindings already filtered for the target context
|
|
133
|
+
* @param allSecrets - All scoped secrets in the org
|
|
134
|
+
* @param decryptFn - Pure decryption function (keeps this module crypto-free)
|
|
135
|
+
* @param hostFacts - Optional fan-out child identity for per-host resolution
|
|
136
|
+
* @returns Flat record of decrypted secret key-value pairs
|
|
137
|
+
*/
|
|
138
|
+
function resolveSecretsForContext(bindings, allSecrets, decryptFn, hostFacts) {
|
|
139
|
+
const resolved = resolveSecretsWithProvenance(bindings, allSecrets, hostFacts);
|
|
110
140
|
const result = {};
|
|
111
141
|
for (const [key, { secret }] of resolved) result[key] = decryptFn(secret);
|
|
112
142
|
return result;
|
|
113
143
|
}
|
|
114
144
|
//#endregion
|
|
115
|
-
export { matchScopePattern, resolveSecretsForContext, stripScopePrefix };
|
|
145
|
+
export { matchScopePattern, resolveSecretsForContext, resolveSecretsWithProvenance, stripScopePrefix };
|
|
116
146
|
|
|
117
147
|
//# sourceMappingURL=scope-resolver.js.map
|
package/dist/context/types.d.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import type { ApproverClause } from '../approval/types.js';
|
|
9
|
+
import type { HoldType } from './hold-type.js';
|
|
10
|
+
import type { HeldRunStatus } from './held-run-status.js';
|
|
11
|
+
import type { ConcurrencyStrategy } from './concurrency-strategy.js';
|
|
9
12
|
/** Context entity — org-level deployment target with protection rules. */
|
|
10
13
|
export interface Context {
|
|
11
14
|
id: string;
|
|
@@ -17,7 +20,7 @@ export interface Context {
|
|
|
17
20
|
triggerTypeFilters: string[];
|
|
18
21
|
repoPatterns: string[];
|
|
19
22
|
concurrencyLimit: number | null;
|
|
20
|
-
concurrencyStrategy:
|
|
23
|
+
concurrencyStrategy: ConcurrencyStrategy;
|
|
21
24
|
concurrencyTimeoutMs: number;
|
|
22
25
|
requiredReviewers: string[] | null;
|
|
23
26
|
waitTimerSeconds: number | null;
|
|
@@ -82,9 +85,9 @@ export interface ContextSourceOverride {
|
|
|
82
85
|
}
|
|
83
86
|
/** Trust tier for contributor-based CI execution gating (single source of truth). */
|
|
84
87
|
export declare const TrustTierSchema: z.ZodEnum<{
|
|
85
|
-
unknown: "unknown";
|
|
86
88
|
known: "known";
|
|
87
89
|
trusted: "trusted";
|
|
90
|
+
unknown: "unknown";
|
|
88
91
|
}>;
|
|
89
92
|
export type TrustTier = z.infer<typeof TrustTierSchema>;
|
|
90
93
|
/** Held run record for protection gate enforcement. */
|
|
@@ -94,8 +97,8 @@ export interface HeldRun {
|
|
|
94
97
|
runId: string;
|
|
95
98
|
jobId: string;
|
|
96
99
|
contextId: string;
|
|
97
|
-
holdType:
|
|
98
|
-
status:
|
|
100
|
+
holdType: HoldType;
|
|
101
|
+
status: HeldRunStatus;
|
|
99
102
|
reason: string | null;
|
|
100
103
|
approvedBy: string | null;
|
|
101
104
|
createdAt: string;
|
|
@@ -107,7 +110,7 @@ export interface ProtectionGateResult {
|
|
|
107
110
|
action: 'pass' | 'reject' | 'hold' | 'queue' | 'wait';
|
|
108
111
|
reason?: string;
|
|
109
112
|
holdUntil?: string;
|
|
110
|
-
holdType?:
|
|
113
|
+
holdType?: HoldType;
|
|
111
114
|
/**
|
|
112
115
|
* Approver clauses for a reviewer hold, mapped from the context's
|
|
113
116
|
* `requiredReviewers`. Each reviewer string maps to a `{ user }` clause
|