@kici-dev/orchestrator 0.20.1 → 0.21.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/cli/api-client.d.ts +2 -0
- package/dist/cli/commands/runs.d.ts +3 -1
- package/dist/cli/commands/shared/run-ref.d.ts +4 -0
- package/dist/cli/commands/source.d.ts +1 -1
- package/dist/cli.js +113 -27
- package/dist/db/types.d.ts +3 -3
- package/dist/index.js +1 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +6 -4
- package/dist/pipeline/test-pipeline.d.ts +21 -1
- package/dist/reporting/check-run-reporter.d.ts +2 -0
- package/dist/reporting/check-run-summary.d.ts +12 -0
- package/dist/reporting/execution-tracker.d.ts +14 -3
- package/dist/reporting/run-workflow-names.d.ts +2 -2
- package/dist/reporting/step-display-order.d.ts +9 -5
- package/dist/server.js +258 -91
- package/dist/standalone.js +238 -89
- package/dist/ws/test-relay-handlers.d.ts +8 -0
- package/installer-image-digests.json +3 -3
- package/package.json +4 -4
- package/sbom.spdx.json +53 -53
package/dist/standalone.js
CHANGED
|
@@ -5,7 +5,7 @@ __cjs_dirname(__filename);
|
|
|
5
5
|
import "node:module";
|
|
6
6
|
import "@kici-dev/shared/load-service-env-file";
|
|
7
7
|
import { AgentDeliveryMode, AgentPlatform, BaseColdStore, ChunkLru, ChunkRequestWaiter, CommandError, PgTableAdapterBase, addLogsToArchive, applyProxyKeepAliveTimeouts, chunkBuffer, closeDatabase, computeBackoffDelay, computeMigrationsHash, createDb, createHealthRoutes, createLogger, createMeter, createMetricsRoutes, createPool, createS3Client, decrypt, decryptJson, depCacheKeyOf, deriveKey, encodeKeySegment, encrypt, enrichRequestContext, formatDuration, getPrometheusExporter, getReconnectDelay, getRequestContext, guardStartup, initTelemetry, isPgUniqueViolation, logger, parseDatabaseUrl, redactConfig, requestContext, retryOnBrokenConnection, serializeError, setServiceName, setupGracefulShutdown, shQuote, sha256, stderrTail, stopHttpServer, storeMigrationContentHash, toCommandError, toErrorMessage, trackOpenSockets, validateRequiredTools } from "@kici-dev/shared";
|
|
8
|
-
import { ACTOR_AGENT_SUFFIX, AGENT_API_REQUEST_TIMEOUT_MS, AGENT_FEATURE_LABELS, ALLOWED_SYSTEM_VARS, ARTIFACT_NAME_MAX_LENGTH, AccessLogAction, AccessLogOutcome, AccessLogSource, AccessLogTargetType, ActorType, AgentFailureCategory, ApprovalDecision, ArtifactCompleteAckOutcome, ArtifactDownloadOutcome, ArtifactUploadOutcome, AttestationOrigin, BREAKING_FLOOR, CANONICAL_STATUSES, CONTAINER_BUILD_RUNTIME_LABEL, CacheRefScope, CheckMode, CheckRunConclusion, CheckStepOutcome, CiTrustLevel, ConcurrencyStrategy, ContextDeleteErrorCode, ContextGateRejectReason, ContextType, DASHBOARD_REQUEST_TYPE_SET, DEFAULT_APPROVAL_EXPIRY_SECONDS, DEFAULT_CONCURRENCY_STRATEGY, DEFAULT_HOLD_EXPIRY_SECONDS, DEFERRED_PATHS_SUMMARY_SUFFIX, EventLogSource, EventLogStatus, ExecutionJobStatus, ExecutionRunStatus, ExecutionStepStatus, FanoutCause, FanoutError, FleetHostWriteRefusal, ForkPolicy, GLOBAL_EVAL_SKIPS_RESULT_AWARE_LABEL, HeldRunQueueType, HeldRunStatus, HoldScope, HoldType, ID_TOKEN_CLAIM_NAMES, INIT_LABEL, INIT_RUNNER_ROLE_LABEL, INSTALL_JOB_ID_PREFIX, INVALID_JOIN_REQUEST_MESSAGE, InitFailureCategory, InventorySelectorSchema, JOB_IMAGE_RUNTIME_LABEL, JOIN_PROTOCOL_UPGRADE_TOKEN, JOIN_PROTOCOL_V1_REMOVED_MESSAGE, JobRejectReason, JoinErrorCode, JoinRequestKind, KICI_AGENT_ENV_PREFIX, KICI_EVENT_NAME_PREFIX, KNOWN_ROLES, LabelMatcher, LockFileParseError, LogStream, MAX_APPROVAL_EXPIRY_SECONDS, MIN_APPROVAL_EXPIRY_SECONDS, MIN_PROTOCOL_VERSION, MinimumTrustSchema, ORCH_AGENT_CAPABILITIES, OWN_INGRESS_MODES, OrchLogPhase, OrchRole, OrchestratorMode, PEER_MUTUAL_AUTH_REQUIRED_REASON, PLATFORM_CONNECTED_MODES, PLATFORM_TO_ORCH_RECOGNIZED_TYPES, PRIVILEGED_ROOT_LABEL, PROTOCOL_VERSION, PayloadOmittedReason, PeerAuthMode, PeerAuthScheme, PeerForgetOutcome, RegisterableTriggerType, RunFailureClass, RuntimeFact, SCALER_EVENT_NAMES, SCHEMA_VERSION, SECURITY_HOLD_JOB_IDS, SSH_TRANSPORT_CAPABILITY, ScaleDownReason, ScaleDownReason as ScaleDownReason$1, ScalerBackendType, ScalerEventType, ScalerEventType as ScalerEventType$1, ScalerOrphansAction, ScalerReloadOutcome, ScalerScaleDownPayload, ScalerScaleUpPayload, ScalerVmStatus, ScalerVmStopOutcome, ScalerVmTracker, SetupStepType, SourceOrigin, SourceSubtype, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES, TEST_RUN_LOG_CURSOR_START, TRIGGER_EVENT_TYPES, TimeoutReason, TraceCheck, TriggerSource, TrustTierSchema, UnavailablePathsTrace, VariantKind, WS_CLOSE_AGENT_AUTH_FAILED, WS_CLOSE_AUTH_TIMEOUT, WS_CLOSE_DISPATCH_ACK_TIMEOUT, WS_CLOSE_GOING_AWAY, WS_CLOSE_HEARTBEAT_TIMEOUT, WS_CLOSE_INTERNAL_ERROR, WS_CLOSE_INVALID_MESSAGE, WS_CLOSE_PLAN_LIMIT, WS_CLOSE_PROTOCOL_ERROR, WS_CLOSE_SUPERSEDED_BY_RECONNECT, WS_CLOSE_UNAUTHORIZED, WS_MAX_PAYLOAD_BYTES, WebhookRelayResult, WsRateLimiter, accessLogWarmSqlCase, agentAuthRequestSchema, agentLabelOf, agentToOrchestratorMessageSchema, agentTypeLabel, appendChecks, approvalTimeoutSecondsSchema, artifactInvalidNameError, assertValidSecretKey, attestationVerifyStatusSchema, buildEventClaims, buildIdTokenSubject, buildJoinRefusal, buildTrustedPassthroughEnv, canonicalizeCapability, canonicalizeLabel, canonicalizeLabelSet, canonicalizeLabels, canonicalizeMatcher, checkArtifactName, classifyJoinRequest, coerceDispatchInputs, collectDiscriminatorTypes, createContentRequirementsTraceEntry, createDispatchFailureTraceEntry, createGlobalFilterTraceEntry, createWorkflowDecision, deriveOsArchLabels, diffRangeKindSchema, fanoutEnvelopeFields, flattenActor, formatLabelMatcher, getAccessLogColdDays, getSecretAuditLogColdDays, githubIngressPath, githubWebhookPath, globalEvalRoundResultSchema, hostLabel, hostSatisfiesTarget, hostToScalerPlatform, installGateJobId, isFailureStatus, isKnownCapability, isLockDynamicJobFn, isLockParallelStep, isLockStaticJob, isPullRequestFamilyTriggerEvent, isSecurityHoldJobId, isSelfReportedLabel, joinRequestSchema, logPullPlatformToOrchSchema, matchAllWorkflows, matchScopePattern, matchWorkflowsForEvent, matcherSatisfiedBy, materializeFanout, materializeResolvedHosts, materializeResolvedMatrix, matrixEnvelopeFields, mergeOrderedMaps, minAccessLogWarmDays, minSecretAuditLogWarmDays, partitionMatchers, peerAuthRequestSchema, peerFromPeerMessageSchema, peerHelloResponseSchema, peerHelloSchema, platformToOsArchLabels, platformToTaints, reservedEventNamePrefix, resolveDiffRange, resolveRoleLabels, resolveScheduleInputs, resolveSecretsWithProvenance, resourceRequestNestedSchema, runtimeLabel, scalerAgentLabels, scalerLabel, scalerPlatformSchema, scheduleTriggerKey, secretAuditLogWarmSqlCase, shouldRecordAccess, shouldRecordSecretResolve, sleep, stringifyActor, testRelayTriggerRequestSchema, testRelayUploadsInitRequestSchema, toIsoString, truncateDecisionsToByteBudget, truncateReasonText, trustPolicyUpdateSchema, trustedContributorHoldReason, validateScopeName, validateSecretKey, wrapUntrusted } from "@kici-dev/engine";
|
|
8
|
+
import { ACTOR_AGENT_SUFFIX, AGENT_API_REQUEST_TIMEOUT_MS, AGENT_FEATURE_LABELS, ALLOWED_SYSTEM_VARS, ARTIFACT_NAME_MAX_LENGTH, AccessLogAction, AccessLogOutcome, AccessLogSource, AccessLogTargetType, ActorType, AgentFailureCategory, ApprovalDecision, ArtifactCompleteAckOutcome, ArtifactDownloadOutcome, ArtifactUploadOutcome, AttestationOrigin, BREAKING_FLOOR, CANONICAL_STATUSES, CONTAINER_BUILD_RUNTIME_LABEL, CacheRefScope, CheckMode, CheckRunConclusion, CheckStepOutcome, CiTrustLevel, ConcurrencyStrategy, ContextDeleteErrorCode, ContextGateRejectReason, ContextType, DASHBOARD_REQUEST_TYPE_SET, DEFAULT_APPROVAL_EXPIRY_SECONDS, DEFAULT_CONCURRENCY_STRATEGY, DEFAULT_HOLD_EXPIRY_SECONDS, DEFERRED_PATHS_SUMMARY_SUFFIX, EventLogSource, EventLogStatus, ExecutionJobStatus, ExecutionRunStatus, ExecutionStepStatus, FanoutCause, FanoutError, FleetHostWriteRefusal, ForkPolicy, GLOBAL_EVAL_SKIPS_RESULT_AWARE_LABEL, HeldRunQueueType, HeldRunStatus, HoldScope, HoldType, ID_TOKEN_CLAIM_NAMES, INIT_LABEL, INIT_RUNNER_ROLE_LABEL, INIT_STEP_TYPE_PREFIX, INSTALL_JOB_ID_PREFIX, INVALID_JOIN_REQUEST_MESSAGE, InitFailureCategory, InventorySelectorSchema, JOB_IMAGE_RUNTIME_LABEL, JOIN_PROTOCOL_UPGRADE_TOKEN, JOIN_PROTOCOL_V1_REMOVED_MESSAGE, JobRejectReason, JoinErrorCode, JoinRequestKind, KICI_AGENT_ENV_PREFIX, KICI_EVENT_NAME_PREFIX, KNOWN_ROLES, LabelMatcher, LockFileParseError, LogStream, MAX_APPROVAL_EXPIRY_SECONDS, MIN_APPROVAL_EXPIRY_SECONDS, MIN_PROTOCOL_VERSION, MinimumTrustSchema, ORCH_AGENT_CAPABILITIES, OWN_INGRESS_MODES, OrchLogPhase, OrchRole, OrchestratorMode, PEER_MUTUAL_AUTH_REQUIRED_REASON, PLATFORM_CONNECTED_MODES, PLATFORM_TO_ORCH_RECOGNIZED_TYPES, PRIVILEGED_ROOT_LABEL, PROTOCOL_VERSION, PayloadOmittedReason, PeerAuthMode, PeerAuthScheme, PeerForgetOutcome, RegisterableTriggerType, RunFailureClass, RuntimeFact, SCALER_EVENT_NAMES, SCHEMA_VERSION, SECURITY_HOLD_JOB_IDS, SSH_TRANSPORT_CAPABILITY, ScaleDownReason, ScaleDownReason as ScaleDownReason$1, ScalerBackendType, ScalerEventType, ScalerEventType as ScalerEventType$1, ScalerOrphansAction, ScalerReloadOutcome, ScalerScaleDownPayload, ScalerScaleUpPayload, ScalerVmStatus, ScalerVmStopOutcome, ScalerVmTracker, SetupStepType, SourceOrigin, SourceSubtype, StepPhase, TERMINAL_JOB_STATES, TERMINAL_RUN_STATES, TEST_RUN_LOG_CURSOR_START, TRIGGER_EVENT_TYPES, TimeoutReason, TraceCheck, TriggerSource, TrustTierSchema, UnavailablePathsTrace, VariantKind, WS_CLOSE_AGENT_AUTH_FAILED, WS_CLOSE_AUTH_TIMEOUT, WS_CLOSE_DISPATCH_ACK_TIMEOUT, WS_CLOSE_GOING_AWAY, WS_CLOSE_HEARTBEAT_TIMEOUT, WS_CLOSE_INTERNAL_ERROR, WS_CLOSE_INVALID_MESSAGE, WS_CLOSE_PLAN_LIMIT, WS_CLOSE_PROTOCOL_ERROR, WS_CLOSE_SUPERSEDED_BY_RECONNECT, WS_CLOSE_UNAUTHORIZED, WS_MAX_PAYLOAD_BYTES, WebhookRelayResult, WsRateLimiter, accessLogWarmSqlCase, agentAuthRequestSchema, agentLabelOf, agentToOrchestratorMessageSchema, agentTypeLabel, appendChecks, approvalTimeoutSecondsSchema, artifactInvalidNameError, assertValidSecretKey, attestationVerifyStatusSchema, buildEventClaims, buildIdTokenSubject, buildJoinRefusal, buildTrustedPassthroughEnv, canonicalizeCapability, canonicalizeLabel, canonicalizeLabelSet, canonicalizeLabels, canonicalizeMatcher, checkArtifactName, classifyJoinRequest, coerceDispatchInputs, collectDiscriminatorTypes, compareStepsByPhase, createContentRequirementsTraceEntry, createDispatchFailureTraceEntry, createGlobalFilterTraceEntry, createWorkflowDecision, deriveOsArchLabels, diffRangeKindSchema, fanoutEnvelopeFields, flattenActor, formatLabelMatcher, getAccessLogColdDays, getSecretAuditLogColdDays, githubIngressPath, githubWebhookPath, globalEvalRoundResultSchema, hostLabel, hostSatisfiesTarget, hostToScalerPlatform, initStepIndex, installGateJobId, isFailureStatus, isKnownCapability, isLockDynamicJobFn, isLockParallelStep, isLockStaticJob, isPullRequestFamilyTriggerEvent, isRunId, isSecurityHoldJobId, isSelfReportedLabel, joinRequestSchema, logPullPlatformToOrchSchema, matchAllWorkflows, matchScopePattern, matchWorkflowsForEvent, matcherSatisfiedBy, materializeFanout, materializeResolvedHosts, materializeResolvedMatrix, matrixEnvelopeFields, mergeOrderedMaps, minAccessLogWarmDays, minSecretAuditLogWarmDays, parseRunIdRef, partitionMatchers, peerAuthRequestSchema, peerFromPeerMessageSchema, peerHelloResponseSchema, peerHelloSchema, platformToOsArchLabels, platformToTaints, reservedEventNamePrefix, resolveDiffRange, resolveRoleLabels, resolveScheduleInputs, resolveSecretsWithProvenance, resourceRequestNestedSchema, runIdPrefixRange, runtimeLabel, scalerAgentLabels, scalerLabel, scalerPlatformSchema, scheduleTriggerKey, secretAuditLogWarmSqlCase, shouldRecordAccess, shouldRecordSecretResolve, sleep, stepPhase, stepTypeOf, stringifyActor, testRelayTriggerRequestSchema, testRelayUploadsInitRequestSchema, toIsoString, truncateDecisionsToByteBudget, truncateReasonText, trustPolicyUpdateSchema, trustedContributorHoldReason, validateScopeName, validateSecretKey, wrapUntrusted } from "@kici-dev/engine";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { X509Certificate, createCipheriv, createDecipheriv, createHash, createHmac, createPrivateKey, createPublicKey, diffieHellman, generateKeyPairSync, hkdfSync, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
11
11
|
import { sql } from "kysely";
|
|
@@ -5979,7 +5979,7 @@ var init_peer_client = __esmMin((() => {
|
|
|
5979
5979
|
PeerDialOrigin["Discovered"] = "discovered";
|
|
5980
5980
|
return PeerDialOrigin;
|
|
5981
5981
|
}({});
|
|
5982
|
-
SOFTWARE_VERSION$1 = "0.
|
|
5982
|
+
SOFTWARE_VERSION$1 = "0.21.0";
|
|
5983
5983
|
PeerClient$1 = class {
|
|
5984
5984
|
ws = null;
|
|
5985
5985
|
_state = "disconnected";
|
|
@@ -8450,7 +8450,7 @@ var init_peer_handler = __esmMin((() => {
|
|
|
8450
8450
|
init_join_token();
|
|
8451
8451
|
init_run_detached();
|
|
8452
8452
|
logger$154 = createLogger({ prefix: "peer-handler" });
|
|
8453
|
-
SOFTWARE_VERSION = "0.
|
|
8453
|
+
SOFTWARE_VERSION = "0.21.0";
|
|
8454
8454
|
ADVERTISED_PEER_AUTH_SCHEMES = [PeerAuthScheme.enum["mutual-v2"]];
|
|
8455
8455
|
MAX_TOKEN_CANDIDATES = 8;
|
|
8456
8456
|
RATE_LIMIT_MAX = 5;
|
|
@@ -12559,6 +12559,17 @@ var init_reduced_privilege_note = __esmMin((() => {
|
|
|
12559
12559
|
* - Size safety: 65535 byte limit with progressive truncation
|
|
12560
12560
|
*/
|
|
12561
12561
|
/**
|
|
12562
|
+
* Record one step's progress: replace the entry for its index, or add it in display
|
|
12563
|
+
* order. Pseudo-steps have high indices (init at 1_000_000 + n), so the list is keyed
|
|
12564
|
+
* by index rather than position; a positional list would pad every gap up to it.
|
|
12565
|
+
*/
|
|
12566
|
+
function applyStepProgress(steps, entry) {
|
|
12567
|
+
return [...steps.map((s, position) => ({
|
|
12568
|
+
...s,
|
|
12569
|
+
stepIndex: s.stepIndex ?? position
|
|
12570
|
+
})).filter((s) => s.stepIndex !== entry.stepIndex), entry].sort(compareStepsByPhase);
|
|
12571
|
+
}
|
|
12572
|
+
/**
|
|
12562
12573
|
* Cut `summary` to fit the API's cap, whatever a caller wrapped around it. The last line of
|
|
12563
12574
|
* defence: `reservedBytes` keeps a summary readable by dropping log tiers first.
|
|
12564
12575
|
*/
|
|
@@ -13543,27 +13554,13 @@ var init_check_run_reporter = __esmMin((() => {
|
|
|
13543
13554
|
if (!checkRunId) return;
|
|
13544
13555
|
if (this.terminalSent.has(key)) return;
|
|
13545
13556
|
this.trackRunKey(opts.runId, key);
|
|
13546
|
-
|
|
13547
|
-
if (!steps) {
|
|
13548
|
-
steps = [];
|
|
13549
|
-
this.stepProgress.set(key, steps);
|
|
13550
|
-
}
|
|
13551
|
-
if (opts.stepIndex < steps.length) steps[opts.stepIndex] = {
|
|
13557
|
+
const steps = applyStepProgress(this.stepProgress.get(key) ?? [], {
|
|
13552
13558
|
name: opts.stepName,
|
|
13553
13559
|
status: opts.state,
|
|
13554
|
-
durationMs: opts.durationMs
|
|
13555
|
-
|
|
13556
|
-
|
|
13557
|
-
|
|
13558
|
-
name: `Step ${steps.length}`,
|
|
13559
|
-
status: "pending"
|
|
13560
|
-
});
|
|
13561
|
-
steps.push({
|
|
13562
|
-
name: opts.stepName,
|
|
13563
|
-
status: opts.state,
|
|
13564
|
-
durationMs: opts.durationMs
|
|
13565
|
-
});
|
|
13566
|
-
}
|
|
13560
|
+
durationMs: opts.durationMs,
|
|
13561
|
+
stepIndex: opts.stepIndex,
|
|
13562
|
+
...opts.stepType && { stepType: opts.stepType }
|
|
13563
|
+
});
|
|
13567
13564
|
await this.persistStepProgress(key, steps, opts.runId);
|
|
13568
13565
|
if (opts.state === ExecutionStepStatus.enum.running && !this.inProgressSent.get(key)) {
|
|
13569
13566
|
await this.persistInProgressSent(key, opts.runId);
|
|
@@ -18772,7 +18769,7 @@ var init_execution_tracker = __esmMin((() => {
|
|
|
18772
18769
|
const run = this.runs.get(runId);
|
|
18773
18770
|
const prefix = `needs-pending-${jobName}-`;
|
|
18774
18771
|
if (run) {
|
|
18775
|
-
for (const key of run.jobs
|
|
18772
|
+
for (const [key, job] of run.jobs) if (key.startsWith(prefix) && job.name === jobName) return key;
|
|
18776
18773
|
}
|
|
18777
18774
|
return (await this.db.selectFrom("execution_jobs").select("job_id").where("run_id", "=", runId).where("job_name", "=", jobName).where("job_id", "like", `${prefix}%`).executeTakeFirst())?.job_id;
|
|
18778
18775
|
}
|
|
@@ -19846,6 +19843,10 @@ var init_execution_tracker = __esmMin((() => {
|
|
|
19846
19843
|
async recordInitFailureRun(args) {
|
|
19847
19844
|
const now = /* @__PURE__ */ new Date();
|
|
19848
19845
|
const customerId = await this.resolveCustomerId(args.routingKey);
|
|
19846
|
+
const testRunColumns = args.testRun ? {
|
|
19847
|
+
is_test_run: true,
|
|
19848
|
+
fixture_id: args.testRun.fixtureId
|
|
19849
|
+
} : {};
|
|
19849
19850
|
const crossRepoWorkflowRepo = crossRepoWorkflowRepoOf(args);
|
|
19850
19851
|
if (!guardedWriteApplied(await this.db.insertInto("execution_runs").values({
|
|
19851
19852
|
run_id: args.runId,
|
|
@@ -19867,7 +19868,8 @@ var init_execution_tracker = __esmMin((() => {
|
|
|
19867
19868
|
init_failure: JSON.stringify(args.initFailure),
|
|
19868
19869
|
...inheritedChainDepth(args.chainDepth),
|
|
19869
19870
|
...rerunLineageColumns(args.rerunLineage),
|
|
19870
|
-
...crossRepoWorkflowRepo && { workflow_repo_identifier: crossRepoWorkflowRepo }
|
|
19871
|
+
...crossRepoWorkflowRepo && { workflow_repo_identifier: crossRepoWorkflowRepo },
|
|
19872
|
+
...testRunColumns
|
|
19871
19873
|
}).onConflict((oc) => oc.column("run_id").doUpdateSet({
|
|
19872
19874
|
completed_at: now,
|
|
19873
19875
|
duration_ms: sql`extract(epoch from (${now} - execution_runs.started_at)) * 1000`,
|
|
@@ -19876,7 +19878,8 @@ var init_execution_tracker = __esmMin((() => {
|
|
|
19876
19878
|
failure_class: RunFailureClass.enum.never_started,
|
|
19877
19879
|
init_failure: JSON.stringify(args.initFailure),
|
|
19878
19880
|
...inheritedChainDepth(args.chainDepth),
|
|
19879
|
-
...crossRepoWorkflowRepo && { workflow_repo_identifier: crossRepoWorkflowRepo }
|
|
19881
|
+
...crossRepoWorkflowRepo && { workflow_repo_identifier: crossRepoWorkflowRepo },
|
|
19882
|
+
...testRunColumns
|
|
19880
19883
|
}).where("execution_runs.status", "not in", [...TERMINAL_RUN_STATES])).executeTakeFirst())) {
|
|
19881
19884
|
logger$129.debug("Init-failure record skipped: run already terminal in DB", { runId: args.runId });
|
|
19882
19885
|
return;
|
|
@@ -19981,6 +19984,10 @@ var init_execution_tracker = __esmMin((() => {
|
|
|
19981
19984
|
*/
|
|
19982
19985
|
async recordRunHeld(args) {
|
|
19983
19986
|
const now = /* @__PURE__ */ new Date();
|
|
19987
|
+
const testRunColumns = args.testRun ? {
|
|
19988
|
+
is_test_run: true,
|
|
19989
|
+
fixture_id: args.testRun.fixtureId
|
|
19990
|
+
} : {};
|
|
19984
19991
|
const customerId = await this.resolveCustomerId(args.routingKey);
|
|
19985
19992
|
const crossRepoProvenance = crossRepoProvenanceOf(args.repoIdentifier, {
|
|
19986
19993
|
identifier: args.workflowRepoIdentifier,
|
|
@@ -20008,11 +20015,13 @@ var init_execution_tracker = __esmMin((() => {
|
|
|
20008
20015
|
...rerunLineageColumns(args.rerunLineage),
|
|
20009
20016
|
...crossRepoProvenance && workflowProvenanceColumns(crossRepoProvenance),
|
|
20010
20017
|
...args.isGlobalEvalRound === true && { is_global_eval_round: true },
|
|
20011
|
-
...args.dispatchRoutingKey != null && args.dispatchRoutingKey !== args.routingKey && { dispatch_routing_key: args.dispatchRoutingKey }
|
|
20018
|
+
...args.dispatchRoutingKey != null && args.dispatchRoutingKey !== args.routingKey && { dispatch_routing_key: args.dispatchRoutingKey },
|
|
20019
|
+
...testRunColumns
|
|
20012
20020
|
}).onConflict((oc) => oc.column("run_id").doUpdateSet({
|
|
20013
20021
|
status: ExecutionRunStatus.enum.held,
|
|
20014
20022
|
...args.contextName && { context: args.contextName },
|
|
20015
20023
|
...args.prNumber != null && { pr_number: args.prNumber },
|
|
20024
|
+
...testRunColumns,
|
|
20016
20025
|
...inheritedChainDepth(args.chainDepth),
|
|
20017
20026
|
...crossRepoProvenance && {
|
|
20018
20027
|
workflow_repo_identifier: crossRepoProvenance.identifier,
|
|
@@ -20278,7 +20287,8 @@ var init_execution_tracker = __esmMin((() => {
|
|
|
20278
20287
|
log_path: logPath,
|
|
20279
20288
|
routing_key: this.runs.get(runId)?.routingKey ?? null
|
|
20280
20289
|
};
|
|
20281
|
-
|
|
20290
|
+
const stepType = stepTypeOf(data);
|
|
20291
|
+
if (stepType) values.step_type = stepType;
|
|
20282
20292
|
if (state === ExecutionStepStatus.enum.running) values.started_at = now;
|
|
20283
20293
|
if (TERMINAL_JOB_STATES.has(state)) {
|
|
20284
20294
|
values.completed_at = now;
|
|
@@ -33852,7 +33862,8 @@ async function recordInitFailureFromSkip(args) {
|
|
|
33852
33862
|
},
|
|
33853
33863
|
triggerEvent: dispatchTriggerEvent(ctx),
|
|
33854
33864
|
commitMessage: extractCommitMessage(setup.info.event, setup.info.payload),
|
|
33855
|
-
...preDispatchRunProvenance(ctx)
|
|
33865
|
+
...preDispatchRunProvenance(ctx),
|
|
33866
|
+
...ctx.testRun && { testRun: ctx.testRun }
|
|
33856
33867
|
});
|
|
33857
33868
|
await completeChecksForUndispatchedRun({
|
|
33858
33869
|
ctx,
|
|
@@ -33903,7 +33914,8 @@ async function holdWorkflowForInstallGate(args) {
|
|
|
33903
33914
|
commitMessage: extractCommitMessage(setup.info.event, setup.info.payload),
|
|
33904
33915
|
workflowSha: ctx.global?.workflowSha ?? null,
|
|
33905
33916
|
workflowBranch: ctx.global?.workflowBranch ?? null,
|
|
33906
|
-
...preDispatchRunProvenance(ctx)
|
|
33917
|
+
...preDispatchRunProvenance(ctx),
|
|
33918
|
+
...ctx.testRun && { testRun: ctx.testRun }
|
|
33907
33919
|
});
|
|
33908
33920
|
if (deps.heldRunStore) await deps.heldRunStore.createHold(ctx.resolvedOrgId, {
|
|
33909
33921
|
runId,
|
|
@@ -33979,7 +33991,8 @@ async function holdRunForSecurityPolicy(args) {
|
|
|
33979
33991
|
prNumber: event.prNumber ?? null,
|
|
33980
33992
|
workflowSha: ctx.global?.workflowSha ?? null,
|
|
33981
33993
|
workflowBranch: ctx.global?.workflowBranch ?? null,
|
|
33982
|
-
...preDispatchRunProvenance(ctx)
|
|
33994
|
+
...preDispatchRunProvenance(ctx),
|
|
33995
|
+
...ctx.testRun && { testRun: ctx.testRun }
|
|
33983
33996
|
});
|
|
33984
33997
|
let heldRow;
|
|
33985
33998
|
if (deps.heldRunStore) {
|
|
@@ -62761,7 +62774,9 @@ function createAdminQueueExecutionRoutes(deps) {
|
|
|
62761
62774
|
});
|
|
62762
62775
|
app.get("/executions/:runId", async (c) => {
|
|
62763
62776
|
deps.rbac.requirePermission(c.get("role"), "secret.read");
|
|
62764
|
-
const
|
|
62777
|
+
const rawRunId = c.req.param("runId");
|
|
62778
|
+
if (!isRunId(rawRunId)) return c.json({ error: `Invalid run id '${rawRunId}': a run id is a full UUID.` }, 400);
|
|
62779
|
+
const runId = rawRunId.toLowerCase();
|
|
62765
62780
|
const runRow = await sql`
|
|
62766
62781
|
SELECT id, run_id, workflow_name, status, provider, repo_identifier,
|
|
62767
62782
|
ref, sha, routing_key, context, trust_tier, created_at,
|
|
@@ -63663,6 +63678,10 @@ var init_decorating_secret_resolver = __esmMin((() => {
|
|
|
63663
63678
|
* decorating secret resolver), and the fixture's `secrets` context mapping
|
|
63664
63679
|
* resolves into namespaced secrets;
|
|
63665
63680
|
* - direct workflow execution (bypass trigger matching) is supported.
|
|
63681
|
+
*
|
|
63682
|
+
* Like a webhook, it creates one run per matched workflow. A caller that
|
|
63683
|
+
* follows a single run (no `acceptsMultipleRuns`) gets one shared run instead,
|
|
63684
|
+
* and only when the workflows' job names cannot collide in it.
|
|
63666
63685
|
*/
|
|
63667
63686
|
/**
|
|
63668
63687
|
* Repo identity for an inline-lock (local working tree) run. Derived from the
|
|
@@ -63881,9 +63900,90 @@ function buildTestDispatchContext(shared, workflow, decision, runId) {
|
|
|
63881
63900
|
...Object.keys(shared.runWideFlatSecrets).length > 0 && { runWideFlatSecrets: shared.runWideFlatSecrets }
|
|
63882
63901
|
};
|
|
63883
63902
|
}
|
|
63903
|
+
/** The static job names a workflow defines; null when it has a dynamic job, whose names are unknown until it runs. */
|
|
63904
|
+
function staticJobNames(workflow) {
|
|
63905
|
+
const names = [];
|
|
63906
|
+
for (const job of workflow.jobs) {
|
|
63907
|
+
if (job._type !== "static") return null;
|
|
63908
|
+
names.push(job.name);
|
|
63909
|
+
}
|
|
63910
|
+
return names;
|
|
63911
|
+
}
|
|
63912
|
+
/**
|
|
63913
|
+
* The job names two or more matched workflows define, sorted. Null when a
|
|
63914
|
+
* matched workflow has a dynamic job, so the names cannot be compared before
|
|
63915
|
+
* the run.
|
|
63916
|
+
*/
|
|
63917
|
+
function sharedJobNames(fullLockFile, matchedDecisions) {
|
|
63918
|
+
const owners = /* @__PURE__ */ new Map();
|
|
63919
|
+
for (const decision of matchedDecisions) {
|
|
63920
|
+
const workflow = fullLockFile.workflows.find((w) => w.name === decision.workflowName);
|
|
63921
|
+
if (!workflow) continue;
|
|
63922
|
+
const names = staticJobNames(workflow);
|
|
63923
|
+
if (names === null) return null;
|
|
63924
|
+
for (const name of new Set(names)) owners.set(name, (owners.get(name) ?? 0) + 1);
|
|
63925
|
+
}
|
|
63926
|
+
return [...owners.entries()].filter(([, count]) => count > 1).map(([name]) => name).sort();
|
|
63927
|
+
}
|
|
63928
|
+
/**
|
|
63929
|
+
* Split the matched workflows into runs: one run per workflow, as every other
|
|
63930
|
+
* run producer does.
|
|
63931
|
+
*
|
|
63932
|
+
* A caller that does not set `acceptsMultipleRuns` follows only `runId`. For
|
|
63933
|
+
* it, several matched workflows share one run when their job names cannot
|
|
63934
|
+
* collide (a run keys its needs edges, pending contexts and logs by job name),
|
|
63935
|
+
* and the trigger is refused when they can.
|
|
63936
|
+
*/
|
|
63937
|
+
function planTestRuns(input, fullLockFile, matchedDecisions, firstRunId) {
|
|
63938
|
+
if (input.acceptsMultipleRuns || matchedDecisions.length === 1) return matchedDecisions.map((decision, i) => ({
|
|
63939
|
+
runId: i === 0 ? firstRunId : randomUUID(),
|
|
63940
|
+
decisions: [decision]
|
|
63941
|
+
}));
|
|
63942
|
+
const shared = sharedJobNames(fullLockFile, matchedDecisions);
|
|
63943
|
+
if (shared === null || shared.length > 0) return { rejected: `This fixture matches several workflows (${matchedDecisions.map((d) => d.workflowName).join(", ")}) and ${shared === null ? "one of them defines dynamic jobs, whose names cannot be checked before the run" : `they share job names (${shared.join(", ")})`}. This kici version follows a single run, so upgrade kici to run each workflow as its own run, or pass --workflow <name> to run one workflow.` };
|
|
63944
|
+
return [{
|
|
63945
|
+
runId: firstRunId,
|
|
63946
|
+
decisions: matchedDecisions
|
|
63947
|
+
}];
|
|
63948
|
+
}
|
|
63949
|
+
/**
|
|
63950
|
+
* Dispatch one planned run. A run of several workflows (the shared run of a
|
|
63951
|
+
* caller without `acceptsMultipleRuns`) is held open from its first
|
|
63952
|
+
* registration until its last workflow has registered: a workflow whose jobs
|
|
63953
|
+
* all end before the next one registers would otherwise complete the run, and
|
|
63954
|
+
* the next workflow would start it over.
|
|
63955
|
+
*/
|
|
63956
|
+
async function dispatchPlannedRun(shared, deps, planned) {
|
|
63957
|
+
const { runId, decisions } = planned;
|
|
63958
|
+
const out = {
|
|
63959
|
+
jobIds: [],
|
|
63960
|
+
deferredJobCount: 0,
|
|
63961
|
+
warnings: []
|
|
63962
|
+
};
|
|
63963
|
+
let runHeld = false;
|
|
63964
|
+
const holdRunOpen = () => {
|
|
63965
|
+
if (!runHeld && decisions.length > 1) runHeld = deps.executionTracker?.holdRunForPendingJobs(runId) ?? false;
|
|
63966
|
+
};
|
|
63967
|
+
try {
|
|
63968
|
+
for (const decision of decisions) {
|
|
63969
|
+
const workflow = shared.fullLockFile.workflows.find((w) => w.name === decision.workflowName);
|
|
63970
|
+
if (!workflow) continue;
|
|
63971
|
+
const ctx = buildTestDispatchContext(shared, workflow, decision, runId);
|
|
63972
|
+
ctx.onRunRegistered = holdRunOpen;
|
|
63973
|
+
const result = await dispatchMatchedWorkflow(ctx);
|
|
63974
|
+
out.jobIds.push(...result.dispatchedJobIds);
|
|
63975
|
+
out.deferredJobCount += result.deferredJobCount ?? 0;
|
|
63976
|
+
if (result.envWarnings?.length) out.warnings.push(...result.envWarnings);
|
|
63977
|
+
}
|
|
63978
|
+
} finally {
|
|
63979
|
+
if (runHeld) await deps.executionTracker?.releasePendingJobsHold(runId);
|
|
63980
|
+
}
|
|
63981
|
+
return out;
|
|
63982
|
+
}
|
|
63884
63983
|
/**
|
|
63885
63984
|
* Process a test trigger through the shared dispatch core: one
|
|
63886
|
-
* `WorkflowDispatchContext` per matched workflow, into `dispatchMatchedWorkflow
|
|
63985
|
+
* `WorkflowDispatchContext` per matched workflow, into `dispatchMatchedWorkflow`,
|
|
63986
|
+
* each under its own run id.
|
|
63887
63987
|
*/
|
|
63888
63988
|
async function processTestTrigger(input, deps) {
|
|
63889
63989
|
const runId = randomUUID();
|
|
@@ -63919,6 +64019,13 @@ async function processTestTrigger(input, deps) {
|
|
|
63919
64019
|
jobIds: []
|
|
63920
64020
|
};
|
|
63921
64021
|
const matchedDecisions = matchResult;
|
|
64022
|
+
const plan = planTestRuns(input, fullLockFile, matchedDecisions, runId);
|
|
64023
|
+
if (!Array.isArray(plan)) return {
|
|
64024
|
+
runId,
|
|
64025
|
+
status: "rejected",
|
|
64026
|
+
reason: plan.rejected,
|
|
64027
|
+
jobIds: []
|
|
64028
|
+
};
|
|
63922
64029
|
const resolvedOrgId = deps.db ? await resolveOrgId(deps.db, input.routingKey) : "__default__";
|
|
63923
64030
|
const dispatchInputsResult = resolveDispatchInputs(input, fullLockFile, matchedDecisions);
|
|
63924
64031
|
if ("rejected" in dispatchInputsResult) return {
|
|
@@ -63934,13 +64041,13 @@ async function processTestTrigger(input, deps) {
|
|
|
63934
64041
|
reason: namespacedResult.rejected,
|
|
63935
64042
|
jobIds: []
|
|
63936
64043
|
};
|
|
63937
|
-
await storeFixturePayload(input, deps, runId);
|
|
64044
|
+
for (const planned of plan) await storeFixturePayload(input, deps, planned.runId);
|
|
63938
64045
|
const cliSecrets = decryptCliSecrets(input);
|
|
63939
64046
|
const testDeps = deps.secretResolver ? {
|
|
63940
64047
|
...deps,
|
|
63941
64048
|
secretResolver: new DecoratingSecretResolver(deps.secretResolver, cliSecrets)
|
|
63942
64049
|
} : deps;
|
|
63943
|
-
deps.executionTracker?.markTestRun(runId);
|
|
64050
|
+
for (const planned of plan) deps.executionTracker?.markTestRun(planned.runId);
|
|
63944
64051
|
const provider = inlineProvider ?? input.routingKey.split(":")[0] ?? "local";
|
|
63945
64052
|
const shared = {
|
|
63946
64053
|
input,
|
|
@@ -63966,34 +64073,31 @@ async function processTestTrigger(input, deps) {
|
|
|
63966
64073
|
const jobIds = [];
|
|
63967
64074
|
const warnings = [];
|
|
63968
64075
|
let deferredJobCount = 0;
|
|
63969
|
-
|
|
63970
|
-
const
|
|
63971
|
-
|
|
63972
|
-
|
|
63973
|
-
|
|
63974
|
-
|
|
63975
|
-
|
|
63976
|
-
|
|
63977
|
-
|
|
63978
|
-
|
|
63979
|
-
|
|
63980
|
-
jobIds.push(...result.dispatchedJobIds);
|
|
63981
|
-
deferredJobCount += result.deferredJobCount ?? 0;
|
|
63982
|
-
if (result.envWarnings?.length) warnings.push(...result.envWarnings);
|
|
63983
|
-
}
|
|
63984
|
-
} finally {
|
|
63985
|
-
if (runHeld) await deps.executionTracker?.releasePendingJobsHold(runId);
|
|
64076
|
+
const runs = [];
|
|
64077
|
+
for (const planned of plan) {
|
|
64078
|
+
const dispatched = await dispatchPlannedRun(shared, deps, planned);
|
|
64079
|
+
jobIds.push(...dispatched.jobIds);
|
|
64080
|
+
deferredJobCount += dispatched.deferredJobCount;
|
|
64081
|
+
warnings.push(...dispatched.warnings);
|
|
64082
|
+
if (planned.decisions.length === 1) runs.push({
|
|
64083
|
+
runId: planned.runId,
|
|
64084
|
+
workflowName: planned.decisions[0].workflowName,
|
|
64085
|
+
jobIds: dispatched.jobIds
|
|
64086
|
+
});
|
|
63986
64087
|
}
|
|
64088
|
+
const runsField = runs.length === plan.length ? { runs } : {};
|
|
63987
64089
|
if (jobIds.length === 0 && deferredJobCount === 0) return {
|
|
63988
64090
|
runId,
|
|
63989
64091
|
status: "rejected",
|
|
63990
64092
|
reason: "No jobs dispatched (all matched workflows had no static jobs or dispatch was rejected)",
|
|
63991
|
-
jobIds: []
|
|
64093
|
+
jobIds: [],
|
|
64094
|
+
...runsField
|
|
63992
64095
|
};
|
|
63993
64096
|
return {
|
|
63994
64097
|
runId,
|
|
63995
64098
|
status: "accepted",
|
|
63996
64099
|
jobIds,
|
|
64100
|
+
...runsField,
|
|
63997
64101
|
...warnings.length > 0 && { warnings }
|
|
63998
64102
|
};
|
|
63999
64103
|
}
|
|
@@ -64075,17 +64179,19 @@ async function handleTestTrigger(msg, deps) {
|
|
|
64075
64179
|
checkMode: msg.checkMode,
|
|
64076
64180
|
target: msg.target,
|
|
64077
64181
|
dispatchInputs: msg.dispatchInputs,
|
|
64182
|
+
...msg.acceptsMultipleRuns && { acceptsMultipleRuns: true },
|
|
64078
64183
|
requestId: msg.requestId,
|
|
64079
64184
|
actor: msg.actor
|
|
64080
64185
|
}, deps);
|
|
64081
|
-
|
|
64186
|
+
const runIds = result.runs?.length ? result.runs.map((r) => r.runId) : [result.runId];
|
|
64187
|
+
for (const runId of runIds) runDetached(logger$59, "Access log write", () => deps.accessLog?.record({
|
|
64082
64188
|
orgId: deps.orgId ?? null,
|
|
64083
64189
|
routingKey: deps.routingKey ?? msg.routingKey,
|
|
64084
64190
|
actor: msg.actor,
|
|
64085
64191
|
action: "run.trigger",
|
|
64086
64192
|
target: {
|
|
64087
64193
|
type: "run",
|
|
64088
|
-
id:
|
|
64194
|
+
id: runId
|
|
64089
64195
|
},
|
|
64090
64196
|
requestId: msg.requestId,
|
|
64091
64197
|
source: deps.accessLogSource ?? AccessLogSource.enum.platform_proxy,
|
|
@@ -64093,10 +64199,11 @@ async function handleTestTrigger(msg, deps) {
|
|
|
64093
64199
|
...result.reason ? { errorMessage: result.reason } : {}
|
|
64094
64200
|
}), {
|
|
64095
64201
|
requestId: msg.requestId,
|
|
64096
|
-
runId
|
|
64202
|
+
runId
|
|
64097
64203
|
});
|
|
64098
64204
|
return {
|
|
64099
64205
|
runId: result.runId,
|
|
64206
|
+
...result.runs && { runs: result.runs },
|
|
64100
64207
|
status: result.status,
|
|
64101
64208
|
...result.reason ? { reason: result.reason } : {},
|
|
64102
64209
|
jobIds: result.jobIds,
|
|
@@ -64131,7 +64238,8 @@ async function handleTestRunStatus(msg, deps) {
|
|
|
64131
64238
|
const run = await deps.db.selectFrom("execution_runs").select([
|
|
64132
64239
|
"run_id",
|
|
64133
64240
|
"status",
|
|
64134
|
-
"is_test_run"
|
|
64241
|
+
"is_test_run",
|
|
64242
|
+
"failure_reason"
|
|
64135
64243
|
]).where("run_id", "=", msg.runId).executeTakeFirst();
|
|
64136
64244
|
if (!run || !run.is_test_run) return { error: "Run not found" };
|
|
64137
64245
|
const jobs = await deps.db.selectFrom("execution_jobs").select([
|
|
@@ -64151,6 +64259,7 @@ async function handleTestRunStatus(msg, deps) {
|
|
|
64151
64259
|
errorMessage: j.error_message ?? null,
|
|
64152
64260
|
durationMs: j.duration_ms ?? null
|
|
64153
64261
|
})),
|
|
64262
|
+
failureReason: run.failure_reason ?? null,
|
|
64154
64263
|
done: TERMINAL_RUN_STATES.has(run.status)
|
|
64155
64264
|
};
|
|
64156
64265
|
}
|
|
@@ -64227,14 +64336,18 @@ async function collectRunLogLines(runId, logStorage) {
|
|
|
64227
64336
|
async function readFromFileOffsets(runId, logStorage, offsets, terminal) {
|
|
64228
64337
|
const next = { ...offsets };
|
|
64229
64338
|
const lines = [];
|
|
64339
|
+
const lineJobNames = [];
|
|
64230
64340
|
if (logStorage) for (const file of await listRunLogFiles(runId, logStorage)) {
|
|
64231
64341
|
const fileLines = splitLogLines((await logStorage.read(file.path)).data, terminal);
|
|
64232
64342
|
const from = Math.min(offsets[file.key] ?? 0, fileLines.length);
|
|
64233
|
-
|
|
64343
|
+
const fresh = fileLines.slice(from);
|
|
64344
|
+
lines.push(...fresh);
|
|
64345
|
+
for (let i = 0; i < fresh.length; i++) lineJobNames.push(file.jobName);
|
|
64234
64346
|
next[file.key] = Math.max(offsets[file.key] ?? 0, fileLines.length);
|
|
64235
64347
|
}
|
|
64236
64348
|
return {
|
|
64237
64349
|
lines,
|
|
64350
|
+
lineJobNames,
|
|
64238
64351
|
nextCursor: Object.values(next).reduce((sum, n) => sum + n, 0),
|
|
64239
64352
|
done: terminal,
|
|
64240
64353
|
nextLogCursor: encodeRunLogCursor(next)
|
|
@@ -66694,26 +66807,29 @@ var init_admin_registrations = __esmMin((() => {
|
|
|
66694
66807
|
*
|
|
66695
66808
|
* Sorting `execution_steps` by `step_index` alone puts a pseudo-step where its index landed,
|
|
66696
66809
|
* not where it ran. That suits the cache pseudo-steps (indices above the real steps, mostly
|
|
66697
|
-
* bracketing the work), but a job's image build
|
|
66810
|
+
* bracketing the work), but a job's image build and its init commands run strictly FIRST.
|
|
66698
66811
|
*
|
|
66699
66812
|
* The index cannot express this: `step.status` rejects a negative index (an agent sending one
|
|
66700
66813
|
* to an older orchestrator is disconnected mid-job), and the check-run reporter
|
|
66701
66814
|
* (`steps[stepIndex]`) and the dashboard (log fetching gated on `stepIndex >= 0`) treat a
|
|
66702
|
-
* negative index as absent. So the ordering is expressed by TYPE,
|
|
66815
|
+
* negative index as absent. So the ordering is expressed by TYPE, as the engine's
|
|
66816
|
+
* `StepPhase`, here, once.
|
|
66703
66817
|
*/
|
|
66704
66818
|
/**
|
|
66705
|
-
* Sort key placing setup pseudo-steps
|
|
66706
|
-
* the `step_index` sort:
|
|
66819
|
+
* Sort key placing steps by `StepPhase`: setup pseudo-steps, then init commands, then the
|
|
66820
|
+
* job's steps. Apply BEFORE the `step_index` sort:
|
|
66707
66821
|
*
|
|
66708
66822
|
* ```ts
|
|
66709
66823
|
* qb.orderBy(setupStepsFirst()).orderBy('step_index', 'asc')
|
|
66710
66824
|
* ```
|
|
66711
66825
|
*
|
|
66712
66826
|
* Untyped steps and every real step share one key, so `step_index` alone orders each group.
|
|
66827
|
+
* The init pattern matches `initStepIndex`: the prefix followed by digits only.
|
|
66713
66828
|
*/
|
|
66714
66829
|
function setupStepsFirst() {
|
|
66715
66830
|
const setupTypes = SetupStepType.options;
|
|
66716
|
-
|
|
66831
|
+
const initPattern = `^${INIT_STEP_TYPE_PREFIX}[0-9]+$`;
|
|
66832
|
+
return sql`case when step_type in (${sql.join(setupTypes.map((t) => sql.lit(t)))}) then ${sql.lit(StepPhase.options.indexOf(StepPhase.enum.setup))} when step_type ~ ${sql.lit(initPattern)} then ${sql.lit(StepPhase.options.indexOf(StepPhase.enum.init))} else ${sql.lit(StepPhase.options.indexOf(StepPhase.enum.steps))} end`;
|
|
66717
66833
|
}
|
|
66718
66834
|
var init_step_display_order = __esmMin((() => {}));
|
|
66719
66835
|
//#endregion
|
|
@@ -67216,6 +67332,19 @@ function parseCountFlag(raw) {
|
|
|
67216
67332
|
function spansWorkflow(workflowName) {
|
|
67217
67333
|
return (eb) => eb.or([eb("execution_runs.workflow_name", "=", workflowName), eb.exists(eb.selectFrom("execution_jobs").select("execution_jobs.job_id").whereRef("execution_jobs.run_id", "=", "execution_runs.run_id").where("execution_jobs.workflow_name", "=", workflowName))]);
|
|
67218
67334
|
}
|
|
67335
|
+
/**
|
|
67336
|
+
* `?runIdPrefix=`: the run ids that start with the given characters, as a UUID range
|
|
67337
|
+
* the primary-key index answers. A full run id is its own one-id range.
|
|
67338
|
+
*/
|
|
67339
|
+
function parseRunIdPrefixQuery(raw) {
|
|
67340
|
+
if (raw === void 0 || raw === "") return void 0;
|
|
67341
|
+
const ref = parseRunIdRef(raw);
|
|
67342
|
+
if (ref.kind === "invalid") return { error: `Invalid "runIdPrefix" '${raw}': ${ref.reason}` };
|
|
67343
|
+
return ref.kind === "uuid" ? {
|
|
67344
|
+
low: ref.runId,
|
|
67345
|
+
high: ref.runId
|
|
67346
|
+
} : runIdPrefixRange(ref.hex);
|
|
67347
|
+
}
|
|
67219
67348
|
/** Run routes, mounted at /api/v1/admin/runs. */
|
|
67220
67349
|
function createAdminRunRoutes(deps) {
|
|
67221
67350
|
const app = createAdminApp(logger$51);
|
|
@@ -67225,12 +67354,16 @@ function createAdminRunRoutes(deps) {
|
|
|
67225
67354
|
});
|
|
67226
67355
|
app.use("/api/v1/admin/runs", authMiddleware);
|
|
67227
67356
|
app.use("/api/v1/admin/runs/*", authMiddleware);
|
|
67357
|
+
app.use("/api/v1/admin/runs/:runId", requireFullRunId);
|
|
67358
|
+
app.use("/api/v1/admin/runs/:runId/*", requireFullRunId);
|
|
67228
67359
|
app.get("/api/v1/admin/runs", async (c) => {
|
|
67229
67360
|
deps.rbac.requirePermission(c.get("role"), "run.read");
|
|
67230
67361
|
const statuses = parseStatus(c.req.query("status"));
|
|
67231
67362
|
const workflowName = c.req.query("workflowName");
|
|
67232
67363
|
const repo = c.req.query("repo");
|
|
67233
67364
|
const deliveryId = c.req.query("deliveryId");
|
|
67365
|
+
const runIdPrefix = parseRunIdPrefixQuery(c.req.query("runIdPrefix"));
|
|
67366
|
+
if (runIdPrefix && "error" in runIdPrefix) return c.json({ error: runIdPrefix.error }, 400);
|
|
67234
67367
|
const sinceParsed = parseSince(c.req.query("since"));
|
|
67235
67368
|
if (!sinceParsed.ok) return c.json({ error: "Invalid \"since\" query parameter: expected ISO-8601 timestamp" }, 400);
|
|
67236
67369
|
const since = sinceParsed.value;
|
|
@@ -67247,6 +67380,7 @@ function createAdminRunRoutes(deps) {
|
|
|
67247
67380
|
if (workflowName) countQuery = countQuery.where(spansWorkflow(workflowName));
|
|
67248
67381
|
if (repo) countQuery = countQuery.where("repo_identifier", "=", repo);
|
|
67249
67382
|
if (deliveryId) countQuery = countQuery.where("delivery_id", "=", deliveryId);
|
|
67383
|
+
if (runIdPrefix) countQuery = countQuery.where("run_id", ">=", runIdPrefix.low).where("run_id", "<=", runIdPrefix.high);
|
|
67250
67384
|
if (since) countQuery = countQuery.where("created_at", ">", since);
|
|
67251
67385
|
if (tokenRoutingKey) countQuery = countQuery.where("routing_key", "=", tokenRoutingKey);
|
|
67252
67386
|
if (countOnly) {
|
|
@@ -67282,6 +67416,7 @@ function createAdminRunRoutes(deps) {
|
|
|
67282
67416
|
if (workflowName) query = query.where(spansWorkflow(workflowName));
|
|
67283
67417
|
if (repo) query = query.where("repo_identifier", "=", repo);
|
|
67284
67418
|
if (deliveryId) query = query.where("delivery_id", "=", deliveryId);
|
|
67419
|
+
if (runIdPrefix) query = query.where("run_id", ">=", runIdPrefix.low).where("run_id", "<=", runIdPrefix.high);
|
|
67285
67420
|
if (since) query = query.where("created_at", ">", since);
|
|
67286
67421
|
if (tokenRoutingKey) query = query.where("routing_key", "=", tokenRoutingKey);
|
|
67287
67422
|
const [runs, countResult] = await Promise.all([query.orderBy("created_at", "desc").limit(limit).offset(offset).execute(), countQuery.executeTakeFirstOrThrow()]);
|
|
@@ -67313,7 +67448,7 @@ function createAdminRunRoutes(deps) {
|
|
|
67313
67448
|
});
|
|
67314
67449
|
app.get("/api/v1/admin/runs/:runId", async (c) => {
|
|
67315
67450
|
deps.rbac.requirePermission(c.get("role"), "run.read");
|
|
67316
|
-
const runId = c.req.param("runId");
|
|
67451
|
+
const runId = c.req.param("runId").toLowerCase();
|
|
67317
67452
|
const run = await deps.db.selectFrom("execution_runs").select([
|
|
67318
67453
|
"run_id",
|
|
67319
67454
|
"workflow_name",
|
|
@@ -67377,7 +67512,7 @@ function createAdminRunRoutes(deps) {
|
|
|
67377
67512
|
});
|
|
67378
67513
|
app.get("/api/v1/admin/runs/:runId/jobs", async (c) => {
|
|
67379
67514
|
deps.rbac.requirePermission(c.get("role"), "run.read");
|
|
67380
|
-
const runId = c.req.param("runId");
|
|
67515
|
+
const runId = c.req.param("runId").toLowerCase();
|
|
67381
67516
|
const includeSteps = parseCountFlag(c.req.query("includeSteps"));
|
|
67382
67517
|
const runExists = await deps.db.selectFrom("execution_runs").select([
|
|
67383
67518
|
"run_id",
|
|
@@ -67429,6 +67564,7 @@ function createAdminRunRoutes(deps) {
|
|
|
67429
67564
|
jobSteps = [];
|
|
67430
67565
|
stepsByJob.set(step.job_id, jobSteps);
|
|
67431
67566
|
}
|
|
67567
|
+
const initIndex = initStepIndex(step.step_type);
|
|
67432
67568
|
jobSteps.push({
|
|
67433
67569
|
stepIndex: step.step_index,
|
|
67434
67570
|
stepName: step.step_name,
|
|
@@ -67438,7 +67574,9 @@ function createAdminRunRoutes(deps) {
|
|
|
67438
67574
|
durationMs: step.duration_ms,
|
|
67439
67575
|
exitCode: step.exit_code,
|
|
67440
67576
|
errorMessage: step.error_message,
|
|
67441
|
-
stepType: step.step_type
|
|
67577
|
+
stepType: step.step_type,
|
|
67578
|
+
phase: stepPhase(step.step_type),
|
|
67579
|
+
...initIndex !== void 0 && { initIndex }
|
|
67442
67580
|
});
|
|
67443
67581
|
}
|
|
67444
67582
|
}
|
|
@@ -67465,7 +67603,7 @@ function createAdminRunRoutes(deps) {
|
|
|
67465
67603
|
});
|
|
67466
67604
|
app.get("/api/v1/admin/runs/:runId/structured", async (c) => {
|
|
67467
67605
|
deps.rbac.requirePermission(c.get("role"), "run.read");
|
|
67468
|
-
const runId = c.req.param("runId");
|
|
67606
|
+
const runId = c.req.param("runId").toLowerCase();
|
|
67469
67607
|
const detail = await aggregateRunDetail(deps.db, runId);
|
|
67470
67608
|
if (!detail) return c.json({ error: `Run ${runId} not found` }, 404);
|
|
67471
67609
|
const denied = enforceRoutingKeyScope(c, detail.routingKey);
|
|
@@ -67474,7 +67612,7 @@ function createAdminRunRoutes(deps) {
|
|
|
67474
67612
|
});
|
|
67475
67613
|
app.get("/api/v1/admin/runs/:runId/jobs/:jobId/steps/:stepIndex/logs", async (c) => {
|
|
67476
67614
|
deps.rbac.requirePermission(c.get("role"), "run.read");
|
|
67477
|
-
const runId = c.req.param("runId");
|
|
67615
|
+
const runId = c.req.param("runId").toLowerCase();
|
|
67478
67616
|
const jobId = c.req.param("jobId");
|
|
67479
67617
|
const stepIndex = parseInt(c.req.param("stepIndex"), 10);
|
|
67480
67618
|
if (Number.isNaN(stepIndex)) return c.json({ error: "Invalid stepIndex" }, 400);
|
|
@@ -67500,7 +67638,7 @@ function createAdminRunRoutes(deps) {
|
|
|
67500
67638
|
});
|
|
67501
67639
|
app.get("/api/v1/admin/runs/:runId/ephemeral-key", async (c) => {
|
|
67502
67640
|
deps.rbac.requirePermission(c.get("role"), "run.read");
|
|
67503
|
-
const runId = c.req.param("runId");
|
|
67641
|
+
const runId = c.req.param("runId").toLowerCase();
|
|
67504
67642
|
const runExists = await deps.db.selectFrom("execution_runs").select(["run_id", "routing_key"]).where("run_id", "=", runId).executeTakeFirst();
|
|
67505
67643
|
if (!runExists) return c.json({ error: `Run ${runId} not found` }, 404);
|
|
67506
67644
|
const denied = enforceRoutingKeyScope(c, runExists.routing_key);
|
|
@@ -67518,7 +67656,7 @@ function createAdminRunRoutes(deps) {
|
|
|
67518
67656
|
app.get("/api/v1/admin/runs/:runId/secret-outputs", async (c) => {
|
|
67519
67657
|
const role = c.get("role");
|
|
67520
67658
|
deps.rbac.requirePermission(role, "run.read");
|
|
67521
|
-
const runId = c.req.param("runId");
|
|
67659
|
+
const runId = c.req.param("runId").toLowerCase();
|
|
67522
67660
|
const outputKeyFilter = c.req.query("outputKey");
|
|
67523
67661
|
const reveal = parseCountFlag(c.req.query("reveal"));
|
|
67524
67662
|
if (reveal) {
|
|
@@ -67589,7 +67727,7 @@ function createAdminRunRoutes(deps) {
|
|
|
67589
67727
|
});
|
|
67590
67728
|
return app;
|
|
67591
67729
|
}
|
|
67592
|
-
var logger$51, RUN_STATUSES;
|
|
67730
|
+
var logger$51, RUN_STATUSES, requireFullRunId;
|
|
67593
67731
|
var init_admin_runs = __esmMin((() => {
|
|
67594
67732
|
init_step_display_order();
|
|
67595
67733
|
init_secret_output_crypto();
|
|
@@ -67603,6 +67741,11 @@ var init_admin_runs = __esmMin((() => {
|
|
|
67603
67741
|
init_admin_env();
|
|
67604
67742
|
logger$51 = createLogger({ prefix: "admin-runs" });
|
|
67605
67743
|
RUN_STATUSES = new Set(CANONICAL_STATUSES);
|
|
67744
|
+
requireFullRunId = async (c, next) => {
|
|
67745
|
+
const runId = c.req.param("runId") ?? "";
|
|
67746
|
+
if (!isRunId(runId)) return c.json({ error: `Invalid run id '${runId}': a run id is a full UUID. List runs (or filter with ?runIdPrefix=) to find it.` }, 400);
|
|
67747
|
+
return next();
|
|
67748
|
+
};
|
|
67606
67749
|
}));
|
|
67607
67750
|
//#endregion
|
|
67608
67751
|
//#region src/cold-store/load-event-log-range.ts
|
|
@@ -68700,14 +68843,14 @@ function createHealthRoutes$1(deps = {}) {
|
|
|
68700
68843
|
return createHealthRoutes({
|
|
68701
68844
|
livenessInfo: () => {
|
|
68702
68845
|
return {
|
|
68703
|
-
version: "0.
|
|
68704
|
-
buildDate: "2026-10-
|
|
68705
|
-
sdkVersion: "0.
|
|
68846
|
+
version: "0.21.0",
|
|
68847
|
+
buildDate: "2026-10-18T02:27:04.078Z",
|
|
68848
|
+
sdkVersion: "0.21.0",
|
|
68706
68849
|
sdkBundleHash: "b012bd8bace3df9e574252bf290c652bc39766da617acf51729a08586756266d",
|
|
68707
|
-
sharedVersion: "0.
|
|
68850
|
+
sharedVersion: "0.21.0",
|
|
68708
68851
|
sharedBundleHash: "23378ace59973f47203be3ff171bb7d5e66702eaa335927b390f94b9771a1f8b",
|
|
68709
|
-
engineVersion: "0.
|
|
68710
|
-
engineBundleHash: "
|
|
68852
|
+
engineVersion: "0.21.0",
|
|
68853
|
+
engineBundleHash: "d9dfe7c806a7fdbd64da7ef48455a139c1d8db8510964ab79e233efdc3f171ba"
|
|
68711
68854
|
};
|
|
68712
68855
|
},
|
|
68713
68856
|
readinessCheck: deps.db ? async () => {
|
|
@@ -68739,7 +68882,7 @@ function createCapabilitiesRoutes() {
|
|
|
68739
68882
|
const app = new Hono();
|
|
68740
68883
|
app.get("/api/v1/capabilities", (c) => {
|
|
68741
68884
|
const manifest = {
|
|
68742
|
-
orchestratorVersion: "0.
|
|
68885
|
+
orchestratorVersion: "0.21.0",
|
|
68743
68886
|
protocolVersion: PROTOCOL_VERSION,
|
|
68744
68887
|
minProtocolVersion: MIN_PROTOCOL_VERSION
|
|
68745
68888
|
};
|
|
@@ -71243,6 +71386,7 @@ function createApp(deps) {
|
|
|
71243
71386
|
jobName: deps.executionTracker.getJobName(msg.runId, msg.jobId) ?? msg.jobId,
|
|
71244
71387
|
stepIndex: msg.stepIndex,
|
|
71245
71388
|
stepName: msg.stepName,
|
|
71389
|
+
stepType: stepTypeOf(msg.data),
|
|
71246
71390
|
state: msg.state,
|
|
71247
71391
|
durationMs: msg.data?.durationMs ?? void 0,
|
|
71248
71392
|
installationId: execContext.installationId,
|
|
@@ -71465,7 +71609,7 @@ function createApp(deps) {
|
|
|
71465
71609
|
app.use("*", (c, next) => c.req.path.startsWith("/api/v1/cache/blob/") ? next() : webhookBodyLimit(c, next));
|
|
71466
71610
|
if (deps.fsCache) registerBlobRoutes(app, deps.fsCache);
|
|
71467
71611
|
app.post("/api/v1/admin/runs/:runId/cancel", async (c) => {
|
|
71468
|
-
const runId = c.req.param("runId");
|
|
71612
|
+
const runId = c.req.param("runId").toLowerCase();
|
|
71469
71613
|
if (!runId) return c.json({ error: "Missing runId" }, 400);
|
|
71470
71614
|
if (!deps.adminDeps) return c.json({ error: "Admin API not available" }, 503);
|
|
71471
71615
|
const auth = await resolveBearerAuth(c, {
|
|
@@ -71500,6 +71644,11 @@ function createApp(deps) {
|
|
|
71500
71644
|
recordAccess("denied", `role "${tokenInfo.role}" lacks run.cancel`);
|
|
71501
71645
|
return c.json({ error: "Permission denied: run.cancel required" }, 403);
|
|
71502
71646
|
}
|
|
71647
|
+
if (!isRunId(runId)) {
|
|
71648
|
+
const error = `Invalid run id '${runId}': a run id is a full UUID.`;
|
|
71649
|
+
recordAccess("allowed", error);
|
|
71650
|
+
return c.json({ error }, 400);
|
|
71651
|
+
}
|
|
71503
71652
|
try {
|
|
71504
71653
|
let force = false;
|
|
71505
71654
|
try {
|
|
@@ -71973,7 +72122,7 @@ var init_app = __esmMin((() => {
|
|
|
71973
72122
|
init_agent_metrics_aggregator();
|
|
71974
72123
|
init_run_detached();
|
|
71975
72124
|
logger$38 = createLogger({ prefix: "app" });
|
|
71976
|
-
ORCHESTRATOR_VERSION$2 = "0.
|
|
72125
|
+
ORCHESTRATOR_VERSION$2 = "0.21.0";
|
|
71977
72126
|
SourceLocationStore = class {
|
|
71978
72127
|
cache = /* @__PURE__ */ new Map();
|
|
71979
72128
|
key(workflowName, jobName) {
|
|
@@ -91588,13 +91737,13 @@ var init_worker_core = __esmMin((() => {
|
|
|
91588
91737
|
init_app_on_error();
|
|
91589
91738
|
init_run_detached();
|
|
91590
91739
|
init_peer_job_cancel();
|
|
91591
|
-
ORCHESTRATOR_VERSION$1 = "0.
|
|
91592
|
-
WORKER_SDK_VERSION = "0.
|
|
91740
|
+
ORCHESTRATOR_VERSION$1 = "0.21.0";
|
|
91741
|
+
WORKER_SDK_VERSION = "0.21.0";
|
|
91593
91742
|
WORKER_SDK_BUNDLE_HASH = "b012bd8bace3df9e574252bf290c652bc39766da617acf51729a08586756266d";
|
|
91594
|
-
WORKER_SHARED_VERSION = "0.
|
|
91743
|
+
WORKER_SHARED_VERSION = "0.21.0";
|
|
91595
91744
|
WORKER_SHARED_BUNDLE_HASH = "23378ace59973f47203be3ff171bb7d5e66702eaa335927b390f94b9771a1f8b";
|
|
91596
|
-
WORKER_ENGINE_VERSION = "0.
|
|
91597
|
-
WORKER_ENGINE_BUNDLE_HASH = "
|
|
91745
|
+
WORKER_ENGINE_VERSION = "0.21.0";
|
|
91746
|
+
WORKER_ENGINE_BUNDLE_HASH = "d9dfe7c806a7fdbd64da7ef48455a139c1d8db8510964ab79e233efdc3f171ba";
|
|
91598
91747
|
logger$2 = createLogger({ prefix: "worker" });
|
|
91599
91748
|
DRAIN_TIMEOUT_MS = 3e5;
|
|
91600
91749
|
}));
|
|
@@ -91623,13 +91772,13 @@ init_webhook_url_resolvers();
|
|
|
91623
91772
|
init_run_detached();
|
|
91624
91773
|
init_peer_job_cancel();
|
|
91625
91774
|
init_orphan_requests();
|
|
91626
|
-
const ORCHESTRATOR_VERSION = "0.
|
|
91627
|
-
const SDK_VERSION = "0.
|
|
91775
|
+
const ORCHESTRATOR_VERSION = "0.21.0";
|
|
91776
|
+
const SDK_VERSION = "0.21.0";
|
|
91628
91777
|
const SDK_BUNDLE_HASH = "b012bd8bace3df9e574252bf290c652bc39766da617acf51729a08586756266d";
|
|
91629
|
-
const SHARED_VERSION = "0.
|
|
91778
|
+
const SHARED_VERSION = "0.21.0";
|
|
91630
91779
|
const SHARED_BUNDLE_HASH = "23378ace59973f47203be3ff171bb7d5e66702eaa335927b390f94b9771a1f8b";
|
|
91631
|
-
const ENGINE_VERSION = "0.
|
|
91632
|
-
const ENGINE_BUNDLE_HASH = "
|
|
91780
|
+
const ENGINE_VERSION = "0.21.0";
|
|
91781
|
+
const ENGINE_BUNDLE_HASH = "d9dfe7c806a7fdbd64da7ef48455a139c1d8db8510964ab79e233efdc3f171ba";
|
|
91633
91782
|
const otelSdk = initTelemetry({
|
|
91634
91783
|
serviceName: "kici-orchestrator",
|
|
91635
91784
|
otlpEndpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT
|