@kici-dev/orchestrator 0.5.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/__test-helpers__/fake-scaler-state-store.d.ts +24 -0
- package/dist/__test-helpers__/mock-db-query.d.ts +130 -0
- package/dist/__test-helpers__/mock-db.d.ts +23 -0
- package/dist/agent/dispatcher.d.ts +81 -13
- package/dist/agent/host-roster.d.ts +37 -5
- package/dist/agent/registry.d.ts +2 -0
- package/dist/app.d.ts +33 -6
- package/dist/approvals/apply-decision.d.ts +67 -19
- package/dist/approvals/held-run-release-wiring.d.ts +45 -0
- package/dist/approvals/independent-wiring.d.ts +55 -0
- package/dist/approvals/resume-router.d.ts +9 -0
- package/dist/approvals/triggerer-subject.d.ts +37 -0
- package/dist/cache/dep-cache.d.ts +54 -10
- package/dist/cache/global-eval-round-cache.d.ts +7 -0
- package/dist/cli/api-client.d.ts +18 -0
- package/dist/cli/commands/cluster-settings.d.ts +11 -0
- package/dist/cli/commands/diagnose.d.ts +6 -0
- package/dist/cli/commands/held-run.d.ts +58 -0
- package/dist/cli/commands/trust-policy.d.ts +117 -12
- package/dist/cli/kici-admin.d.ts +10 -0
- package/dist/cli.js +2272 -1025
- package/dist/cluster/cluster-identity.d.ts +3 -3
- package/dist/cluster/cluster-settings-reader.d.ts +7 -1
- package/dist/cluster/coordinator.d.ts +44 -0
- package/dist/cluster/index.d.ts +4 -0
- package/dist/cluster/peer-handler.d.ts +19 -0
- package/dist/cluster/peer-registry.d.ts +17 -0
- package/dist/cluster/plan-headroom-store.d.ts +26 -0
- package/dist/cluster/worker-eviction.d.ts +69 -0
- package/dist/cold-store/load-event-log-range.d.ts +10 -3
- package/dist/cold-store/load-secret-audit-log-range.d.ts +4 -3
- package/dist/cold-store/tables/secret-audit-log.d.ts +4 -3
- package/dist/config.d.ts +21 -21
- package/dist/contexts/held-runs.d.ts +123 -16
- package/dist/contexts/protection/aggregate.d.ts +3 -2
- package/dist/contexts/protection/branch-gate.d.ts +15 -0
- package/dist/contexts/protection/concurrency-gate.d.ts +8 -2
- package/dist/contexts/protection/pipeline.d.ts +18 -0
- package/dist/contexts/release-queued-holds.d.ts +35 -0
- package/dist/dashboard/attestation-filters.d.ts +8 -0
- package/dist/dashboard/handler.d.ts +6 -24
- package/dist/db/migrations/116_org_plan_headroom.d.ts +4 -0
- package/dist/db/migrations/117_invoke_gate_proxy_jobs.d.ts +4 -0
- package/dist/db/migrations/118_invoke_gate_scheduling.d.ts +4 -0
- package/dist/db/migrations/119_scaler_ha_ownership.d.ts +4 -0
- package/dist/db/migrations/120_cluster_settings_scaler_reaper.d.ts +4 -0
- package/dist/db/migrations/121_org_settings_allow_untrusted_dockerfile_builds.d.ts +19 -0
- package/dist/db/migrations/122_host_roster_scaler_managed.d.ts +25 -0
- package/dist/db/migrations/123_workflow_registrations_default_branch.d.ts +24 -0
- package/dist/db/migrations/124_cluster_settings_provision_backoff.d.ts +4 -0
- package/dist/db/migrations/125_org_trust_directory.d.ts +30 -0
- package/dist/db/migrations/126_held_runs_posted_pending_check.d.ts +28 -0
- package/dist/db/migrations/127_org_trust_policy_approval_expiry_seconds.d.ts +26 -0
- package/dist/db/migrations/128_execution_runs_global_eval_round.d.ts +23 -0
- package/dist/db/migrations/129_execution_runs_dispatch_routing_key.d.ts +24 -0
- package/dist/db/migrations/130_scaler_provision_outcomes.d.ts +32 -0
- package/dist/db/types.d.ts +368 -10
- package/dist/entry-helpers.d.ts +28 -0
- package/dist/events/event-emitter.d.ts +22 -0
- package/dist/events/event-router.d.ts +87 -1
- package/dist/events/types.d.ts +2 -23
- package/dist/git/credential-broker.d.ts +94 -0
- package/dist/git/inherited-secret.d.ts +24 -0
- package/dist/git/installation-token.d.ts +38 -0
- package/dist/git/job-context.d.ts +13 -0
- package/dist/index.js +324 -84
- package/dist/metrics/agent-metrics-aggregator.d.ts +2 -2
- package/dist/metrics/prometheus.d.ts +111 -3
- package/dist/oidc/oidc-mint-registration.d.ts +7 -3
- package/dist/orchestrator-core.d.ts +79 -23
- package/dist/pipeline/direct-ingress-deps.d.ts +100 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +302 -31
- package/dist/pipeline/global-eval-round.d.ts +14 -0
- package/dist/pipeline/internal-event-pipeline.d.ts +140 -0
- package/dist/pipeline/invoke-gate.d.ts +164 -0
- package/dist/pipeline/needs-scheduler.d.ts +13 -0
- package/dist/pipeline/process-webhook.d.ts +119 -31
- package/dist/pipeline/processor.d.ts +226 -21
- package/dist/pipeline/rerun.d.ts +21 -0
- package/dist/pipeline/resume-workflow.d.ts +40 -7
- package/dist/pipeline/route-or-dispatch-jobs.d.ts +9 -0
- package/dist/pipeline/security-hold-check.d.ts +217 -0
- package/dist/pipeline/undispatched-hold-checks.d.ts +23 -0
- package/dist/policy/dashboard-write-policy-listener.d.ts +62 -0
- package/dist/policy/dashboard-write-policy.d.ts +14 -0
- package/dist/provider-registry.d.ts +10 -2
- package/dist/providers/github/check-status-poster.d.ts +11 -0
- package/dist/providers/github/index.d.ts +0 -2
- package/dist/providers/github/normalizer.d.ts +3 -2
- package/dist/providers/local/normalizer.d.ts +3 -5
- package/dist/providers/universal-git/index.d.ts +11 -2
- package/dist/queue/job-queue.d.ts +43 -1
- package/dist/registration/extractor.d.ts +3 -3
- package/dist/registration/registration-index.d.ts +7 -0
- package/dist/registration/registration-store.d.ts +22 -0
- package/dist/reporting/check-run-reporter.d.ts +94 -1
- package/dist/reporting/check-run-summary.d.ts +25 -1
- package/dist/reporting/execution-tracker.d.ts +207 -2
- package/dist/reporting/run-aggregator.d.ts +4 -14
- package/dist/reporting/step-display-order.d.ts +43 -0
- package/dist/routes/admin-held-runs.d.ts +142 -0
- package/dist/routes/admin-trust-policy.d.ts +34 -6
- package/dist/routes/admin.d.ts +9 -0
- package/dist/scaler/backend-factory.d.ts +55 -0
- package/dist/scaler/bare-metal-backend.d.ts +43 -5
- package/dist/scaler/claim-store.d.ts +119 -0
- package/dist/scaler/config.d.ts +4 -0
- package/dist/scaler/container-backend.d.ts +18 -4
- package/dist/scaler/container-routing.d.ts +23 -0
- package/dist/scaler/container-spawn.d.ts +28 -0
- package/dist/scaler/event-backend.d.ts +124 -0
- package/dist/scaler/event-provision-reaper.d.ts +350 -0
- package/dist/scaler/failure-tracker.d.ts +1 -1
- package/dist/scaler/firecracker-backend.d.ts +33 -6
- package/dist/scaler/index.d.ts +11 -1
- package/dist/scaler/label-matcher.d.ts +4 -3
- package/dist/scaler/manager.d.ts +899 -72
- package/dist/scaler/resolve-container-auth.d.ts +45 -0
- package/dist/scaler/scaler-events.d.ts +11 -0
- package/dist/scaler/scaler-state-store.d.ts +294 -10
- package/dist/scaler/types.d.ts +131 -13
- package/dist/scaler/warm-pool.d.ts +132 -29
- package/dist/security/comment-handler.d.ts +50 -11
- package/dist/security/identity-link.d.ts +60 -0
- package/dist/security/lock-source.d.ts +11 -13
- package/dist/security/reduced-privilege-note.d.ts +59 -0
- package/dist/security/trust-directory-store.d.ts +233 -0
- package/dist/security/trust-policy-gate.d.ts +94 -59
- package/dist/security/trust-policy-store.d.ts +3 -0
- package/dist/security/trust-resolver.d.ts +14 -98
- package/dist/server.d.ts +11 -1
- package/dist/server.js +17671 -7350
- package/dist/stale-detector/gate-deadline-detector.d.ts +52 -0
- package/dist/stale-detector/stale-run-detector.d.ts +61 -3
- package/dist/standalone.js +24238 -13822
- package/dist/worker/in-memory-job-queue.d.ts +1 -1
- package/dist/ws/agent-handler.d.ts +28 -4
- package/dist/ws/dashboard-context-handler.d.ts +28 -4
- package/dist/ws/dashboard-global-workflows-handler.d.ts +6 -0
- package/dist/ws/failure-messages.d.ts +2 -0
- package/dist/ws/git-credential-relay.d.ts +39 -0
- package/dist/ws/oidc-token-relay.d.ts +6 -11
- package/dist/ws/platform-client.d.ts +54 -9
- package/installer-image-digests.json +3 -3
- package/package.json +24 -19
- package/sbom.spdx.json +864 -822
- package/dist/approvals/team-membership-lookup.d.ts +0 -13
- package/dist/providers/github/contributor-resolver.d.ts +0 -30
- package/dist/security/contributor-cache.d.ts +0 -83
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The approval subsystem an INDEPENDENT orchestrator runs on.
|
|
3
|
+
*
|
|
4
|
+
* `server.ts` composes this inline for the three Platform-attached modes. An
|
|
5
|
+
* independent orchestrator has no Platform and therefore none of that hook, so
|
|
6
|
+
* every gate that raises a hold was inert there: a fork PR the org policy said
|
|
7
|
+
* to HOLD was dropped instead of held, an SDK `requireApproval` job ran
|
|
8
|
+
* UNGATED (the dispatch site logged an error and dispatched anyway), and the
|
|
9
|
+
* stale detector had no store through which to expire an overdue hold,
|
|
10
|
+
* terminalize its `KiCI Security` check, or drop the pending dispatch context
|
|
11
|
+
* it would have replayed.
|
|
12
|
+
*
|
|
13
|
+
* It lives here rather than inside `standalone.ts` because that module is an
|
|
14
|
+
* entry point: it runs `guardStartup` at import time, so nothing can construct
|
|
15
|
+
* its wiring in a test. A factory can be constructed, and the fields it
|
|
16
|
+
* produces can be asserted — which is the whole point, since every defect this
|
|
17
|
+
* closes was a missing field rather than a wrong algorithm.
|
|
18
|
+
*
|
|
19
|
+
* What it deliberately does NOT produce is a `stepApprovalBridge`. A
|
|
20
|
+
* step-scoped hold is opened by the agent WS `onStepApproval` seam and answered
|
|
21
|
+
* by the dashboard applier, which is Platform-relayed and has no
|
|
22
|
+
* independent-mode equivalent — so wiring the bridge would let an agent open a
|
|
23
|
+
* hold nothing could resolve short of expiry.
|
|
24
|
+
*/
|
|
25
|
+
import type { Kysely } from 'kysely';
|
|
26
|
+
import type { Database } from '../db/types.js';
|
|
27
|
+
import { HeldRunStore, type ReleaseSignal } from '../contexts/held-runs.js';
|
|
28
|
+
import { type ProcessingDeps, type ReadyDispatchContextRow } from '../pipeline/processor.js';
|
|
29
|
+
import type { Dispatcher } from '../agent/dispatcher.js';
|
|
30
|
+
import type { ExecutionTracker } from '../reporting/execution-tracker.js';
|
|
31
|
+
import type { RunCoordinator } from '../cluster/coordinator.js';
|
|
32
|
+
import type { InvokeGateDeps } from '../pipeline/invoke-gate.js';
|
|
33
|
+
/** The subsystem fields the composition reads. A subset of `OrchestratorSubsystems`. */
|
|
34
|
+
export interface IndependentApprovalSubsystems {
|
|
35
|
+
db: Kysely<Database>;
|
|
36
|
+
dispatcher: Dispatcher;
|
|
37
|
+
executionTracker?: ExecutionTracker | undefined;
|
|
38
|
+
coordinator?: RunCoordinator | undefined;
|
|
39
|
+
invokeGateDeps?: InvokeGateDeps | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* The live direct-ingress deps bag. Read lazily inside the release callbacks:
|
|
42
|
+
* it is populated by `createApp`, which runs after the mode hook, and reading
|
|
43
|
+
* it at wiring time throws.
|
|
44
|
+
*/
|
|
45
|
+
buildProcessingDeps: () => ProcessingDeps;
|
|
46
|
+
}
|
|
47
|
+
/** What the independent mode hook merges into `appDepsExtras`. */
|
|
48
|
+
export interface IndependentApprovalExtras {
|
|
49
|
+
heldRunStore: HeldRunStore;
|
|
50
|
+
onWorkflowRelease: (signal: ReleaseSignal) => Promise<void>;
|
|
51
|
+
onJobRelease: (signal: ReleaseSignal) => Promise<void>;
|
|
52
|
+
matchContext: (orgId: string, name: string) => Promise<ReadyDispatchContextRow | null>;
|
|
53
|
+
}
|
|
54
|
+
export declare function createIndependentApprovalExtras(sub: IndependentApprovalSubsystems): IndependentApprovalExtras;
|
|
55
|
+
//# sourceMappingURL=independent-wiring.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReleaseSignal } from '../contexts/held-runs.js';
|
|
2
|
+
/** The three resume paths a released hold can take. */
|
|
3
|
+
export interface ResumeHandlers {
|
|
4
|
+
onStepRelease?: (signal: ReleaseSignal) => Promise<void>;
|
|
5
|
+
onWorkflowRelease?: (signal: ReleaseSignal) => Promise<void>;
|
|
6
|
+
onJobRelease: (signal: ReleaseSignal) => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export declare function routeRelease(signal: ReleaseSignal, handlers: ResumeHandlers): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=resume-router.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `service:` namespace both approving surfaces use for a non-human
|
|
3
|
+
* principal: `kici-admin`'s token identity (its only subject), and the
|
|
4
|
+
* dashboard's rendering of a `service_account` actor.
|
|
5
|
+
*/
|
|
6
|
+
export declare const SERVICE_SUBJECT_PREFIX = "service:";
|
|
7
|
+
/** Render an admin-token user id in the shared `service:` namespace. */
|
|
8
|
+
export declare function adminActorSub(tokenUserId: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the subject of whoever triggered a run, in the approver vocabulary.
|
|
11
|
+
*
|
|
12
|
+
* | `triggered_by` | Returns | Why |
|
|
13
|
+
* |---|---|---|
|
|
14
|
+
* | `user:<sub>` | `<sub>` | what a `{user}` clause and both surfaces' own subject use |
|
|
15
|
+
* | `platform_operator:<sub>` | `<sub>` | same Keycloak subject as a `user` |
|
|
16
|
+
* | `service_account:<id>` | `service:<id>` | the namespace {@link adminActorSub} and the dashboard both produce |
|
|
17
|
+
* | `system:<component>` | `system:<component>` | the dashboard renders a system actor with its prefix intact |
|
|
18
|
+
* | `api_key:<keyId>` | `<keyId>` | see the residual below |
|
|
19
|
+
* | anything else | unchanged | no other actor type reaches these surfaces |
|
|
20
|
+
*
|
|
21
|
+
* The agent suffix is stripped first. `stringifyActor` renders a `user` or
|
|
22
|
+
* `api_key` who acted through an agent as `user:<sub> via agent:<label>`, and a
|
|
23
|
+
* reader splitting on the first colon gets `<sub> via agent:<label>` — which
|
|
24
|
+
* matches no live subject, so the gate was inert for every agent-mediated
|
|
25
|
+
* trigger. That is the `user` case, i.e. the common one.
|
|
26
|
+
*
|
|
27
|
+
* **Residual: `api_key`.** A key's persisted identifier is its `keyId`, while
|
|
28
|
+
* both surfaces render a live `api_key` actor as its owner's Keycloak subject
|
|
29
|
+
* (`ownerSub`). Those are different identifiers for the same principal and no
|
|
30
|
+
* string transform bridges them — closing it needs a keyId → ownerSub lookup,
|
|
31
|
+
* which is a store this pure function does not have. So an owner approving a
|
|
32
|
+
* hold on a run their own key triggered is still admitted. Recorded rather than
|
|
33
|
+
* papered over: returning the bare keyId is at least honest about what was
|
|
34
|
+
* stored.
|
|
35
|
+
*/
|
|
36
|
+
export declare function triggererSubjectFor(triggeredBy: string | null | undefined): string | undefined;
|
|
37
|
+
//# sourceMappingURL=triggerer-subject.d.ts.map
|
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dependency-specific cache layer wrapping CacheStorage.
|
|
3
3
|
*
|
|
4
|
-
* Stores
|
|
5
|
-
*
|
|
6
|
-
* Refreshes TTL on reads
|
|
4
|
+
* Stores dependency tarballs under their own content hash, with a small pointer
|
|
5
|
+
* resolving lockfileHash + platform + arch to that hash. Shared CacheStorage
|
|
6
|
+
* backend with SourceCache (same S3 bucket). Refreshes TTL on reads
|
|
7
|
+
* (touch-on-read).
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
+
* Keys: `deps/{platform}-{arch}/{depsHash}.tar.gz` (immutable) and
|
|
10
|
+
* `deps/{platform}-{arch}/{lockfileHash}.hash` (the pointer).
|
|
9
11
|
*/
|
|
10
12
|
import type { CacheStorage } from '../storage/types.js';
|
|
11
13
|
import type { ClusterSettingsReader } from '../cluster/cluster-settings-reader.js';
|
|
14
|
+
/**
|
|
15
|
+
* Cache key for a dependency tarball, addressed by the tarball's OWN content
|
|
16
|
+
* hash: `deps/{platform}-{arch}/{depsHash}.tar.gz`.
|
|
17
|
+
*
|
|
18
|
+
* Content-addressing is what makes a mismatched pair unrepresentable. When the
|
|
19
|
+
* tarball lived at a lockfile-derived name, it and its companion `.hash` were
|
|
20
|
+
* two independently-written objects under keys that any two builds sharing a
|
|
21
|
+
* lockfile + platform + arch also share — so concurrent builders could leave the
|
|
22
|
+
* `.tar.gz` from one and the `.hash` from the other, and the reader failed
|
|
23
|
+
* verification durably. Naming the object by its own hash means every pair a
|
|
24
|
+
* reader can observe is self-consistent, and the bytes at a given key can never
|
|
25
|
+
* change after a URL is signed for it.
|
|
26
|
+
*/
|
|
27
|
+
export declare function depTarballKey(depsHash: string, platform: string, arch: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Cache key for the pointer that resolves a lockfile to the content hash of the
|
|
30
|
+
* tarball built from it: `deps/{platform}-{arch}/{lockfileHash}.hash`.
|
|
31
|
+
*
|
|
32
|
+
* This is the one mutable object in the scheme. A concurrent write replaces a
|
|
33
|
+
* pointer wholesale — there is no window in which it is half-written — so the
|
|
34
|
+
* worst a racing pair of builders can do is leave whichever pointer landed last,
|
|
35
|
+
* and both tarballs remain valid and fetchable.
|
|
36
|
+
*/
|
|
37
|
+
export declare function depPointerKey(lockfileHash: string, platform: string, arch: string): string;
|
|
12
38
|
export declare class DepCache {
|
|
13
39
|
private readonly storage;
|
|
14
40
|
private readonly maxTarballBytes;
|
|
@@ -30,7 +56,17 @@ export declare class DepCache {
|
|
|
30
56
|
* `cache_ttl_days` change takes effect on the next lookup.
|
|
31
57
|
*/
|
|
32
58
|
private resolveTtlMsOverride;
|
|
33
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Resolve a lockfile to the content hash of the tarball built from it, or
|
|
61
|
+
* null when no pointer exists. The single place the indirection is read.
|
|
62
|
+
*/
|
|
63
|
+
private resolvePointer;
|
|
64
|
+
/**
|
|
65
|
+
* Check if a dep tarball exists in cache.
|
|
66
|
+
*
|
|
67
|
+
* Both halves must be present: a pointer whose tarball has aged out is a miss,
|
|
68
|
+
* not a hit, or the caller skips a rebuild and dispatches a URL that 404s.
|
|
69
|
+
*/
|
|
34
70
|
has(lockfileHash: string, platform: string, arch: string): Promise<boolean>;
|
|
35
71
|
/**
|
|
36
72
|
* Get a pre-signed download URL for the dep tarball (for agent delivery).
|
|
@@ -38,9 +74,10 @@ export declare class DepCache {
|
|
|
38
74
|
*/
|
|
39
75
|
getUrl(lockfileHash: string, platform: string, arch: string): Promise<string | null>;
|
|
40
76
|
/**
|
|
41
|
-
* Get a pre-signed download URL and the tarball content hash
|
|
42
|
-
*
|
|
43
|
-
*
|
|
77
|
+
* Get a pre-signed download URL and the tarball content hash.
|
|
78
|
+
*
|
|
79
|
+
* Returns null on cache miss — including an entry with no pointer, which is
|
|
80
|
+
* unverifiable and so is deliberately not served.
|
|
44
81
|
*/
|
|
45
82
|
getUrlAndHash(lockfileHash: string, platform: string, arch: string): Promise<{
|
|
46
83
|
url: string;
|
|
@@ -49,7 +86,14 @@ export declare class DepCache {
|
|
|
49
86
|
/**
|
|
50
87
|
* Get a pre-signed upload URL for direct agent-to-S3 upload.
|
|
51
88
|
*/
|
|
52
|
-
getUploadUrl(
|
|
89
|
+
getUploadUrl(depsHash: string, platform: string, arch: string): Promise<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Publish the pointer that makes an uploaded tarball discoverable by lockfile.
|
|
92
|
+
*
|
|
93
|
+
* Called only after the agent confirms its upload completed. Publishing before
|
|
94
|
+
* the bytes land would let a reader follow the pointer to a missing object.
|
|
95
|
+
*/
|
|
96
|
+
publishPointer(lockfileHash: string, platform: string, arch: string, depsHash: string): Promise<void>;
|
|
53
97
|
/**
|
|
54
98
|
* Store a dep tarball in cache.
|
|
55
99
|
* Throws if tarball exceeds max size (per user decision).
|
|
@@ -60,7 +104,7 @@ export declare class DepCache {
|
|
|
60
104
|
* Used by build agents to compute the hash for depsHash protocol field.
|
|
61
105
|
*/
|
|
62
106
|
static computeHash(data: Buffer): string;
|
|
63
|
-
/** Remove a dep tarball from cache. */
|
|
107
|
+
/** Remove a dep tarball and its pointer from cache. */
|
|
64
108
|
remove(lockfileHash: string, platform: string, arch: string): Promise<boolean>;
|
|
65
109
|
}
|
|
66
110
|
//# sourceMappingURL=dep-cache.d.ts.map
|
|
@@ -29,6 +29,12 @@ import type { GlobalEvalRoundResult } from '@kici-dev/engine';
|
|
|
29
29
|
* reason `groupCandidates` keeps it in the group key — it selects the provider
|
|
30
30
|
* bundle that mints the clone credentials.
|
|
31
31
|
*
|
|
32
|
+
* `sourceRepoIdentifier` is its own component, placed next to `sourceSha` so the
|
|
33
|
+
* source repo and its commit stay adjacent. Every event already carries its
|
|
34
|
+
* source repo, so the event digest covers it too — but keying it explicitly
|
|
35
|
+
* makes that coverage structural rather than incidental: an event shape that
|
|
36
|
+
* stopped carrying the source repo could otherwise collide two repos' rounds.
|
|
37
|
+
*
|
|
32
38
|
* The candidate list and the event are folded in as a SHA-256 digest of their
|
|
33
39
|
* JSON, so the key stays a bounded string no matter how large a payload is. Both
|
|
34
40
|
* are rebuilt by the same code from the same delivery, so a genuine redelivery
|
|
@@ -43,6 +49,7 @@ export declare function globalEvalRoundCacheKey(args: {
|
|
|
43
49
|
workflowRepoIdentifier: string;
|
|
44
50
|
workflowSha: string;
|
|
45
51
|
workflowRoutingKey: string;
|
|
52
|
+
sourceRepoIdentifier: string;
|
|
46
53
|
sourceSha: string;
|
|
47
54
|
/** The exact per-candidate payload the round job carries. */
|
|
48
55
|
candidates: unknown;
|
package/dist/cli/api-client.d.ts
CHANGED
|
@@ -84,6 +84,24 @@ export interface GenericSourceResponse {
|
|
|
84
84
|
* print an empty one.
|
|
85
85
|
*/
|
|
86
86
|
export declare function firstCauseMessage(err: unknown): string;
|
|
87
|
+
/**
|
|
88
|
+
* True when a rejected `fetch` failed at the transport layer, as opposed to
|
|
89
|
+
* failing while the request was still being built.
|
|
90
|
+
*
|
|
91
|
+
* `fetch` uses a `TypeError` for both, so they are easy to conflate — but only
|
|
92
|
+
* a transport failure means the orchestrator was actually dialled. Measured
|
|
93
|
+
* against Node 24's undici:
|
|
94
|
+
*
|
|
95
|
+
* closed port → TypeError('fetch failed') cause: Error(ECONNREFUSED)
|
|
96
|
+
* invalid header → TypeError('Headers.append: … invalid header …') cause: undefined
|
|
97
|
+
* invalid URL → TypeError('Failed to parse URL from …') cause: Error(ERR_INVALID_URL)
|
|
98
|
+
*
|
|
99
|
+
* The discriminator is therefore the message, NOT the presence of a `cause`:
|
|
100
|
+
* the invalid-URL case carries one. undici uses exactly `fetch failed` for a
|
|
101
|
+
* transport failure and a descriptive message for everything it rejects before
|
|
102
|
+
* opening a connection.
|
|
103
|
+
*/
|
|
104
|
+
export declare function isTransportFailure(err: unknown): boolean;
|
|
87
105
|
export declare function fetchAdminApi(url: string, init: RequestInit, baseUrl: string): Promise<Response>;
|
|
88
106
|
export declare class AdminApiClient {
|
|
89
107
|
private readonly baseUrl;
|
|
@@ -71,6 +71,17 @@ export declare function buildClusterPatch(opts: Record<string, string | undefine
|
|
|
71
71
|
* Returns the lines rather than printing them so the check is unit-testable.
|
|
72
72
|
*/
|
|
73
73
|
export declare function unpairedEvalTimeoutWarnings(patch: PatchBody): string[];
|
|
74
|
+
/**
|
|
75
|
+
* Warn for every deprecated knob the patch sets.
|
|
76
|
+
*
|
|
77
|
+
* The set still goes through — the column and the route field are a released
|
|
78
|
+
* operator surface and keep accepting writes — so the warning says what the
|
|
79
|
+
* stored value now does, which is nothing.
|
|
80
|
+
*
|
|
81
|
+
* Returns the lines rather than printing them so the check is unit-testable,
|
|
82
|
+
* matching {@link unpairedEvalTimeoutWarnings}.
|
|
83
|
+
*/
|
|
84
|
+
export declare function deprecatedKnobWarnings(patch: PatchBody): string[];
|
|
74
85
|
/** Build the reset PATCH body: all knobs → null, or just the flagged ones. */
|
|
75
86
|
export declare function buildClusterReset(opts: Record<string, boolean | undefined>): PatchBody;
|
|
76
87
|
/**
|
|
@@ -11,5 +11,11 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { Command } from 'commander';
|
|
13
13
|
import type { AdminApiClient } from '../api-client.js';
|
|
14
|
+
/**
|
|
15
|
+
* Width of the table's Message column. A check message is cut at this many
|
|
16
|
+
* characters, so anything a check wants an operator to see in the default
|
|
17
|
+
* (non-`--json`) output has to fit inside it.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DIAGNOSE_TABLE_MESSAGE_WIDTH = 42;
|
|
14
20
|
export declare function registerDiagnoseCommand(program: Command, getClient: () => AdminApiClient): void;
|
|
15
21
|
//# sourceMappingURL=diagnose.d.ts.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Held-run commands for kici-admin.
|
|
3
|
+
*
|
|
4
|
+
* Subcommand namespace: `kici-admin held-run <list|approve|reject>`.
|
|
5
|
+
*
|
|
6
|
+
* The local answer to a held run, for an orchestrator that has no Platform.
|
|
7
|
+
* Every other release surface is Platform-relayed — the dashboard approval
|
|
8
|
+
* queue, `kici approve` / `kici reject`, and the developer MCP tools all reach
|
|
9
|
+
* the applier over the control-plane connection — and the one surface that is
|
|
10
|
+
* not, a `/kici approve` pull-request comment, releases the `security` queue
|
|
11
|
+
* only. So on an independent orchestrator an approval-queue hold had no answer
|
|
12
|
+
* at all and could only expire.
|
|
13
|
+
*
|
|
14
|
+
* These verbs are therefore the mirror image of `trust-policy set`: they work
|
|
15
|
+
* only where no Platform is attached, and the route refuses with 409 wherever
|
|
16
|
+
* one is. That refusal is surfaced verbatim rather than reworded here, so the
|
|
17
|
+
* CLI cannot drift from what the server actually said.
|
|
18
|
+
*
|
|
19
|
+
* A hold is named the same way `kici approve` names one — `--job` / `--step` /
|
|
20
|
+
* `--hold-type` / `--hold`, resolved by the shared `resolveHeldRunId` — so an
|
|
21
|
+
* operator disambiguating a doubly-held job types the same flags on either
|
|
22
|
+
* surface. That sharing is the point: a job gated by BOTH a reviewer hold and a
|
|
23
|
+
* security hold writes two pending rows requiring two decisions, and a second
|
|
24
|
+
* resolver would be free to disagree about which one `--job` means.
|
|
25
|
+
*/
|
|
26
|
+
import type { Command } from 'commander';
|
|
27
|
+
import { ApprovalDecision, type HeldRunSummary } from '@kici-dev/engine';
|
|
28
|
+
import type { AdminApiClient } from '../api-client.js';
|
|
29
|
+
/** One held-run row as the admin route reports it, with the clause list it adds. */
|
|
30
|
+
type AdminHeldRunSummary = HeldRunSummary & {
|
|
31
|
+
/** Approver clauses the hold's requirement carries; `[]` when it names nobody. */
|
|
32
|
+
clauses?: Array<{
|
|
33
|
+
team: string;
|
|
34
|
+
} | {
|
|
35
|
+
user: string;
|
|
36
|
+
}>;
|
|
37
|
+
};
|
|
38
|
+
/** The decision response the admin route returns. */
|
|
39
|
+
interface DecisionResponse {
|
|
40
|
+
status: string;
|
|
41
|
+
remainingClauses?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Render the pending holds for a run.
|
|
45
|
+
*
|
|
46
|
+
* Per-entry rather than a count: "which hold do I answer, and who may answer
|
|
47
|
+
* it" is the question this command exists to answer, and the hold id it prints
|
|
48
|
+
* is the `--hold <id>` disambiguator the resolver hands back when nothing else
|
|
49
|
+
* separates two holds.
|
|
50
|
+
*/
|
|
51
|
+
export declare function formatHeldRuns(res: {
|
|
52
|
+
heldRuns: AdminHeldRunSummary[];
|
|
53
|
+
}, format: string): string;
|
|
54
|
+
/** Say what the applier did, in the vocabulary `ApplyDecisionResult.status` uses. */
|
|
55
|
+
export declare function formatDecision(res: DecisionResponse, decision: ApprovalDecision): string;
|
|
56
|
+
export declare function registerHeldRunCommands(program: Command, getClient: () => AdminApiClient): void;
|
|
57
|
+
export {};
|
|
58
|
+
//# sourceMappingURL=held-run.d.ts.map
|
|
@@ -1,26 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Org trust-policy commands for kici-admin.
|
|
3
3
|
*
|
|
4
|
-
* Subcommand namespace:
|
|
4
|
+
* Subcommand namespace:
|
|
5
|
+
* `kici-admin trust-policy <show|set|directory|directory-set|directory-remove>`.
|
|
5
6
|
*
|
|
6
7
|
* Talks to the orchestrator admin API directly (not the Platform dashboard
|
|
7
8
|
* proxy), so the CLI stays operable even when Platform is unavailable. Backed
|
|
8
9
|
* by `packages/orchestrator/src/routes/admin-trust-policy.ts`.
|
|
9
10
|
*
|
|
10
|
-
* `
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* `show` and `directory` read, in every mode. `set`, `directory-set`, and
|
|
12
|
+
* `directory-remove` write, and only work on an independent orchestrator:
|
|
13
|
+
* wherever a Platform is attached it owns both the policy and the approval
|
|
14
|
+
* directory, and the route refuses with 409. That message is surfaced verbatim
|
|
15
|
+
* rather than reworded here, so the CLI cannot drift from what the server
|
|
16
|
+
* actually said.
|
|
17
|
+
*
|
|
18
|
+
* The two directory writers live in this namespace rather than one of their
|
|
19
|
+
* own because they are the same org-trust concept as the policy, gated by the
|
|
20
|
+
* same mode rule against the same `--customer-id`, and reached through the same
|
|
21
|
+
* admin route file. Splitting them out would put half of "who may approve" in a
|
|
22
|
+
* second top-level command with its own copy of that story. They are siblings
|
|
23
|
+
* of the `directory` reader rather than subcommands under it, because turning
|
|
24
|
+
* that leaf into a group would break `kici-admin trust-policy directory` for
|
|
25
|
+
* anyone already running it.
|
|
14
26
|
*/
|
|
15
27
|
import type { Command } from 'commander';
|
|
16
|
-
import { TrustPolicyEnforcement } from '../../security/trust-policy-gate.js';
|
|
17
28
|
import type { AdminApiClient } from '../api-client.js';
|
|
18
29
|
/**
|
|
19
30
|
* The policy shape the admin route returns.
|
|
20
31
|
*
|
|
21
|
-
* The four policy fields are OPTIONAL because
|
|
22
|
-
*
|
|
23
|
-
*
|
|
32
|
+
* The four policy fields are OPTIONAL because a **v0.5.0 independent**
|
|
33
|
+
* orchestrator omits them: there, and only there, no policy row and no attached
|
|
34
|
+
* Platform meant no policy was resolved at all, and it reported
|
|
35
|
+
* `enforcement: 'legacy'` with the fields absent. A v0.5.0 Platform-attached
|
|
36
|
+
* orchestrator with no row still sent the fail-closed values. This build's route
|
|
37
|
+
* always sends them in every mode, so the `unknown` fallbacks below — and the
|
|
38
|
+
* `no policy stored` provenance wording — render only against that older
|
|
39
|
+
* independent orchestrator.
|
|
40
|
+
*
|
|
41
|
+
* The route also sends a deprecated `enforcement` field. Nothing here reads it —
|
|
42
|
+
* on this build it is always `policy` — so it is absent from this shape;
|
|
43
|
+
* `--format json` stringifies the parsed policy object, so the field still
|
|
44
|
+
* reaches the operator verbatim.
|
|
24
45
|
*/
|
|
25
46
|
export interface TrustPolicyView {
|
|
26
47
|
customerId: string;
|
|
@@ -28,15 +49,39 @@ export interface TrustPolicyView {
|
|
|
28
49
|
unknownContributorPolicy?: string;
|
|
29
50
|
workflowChangePolicy?: string;
|
|
30
51
|
approvalExpiryHours?: number;
|
|
31
|
-
/**
|
|
32
|
-
|
|
52
|
+
/**
|
|
53
|
+
* The authoritative hold window. Absent from any orchestrator that predates
|
|
54
|
+
* it, in which case `approvalExpiryHours` is the only window on offer.
|
|
55
|
+
*/
|
|
56
|
+
approvalExpirySeconds?: number;
|
|
33
57
|
source: string | null;
|
|
34
58
|
updatedAt: string | null;
|
|
35
59
|
effectiveDefault?: boolean;
|
|
36
60
|
platformManaged?: boolean;
|
|
37
61
|
}
|
|
38
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* Render the policy as an aligned table, or as JSON when asked.
|
|
64
|
+
*
|
|
65
|
+
* The two deprecated arms are deliberately absent from the table: no dispatch
|
|
66
|
+
* decision reads either one, so a row claiming `Unknown contributor policy:
|
|
67
|
+
* hold` would assert an enforcement that is not happening. They are still
|
|
68
|
+
* stored and still echoed back, and `--format json` prints the policy object the
|
|
69
|
+
* route returned, so the values remain reachable for anyone who needs them.
|
|
70
|
+
*/
|
|
39
71
|
export declare function formatPolicy(policy: TrustPolicyView, format: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* Render the enforced hold window.
|
|
74
|
+
*
|
|
75
|
+
* A whole number of hours still prints as `72 h`, exactly as it always did, so
|
|
76
|
+
* no existing policy's output moves. Anything finer prints in seconds, because
|
|
77
|
+
* the hours spelling cannot express it and rounding would report a window the
|
|
78
|
+
* orchestrator is not applying.
|
|
79
|
+
*
|
|
80
|
+
* A policy carrying neither field is an orchestrator old enough to have
|
|
81
|
+
* resolved no policy at all (see {@link TrustPolicyView}); one carrying only
|
|
82
|
+
* hours is an orchestrator that predates the seconds window.
|
|
83
|
+
*/
|
|
84
|
+
export declare function formatExpiry(policy: TrustPolicyView): string;
|
|
40
85
|
/**
|
|
41
86
|
* Turn CLI flags into a PATCH body, exiting on an unknown value.
|
|
42
87
|
*
|
|
@@ -44,5 +89,65 @@ export declare function formatPolicy(policy: TrustPolicyView, format: string): s
|
|
|
44
89
|
* clear local message naming the accepted values instead of a 400 from the API.
|
|
45
90
|
*/
|
|
46
91
|
export declare function buildPolicyPatch(opts: Record<string, string | undefined>): Record<string, string | number>;
|
|
92
|
+
/**
|
|
93
|
+
* Warn when a patch names both spellings of the hold window.
|
|
94
|
+
*
|
|
95
|
+
* The route resolves this deterministically — the more specific seconds value
|
|
96
|
+
* wins — but an operator who passed both asked for two different things, so the
|
|
97
|
+
* one that is not applied is named rather than dropped in silence.
|
|
98
|
+
*
|
|
99
|
+
* Returns the lines rather than printing them so the check is unit-testable,
|
|
100
|
+
* matching `policyDeprecationWarnings` below.
|
|
101
|
+
*/
|
|
102
|
+
export declare function policyExpiryWarnings(patch: Record<string, string | number>): string[];
|
|
103
|
+
/**
|
|
104
|
+
* Warn about deprecated flags and deprecated values in an already-built patch.
|
|
105
|
+
*
|
|
106
|
+
* Every one of these still PATCHes through unchanged — the orchestrator stores
|
|
107
|
+
* what it is given, and an older Platform or CLI keeps seeing the value it
|
|
108
|
+
* expects. The warning says what the value does now, which for all three is
|
|
109
|
+
* nothing the fork switch reads.
|
|
110
|
+
*
|
|
111
|
+
* Returns the lines rather than printing them so the check is unit-testable,
|
|
112
|
+
* matching `unpairedEvalTimeoutWarnings` in `cluster-settings.ts`.
|
|
113
|
+
*/
|
|
114
|
+
export declare function policyDeprecationWarnings(patch: Record<string, string | number>): string[];
|
|
115
|
+
/** One identity link as the directory route reports it. */
|
|
116
|
+
interface DirectoryIdentityLink {
|
|
117
|
+
userId: string;
|
|
118
|
+
provider: string;
|
|
119
|
+
providerUsername: string;
|
|
120
|
+
providerUserId?: string | null;
|
|
121
|
+
}
|
|
122
|
+
/** One operator-defined team as the directory route reports it. */
|
|
123
|
+
interface DirectoryTeam {
|
|
124
|
+
teamName: string;
|
|
125
|
+
memberUserIds: string[];
|
|
126
|
+
}
|
|
127
|
+
/** The cached approval directory as the admin route returns it. */
|
|
128
|
+
export interface TrustDirectoryView {
|
|
129
|
+
customerId: string;
|
|
130
|
+
identityLinks: DirectoryIdentityLink[];
|
|
131
|
+
memberCiTrustLevels: Record<string, string>;
|
|
132
|
+
teamMemberships: DirectoryTeam[];
|
|
133
|
+
updatedAt: string;
|
|
134
|
+
}
|
|
135
|
+
interface DirectoryResponse {
|
|
136
|
+
directory: TrustDirectoryView | null;
|
|
137
|
+
platformManaged: boolean;
|
|
138
|
+
/** Present on `directory-remove`: false when the member held nothing. */
|
|
139
|
+
removed?: boolean;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Render the stored approval directory, or say why there is nothing to render.
|
|
143
|
+
*
|
|
144
|
+
* The directory is what `/kici approve` is resolved against: a commenter is
|
|
145
|
+
* matched to a KiCI user through the identity links, that user's CI trust level
|
|
146
|
+
* decides whether the approval counts, and a `{team}` clause is matched against
|
|
147
|
+
* the team memberships. So the listing is per-entry rather than a set of counts
|
|
148
|
+
* — "who can approve right now" is the question this command exists to answer.
|
|
149
|
+
*/
|
|
150
|
+
export declare function formatDirectory(res: DirectoryResponse, format: string): string;
|
|
47
151
|
export declare function registerTrustPolicyCommands(program: Command, getClient: () => AdminApiClient): void;
|
|
152
|
+
export {};
|
|
48
153
|
//# sourceMappingURL=trust-policy.d.ts.map
|
package/dist/cli/kici-admin.d.ts
CHANGED
|
@@ -10,6 +10,16 @@
|
|
|
10
10
|
* --token / -t Admin API token (env: KICI_ADMIN_TOKEN, required)
|
|
11
11
|
*/
|
|
12
12
|
import { Command } from 'commander';
|
|
13
|
+
/**
|
|
14
|
+
* The version `kici-admin --version` reports.
|
|
15
|
+
*
|
|
16
|
+
* `scripts/build-service.mjs` defines `KICI_PKG_VERSION` for every orchestrator
|
|
17
|
+
* build entry, `cli.js` included, so a shipped binary reports the package
|
|
18
|
+
* version it was built from. The fallback covers a source run (tests, `tsx`),
|
|
19
|
+
* where no bundler substituted the constant — the same shape `server.ts` and
|
|
20
|
+
* `standalone.ts` use for `ORCHESTRATOR_VERSION`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const CLI_VERSION: string;
|
|
13
23
|
/**
|
|
14
24
|
* Build the kici-admin Commander program with every command group registered.
|
|
15
25
|
* Exported so the surface registry can walk the real command tree without
|