@kici-dev/agent 0.6.0 → 0.7.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/config.d.ts +38 -38
- package/dist/eval-runner.js +1866 -0
- package/dist/execution/dep-installer.d.ts +28 -7
- package/dist/execution/eval-context.d.ts +114 -0
- package/dist/execution/global-eval-types.d.ts +26 -0
- package/dist/execution/job-runner.d.ts +23 -75
- package/dist/execution/npm-registry-config.d.ts +6 -0
- package/dist/execution/rule-evaluator.d.ts +2 -1
- package/dist/execution/sandbox/bare-metal-sandbox.d.ts +6 -0
- package/dist/execution/sandbox/container-hardening.d.ts +8 -0
- package/dist/execution/sandbox/container-sandbox.d.ts +9 -0
- package/dist/execution/sandbox/eval-dispatch.d.ts +28 -0
- package/dist/execution/sandbox/eval-fork-runner.d.ts +43 -0
- package/dist/execution/sandbox/eval-runner.d.ts +21 -0
- package/dist/execution/sandbox/fork-runner.d.ts +23 -0
- package/dist/execution/sandbox/ipc-protocol.d.ts +82 -8
- package/dist/execution/sandbox/job-network.d.ts +91 -0
- package/dist/execution/sandbox/log-masker.d.ts +38 -0
- package/dist/execution/sandbox/types.d.ts +6 -0
- package/dist/execution/sandbox/workflow-runner.d.ts +1 -1
- package/dist/execution/source-packer.d.ts +4 -4
- package/dist/execution/source-restore.d.ts +28 -13
- package/dist/execution/workflow-loader.d.ts +16 -13
- package/dist/execution/yarnrc-berry-config.d.ts +6 -4
- package/dist/index.js +83 -40
- package/dist/provenance/statement-builder.d.ts +19 -8
- package/dist/server.js +1527 -1665
- package/dist/workflow-runner-bundle.js +1117 -185
- package/dist/workflow-runner.js +395 -149
- package/dist/ws/orchestrator-client.d.ts +4 -0
- package/package.json +6 -5
- package/sbom.spdx.json +66 -66
|
@@ -185946,6 +185946,38 @@ var init_attestation_origin = __esmMin((() => {
|
|
|
185946
185946
|
"deferred",
|
|
185947
185947
|
"offline-backfill"
|
|
185948
185948
|
]);
|
|
185949
|
+
}));
|
|
185950
|
+
//#endregion
|
|
185951
|
+
//#region ../engine/dist/provenance/id-token-event-claims.js
|
|
185952
|
+
var provenanceContextSchema;
|
|
185953
|
+
var init_id_token_event_claims = __esmMin((() => {
|
|
185954
|
+
init_zod();
|
|
185955
|
+
provenanceContextSchema = object({
|
|
185956
|
+
/** `owner/repo`, the token's `repository` claim. */
|
|
185957
|
+
repository: string$1().nullable(),
|
|
185958
|
+
/** The branch the run PRESENTS — a pull request's BASE branch. */
|
|
185959
|
+
ref: string$1().nullable(),
|
|
185960
|
+
/** The run's commit SHA, the token's `sha` claim. */
|
|
185961
|
+
sha: string$1().nullable(),
|
|
185962
|
+
/**
|
|
185963
|
+
* The token's `workflow_ref` claim (`<workflow_name>@<sha>`) — NOT the git
|
|
185964
|
+
* ref used to clone a global workflow's repository. The statement's
|
|
185965
|
+
* `workflow.path` is compared against this.
|
|
185966
|
+
*/
|
|
185967
|
+
workflowRef: string$1().nullable(),
|
|
185968
|
+
runId: string$1(),
|
|
185969
|
+
jobId: string$1(),
|
|
185970
|
+
/** The customer's public org id, resolved server-side from the routing key. */
|
|
185971
|
+
orgId: string$1(),
|
|
185972
|
+
/** `triggered` or `run-remote`, derived from the run's local-working-tree flag. */
|
|
185973
|
+
sourceOrigin: string$1(),
|
|
185974
|
+
/** Informational source provider (github / gitlab / …). */
|
|
185975
|
+
provider: string$1().nullable(),
|
|
185976
|
+
/** The orchestrator's provenance issuer, for the statement's `builder.id`. */
|
|
185977
|
+
issuer: string$1(),
|
|
185978
|
+
/** This orchestrator's instance id, also for `builder.id`. */
|
|
185979
|
+
orchestratorId: string$1()
|
|
185980
|
+
}).passthrough();
|
|
185949
185981
|
})), heartbeatSchema, nackSchema;
|
|
185950
185982
|
var init_common = __esmMin((() => {
|
|
185951
185983
|
init_zod();
|
|
@@ -186562,6 +186594,7 @@ var init_access_log = __esmMin((() => {
|
|
|
186562
186594
|
"runs.list.read",
|
|
186563
186595
|
"runs.filters.read",
|
|
186564
186596
|
"sources.list.read",
|
|
186597
|
+
"admin_tokens.list.read",
|
|
186565
186598
|
"run.payload.read",
|
|
186566
186599
|
"run.orch_logs.read",
|
|
186567
186600
|
"step.logs.read",
|
|
@@ -186806,6 +186839,7 @@ var init_event_log = __esmMin((() => {
|
|
|
186806
186839
|
init_zod();
|
|
186807
186840
|
EventLogStatus = _enum([
|
|
186808
186841
|
"received",
|
|
186842
|
+
"shed",
|
|
186809
186843
|
"processed",
|
|
186810
186844
|
"duplicate",
|
|
186811
186845
|
"lockfile_missing",
|
|
@@ -186835,9 +186869,24 @@ var init_deployment_identity = __esmMin((() => {
|
|
|
186835
186869
|
adminInvocation: string$1().min(1).optional(),
|
|
186836
186870
|
adminPath: string$1().min(1).optional()
|
|
186837
186871
|
});
|
|
186872
|
+
}));
|
|
186873
|
+
//#endregion
|
|
186874
|
+
//#region ../engine/dist/protocol/messages/config-paths.js
|
|
186875
|
+
var ConfigPathsSchema;
|
|
186876
|
+
var init_config_paths = __esmMin((() => {
|
|
186877
|
+
init_zod();
|
|
186878
|
+
ConfigPathsSchema = object({
|
|
186879
|
+
/** The env file the service manager loads (`EnvironmentFile=` / `env_file:`). */
|
|
186880
|
+
envFile: string$1().min(1).optional(),
|
|
186881
|
+
/** The scaler YAML file, or the directory when only a directory is configured. */
|
|
186882
|
+
scalerConfig: string$1().min(1).optional(),
|
|
186883
|
+
/** The generated compose file, for a compose deployment. */
|
|
186884
|
+
composeFile: string$1().min(1).optional()
|
|
186885
|
+
});
|
|
186838
186886
|
})), SourceSubtype, OrchestratorMode, SourceProvider, sourceRegistrationSchema, acceptedSourceSchema, sourceRegistrationAckSchema, sourceDeregisterSchema, sourceDeregisterAckSchema;
|
|
186839
186887
|
var init_source_registration = __esmMin((() => {
|
|
186840
186888
|
init_deployment_identity();
|
|
186889
|
+
init_config_paths();
|
|
186841
186890
|
init_zod();
|
|
186842
186891
|
SourceSubtype = _enum([
|
|
186843
186892
|
"github_app",
|
|
@@ -186924,6 +186973,13 @@ var init_source_registration = __esmMin((() => {
|
|
|
186924
186973
|
* publish it, in which case the dashboard treats the shape as `unknown`.
|
|
186925
186974
|
*/
|
|
186926
186975
|
deployment: DeploymentIdentitySchema.optional(),
|
|
186976
|
+
/**
|
|
186977
|
+
* Where this orchestrator's own config files live on its host, so the
|
|
186978
|
+
* dashboard can point an operator straight at them. Optional: an
|
|
186979
|
+
* orchestrator that predates the field omits it, and each member is omitted
|
|
186980
|
+
* independently when that path is not knowable.
|
|
186981
|
+
*/
|
|
186982
|
+
configPaths: ConfigPathsSchema.optional(),
|
|
186927
186983
|
/** Whether this orchestrator has S3 log storage configured. Used for multi-orch pool validation. */
|
|
186928
186984
|
s3LogAccess: boolean$1().optional(),
|
|
186929
186985
|
/** Queue timeout in ms. Platform uses this (with margin) for safety-net GC of stale queued jobs. */
|
|
@@ -187107,6 +187163,27 @@ var init_types$3 = __esmMin((() => {
|
|
|
187107
187163
|
]);
|
|
187108
187164
|
}));
|
|
187109
187165
|
//#endregion
|
|
187166
|
+
//#region ../engine/dist/regex-flags.js
|
|
187167
|
+
/**
|
|
187168
|
+
* Drop `g` and `y` from a flag string. Every other ECMAScript flag —
|
|
187169
|
+
* `d` (hasIndices), `i`, `m`, `s`, `u`, `v` — is meaningful to a single
|
|
187170
|
+
* `.test()` and is preserved.
|
|
187171
|
+
*/
|
|
187172
|
+
function stripStatefulRegexFlags(flags) {
|
|
187173
|
+
return (flags ?? "").replace(STATEFUL_FLAGS, "");
|
|
187174
|
+
}
|
|
187175
|
+
/**
|
|
187176
|
+
* Same, but collapsing an empty result to `undefined` — the shape the SDK's
|
|
187177
|
+
* pattern types and the lock file use for "no flags".
|
|
187178
|
+
*/
|
|
187179
|
+
function normalizeRegexFlags(flags) {
|
|
187180
|
+
return stripStatefulRegexFlags(flags) || void 0;
|
|
187181
|
+
}
|
|
187182
|
+
var STATEFUL_FLAGS;
|
|
187183
|
+
var init_regex_flags = __esmMin((() => {
|
|
187184
|
+
STATEFUL_FLAGS = /[gy]/g;
|
|
187185
|
+
}));
|
|
187186
|
+
//#endregion
|
|
187110
187187
|
//#region ../engine/dist/labels-match.js
|
|
187111
187188
|
var LabelMatcher, HostTargetValue, HostTargetSelector;
|
|
187112
187189
|
var init_labels_match = __esmMin((() => {
|
|
@@ -187304,7 +187381,7 @@ var init_dashboard_global_workflows = __esmMin((() => {
|
|
|
187304
187381
|
settings: globalWorkflowSettingsSchema.optional(),
|
|
187305
187382
|
error: string$1().optional()
|
|
187306
187383
|
});
|
|
187307
|
-
})), dashboardRunDetailRequestSchema, dashboardRunStructuredRequestSchema, dashboardRunStructuredResponseSchema, dashboardRunStateRequestSchema, dashboardRunStateResponseSchema, dashboardStepLogsRequestSchema, dashboardStepDetailSchema, dashboardJobDetailSchema, trustContextSchema, dashboardRunDetailResponseSchema, dashboardStepLogsResponseSchema, dashboardAttestationsListRequestSchema, attestationVerifyStatusSchema, attestationListItemSchema, dashboardAttestationsListResponseSchema, dashboardArtifactsListRequestSchema, artifactListItemSchema, dashboardArtifactsListResponseSchema, attestationListSummarySchema, attestationListFiltersSchema, dashboardAttestationsListAllRequestSchema, dashboardAttestationsListAllResponseSchema, dashboardAttestationGetRequestSchema, dashboardAttestationGetResponseSchema, dashboardAttestationRetryRequestSchema, dashboardAttestationRetryResponseSchema, dashboardRunSummarySchema, dashboardRunsListRequestSchema, dashboardRunsListResponseSchema, dashboardRunsFiltersRequestSchema, dashboardRunsFiltersResponseSchema, dashboardSourceSummarySchema, dashboardSourcesListRequestSchema, dashboardSourcesListResponseSchema, runRerunRequestSchema, runRerunResponseSchema, manualScheduleRequestSchema, manualScheduleResponseSchema, runCancelRequestSchema, runCancelResponseSchema, dashboardPayloadRequestSchema, dashboardPayloadResponseSchema, runLineageSchema, eventLogListItemSchema, dashboardEventLogListRequestSchema, dashboardEventLogListResponseSchema, dashboardEventLogDetailRequestSchema, dashboardEventLogDetailResponseSchema, dashboardEventLogActivityRequestSchema, eventLogActivityCountsSchema, dashboardEventLogActivityResponseSchema, EventLogPayloadStreamError, dashboardEventLogPayloadStreamRequestSchema, dashboardEventLogPayloadChunkSchema, browserEventLogPayloadChunkSchema, dashboardEventDlqListItemSchema, dashboardEventDlqListRequestSchema, dashboardEventDlqListResponseSchema, dashboardEventDlqCountRequestSchema, dashboardEventDlqCountResponseSchema, dashboardEventDlqRetryRequestSchema, dashboardEventDlqRetryResponseSchema, dashboardEventDlqDiscardRequestSchema, dashboardEventDlqDiscardResponseSchema, contextTypeSchema, contextListRequestSchema, contextListResponseSchema, contextGetRequestSchema, contextGetResponseSchema, contextCreateRequestSchema, contextCreateResponseSchema, contextUpdateRequestSchema, contextUpdateResponseSchema, contextTestAccessSetRequestSchema, contextTestAccessSetResponseSchema, contextDeleteRequestSchema, ContextDeleteErrorCode, contextDeleteResponseSchema, contextVarsListRequestSchema, contextVarsListResponseSchema, contextVarSetRequestSchema, contextVarSetResponseSchema, contextVarDeleteRequestSchema, contextVarDeleteResponseSchema, contextSourceOverridesListRequestSchema, contextSourceOverridesListResponseSchema, contextSourceOverrideSetRequestSchema, contextSourceOverrideSetResponseSchema, contextSourceOverrideDeleteRequestSchema, contextSourceOverrideDeleteResponseSchema, contextBindingEntrySchema, contextBindingsListRequestSchema, contextBindingsListResponseSchema, contextBindingsSetRequestSchema, contextBindingsSetResponseSchema, contextSecretsListRequestSchema, contextSecretsListResponseSchema, contextSecretSetRequestSchema, contextSecretSetResponseSchema, contextSecretDeleteRequestSchema, contextSecretDeleteResponseSchema, contextSecretScopeCreateRequestSchema, contextSecretScopeCreateResponseSchema, contextSecretScopeRenameRequestSchema, contextSecretScopeRenameResponseSchema, contextSecretScopeDeleteRequestSchema, contextSecretScopeDeleteResponseSchema, contextHistoryRequestSchema, contextHistoryResponseSchema, HeldRunQueueType, heldRunsListRequestSchema, heldRunsListResponseSchema, heldRunApproveRequestSchema, heldRunApproveResponseSchema, heldRunRejectRequestSchema, heldRunRejectResponseSchema, dashboardDiagnosticsRequestSchema, fleetPinnedRunSchema, FleetHostDisposition, fleetPreviewHostSchema, dashboardFleetHostsRequestSchema, dashboardFleetHostRequestSchema, dashboardFleetPreviewRequestSchema, dashboardFleetHostsResponseSchema, dashboardFleetHostResponseSchema, dashboardFleetPreviewResponseSchema, dashboardFleetWorkflowsForHostRequestSchema, fleetHostWorkflowSchema, dashboardFleetWorkflowsForHostResponseSchema, fleetHostDeclareRequestSchema, fleetHostDeclareResponseSchema, fleetHostRemoveRequestSchema, fleetHostRemoveResponseSchema, diagnosticsAgentSchema, diagnosticsScalerSchema, diagnosticsPeerAgentSchema, diagnosticsPeerSchema, dashboardDiagnosticsResponseSchema, dashboardScalerCapacityRequestSchema, scalerCapacityItemSchema, dashboardScalerCapacityResponseSchema, dashboardScalerAgentsRequestSchema, dashboardScalerAgentsResponseSchema, registrationDisableRequestSchema, registrationDisableResponseSchema, registrationDeleteRequestSchema, registrationDeleteResponseSchema, registrationsListRequestSchema, registrationSourceSchema, registrationItemSchema, registrationsListResponseSchema, backendsListRequestSchema, backendItemSchema, backendsListResponseSchema, backendGetRequestSchema, backendGetResponseSchema, backendsSyncAllRequestSchema, backendsSyncAllResponseSchema, backendSyncRequestSchema, backendSyncResponseSchema, backendTestRequestSchema, backendTestResponseSchema, testRelayUploadsInitRequestSchema, testRelayUploadsInitResponseSchema, testRelayTriggerRequestSchema, testRelayTriggerResponseSchema, testRelayRunStatusRequestSchema, testRelayRunStatusResponseSchema, testRelayRunLogsRequestSchema, testRelayRunLogsResponseSchema, testRelayCancelRequestSchema, testRelayCancelResponseSchema, dashboardPlatformToOrchSchema, runListPrincipalUserSchema, runListSourceSchema, runListItemSchema, diagnosticsInfraAgentSchema, diagnosticsInfraScalerSchema, diagnosticsInfraOrchestratorSchema, diagnosticsInfraAlertSchema, diagnosticsExecutionMetricsSchema, identityLinkItemSchema, memberIdentityLinkSchema, memberRoleAssignmentSchema, orgMemberSchema, DASHBOARD_REQUEST_TYPES;
|
|
187384
|
+
})), dashboardRunDetailRequestSchema, dashboardRunStructuredRequestSchema, dashboardRunStructuredResponseSchema, dashboardRunStateRequestSchema, dashboardRunStateResponseSchema, dashboardStepLogsRequestSchema, dashboardStepDetailSchema, dashboardJobDetailSchema, trustContextSchema, dashboardRunDetailResponseSchema, dashboardStepLogsResponseSchema, dashboardAttestationsListRequestSchema, attestationVerifyStatusSchema, attestationListItemSchema, dashboardAttestationsListResponseSchema, dashboardArtifactsListRequestSchema, artifactListItemSchema, dashboardArtifactsListResponseSchema, attestationListSummarySchema, attestationListFiltersSchema, dashboardAttestationsListAllRequestSchema, dashboardAttestationsListAllResponseSchema, dashboardAttestationGetRequestSchema, dashboardAttestationGetResponseSchema, dashboardAttestationRetryRequestSchema, dashboardAttestationRetryResponseSchema, dashboardRunSummarySchema, dashboardRunsListRequestSchema, dashboardRunsListResponseSchema, dashboardRunsFiltersRequestSchema, dashboardRunsFiltersResponseSchema, dashboardSourceSummarySchema, dashboardSourcesListRequestSchema, dashboardSourcesListResponseSchema, dashboardAdminTokenSummarySchema, dashboardAdminTokensListRequestSchema, dashboardAdminTokensListResponseSchema, runRerunRequestSchema, runRerunResponseSchema, manualScheduleRequestSchema, manualScheduleResponseSchema, runCancelRequestSchema, runCancelResponseSchema, dashboardPayloadRequestSchema, dashboardPayloadResponseSchema, runLineageSchema, eventLogListItemSchema, dashboardEventLogListRequestSchema, dashboardEventLogListResponseSchema, dashboardEventLogDetailRequestSchema, dashboardEventLogDetailResponseSchema, dashboardEventLogActivityRequestSchema, eventLogActivityCountsSchema, dashboardEventLogActivityResponseSchema, EventLogPayloadStreamError, dashboardEventLogPayloadStreamRequestSchema, dashboardEventLogPayloadChunkSchema, browserEventLogPayloadChunkSchema, dashboardEventDlqListItemSchema, dashboardEventDlqListRequestSchema, dashboardEventDlqListResponseSchema, dashboardEventDlqCountRequestSchema, dashboardEventDlqCountResponseSchema, dashboardEventDlqRetryRequestSchema, dashboardEventDlqRetryResponseSchema, dashboardEventDlqDiscardRequestSchema, dashboardEventDlqDiscardResponseSchema, contextTypeSchema, contextListRequestSchema, contextListResponseSchema, contextGetRequestSchema, contextGetResponseSchema, contextCreateRequestSchema, contextCreateResponseSchema, contextUpdateRequestSchema, contextUpdateResponseSchema, contextTestAccessSetRequestSchema, contextTestAccessSetResponseSchema, contextDeleteRequestSchema, ContextDeleteErrorCode, contextDeleteResponseSchema, contextVarsListRequestSchema, contextVarsListResponseSchema, contextVarSetRequestSchema, contextVarSetResponseSchema, contextVarDeleteRequestSchema, contextVarDeleteResponseSchema, contextSourceOverridesListRequestSchema, contextSourceOverridesListResponseSchema, contextSourceOverrideSetRequestSchema, contextSourceOverrideSetResponseSchema, contextSourceOverrideDeleteRequestSchema, contextSourceOverrideDeleteResponseSchema, contextBindingEntrySchema, contextBindingsListRequestSchema, contextBindingsListResponseSchema, contextBindingsSetRequestSchema, contextBindingsSetResponseSchema, contextSecretsListRequestSchema, contextSecretsListResponseSchema, contextSecretSetRequestSchema, contextSecretSetResponseSchema, contextSecretDeleteRequestSchema, contextSecretDeleteResponseSchema, contextSecretScopeCreateRequestSchema, contextSecretScopeCreateResponseSchema, contextSecretScopeRenameRequestSchema, contextSecretScopeRenameResponseSchema, contextSecretScopeDeleteRequestSchema, contextSecretScopeDeleteResponseSchema, contextHistoryRequestSchema, contextHistoryResponseSchema, HeldRunQueueType, heldRunsListRequestSchema, heldRunsListResponseSchema, heldRunApproveRequestSchema, heldRunApproveResponseSchema, heldRunRejectRequestSchema, heldRunRejectResponseSchema, dashboardDiagnosticsRequestSchema, fleetPinnedRunSchema, FleetHostDisposition, fleetPreviewHostSchema, dashboardFleetHostsRequestSchema, dashboardFleetHostRequestSchema, dashboardFleetPreviewRequestSchema, dashboardFleetHostsResponseSchema, dashboardFleetHostResponseSchema, dashboardFleetPreviewResponseSchema, dashboardFleetWorkflowsForHostRequestSchema, fleetHostWorkflowSchema, dashboardFleetWorkflowsForHostResponseSchema, fleetHostDeclareRequestSchema, fleetHostDeclareResponseSchema, fleetHostRemoveRequestSchema, fleetHostRemoveResponseSchema, diagnosticsAgentSchema, diagnosticsScalerSchema, diagnosticsPeerAgentSchema, diagnosticsPeerSchema, dashboardDiagnosticsResponseSchema, dashboardScalerCapacityRequestSchema, scalerCapacityItemSchema, dashboardScalerCapacityResponseSchema, dashboardScalerAgentsRequestSchema, dashboardScalerAgentsResponseSchema, registrationDisableRequestSchema, registrationDisableResponseSchema, registrationDeleteRequestSchema, registrationDeleteResponseSchema, registrationsListRequestSchema, registrationSourceSchema, registrationItemSchema, registrationsListResponseSchema, backendsListRequestSchema, backendItemSchema, backendsListResponseSchema, backendGetRequestSchema, backendGetResponseSchema, backendsSyncAllRequestSchema, backendsSyncAllResponseSchema, backendSyncRequestSchema, backendSyncResponseSchema, backendTestRequestSchema, backendTestResponseSchema, testRelayUploadsInitRequestSchema, testRelayUploadsInitResponseSchema, testRelayTriggerRequestSchema, testRelayTriggerResponseSchema, testRelayRunStatusRequestSchema, testRelayRunStatusResponseSchema, testRelayRunLogsRequestSchema, testRelayRunLogsResponseSchema, testRelayCancelRequestSchema, testRelayCancelResponseSchema, dashboardPlatformToOrchSchema, runListPrincipalUserSchema, runListSourceSchema, runListItemSchema, diagnosticsInfraAgentSchema, diagnosticsInfraScalerSchema, diagnosticsInfraOrchestratorSchema, diagnosticsInfraAlertSchema, diagnosticsExecutionMetricsSchema, identityLinkItemSchema, memberIdentityLinkSchema, memberRoleAssignmentSchema, orgMemberSchema, DASHBOARD_REQUEST_TYPES;
|
|
187308
187385
|
var init_dashboard = __esmMin((() => {
|
|
187309
187386
|
init_check_mode();
|
|
187310
187387
|
init_source_origin();
|
|
@@ -187780,6 +187857,40 @@ var init_dashboard = __esmMin((() => {
|
|
|
187780
187857
|
nextCursor: string$1().optional(),
|
|
187781
187858
|
error: string$1().optional()
|
|
187782
187859
|
});
|
|
187860
|
+
dashboardAdminTokenSummarySchema = object({
|
|
187861
|
+
id: string$1(),
|
|
187862
|
+
label: string$1(),
|
|
187863
|
+
/** `owner`, `admin` or `auditor` — the orchestrator's three fixed roles. */
|
|
187864
|
+
role: string$1(),
|
|
187865
|
+
/**
|
|
187866
|
+
* The intended holder the operator recorded at `token create --subject` (an
|
|
187867
|
+
* OIDC `sub` or an email), or null when nobody did. Null is the report's
|
|
187868
|
+
* `unlinked` finding.
|
|
187869
|
+
*/
|
|
187870
|
+
subject: string$1().nullable(),
|
|
187871
|
+
/**
|
|
187872
|
+
* The single routing key this token is restricted to, or null for an
|
|
187873
|
+
* unscoped token. Load-bearing for the report: a routing-key-scoped token is
|
|
187874
|
+
* refused outright on the secret, context, org-settings and trust-policy
|
|
187875
|
+
* routes, so it is materially less privileged than its role alone suggests.
|
|
187876
|
+
*/
|
|
187877
|
+
routingKey: string$1().nullable(),
|
|
187878
|
+
createdAt: string$1(),
|
|
187879
|
+
expiresAt: string$1().nullable(),
|
|
187880
|
+
lastUsedAt: string$1().nullable(),
|
|
187881
|
+
revoked: boolean$1()
|
|
187882
|
+
});
|
|
187883
|
+
dashboardAdminTokensListRequestSchema = object({
|
|
187884
|
+
type: literal("dashboard.admin-tokens.list"),
|
|
187885
|
+
requestId: string$1(),
|
|
187886
|
+
actor: actorPrincipalSchema
|
|
187887
|
+
});
|
|
187888
|
+
dashboardAdminTokensListResponseSchema = object({
|
|
187889
|
+
type: literal("dashboard.admin-tokens.list.response"),
|
|
187890
|
+
requestId: string$1(),
|
|
187891
|
+
tokens: array(dashboardAdminTokenSummarySchema),
|
|
187892
|
+
error: string$1().optional()
|
|
187893
|
+
});
|
|
187783
187894
|
runRerunRequestSchema = object({
|
|
187784
187895
|
type: literal("run.rerun.request"),
|
|
187785
187896
|
requestId: string$1(),
|
|
@@ -188858,7 +188969,18 @@ var init_dashboard = __esmMin((() => {
|
|
|
188858
188969
|
* (legacy / global-workflow rows). Mirrors the shape attached to run
|
|
188859
188970
|
* summaries so the dashboard can reuse the same icon/label helpers.
|
|
188860
188971
|
*/
|
|
188861
|
-
source: registrationSourceSchema.nullable().optional()
|
|
188972
|
+
source: registrationSourceSchema.nullable().optional(),
|
|
188973
|
+
/**
|
|
188974
|
+
* True when this registration is a global workflow — one whose trigger
|
|
188975
|
+
* carries `repos:`, so it fires on events from other repos in the org.
|
|
188976
|
+
*
|
|
188977
|
+
* Optional because an orchestrator predating this field omits it. A reader
|
|
188978
|
+
* that needs a value for such a row derives one from `triggers[].repos`,
|
|
188979
|
+
* which is the same signal the orchestrator classifies on at registration
|
|
188980
|
+
* time. Prefer this field when present: it mirrors the `is_global` column
|
|
188981
|
+
* the dispatcher itself reads, so it cannot drift from dispatch behaviour.
|
|
188982
|
+
*/
|
|
188983
|
+
isGlobal: boolean$1().optional()
|
|
188862
188984
|
});
|
|
188863
188985
|
registrationsListResponseSchema = object({
|
|
188864
188986
|
type: literal("dashboard.registrations.list.response"),
|
|
@@ -189082,6 +189204,7 @@ var init_dashboard = __esmMin((() => {
|
|
|
189082
189204
|
dashboardRunsListRequestSchema,
|
|
189083
189205
|
dashboardRunsFiltersRequestSchema,
|
|
189084
189206
|
dashboardSourcesListRequestSchema,
|
|
189207
|
+
dashboardAdminTokensListRequestSchema,
|
|
189085
189208
|
runRerunRequestSchema,
|
|
189086
189209
|
manualScheduleRequestSchema,
|
|
189087
189210
|
runCancelRequestSchema,
|
|
@@ -189158,6 +189281,7 @@ var init_dashboard = __esmMin((() => {
|
|
|
189158
189281
|
dashboardRunsListResponseSchema,
|
|
189159
189282
|
dashboardRunsFiltersResponseSchema,
|
|
189160
189283
|
dashboardSourcesListResponseSchema,
|
|
189284
|
+
dashboardAdminTokensListResponseSchema,
|
|
189161
189285
|
runRerunResponseSchema,
|
|
189162
189286
|
manualScheduleResponseSchema,
|
|
189163
189287
|
runCancelResponseSchema,
|
|
@@ -189381,6 +189505,26 @@ var init_dashboard = __esmMin((() => {
|
|
|
189381
189505
|
adminInvocation: string$1().nullable(),
|
|
189382
189506
|
adminPath: string$1().nullable()
|
|
189383
189507
|
}),
|
|
189508
|
+
/**
|
|
189509
|
+
* Absolute host paths of this orchestrator's own config files, for an
|
|
189510
|
+
* operator who wants to inspect them.
|
|
189511
|
+
*
|
|
189512
|
+
* Optional, and every member nullable. The Platform always emits the object
|
|
189513
|
+
* — all-nulls when nothing is known — but the field is declared optional
|
|
189514
|
+
* because a customer-installed `kici` CLI validates this same schema and may
|
|
189515
|
+
* be pointed at a Platform that predates the field. Members are null rather
|
|
189516
|
+
* than absent so a reader never has to distinguish "not sent" from "not
|
|
189517
|
+
* known".
|
|
189518
|
+
*
|
|
189519
|
+
* Null for every connection served from the database rather than from a live
|
|
189520
|
+
* in-memory registry entry: these are live-host runtime paths and are not
|
|
189521
|
+
* persisted, the same treatment `deployment.adminInvocation` already gets.
|
|
189522
|
+
*/
|
|
189523
|
+
configPaths: object({
|
|
189524
|
+
envFile: string$1().nullable(),
|
|
189525
|
+
scalerConfig: string$1().nullable(),
|
|
189526
|
+
composeFile: string$1().nullable()
|
|
189527
|
+
}).optional(),
|
|
189384
189528
|
s3LogAccess: boolean$1().nullable().optional(),
|
|
189385
189529
|
agentCount: number$1(),
|
|
189386
189530
|
runningJobs: number$1(),
|
|
@@ -189721,7 +189865,7 @@ var init_dashboard_write_operations = __esmMin((() => {
|
|
|
189721
189865
|
category: "Held runs",
|
|
189722
189866
|
label: "Approve held run",
|
|
189723
189867
|
sensitivity: "dispatch",
|
|
189724
|
-
cliEquivalent: "kici-admin
|
|
189868
|
+
cliEquivalent: "kici-admin held-run approve"
|
|
189725
189869
|
},
|
|
189726
189870
|
{
|
|
189727
189871
|
name: "held_runs.reject",
|
|
@@ -189729,7 +189873,7 @@ var init_dashboard_write_operations = __esmMin((() => {
|
|
|
189729
189873
|
category: "Held runs",
|
|
189730
189874
|
label: "Reject held run",
|
|
189731
189875
|
sensitivity: "dispatch",
|
|
189732
|
-
cliEquivalent: "kici-admin
|
|
189876
|
+
cliEquivalent: "kici-admin held-run reject"
|
|
189733
189877
|
},
|
|
189734
189878
|
{
|
|
189735
189879
|
name: "event_dlq.retry",
|
|
@@ -189820,6 +189964,7 @@ var init_dashboard_write_operations = __esmMin((() => {
|
|
|
189820
189964
|
cliEquivalent: "kici-admin host remove"
|
|
189821
189965
|
}
|
|
189822
189966
|
]);
|
|
189967
|
+
Object.freeze(["held_runs.approve", "held_runs.reject"]);
|
|
189823
189968
|
DASHBOARD_WRITE_OPERATIONS_BY_NAME = new Map(DASHBOARD_WRITE_OPERATIONS.map((d) => [d.name, d]));
|
|
189824
189969
|
new Map(DASHBOARD_WRITE_OPERATIONS.map((d) => [d.wireMessageType, d]));
|
|
189825
189970
|
DashboardWritePolicyState = _enum([
|
|
@@ -191112,8 +191257,13 @@ var init_peer = __esmMin((() => {
|
|
|
191112
191257
|
providerContext: record(string$1(), unknown()).optional(),
|
|
191113
191258
|
/** Pre-signed source tarball download URL (cache hit). */
|
|
191114
191259
|
sourceTarUrl: string$1().optional(),
|
|
191115
|
-
/**
|
|
191260
|
+
/**
|
|
191261
|
+
* @deprecated Use `sourceTarDigest` — this carries the workflow
|
|
191262
|
+
* `contentHash`, not a hash of the tarball bytes.
|
|
191263
|
+
*/
|
|
191116
191264
|
sourceTarHash: string$1().optional(),
|
|
191265
|
+
/** SHA-256 of the source tarball's own bytes, for integrity verification. */
|
|
191266
|
+
sourceTarDigest: string$1().optional(),
|
|
191117
191267
|
/** Pre-signed dependency tarball download URL (cache hit). */
|
|
191118
191268
|
depsUrl: string$1().optional(),
|
|
191119
191269
|
/** Dependency tarball hash for cache keying. */
|
|
@@ -191337,6 +191487,7 @@ var init_peer = __esmMin((() => {
|
|
|
191337
191487
|
]);
|
|
191338
191488
|
})), CacheRefScope, invokeResultSchema, gitAuthSchema, jobDispatchSchema, jobCancelSchema, registerAckSchema, agentRegisterSchema, agentStatusSchema, jobStatusSchema, globalEvalCandidateResultSchema, JobRejectReason, jobRejectSchema, jobAckSchema, agentLogChunkSchema, agentStepStatusSchema, jobHeartbeatSchema, agentLogSchema, jobConcurrencyReportSchema, jobConcurrencyAckSchema, configAckSchema, cacheUploadRequestSchema, cacheUploadResponseSchema, cacheUploadCompleteSchema, cacheUserRestoreRequestSchema, cacheUserRestoreResponseSchema, cacheUserSaveRequestSchema, cacheUserSaveResponseSchema, cacheUserSaveCompleteSchema, provenanceUploadRequestSchema, provenanceUploadResponseSchema, provenanceUploadCompleteSchema, provenanceUploadDeferSchema, ArtifactUploadOutcome, ArtifactRejectReason, ArtifactDownloadOutcome, artifactsUploadRequestSchema, artifactsUploadResponseSchema, artifactsUploadCompleteSchema, ArtifactCompleteAckOutcome, artifactsUploadCompleteAckSchema, artifactsDownloadRequestSchema, artifactsDownloadResponseSchema, eventEmitSchema, eventEmitResponseSchema, scalerClaimCredentialsSchema, scalerClaimCredentialsResponseSchema, agentMetricsSchema, agentAuthRequestSchema, agentAuthSuccessSchema, agentAuthFailureSchema, agentApiRequestSchema, agentApiResponseSchema, fleetLogsRequestSchema, fleetBundleChunkSchema, fleetBundleErrorSchema, StepApprovalOutcome, stepApprovalPayloadSchema, stepApprovalRequestSchema, stepApprovalResolvedSchema;
|
|
191339
191489
|
var init_orchestrator_agent = __esmMin((() => {
|
|
191490
|
+
init_id_token_event_claims();
|
|
191340
191491
|
init_execution_status();
|
|
191341
191492
|
init_dsse();
|
|
191342
191493
|
init_types$4();
|
|
@@ -191402,8 +191553,19 @@ var init_orchestrator_agent = __esmMin((() => {
|
|
|
191402
191553
|
concurrencyWaitTimeoutMs: number().optional(),
|
|
191403
191554
|
/** URL or file:// path to a pre-packed `.kici/` source tarball. If present, agent extracts it into workDir instead of cloning the repo. */
|
|
191404
191555
|
sourceTarUrl: string$1().optional(),
|
|
191405
|
-
/**
|
|
191556
|
+
/**
|
|
191557
|
+
* @deprecated Use `sourceTarDigest`. Despite its name this carries the
|
|
191558
|
+
* workflow `contentHash`, not a hash of the tarball bytes, so an agent
|
|
191559
|
+
* could not verify a restored tarball against it. Kept on the wire for
|
|
191560
|
+
* older agents; removed at v1.0.0.
|
|
191561
|
+
*/
|
|
191406
191562
|
sourceTarHash: string$1().optional(),
|
|
191563
|
+
/**
|
|
191564
|
+
* SHA-256 of the source tarball's own bytes, for integrity verification
|
|
191565
|
+
* before extraction. The sibling of `depsHash`, which has always carried
|
|
191566
|
+
* the dependency tarball's real digest.
|
|
191567
|
+
*/
|
|
191568
|
+
sourceTarDigest: string$1().optional(),
|
|
191407
191569
|
/** URL or file:// path to pre-built dependency tarball. If present, agent extracts to .kici/node_modules/ instead of running install. */
|
|
191408
191570
|
depsUrl: string$1().optional(),
|
|
191409
191571
|
/** SHA-256 hash of the dependency tarball for integrity verification. */
|
|
@@ -191412,6 +191574,22 @@ var init_orchestrator_agent = __esmMin((() => {
|
|
|
191412
191574
|
requestId: string$1().optional(),
|
|
191413
191575
|
/** Base64-encoded X25519 public key for the workflow run (for encrypting secret outputs). */
|
|
191414
191576
|
runPublicKey: string$1().optional(),
|
|
191577
|
+
/**
|
|
191578
|
+
* The orchestrator's own view of the build, for a provenance statement the
|
|
191579
|
+
* agent has to freeze before its identity token exists (the deferred path).
|
|
191580
|
+
*
|
|
191581
|
+
* Every field is what `buildIdTokenClaims` derives from the run row, so a
|
|
191582
|
+
* frozen statement built from this is field-for-field what a live mint
|
|
191583
|
+
* would have produced — and the server can therefore cross-check it. The
|
|
191584
|
+
* agent's local guess is NOT: the job's checkout `ref` is a pull request's
|
|
191585
|
+
* HEAD branch where the claim is the BASE branch, and `workflowRef` here is
|
|
191586
|
+
* the `<name>@<sha>` claim rather than a global workflow's clone ref.
|
|
191587
|
+
*
|
|
191588
|
+
* Additive and optional: an older orchestrator omits it and the agent falls
|
|
191589
|
+
* back to its local guess. That fallback statement fails the capture
|
|
191590
|
+
* cross-check, so the defer is dropped rather than stored unchecked.
|
|
191591
|
+
*/
|
|
191592
|
+
provenanceContext: provenanceContextSchema.optional(),
|
|
191415
191593
|
/** Plain outputs from upstream jobs (keyed by job name, then by step name). Populated for downstream jobs with `needs` dependencies. */
|
|
191416
191594
|
upstreamJobOutputs: record(string$1(), record(string$1(), unknown())).optional(),
|
|
191417
191595
|
/** Terminal status of each upstream job (keyed by job name; per-child for fan-out). Powers `ctx.needs.<job>.status`. */
|
|
@@ -191772,7 +191950,18 @@ var init_orchestrator_agent = __esmMin((() => {
|
|
|
191772
191950
|
* and hashed it by the time it asks. Optional so an older agent that omits it
|
|
191773
191951
|
* still gets a usable (lockfile-keyed) URL during a mixed-version rollout.
|
|
191774
191952
|
*/
|
|
191775
|
-
depsHash: string$1().optional()
|
|
191953
|
+
depsHash: string$1().optional(),
|
|
191954
|
+
/**
|
|
191955
|
+
* SHA-256 of the source tarball about to be uploaded. Source uploads only.
|
|
191956
|
+
*
|
|
191957
|
+
* The source tarball is stored under its own content hash, so the
|
|
191958
|
+
* orchestrator needs it to sign the upload URL — the agent has already packed
|
|
191959
|
+
* and hashed it by the time it asks. Optional so an older agent that omits it
|
|
191960
|
+
* still gets a usable URL during a mixed-version rollout.
|
|
191961
|
+
*/
|
|
191962
|
+
sourceTarDigest: string$1().optional(),
|
|
191963
|
+
/** In-repo `workspace:` sibling closure digest; part of the dep pointer key. */
|
|
191964
|
+
siblingsDigest: string$1().optional()
|
|
191776
191965
|
});
|
|
191777
191966
|
cacheUploadResponseSchema = object({
|
|
191778
191967
|
type: literal("cache.upload.response"),
|
|
@@ -191789,7 +191978,11 @@ var init_orchestrator_agent = __esmMin((() => {
|
|
|
191789
191978
|
platform: string$1(),
|
|
191790
191979
|
arch: string$1(),
|
|
191791
191980
|
/** SHA-256 hash of the dependency tarball for integrity verification. Only present for deps uploads. */
|
|
191792
|
-
depsHash: string$1().optional()
|
|
191981
|
+
depsHash: string$1().optional(),
|
|
191982
|
+
/** SHA-256 of the source tarball's own bytes. Only present for source uploads. */
|
|
191983
|
+
sourceTarDigest: string$1().optional(),
|
|
191984
|
+
/** In-repo `workspace:` sibling closure digest; part of the dep pointer key. */
|
|
191985
|
+
siblingsDigest: string$1().optional()
|
|
191793
191986
|
});
|
|
191794
191987
|
cacheUserRestoreRequestSchema = object({
|
|
191795
191988
|
type: literal("cache.user.restore.request"),
|
|
@@ -191869,8 +192062,12 @@ var init_orchestrator_agent = __esmMin((() => {
|
|
|
191869
192062
|
subjectName: string$1(),
|
|
191870
192063
|
/** Primary subject digest (lowercase hex). */
|
|
191871
192064
|
subjectDigest: string$1(),
|
|
191872
|
-
/**
|
|
191873
|
-
audience
|
|
192065
|
+
/**
|
|
192066
|
+
* Requested token audience for the later mint. Bounded to match the LIVE
|
|
192067
|
+
* mint's `oidcTokenRequestParamsSchema` — the two are the same
|
|
192068
|
+
* agent-supplied value and had no reason to differ.
|
|
192069
|
+
*/
|
|
192070
|
+
audience: string$1().min(1).max(255),
|
|
191874
192071
|
/** Bundle media type. */
|
|
191875
192072
|
mediaType: string$1(),
|
|
191876
192073
|
/** SHA-256 of the frozen DSSE statement payload — the later-mint binding. */
|
|
@@ -202296,6 +202493,20 @@ var init_plan_type = __esmMin((() => {
|
|
|
202296
202493
|
PlanType.exclude(["free"]);
|
|
202297
202494
|
PlanType.options;
|
|
202298
202495
|
PlanType.enum.free, PlanType.enum.pro, PlanType.enum.team, PlanType.enum.business;
|
|
202496
|
+
})), StripeSubscriptionStatus;
|
|
202497
|
+
var init_subscription_status = __esmMin((() => {
|
|
202498
|
+
init_zod();
|
|
202499
|
+
StripeSubscriptionStatus = _enum([
|
|
202500
|
+
"incomplete",
|
|
202501
|
+
"incomplete_expired",
|
|
202502
|
+
"trialing",
|
|
202503
|
+
"active",
|
|
202504
|
+
"past_due",
|
|
202505
|
+
"canceled",
|
|
202506
|
+
"unpaid",
|
|
202507
|
+
"paused"
|
|
202508
|
+
]);
|
|
202509
|
+
StripeSubscriptionStatus.enum.active, StripeSubscriptionStatus.enum.trialing, StripeSubscriptionStatus.enum.past_due;
|
|
202299
202510
|
})), InfraAlertType;
|
|
202300
202511
|
var init_infra_alert = __esmMin((() => {
|
|
202301
202512
|
init_zod();
|
|
@@ -202314,6 +202525,7 @@ var init_dist$1 = __esmMin((() => {
|
|
|
202314
202525
|
init_check_mode();
|
|
202315
202526
|
init_source_origin();
|
|
202316
202527
|
init_attestation_origin();
|
|
202528
|
+
init_id_token_event_claims();
|
|
202317
202529
|
init_common();
|
|
202318
202530
|
init_actor();
|
|
202319
202531
|
init_pat_kind();
|
|
@@ -202328,11 +202540,13 @@ var init_dist$1 = __esmMin((() => {
|
|
|
202328
202540
|
init_run_events();
|
|
202329
202541
|
init_event_log();
|
|
202330
202542
|
init_deployment_identity();
|
|
202543
|
+
init_config_paths();
|
|
202331
202544
|
init_source_registration();
|
|
202332
202545
|
init_scaler_backend_type();
|
|
202333
202546
|
init_types$4();
|
|
202334
202547
|
init_presentation();
|
|
202335
202548
|
init_types$3();
|
|
202549
|
+
init_regex_flags();
|
|
202336
202550
|
init_labels_match();
|
|
202337
202551
|
init_concurrency_strategy();
|
|
202338
202552
|
init_inventory();
|
|
@@ -202374,6 +202588,7 @@ var init_dist$1 = __esmMin((() => {
|
|
|
202374
202588
|
init_expand$1();
|
|
202375
202589
|
init_name();
|
|
202376
202590
|
init_plan_type();
|
|
202591
|
+
init_subscription_status();
|
|
202377
202592
|
init_infra_alert();
|
|
202378
202593
|
}));
|
|
202379
202594
|
//#endregion
|
|
@@ -203445,6 +203660,25 @@ var init_outputs = __esmMin((() => {
|
|
|
203445
203660
|
function assertSecretName(value, field, subject = "git credential") {
|
|
203446
203661
|
if (value.startsWith("-----BEGIN") || /^gh[pousr]_/.test(value) || value.startsWith("github_pat_")) throw new Error(`${subject} '${field}' looks like the credential itself, not the name of a secret holding it. Store it with \`kici-admin secret set\` and name it here.`);
|
|
203447
203662
|
}
|
|
203663
|
+
/**
|
|
203664
|
+
* Every `<name>Secret` field in a credential map names a secret in qualified
|
|
203665
|
+
* `<context>:<secret-name>` form, and never carries the material itself.
|
|
203666
|
+
*
|
|
203667
|
+
* `subject` is the declaring surface as the author wrote it — `job('build')` or
|
|
203668
|
+
* `dynamicJob('reports')` — so a message names the call the author can find.
|
|
203669
|
+
* A `<name>Value` sibling is material by design and is not checked here.
|
|
203670
|
+
*/
|
|
203671
|
+
function validateGitCredentials(subject, credentials) {
|
|
203672
|
+
for (const [alias, ref] of Object.entries(credentials)) {
|
|
203673
|
+
const bag = ref;
|
|
203674
|
+
for (const [field, value] of Object.entries(bag)) {
|
|
203675
|
+
if (!field.endsWith("Secret") || typeof value !== "string") continue;
|
|
203676
|
+
assertSecretName(value, `${alias}.${field}`);
|
|
203677
|
+
const idx = value.indexOf(":");
|
|
203678
|
+
if (idx <= 0 || idx >= value.length - 1 || value.slice(idx + 1).includes(":")) throw new Error(`${subject}: gitCredentials.${alias}.${field} must use qualified <context>:<secret-name> syntax (got: ${value})`);
|
|
203679
|
+
}
|
|
203680
|
+
}
|
|
203681
|
+
}
|
|
203448
203682
|
var init_git_types = __esmMin((() => {}));
|
|
203449
203683
|
//#endregion
|
|
203450
203684
|
//#region ../sdk/dist/job.js
|
|
@@ -203497,7 +203731,7 @@ function job(nameOrOptions, maybeOptions) {
|
|
|
203497
203731
|
if (options.runsOn !== void 0 && options.runsOnAll !== void 0) throw new Error(`job('${name}'): runsOn and runsOnAll are mutually exclusive`);
|
|
203498
203732
|
if (options.container !== void 0) validateContainerImageSource(name, options.container);
|
|
203499
203733
|
if (options.container && typeof options.container === "object" && options.container.auth) validateContainerAuth(name, options.container.auth);
|
|
203500
|
-
if (options.gitCredentials) validateGitCredentials(name
|
|
203734
|
+
if (options.gitCredentials) validateGitCredentials(`job('${name}')`, options.gitCredentials);
|
|
203501
203735
|
if (options.invoke === void 0 && options.runsOn === void 0 && options.runsOnAll === void 0) throw new Error(`job('${name}'): one of runsOn or runsOnAll is required`);
|
|
203502
203736
|
if (options.onUnreachable !== void 0 && options.runsOnAll === void 0) console.warn(`[kici] job('${name}'): onUnreachable is ignored without runsOnAll`);
|
|
203503
203737
|
if (options.includeUninitialized !== void 0 && options.runsOnAll === void 0) console.warn(`[kici] job('${name}'): includeUninitialized is ignored without runsOnAll`);
|
|
@@ -203615,17 +203849,6 @@ function validateContainerAuth(name, auth) {
|
|
|
203615
203849
|
if (idx <= 0 || idx >= value.length - 1 || value.slice(idx + 1).includes(":")) throw new Error(`job('${name}'): container.auth.${field} must use qualified <context>:<secret-name> syntax (got: ${value})`);
|
|
203616
203850
|
}
|
|
203617
203851
|
}
|
|
203618
|
-
function validateGitCredentials(name, credentials) {
|
|
203619
|
-
for (const [alias, ref] of Object.entries(credentials)) {
|
|
203620
|
-
const bag = ref;
|
|
203621
|
-
for (const [field, value] of Object.entries(bag)) {
|
|
203622
|
-
if (!field.endsWith("Secret") || typeof value !== "string") continue;
|
|
203623
|
-
assertSecretName(value, `${alias}.${field}`);
|
|
203624
|
-
const idx = value.indexOf(":");
|
|
203625
|
-
if (idx <= 0 || idx >= value.length - 1 || value.slice(idx + 1).includes(":")) throw new Error(`job('${name}'): gitCredentials.${alias}.${field} must use qualified <context>:<secret-name> syntax (got: ${value})`);
|
|
203626
|
-
}
|
|
203627
|
-
}
|
|
203628
|
-
}
|
|
203629
203852
|
var init_job = __esmMin((() => {
|
|
203630
203853
|
init_outputs();
|
|
203631
203854
|
init_git_types();
|
|
@@ -204124,12 +204347,17 @@ var init_invoke = __esmMin((() => {
|
|
|
204124
204347
|
/**
|
|
204125
204348
|
* Convert a string or RegExp to a BranchPattern.
|
|
204126
204349
|
* Strings become glob patterns, RegExp becomes regex patterns.
|
|
204350
|
+
*
|
|
204351
|
+
* `g` and `y` are dropped from the author's flags: the matcher memoizes one
|
|
204352
|
+
* `RegExp` per (flags, pattern), and a sticky instance carries `lastIndex`
|
|
204353
|
+
* between calls — so `/^release-\d+/g` would match `release-1`, miss
|
|
204354
|
+
* `release-2`, and match `release-3`, silently skipping every second push.
|
|
204127
204355
|
*/
|
|
204128
204356
|
function toBranchPattern(input) {
|
|
204129
204357
|
if (input instanceof RegExp) return {
|
|
204130
204358
|
type: "regex",
|
|
204131
204359
|
pattern: input.source,
|
|
204132
|
-
flags: input.flags
|
|
204360
|
+
flags: normalizeRegexFlags(input.flags)
|
|
204133
204361
|
};
|
|
204134
204362
|
return {
|
|
204135
204363
|
type: "glob",
|
|
@@ -204144,6 +204372,7 @@ function asArray(value) {
|
|
|
204144
204372
|
}
|
|
204145
204373
|
var DEFAULT_PR_EVENTS;
|
|
204146
204374
|
var init_types$2 = __esmMin((() => {
|
|
204375
|
+
init_dist$1();
|
|
204147
204376
|
DEFAULT_PR_EVENTS = [
|
|
204148
204377
|
"opened",
|
|
204149
204378
|
"synchronize",
|
|
@@ -204283,12 +204512,16 @@ var init_tag = __esmMin((() => {
|
|
|
204283
204512
|
/**
|
|
204284
204513
|
* Convert a string or RegExp to a BodyMatchPattern.
|
|
204285
204514
|
* Strings become glob patterns, RegExp becomes regex patterns.
|
|
204515
|
+
*
|
|
204516
|
+
* `g` and `y` are dropped for the same reason as `toBranchPattern`: the reader
|
|
204517
|
+
* memoizes the compiled instance, so a sticky one alternates verdicts between
|
|
204518
|
+
* comments.
|
|
204286
204519
|
*/
|
|
204287
204520
|
function toBodyMatchPattern(input) {
|
|
204288
204521
|
if (input instanceof RegExp) return {
|
|
204289
204522
|
type: "regex",
|
|
204290
204523
|
pattern: input.source,
|
|
204291
|
-
flags: input.flags
|
|
204524
|
+
flags: normalizeRegexFlags(input.flags)
|
|
204292
204525
|
};
|
|
204293
204526
|
return {
|
|
204294
204527
|
type: "glob",
|
|
@@ -204322,6 +204555,7 @@ function comment(config) {
|
|
|
204322
204555
|
}
|
|
204323
204556
|
var init_comment = __esmMin((() => {
|
|
204324
204557
|
init_types$2();
|
|
204558
|
+
init_dist$1();
|
|
204325
204559
|
}));
|
|
204326
204560
|
//#endregion
|
|
204327
204561
|
//#region ../sdk/dist/triggers/review.js
|
|
@@ -204871,6 +205105,27 @@ function defineDispatchInputs(map) {
|
|
|
204871
205105
|
}
|
|
204872
205106
|
var init_dispatch_inputs = __esmMin((() => {}));
|
|
204873
205107
|
//#endregion
|
|
205108
|
+
//#region ../sdk/dist/triggers/index.js
|
|
205109
|
+
var init_triggers = __esmMin((() => {
|
|
205110
|
+
init_types$2();
|
|
205111
|
+
init_pr();
|
|
205112
|
+
init_push();
|
|
205113
|
+
init_tag();
|
|
205114
|
+
init_comment();
|
|
205115
|
+
init_review();
|
|
205116
|
+
init_review_comment();
|
|
205117
|
+
init_release();
|
|
205118
|
+
init_dispatch();
|
|
205119
|
+
init_create();
|
|
205120
|
+
init_delete();
|
|
205121
|
+
init_status();
|
|
205122
|
+
init_workflow_run();
|
|
205123
|
+
init_fork();
|
|
205124
|
+
init_star();
|
|
205125
|
+
init_watch();
|
|
205126
|
+
init_webhook();
|
|
205127
|
+
}));
|
|
205128
|
+
//#endregion
|
|
204874
205129
|
//#region ../sdk/dist/hooks/index.js
|
|
204875
205130
|
/**
|
|
204876
205131
|
* Normalize HookInput to extract run function and optional timeout.
|
|
@@ -205225,12 +205480,16 @@ function isDynamicJobFn(item) {
|
|
|
205225
205480
|
* Two forms:
|
|
205226
205481
|
* - **Function form** (event-only): `dynamicJob('shards', async ({ ctx }) => [...])`.
|
|
205227
205482
|
* Dispatched at webhook time; deterministic from `ctx.event` alone.
|
|
205228
|
-
* - **Options form
|
|
205229
|
-
*
|
|
205230
|
-
* evaluated with the upstreams' frozen outputs
|
|
205483
|
+
* - **Options form**: `dynamicJob('reports', { needs, generate, gitCredentials })`.
|
|
205484
|
+
* With `needs` it is result-aware — deferred until every job/group in `needs`
|
|
205485
|
+
* completes, then `generate` is evaluated with the upstreams' frozen outputs
|
|
205486
|
+
* available as `ctx.needs`. `needs` is optional: without it the generator is
|
|
205487
|
+
* dispatched at webhook time, as the function form is. `gitCredentials`
|
|
205488
|
+
* declares the named credentials every generated job inherits, and only the
|
|
205489
|
+
* options form can carry it.
|
|
205231
205490
|
*
|
|
205232
205491
|
* @param groupName - The group name (must match what static jobs reference)
|
|
205233
|
-
* @param fnOrConfig - The generator function, or a
|
|
205492
|
+
* @param fnOrConfig - The generator function, or a `{ needs, generate, gitCredentials }` config
|
|
205234
205493
|
*/
|
|
205235
205494
|
function dynamicJob(groupName, fnOrConfig) {
|
|
205236
205495
|
const isConfig = typeof fnOrConfig !== "function";
|
|
@@ -205239,10 +205498,17 @@ function dynamicJob(groupName, fnOrConfig) {
|
|
|
205239
205498
|
value: groupName,
|
|
205240
205499
|
enumerable: false
|
|
205241
205500
|
});
|
|
205242
|
-
if (isConfig) Object.defineProperty(tagged, DYNAMIC_JOB_NEEDS_TAG, {
|
|
205501
|
+
if (isConfig && fnOrConfig.needs) Object.defineProperty(tagged, DYNAMIC_JOB_NEEDS_TAG, {
|
|
205243
205502
|
value: fnOrConfig.needs,
|
|
205244
205503
|
enumerable: false
|
|
205245
205504
|
});
|
|
205505
|
+
if (isConfig && fnOrConfig.gitCredentials) {
|
|
205506
|
+
validateGitCredentials(`dynamicJob('${groupName}')`, fnOrConfig.gitCredentials);
|
|
205507
|
+
Object.defineProperty(tagged, DYNAMIC_JOB_GIT_CREDENTIALS_TAG, {
|
|
205508
|
+
value: fnOrConfig.gitCredentials,
|
|
205509
|
+
enumerable: false
|
|
205510
|
+
});
|
|
205511
|
+
}
|
|
205246
205512
|
return tagged;
|
|
205247
205513
|
}
|
|
205248
205514
|
/**
|
|
@@ -205259,10 +205525,19 @@ function getDynamicJobGroup(fn) {
|
|
|
205259
205525
|
function getDynamicJobNeeds(fn) {
|
|
205260
205526
|
return fn[DYNAMIC_JOB_NEEDS_TAG];
|
|
205261
205527
|
}
|
|
205262
|
-
|
|
205528
|
+
/**
|
|
205529
|
+
* Read the named git credentials a generator declared.
|
|
205530
|
+
* Returns undefined for the bare function form, which cannot declare any.
|
|
205531
|
+
*/
|
|
205532
|
+
function getDynamicJobGitCredentials(fn) {
|
|
205533
|
+
return fn[DYNAMIC_JOB_GIT_CREDENTIALS_TAG];
|
|
205534
|
+
}
|
|
205535
|
+
var DYNAMIC_JOB_GROUP_TAG, DYNAMIC_JOB_NEEDS_TAG, DYNAMIC_JOB_GIT_CREDENTIALS_TAG;
|
|
205263
205536
|
var init_types$1 = __esmMin((() => {
|
|
205537
|
+
init_git_types();
|
|
205264
205538
|
DYNAMIC_JOB_GROUP_TAG = Symbol.for("kici:dynamicJobGroup");
|
|
205265
205539
|
DYNAMIC_JOB_NEEDS_TAG = Symbol.for("kici:dynamicJobNeeds");
|
|
205540
|
+
DYNAMIC_JOB_GIT_CREDENTIALS_TAG = Symbol.for("kici:dynamicJobGitCredentials");
|
|
205266
205541
|
}));
|
|
205267
205542
|
//#endregion
|
|
205268
205543
|
//#region ../sdk/dist/needs-context.js
|
|
@@ -205579,6 +205854,7 @@ var dist_exports = /* @__PURE__ */ __exportAll({
|
|
|
205579
205854
|
CacheSpecSchema: () => CacheSpecSchema,
|
|
205580
205855
|
ChangedFilesUnavailableError: () => ChangedFilesUnavailableError,
|
|
205581
205856
|
DYNAMIC_GROUP_TAG: () => DYNAMIC_GROUP_TAG,
|
|
205857
|
+
DYNAMIC_JOB_GIT_CREDENTIALS_TAG: () => DYNAMIC_JOB_GIT_CREDENTIALS_TAG,
|
|
205582
205858
|
DYNAMIC_JOB_GROUP_TAG: () => DYNAMIC_JOB_GROUP_TAG,
|
|
205583
205859
|
DYNAMIC_JOB_NEEDS_TAG: () => DYNAMIC_JOB_NEEDS_TAG,
|
|
205584
205860
|
SCALER_EVENT_NAMES: () => SCALER_EVENT_NAMES,
|
|
@@ -205617,6 +205893,7 @@ var dist_exports = /* @__PURE__ */ __exportAll({
|
|
|
205617
205893
|
flattenStepInputs: () => flattenStepInputs,
|
|
205618
205894
|
fork: () => fork,
|
|
205619
205895
|
genericWebhook: () => genericWebhook,
|
|
205896
|
+
getDynamicJobGitCredentials: () => getDynamicJobGitCredentials,
|
|
205620
205897
|
getDynamicJobGroup: () => getDynamicJobGroup,
|
|
205621
205898
|
getDynamicJobNeeds: () => getDynamicJobNeeds,
|
|
205622
205899
|
getJobOutputsMap: () => getJobOutputsMap,
|
|
@@ -205725,6 +206002,7 @@ var init_dist = __esmMin((() => {
|
|
|
205725
206002
|
init_schedule();
|
|
205726
206003
|
init_lifecycle();
|
|
205727
206004
|
init_dispatch_inputs();
|
|
206005
|
+
init_triggers();
|
|
205728
206006
|
init_hooks();
|
|
205729
206007
|
init_rule();
|
|
205730
206008
|
init_context();
|
|
@@ -205745,6 +206023,21 @@ var init_dist = __esmMin((() => {
|
|
|
205745
206023
|
init_zod();
|
|
205746
206024
|
}));
|
|
205747
206025
|
//#endregion
|
|
206026
|
+
//#region ../sdk/dist/internal.js
|
|
206027
|
+
init_dist();
|
|
206028
|
+
init_api_types();
|
|
206029
|
+
init_approval();
|
|
206030
|
+
init_cache_types();
|
|
206031
|
+
init_filter_context();
|
|
206032
|
+
init_outputs();
|
|
206033
|
+
init_context();
|
|
206034
|
+
init_evaluator();
|
|
206035
|
+
init_rules();
|
|
206036
|
+
init_needs_context();
|
|
206037
|
+
init_secrets();
|
|
206038
|
+
init_expand();
|
|
206039
|
+
init_matrix();
|
|
206040
|
+
//#endregion
|
|
205748
206041
|
//#region ../../node_modules/.pnpm/jose@6.2.10/node_modules/jose/dist/webapi/lib/buffer_utils.js
|
|
205749
206042
|
const encoder = new TextEncoder();
|
|
205750
206043
|
const decoder = new TextDecoder();
|
|
@@ -206239,13 +206532,21 @@ async function generateKeyPair(alg, options) {
|
|
|
206239
206532
|
}
|
|
206240
206533
|
//#endregion
|
|
206241
206534
|
//#region ../engine/dist/provenance/statement-hash.js
|
|
206242
|
-
init_dist();
|
|
206243
206535
|
/**
|
|
206244
|
-
* Lowercase-hex SHA-256 of the DSSE statement payload bytes.
|
|
206245
|
-
*
|
|
206246
|
-
*
|
|
206247
|
-
*
|
|
206248
|
-
*
|
|
206536
|
+
* Lowercase-hex SHA-256 of the DSSE statement payload bytes.
|
|
206537
|
+
*
|
|
206538
|
+
* One of the two bindings a deferred OIDC mint commits to: the later token
|
|
206539
|
+
* carries this hash as a claim, so the identity cannot be re-bound to a
|
|
206540
|
+
* different frozen statement at retry time.
|
|
206541
|
+
*
|
|
206542
|
+
* It is a binding, not a substitute for the build-context cross-check. The
|
|
206543
|
+
* verifier requires BOTH for a non-live origin — the hash proves the statement
|
|
206544
|
+
* has not been swapped, and the cross-check proves the statement agrees with
|
|
206545
|
+
* the run the token names. A hash-only rule verified a bundle whose statement
|
|
206546
|
+
* claimed a release SHA the build never touched.
|
|
206547
|
+
*
|
|
206548
|
+
* Browser-safe: `crypto.subtle` only, so the verifier (dashboard + CLI) can
|
|
206549
|
+
* recompute it.
|
|
206249
206550
|
*/
|
|
206250
206551
|
async function computeStatementHash(payload) {
|
|
206251
206552
|
const digest = await crypto.subtle.digest("SHA-256", payload);
|
|
@@ -206407,7 +206708,12 @@ inTotoStatementSchema.extend({
|
|
|
206407
206708
|
* for a deferred attestation (no minted identity token yet). Marks
|
|
206408
206709
|
* `attestationOrigin: 'deferred'` in the internal parameters. The caller
|
|
206409
206710
|
* DSSE-signs the returned statement immediately and computes its statement hash
|
|
206410
|
-
* — the
|
|
206711
|
+
* — one of the two bindings the later OIDC mint commits to.
|
|
206712
|
+
*
|
|
206713
|
+
* Emits the same fields `buildProvenanceStatement` emits, so a statement frozen
|
|
206714
|
+
* from an orchestrator-supplied context is field-for-field what a live mint
|
|
206715
|
+
* would have produced. That is what lets the orchestrator cross-check the
|
|
206716
|
+
* statement against its own run row before signing anything that commits to it.
|
|
206411
206717
|
*/
|
|
206412
206718
|
function buildLocalProvenanceStatement(input) {
|
|
206413
206719
|
const c = input.context;
|
|
@@ -206421,11 +206727,14 @@ function buildLocalProvenanceStatement(input) {
|
|
|
206421
206727
|
predicate: {
|
|
206422
206728
|
buildDefinition: {
|
|
206423
206729
|
buildType: KICI_WORKFLOW_BUILD_TYPE,
|
|
206424
|
-
externalParameters: {
|
|
206425
|
-
|
|
206426
|
-
|
|
206427
|
-
|
|
206428
|
-
|
|
206730
|
+
externalParameters: {
|
|
206731
|
+
workflow: {
|
|
206732
|
+
repository: c.repository,
|
|
206733
|
+
ref: c.ref,
|
|
206734
|
+
path: c.workflowRef
|
|
206735
|
+
},
|
|
206736
|
+
...c.provider ? { provider: c.provider } : {}
|
|
206737
|
+
},
|
|
206429
206738
|
internalParameters: {
|
|
206430
206739
|
...c.sha ? { commit: c.sha } : {},
|
|
206431
206740
|
runId: c.runId,
|
|
@@ -206437,7 +206746,7 @@ function buildLocalProvenanceStatement(input) {
|
|
|
206437
206746
|
},
|
|
206438
206747
|
runDetails: {
|
|
206439
206748
|
builder: {
|
|
206440
|
-
id: `${c.issuer}/orchestrator
|
|
206749
|
+
id: `${c.issuer}/orchestrator/${c.orchestratorId ?? "unknown"}`,
|
|
206441
206750
|
version: input.builderVersions
|
|
206442
206751
|
},
|
|
206443
206752
|
metadata: {
|
|
@@ -210720,15 +211029,6 @@ function createArtifactsApi(workDir, transport, roots) {
|
|
|
210720
211029
|
}
|
|
210721
211030
|
//#endregion
|
|
210722
211031
|
//#region src/execution/sandbox/log-masker.ts
|
|
210723
|
-
/**
|
|
210724
|
-
* Secret value masking for log lines.
|
|
210725
|
-
*
|
|
210726
|
-
* Replaces all occurrences of registered secret values with '***' in log output.
|
|
210727
|
-
* Used by the workflow runner to prevent secret leaks in IPC log messages.
|
|
210728
|
-
*
|
|
210729
|
-
* Performance: Builds a single combined regex from all secret values, so each
|
|
210730
|
-
* log line is scanned in a single pass (not O(secrets * lines)).
|
|
210731
|
-
*/
|
|
210732
211032
|
/** Minimum length for a secret value to be maskable (avoids false positives). */
|
|
210733
211033
|
const MIN_MASK_LENGTH = 3;
|
|
210734
211034
|
/**
|
|
@@ -210759,18 +211059,38 @@ var LogMasker = class {
|
|
|
210759
211059
|
* Authorization: Basic headers, base64-encoded config values).
|
|
210760
211060
|
* Values are sorted by length descending so longer values are matched first
|
|
210761
211061
|
* (prevents partial masking when one secret is a substring of another).
|
|
211062
|
+
*
|
|
211063
|
+
* Multi-line values additionally register each of their individual lines.
|
|
211064
|
+
* Log output is split into lines before it reaches the masker, so a value
|
|
211065
|
+
* containing a newline can never match as a whole — a PEM private key or a
|
|
211066
|
+
* kubeconfig would otherwise stream in clear text. Two consequences of the
|
|
211067
|
+
* per-line registration are deliberate:
|
|
211068
|
+
*
|
|
211069
|
+
* - `MIN_MASK_LENGTH` is 3, so short structural lines of a structured secret
|
|
211070
|
+
* are registered too. A `---` YAML separator or a bare `{` from a
|
|
211071
|
+
* service-account JSON is masked wherever it appears in that job's logs.
|
|
211072
|
+
* - PEM header and footer lines (`-----BEGIN OPENSSH PRIVATE KEY-----`) are
|
|
211073
|
+
* not secret on their own and are masked as a side effect.
|
|
211074
|
+
*
|
|
211075
|
+
* Both are strictly safer than leaking the body, and no heuristic separates a
|
|
211076
|
+
* structural line from a body line without risking the reverse mistake.
|
|
210762
211077
|
*/
|
|
210763
211078
|
registerSecrets(secrets) {
|
|
210764
211079
|
const seen = /* @__PURE__ */ new Set();
|
|
210765
211080
|
const values = [];
|
|
210766
|
-
|
|
210767
|
-
seen.
|
|
210768
|
-
|
|
210769
|
-
|
|
211081
|
+
const add = (candidate) => {
|
|
211082
|
+
if (candidate.length < MIN_MASK_LENGTH || seen.has(candidate)) return;
|
|
211083
|
+
seen.add(candidate);
|
|
211084
|
+
values.push(candidate);
|
|
211085
|
+
const b64 = Buffer.from(candidate).toString("base64");
|
|
210770
211086
|
if (b64.length >= MIN_MASK_LENGTH && !seen.has(b64)) {
|
|
210771
211087
|
seen.add(b64);
|
|
210772
211088
|
values.push(b64);
|
|
210773
211089
|
}
|
|
211090
|
+
};
|
|
211091
|
+
for (const value of Object.values(secrets)) {
|
|
211092
|
+
add(value);
|
|
211093
|
+
if (value.includes("\n")) for (const rawLine of value.split("\n")) add(rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine);
|
|
210774
211094
|
}
|
|
210775
211095
|
if (values.length === 0) {
|
|
210776
211096
|
this.pattern = null;
|
|
@@ -210796,6 +211116,57 @@ var LogMasker = class {
|
|
|
210796
211116
|
return this.pattern !== null;
|
|
210797
211117
|
}
|
|
210798
211118
|
};
|
|
211119
|
+
/**
|
|
211120
|
+
* Create a LogMasker initialized with all secret values from the request.
|
|
211121
|
+
*
|
|
211122
|
+
* Collects values from both flat secrets and all namespaced context secrets,
|
|
211123
|
+
* deduplicating before registration.
|
|
211124
|
+
*
|
|
211125
|
+
* Both the runner child and the agent-side fork runner build a masker from the
|
|
211126
|
+
* same request, so the crash tail the agent assembles from the child's stderr is
|
|
211127
|
+
* masked with the same value set the child used for its own log lines.
|
|
211128
|
+
*/
|
|
211129
|
+
function createSecretMasker(request) {
|
|
211130
|
+
const masker = new LogMasker();
|
|
211131
|
+
const allSecrets = {};
|
|
211132
|
+
if (request.secrets) Object.assign(allSecrets, request.secrets);
|
|
211133
|
+
if (request.namespacedSecrets) for (const contextSecrets of Object.values(request.namespacedSecrets)) Object.assign(allSecrets, contextSecrets);
|
|
211134
|
+
masker.registerSecrets(allSecrets);
|
|
211135
|
+
return masker;
|
|
211136
|
+
}
|
|
211137
|
+
/**
|
|
211138
|
+
* Mask every operator-visible text field of an outbound runner message.
|
|
211139
|
+
*
|
|
211140
|
+
* Each message type carrying free text is named here, so a new text-bearing
|
|
211141
|
+
* message type is a visible omission rather than a silent leak. `step.complete`
|
|
211142
|
+
* error text and the `job.complete` failure reason are persisted on the step and
|
|
211143
|
+
* run rows the dashboard renders, so they need the same masking `log.line` gets.
|
|
211144
|
+
*
|
|
211145
|
+
* Returns the message unchanged when no secrets are registered.
|
|
211146
|
+
*/
|
|
211147
|
+
function maskMessageText(msg, masker) {
|
|
211148
|
+
if (!masker.hasSecrets()) return msg;
|
|
211149
|
+
switch (msg.type) {
|
|
211150
|
+
case "log.line": return {
|
|
211151
|
+
...msg,
|
|
211152
|
+
line: masker.mask(msg.line)
|
|
211153
|
+
};
|
|
211154
|
+
case "step.complete": return msg.error ? {
|
|
211155
|
+
...msg,
|
|
211156
|
+
error: {
|
|
211157
|
+
...msg.error,
|
|
211158
|
+
message: masker.mask(msg.error.message)
|
|
211159
|
+
}
|
|
211160
|
+
} : msg;
|
|
211161
|
+
case "job.complete": {
|
|
211162
|
+
const masked = { ...msg };
|
|
211163
|
+
if (masked.error !== void 0) masked.error = masker.mask(masked.error);
|
|
211164
|
+
if (masked.droppedJobs) masked.droppedJobs = masked.droppedJobs.map((j) => masker.mask(j));
|
|
211165
|
+
return masked;
|
|
211166
|
+
}
|
|
211167
|
+
default: return msg;
|
|
211168
|
+
}
|
|
211169
|
+
}
|
|
210799
211170
|
//#endregion
|
|
210800
211171
|
//#region src/execution/sandbox/env-delta.ts
|
|
210801
211172
|
/**
|
|
@@ -210977,18 +211348,23 @@ async function executeHook(opts) {
|
|
|
210977
211348
|
step_type: `hook:${hookType}`
|
|
210978
211349
|
});
|
|
210979
211350
|
const startTime = Date.now();
|
|
211351
|
+
const abortController = new AbortController();
|
|
211352
|
+
let rejectTimeout = () => {};
|
|
211353
|
+
const timeoutRace = new Promise((_, reject) => {
|
|
211354
|
+
rejectTimeout = reject;
|
|
211355
|
+
});
|
|
211356
|
+
const timeoutId = setTimeout(() => {
|
|
211357
|
+
rejectTimeout(/* @__PURE__ */ new Error(`Hook '${normalized.name}' timed out after ${timeoutMs}ms`));
|
|
211358
|
+
abortController.abort();
|
|
211359
|
+
}, timeoutMs);
|
|
210980
211360
|
const mergedCtx = {
|
|
210981
211361
|
...stepContext,
|
|
210982
|
-
outcome
|
|
211362
|
+
outcome,
|
|
211363
|
+
signal: AbortSignal.any([...stepContext.signal ? [stepContext.signal] : [], abortController.signal]),
|
|
211364
|
+
...typeof stepContext.$ === "function" ? { $: stepContext.$({ signal: abortController.signal }) } : {}
|
|
210983
211365
|
};
|
|
210984
|
-
const abortController = new AbortController();
|
|
210985
|
-
const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);
|
|
210986
211366
|
try {
|
|
210987
|
-
await Promise.race([normalized.run(mergedCtx),
|
|
210988
|
-
abortController.signal.addEventListener("abort", () => {
|
|
210989
|
-
reject(/* @__PURE__ */ new Error(`Hook '${normalized.name}' timed out after ${timeoutMs}ms`));
|
|
210990
|
-
});
|
|
210991
|
-
})]);
|
|
211367
|
+
await Promise.race([normalized.run(mergedCtx), timeoutRace]);
|
|
210992
211368
|
clearTimeout(timeoutId);
|
|
210993
211369
|
sendIpc({
|
|
210994
211370
|
type: "step.complete",
|
|
@@ -211130,7 +211506,6 @@ async function runParallelGroup(node, opts) {
|
|
|
211130
211506
|
}
|
|
211131
211507
|
//#endregion
|
|
211132
211508
|
//#region src/execution/sandbox/step-loop.ts
|
|
211133
|
-
init_dist();
|
|
211134
211509
|
init_dist$1();
|
|
211135
211510
|
init_idempotency();
|
|
211136
211511
|
init_dist$4();
|
|
@@ -211257,12 +211632,12 @@ const STEP_FAILURE_LOG_MAX_CHARS = 8192;
|
|
|
211257
211632
|
* One consequence to know: a `$({ quiet: true })` command's output is kept out
|
|
211258
211633
|
* of the log by the `verbose` gate in `streaming-zx-log.ts`, but when such a
|
|
211259
211634
|
* command FAILS, zx packs its captured output into the thrown error's message —
|
|
211260
|
-
* which this function then writes to the log.
|
|
211261
|
-
*
|
|
211262
|
-
*
|
|
211263
|
-
*
|
|
211264
|
-
*
|
|
211265
|
-
* has never been told about is not.
|
|
211635
|
+
* which this function then writes to the log. Surfacing it is the whole point:
|
|
211636
|
+
* a quiet command that fails is exactly the failure an operator cannot
|
|
211637
|
+
* otherwise diagnose. The same text also travels on `step.complete` for the step
|
|
211638
|
+
* row the dashboard renders, and both copies pass the masker — `maskMessageText`
|
|
211639
|
+
* masks `step.complete.error.message` alongside `log.line`. Registered secret
|
|
211640
|
+
* values are masked; anything the masker has never been told about is not.
|
|
211266
211641
|
*/
|
|
211267
211642
|
function emitStepFailureLog(stepName, stepIndex, message, sendFn) {
|
|
211268
211643
|
const prefix = `[kici] Step '${stepName}' failed: `;
|
|
@@ -211299,16 +211674,29 @@ async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, output
|
|
|
211299
211674
|
});
|
|
211300
211675
|
const startTime = Date.now();
|
|
211301
211676
|
const abortController = new AbortController();
|
|
211302
|
-
|
|
211677
|
+
let timedOut = false;
|
|
211678
|
+
let rejectTimeout = () => {};
|
|
211679
|
+
const timeoutRace = new Promise((_, reject) => {
|
|
211680
|
+
rejectTimeout = reject;
|
|
211681
|
+
});
|
|
211682
|
+
const timeoutId = setTimeout(() => {
|
|
211683
|
+
timedOut = true;
|
|
211684
|
+
rejectTimeout(/* @__PURE__ */ new Error(`Step '${step.name}' timed out after ${timeoutMs}ms`));
|
|
211685
|
+
abortController.abort();
|
|
211686
|
+
opts.abortStep?.(stepIndex);
|
|
211687
|
+
}, timeoutMs);
|
|
211303
211688
|
const stepAbortSignal = opts.getStepAbortSignal?.(stepIndex);
|
|
211689
|
+
const stepPromise = runStepWithCheckMode(step, stepIndex, ctx, checkMode, sendFn, opts);
|
|
211690
|
+
let stepSettled = false;
|
|
211691
|
+
const stepSettledPromise = stepPromise.then(() => {
|
|
211692
|
+
stepSettled = true;
|
|
211693
|
+
}, () => {
|
|
211694
|
+
stepSettled = true;
|
|
211695
|
+
});
|
|
211304
211696
|
try {
|
|
211305
211697
|
const phase = await Promise.race([
|
|
211306
|
-
|
|
211307
|
-
|
|
211308
|
-
abortController.signal.addEventListener("abort", () => {
|
|
211309
|
-
reject(/* @__PURE__ */ new Error(`Step '${step.name}' timed out after ${timeoutMs}ms`));
|
|
211310
|
-
});
|
|
211311
|
-
}),
|
|
211698
|
+
stepPromise,
|
|
211699
|
+
timeoutRace,
|
|
211312
211700
|
new Promise((_, reject) => {
|
|
211313
211701
|
if (!jobDeadlineSignal) return;
|
|
211314
211702
|
if (jobDeadlineSignal.aborted) {
|
|
@@ -211325,7 +211713,10 @@ async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, output
|
|
|
211325
211713
|
reject(new StepCancelledError(step.name));
|
|
211326
211714
|
return;
|
|
211327
211715
|
}
|
|
211328
|
-
stepAbortSignal.addEventListener("abort", () =>
|
|
211716
|
+
stepAbortSignal.addEventListener("abort", () => {
|
|
211717
|
+
if (timedOut) return;
|
|
211718
|
+
reject(new StepCancelledError(step.name));
|
|
211719
|
+
});
|
|
211329
211720
|
})
|
|
211330
211721
|
]);
|
|
211331
211722
|
clearTimeout(timeoutId);
|
|
@@ -211357,6 +211748,15 @@ async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, output
|
|
|
211357
211748
|
clearTimeout(timeoutId);
|
|
211358
211749
|
const durationMs = Date.now() - startTime;
|
|
211359
211750
|
const error = e instanceof Error ? e : new Error(String(e));
|
|
211751
|
+
if (timedOut && !stepSettled) {
|
|
211752
|
+
await Promise.race([stepSettledPromise, delayUnref(STEP_ABORT_GRACE_MS)]);
|
|
211753
|
+
if (!stepSettled) sendFn({
|
|
211754
|
+
type: "log.line",
|
|
211755
|
+
stepIndex,
|
|
211756
|
+
line: `[timeout] Step '${step.name}' did not stop within ${STEP_ABORT_GRACE_MS}ms of its abort signal; continuing without it.`,
|
|
211757
|
+
stream: LogStream.enum.stderr
|
|
211758
|
+
});
|
|
211759
|
+
}
|
|
211360
211760
|
if (e instanceof StepCancelledError) {
|
|
211361
211761
|
const secretsAccessed = getSecretsAccessLog?.(stepIndex);
|
|
211362
211762
|
emitSecretMountEvents(getSecretMountRecords?.(stepIndex), stepIndex, sendFn);
|
|
@@ -211405,6 +211805,17 @@ async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, output
|
|
|
211405
211805
|
}
|
|
211406
211806
|
}
|
|
211407
211807
|
/**
|
|
211808
|
+
* How long a timed-out step is given to unwind after its abort signal fires,
|
|
211809
|
+
* before the loop reports that the step ignored it and moves on.
|
|
211810
|
+
*/
|
|
211811
|
+
const STEP_ABORT_GRACE_MS = 2e3;
|
|
211812
|
+
/** A timer-backed delay that never keeps the process alive on its own. */
|
|
211813
|
+
function delayUnref(ms) {
|
|
211814
|
+
return new Promise((resolve) => {
|
|
211815
|
+
setTimeout(resolve, ms).unref?.();
|
|
211816
|
+
});
|
|
211817
|
+
}
|
|
211818
|
+
/**
|
|
211408
211819
|
* Emit one `step.secret_mount` IPC event per `mountFile` / `exposeFile` call
|
|
211409
211820
|
* the step performed. Called from both the success and failure paths so the
|
|
211410
211821
|
* orchestrator's audit trail records every mount regardless of step outcome.
|
|
@@ -212847,10 +213258,12 @@ function redactNpmOutput(input, tokens) {
|
|
|
212847
213258
|
*
|
|
212848
213259
|
* `nodeLinker: node-modules` makes berry lay down a real `node_modules` tree
|
|
212849
213260
|
* (no PnP `.pnp.cjs`), so the agent's packer / restore / sibling-walk /
|
|
212850
|
-
* workflow-loader work unchanged. `enableScripts: false`
|
|
212851
|
-
*
|
|
212852
|
-
*
|
|
212853
|
-
* `--ignore-scripts`.
|
|
213261
|
+
* workflow-loader work unchanged. `enableScripts: false` keeps dependency
|
|
213262
|
+
* lifecycle scripts from running at all — for every install, not only one
|
|
213263
|
+
* against a private registry — the same security model as npm/pnpm/classic
|
|
213264
|
+
* `--ignore-scripts`. An operator opts back in with
|
|
213265
|
+
* `KICI_ALLOW_INSTALL_SCRIPTS=true`, which arrives here as
|
|
213266
|
+
* `ignoreScripts: false`.
|
|
212854
213267
|
*
|
|
212855
213268
|
* Reuses the same `ApplyNpmRegistryConfigArgs` / `ApplyNpmRegistryConfigResult`
|
|
212856
213269
|
* shapes as the npm overlay so `dep-installer` can pick either by flavor.
|
|
@@ -212899,8 +213312,8 @@ async function applyYarnrcBerryConfig(args) {
|
|
|
212899
213312
|
};
|
|
212900
213313
|
const tokenEnv = {};
|
|
212901
213314
|
const tokensForRedaction = [];
|
|
213315
|
+
if (args.ignoreScripts !== false) merged.enableScripts = false;
|
|
212902
213316
|
if (hasPrivateRegistry) {
|
|
212903
|
-
merged.enableScripts = false;
|
|
212904
213317
|
const npmScopes = { ...doc.npmScopes ?? {} };
|
|
212905
213318
|
for (let i = 0; i < registries.length; i++) {
|
|
212906
213319
|
const reg = registries[i];
|
|
@@ -213238,9 +213651,11 @@ function isAbsoluteRel(rel) {
|
|
|
213238
213651
|
* Security: the install runs with an isolated per-invocation cache/store
|
|
213239
213652
|
* directory to prevent cache poisoning across build jobs — a malicious
|
|
213240
213653
|
* package.json in one repo cannot taint the cache used by subsequent builds.
|
|
213241
|
-
* The
|
|
213242
|
-
*
|
|
213243
|
-
*
|
|
213654
|
+
* The install runs with `--ignore-scripts` for every package manager. A
|
|
213655
|
+
* lifecycle script in a committed `package.json` is customer code the agent
|
|
213656
|
+
* never agreed to execute: it would run wherever the install runs, which for a
|
|
213657
|
+
* step-child install is the process holding the job's secrets. Operators who
|
|
213658
|
+
* genuinely need it set `KICI_ALLOW_INSTALL_SCRIPTS=true` on the agent.
|
|
213244
213659
|
*/
|
|
213245
213660
|
init_tmp();
|
|
213246
213661
|
init_dist$2();
|
|
@@ -213278,9 +213693,11 @@ async function detectKiciYarnFlavor(repoRoot, kiciDir) {
|
|
|
213278
213693
|
* between build jobs; the directory is removed after installation.
|
|
213279
213694
|
*
|
|
213280
213695
|
* If `opts.npmRegistries` / `opts.installEnvSecrets` is provided, a job-scoped
|
|
213281
|
-
* `.kici/.npmrc` overlay is synthesized for the install
|
|
213282
|
-
*
|
|
213283
|
-
*
|
|
213696
|
+
* `.kici/.npmrc` overlay is synthesized for the install and restored in
|
|
213697
|
+
* `finally`.
|
|
213698
|
+
*
|
|
213699
|
+
* Lifecycle scripts are disabled for every package manager unless the operator
|
|
213700
|
+
* set `opts.allowInstallScripts`.
|
|
213284
213701
|
*
|
|
213285
213702
|
* @param kiciDir - Path to the `.kici/` directory containing package.json.
|
|
213286
213703
|
* @param opts - Optional registry / installEnv / repoRoot configuration.
|
|
@@ -213302,13 +213719,15 @@ async function installDeps(kiciDir, opts = {}) {
|
|
|
213302
213719
|
yarnFlavor
|
|
213303
213720
|
});
|
|
213304
213721
|
const startTime = Date.now();
|
|
213305
|
-
const
|
|
213722
|
+
const ignoreScripts = opts.allowInstallScripts !== true;
|
|
213723
|
+
const baseEnv = opts.baseEnv ?? process.env;
|
|
213306
213724
|
const isBerry = packageManager === PackageManager.Yarn && yarnFlavor === YarnFlavor.Berry;
|
|
213307
213725
|
const registryConfig = isBerry ? await applyYarnrcBerryConfig({
|
|
213308
213726
|
kiciDir,
|
|
213309
213727
|
npmRegistries: opts.npmRegistries,
|
|
213310
213728
|
installEnvSecrets: opts.installEnvSecrets,
|
|
213311
|
-
jobIdShort: opts.jobIdShort ?? "00000000"
|
|
213729
|
+
jobIdShort: opts.jobIdShort ?? "00000000",
|
|
213730
|
+
ignoreScripts
|
|
213312
213731
|
}) : await applyNpmRegistryConfig({
|
|
213313
213732
|
kiciDir,
|
|
213314
213733
|
npmRegistries: opts.npmRegistries,
|
|
@@ -213318,22 +213737,26 @@ async function installDeps(kiciDir, opts = {}) {
|
|
|
213318
213737
|
try {
|
|
213319
213738
|
if (packageManager === PackageManager.Pnpm) await runPnpmInstall({
|
|
213320
213739
|
kiciDir,
|
|
213321
|
-
|
|
213322
|
-
registryConfig
|
|
213740
|
+
ignoreScripts,
|
|
213741
|
+
registryConfig,
|
|
213742
|
+
baseEnv
|
|
213323
213743
|
});
|
|
213324
213744
|
else if (isBerry) await runYarnBerryInstall({
|
|
213325
213745
|
kiciDir,
|
|
213326
|
-
registryConfig
|
|
213746
|
+
registryConfig,
|
|
213747
|
+
baseEnv
|
|
213327
213748
|
});
|
|
213328
213749
|
else if (packageManager === PackageManager.Yarn) await runYarnInstall({
|
|
213329
213750
|
kiciDir,
|
|
213330
|
-
|
|
213331
|
-
registryConfig
|
|
213751
|
+
ignoreScripts,
|
|
213752
|
+
registryConfig,
|
|
213753
|
+
baseEnv
|
|
213332
213754
|
});
|
|
213333
213755
|
else await runNpmInstall({
|
|
213334
213756
|
kiciDir,
|
|
213335
|
-
|
|
213336
|
-
registryConfig
|
|
213757
|
+
ignoreScripts,
|
|
213758
|
+
registryConfig,
|
|
213759
|
+
baseEnv
|
|
213337
213760
|
});
|
|
213338
213761
|
} catch (e) {
|
|
213339
213762
|
const tokens = registryConfig.tokensForRedaction;
|
|
@@ -213343,8 +213766,8 @@ async function installDeps(kiciDir, opts = {}) {
|
|
|
213343
213766
|
} finally {
|
|
213344
213767
|
await registryConfig.cleanup();
|
|
213345
213768
|
}
|
|
213346
|
-
if (packageManager === PackageManager.Pnpm && await kiciHasLocalProtocolDeps(kiciDir)) await buildWorkspaceClosure(repoRoot);
|
|
213347
|
-
if (packageManager === PackageManager.Yarn) await buildYarnWorkspaceClosure(repoRoot, kiciDir, yarnFlavor);
|
|
213769
|
+
if (packageManager === PackageManager.Pnpm && await kiciHasLocalProtocolDeps(kiciDir)) await buildWorkspaceClosure(repoRoot, baseEnv);
|
|
213770
|
+
if (packageManager === PackageManager.Yarn) await buildYarnWorkspaceClosure(repoRoot, kiciDir, yarnFlavor, baseEnv);
|
|
213348
213771
|
const durationMs = Date.now() - startTime;
|
|
213349
213772
|
process.stderr.write(`[dep-installer:trace] install complete: ${durationMs}ms\n`);
|
|
213350
213773
|
logger$2.info("Deps installed inline", {
|
|
@@ -213352,20 +213775,26 @@ async function installDeps(kiciDir, opts = {}) {
|
|
|
213352
213775
|
durationMs
|
|
213353
213776
|
});
|
|
213354
213777
|
}
|
|
213355
|
-
/**
|
|
213356
|
-
|
|
213357
|
-
|
|
213778
|
+
/**
|
|
213779
|
+
* Build the Node binary directory onto PATH so spawned tools find `node`.
|
|
213780
|
+
*
|
|
213781
|
+
* `baseEnv` is the caller's declared environment for the subprocess. It
|
|
213782
|
+
* defaults to `process.env` because inside the runner child that IS the
|
|
213783
|
+
* sanitized job environment; an agent-process caller passes a sanitized base.
|
|
213784
|
+
*/
|
|
213785
|
+
function envWithNodeOnPath(extraEnv, nodeDir, baseEnv = process.env) {
|
|
213786
|
+
const { NODE_ENV: _NODE_ENV, ...restEnv } = baseEnv;
|
|
213358
213787
|
return {
|
|
213359
213788
|
...restEnv,
|
|
213360
213789
|
...extraEnv,
|
|
213361
|
-
PATH: `${nodeDir}${process.platform === "win32" ? ";" : ":"}${
|
|
213790
|
+
PATH: `${nodeDir}${process.platform === "win32" ? ";" : ":"}${restEnv.PATH ?? ""}`
|
|
213362
213791
|
};
|
|
213363
213792
|
}
|
|
213364
213793
|
/** Run `npm install` in `.kici/` with an isolated cache directory. */
|
|
213365
213794
|
async function runNpmInstall(args) {
|
|
213366
213795
|
const { npmCliPath, nodeExe, nodeDir } = resolveNpm();
|
|
213367
213796
|
const { path: cacheDir, cleanup } = await makeTempDir("npm-cache");
|
|
213368
|
-
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir);
|
|
213797
|
+
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir, args.baseEnv);
|
|
213369
213798
|
const buildArgs = (...prefix) => {
|
|
213370
213799
|
const a = [
|
|
213371
213800
|
...prefix,
|
|
@@ -213375,7 +213804,7 @@ async function runNpmInstall(args) {
|
|
|
213375
213804
|
"--no-audit",
|
|
213376
213805
|
"--no-fund"
|
|
213377
213806
|
];
|
|
213378
|
-
if (args.
|
|
213807
|
+
if (args.ignoreScripts) a.push("--ignore-scripts");
|
|
213379
213808
|
return a;
|
|
213380
213809
|
};
|
|
213381
213810
|
try {
|
|
@@ -213404,7 +213833,7 @@ async function runPnpmInstall(args) {
|
|
|
213404
213833
|
await assertPnpmAvailable();
|
|
213405
213834
|
const { nodeDir } = resolveNpm();
|
|
213406
213835
|
const { path: storeDir, cleanup } = await makeTempDir("pnpm-store");
|
|
213407
|
-
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir);
|
|
213836
|
+
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir, args.baseEnv);
|
|
213408
213837
|
const argv = [
|
|
213409
213838
|
"install",
|
|
213410
213839
|
`--config.store-dir=${storeDir}`,
|
|
@@ -213413,7 +213842,7 @@ async function runPnpmInstall(args) {
|
|
|
213413
213842
|
"--config.side-effects-cache=false",
|
|
213414
213843
|
PNPM_IGNORE_BUILD_GATE_ARG
|
|
213415
213844
|
];
|
|
213416
|
-
if (args.
|
|
213845
|
+
if (args.ignoreScripts) argv.push("--ignore-scripts");
|
|
213417
213846
|
try {
|
|
213418
213847
|
process.stderr.write(`[dep-installer:trace] running: pnpm ${argv.join(" ")}\n`);
|
|
213419
213848
|
await execFileAsync("pnpm", argv, {
|
|
@@ -213427,7 +213856,7 @@ async function runPnpmInstall(args) {
|
|
|
213427
213856
|
}
|
|
213428
213857
|
}
|
|
213429
213858
|
/** Pure: argv for `yarn install` with an isolated cache folder. */
|
|
213430
|
-
function buildYarnInstallArgs(cacheDir,
|
|
213859
|
+
function buildYarnInstallArgs(cacheDir, ignoreScripts) {
|
|
213431
213860
|
const a = [
|
|
213432
213861
|
"install",
|
|
213433
213862
|
"--cache-folder",
|
|
@@ -213435,7 +213864,7 @@ function buildYarnInstallArgs(cacheDir, hasPrivateRegistry) {
|
|
|
213435
213864
|
"--non-interactive",
|
|
213436
213865
|
"--no-progress"
|
|
213437
213866
|
];
|
|
213438
|
-
if (
|
|
213867
|
+
if (ignoreScripts) a.push("--ignore-scripts");
|
|
213439
213868
|
return a;
|
|
213440
213869
|
}
|
|
213441
213870
|
/**
|
|
@@ -213450,8 +213879,8 @@ async function runYarnInstall(args) {
|
|
|
213450
213879
|
await assertYarnAvailable();
|
|
213451
213880
|
const { nodeDir } = resolveNpm();
|
|
213452
213881
|
const { path: cacheDir, cleanup } = await makeTempDir("yarn-cache");
|
|
213453
|
-
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir);
|
|
213454
|
-
const argv = buildYarnInstallArgs(cacheDir, args.
|
|
213882
|
+
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir, args.baseEnv);
|
|
213883
|
+
const argv = buildYarnInstallArgs(cacheDir, args.ignoreScripts);
|
|
213455
213884
|
try {
|
|
213456
213885
|
process.stderr.write(`[dep-installer:trace] running: yarn ${argv.join(" ")}\n`);
|
|
213457
213886
|
await execFileAsync("yarn", argv, {
|
|
@@ -213481,7 +213910,7 @@ async function runYarnBerryInstall(args) {
|
|
|
213481
213910
|
await assertYarnAvailable();
|
|
213482
213911
|
const { nodeDir } = resolveNpm();
|
|
213483
213912
|
const env = {
|
|
213484
|
-
...envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir),
|
|
213913
|
+
...envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir, args.baseEnv),
|
|
213485
213914
|
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0"
|
|
213486
213915
|
};
|
|
213487
213916
|
const argv = buildYarnBerryInstallArgs();
|
|
@@ -213512,11 +213941,11 @@ async function assertYarnAvailable() {
|
|
|
213512
213941
|
* Deep cross-sibling build chains may build out of strict topological order —
|
|
213513
213942
|
* real `.kici` closures are shallow.
|
|
213514
213943
|
*/
|
|
213515
|
-
async function buildYarnWorkspaceClosure(repoRoot, kiciDir, yarnFlavor) {
|
|
213944
|
+
async function buildYarnWorkspaceClosure(repoRoot, kiciDir, yarnFlavor, baseEnv) {
|
|
213516
213945
|
const siblings = await collectInRepoSiblings(repoRoot, kiciDir, resolveYarnNodeModulesRoot(repoRoot, kiciDir));
|
|
213517
213946
|
if (siblings.length === 0) return;
|
|
213518
213947
|
const { nodeDir } = resolveNpm();
|
|
213519
|
-
const env = envWithNodeOnPath({}, nodeDir);
|
|
213948
|
+
const env = envWithNodeOnPath({}, nodeDir, baseEnv);
|
|
213520
213949
|
for (const rel of [...siblings].reverse()) {
|
|
213521
213950
|
const sibDir = join(repoRoot, rel);
|
|
213522
213951
|
if (!await siblingHasBuildScript(sibDir)) continue;
|
|
@@ -213560,9 +213989,9 @@ async function siblingHasBuildScript(sibDir) {
|
|
|
213560
213989
|
* the subprocess stderr/stdout is folded into the thrown error so the job's
|
|
213561
213990
|
* failure message names the real cause instead of a bare "Command failed".
|
|
213562
213991
|
*/
|
|
213563
|
-
async function buildWorkspaceClosure(repoRoot) {
|
|
213992
|
+
async function buildWorkspaceClosure(repoRoot, baseEnv) {
|
|
213564
213993
|
const { nodeDir } = resolveNpm();
|
|
213565
|
-
const env = envWithNodeOnPath({}, nodeDir);
|
|
213994
|
+
const env = envWithNodeOnPath({}, nodeDir, baseEnv);
|
|
213566
213995
|
const argv = [
|
|
213567
213996
|
"--filter",
|
|
213568
213997
|
"{.kici}^...",
|
|
@@ -213605,6 +214034,394 @@ function logSubprocessStreams(e, tokens) {
|
|
|
213605
214034
|
if (e && typeof e === "object" && "stderr" in e) process.stderr.write(`[dep-installer:trace] stderr: ${redactNpmOutput(String(e.stderr), tokens).slice(0, 500)}\n`);
|
|
213606
214035
|
}
|
|
213607
214036
|
//#endregion
|
|
214037
|
+
//#region ../core/dist/kici-ignore.js
|
|
214038
|
+
/**
|
|
214039
|
+
* `.kici/.kiciignore` — the declared list of paths the source digest excludes.
|
|
214040
|
+
*
|
|
214041
|
+
* The digest names a workflow's identity, so *which files it covers* is part of
|
|
214042
|
+
* that identity's definition. Before this file that definition was three
|
|
214043
|
+
* hard-coded constants, and it was wrong: the agent rewrites
|
|
214044
|
+
* `.kici/package-lock.json` (`npm install`, deliberately not `npm ci`, so a
|
|
214045
|
+
* resolved URL baked into the lock file cannot point at the wrong registry) and
|
|
214046
|
+
* `.kici/.npmrc` (a managed auth block, applied for one install and restored
|
|
214047
|
+
* after) inside the very tree whose digest it must reproduce. Neither was
|
|
214048
|
+
* excluded, so the drift gate rejected runs whose source had not changed.
|
|
214049
|
+
*
|
|
214050
|
+
* Making the set declarative rather than hard-coded is what lets a customer
|
|
214051
|
+
* whose own tooling writes into `.kici/` fix the same class of problem without
|
|
214052
|
+
* a release.
|
|
214053
|
+
*
|
|
214054
|
+
* ## Not to be confused with the repo-root `.kiciignore`
|
|
214055
|
+
*
|
|
214056
|
+
* A repo **root** `.kiciignore` already exists and is unrelated: it selects
|
|
214057
|
+
* which working-tree files a `kici run --remote` overlay uploads
|
|
214058
|
+
* (`compiler/src/remote/uploader.ts`, glob-matched, relative to the repo root).
|
|
214059
|
+
* This one lives at `.kici/.kiciignore`, is matched relative to `.kici/`, and
|
|
214060
|
+
* affects only the digest. The two files never read each other.
|
|
214061
|
+
*
|
|
214062
|
+
* ## Semantics
|
|
214063
|
+
*
|
|
214064
|
+
* gitignore-style, and deliberately implemented here rather than delegated to a
|
|
214065
|
+
* glob library: a glob matcher reads `node_modules/` as a directory named
|
|
214066
|
+
* `node_modules` and NOT as everything beneath it, which would silently fail to
|
|
214067
|
+
* exclude the exact paths this file exists to exclude. The digest is a
|
|
214068
|
+
* compat-protected identity input, so its matching rules are spelled out and
|
|
214069
|
+
* tested rather than inherited.
|
|
214070
|
+
*
|
|
214071
|
+
* - Blank lines and `#` comments are dropped; `\#` escapes a leading `#`.
|
|
214072
|
+
* - A trailing `/` makes a pattern directory-only.
|
|
214073
|
+
* - A pattern containing a slash is anchored at `.kici/`; a bare name matches
|
|
214074
|
+
* at any depth.
|
|
214075
|
+
* - `*` matches within one segment, `**` across segments, `?` one character.
|
|
214076
|
+
* - A leading `!` re-includes; the last matching pattern wins.
|
|
214077
|
+
* - A path under an ignored directory is ignored.
|
|
214078
|
+
*
|
|
214079
|
+
* One deliberate deviation from gitignore: a **symlink to a directory counts as
|
|
214080
|
+
* a directory**, so `node_modules/` covers a symlinked `node_modules`. `git`
|
|
214081
|
+
* treats a symlink as a file and would not. The purpose of the entry is
|
|
214082
|
+
* "exclude the dependency tree, whatever shape it takes on disk" — and treating
|
|
214083
|
+
* the link as a file hashed a `symlink:<target>` member into the identity that
|
|
214084
|
+
* the agent's own tree, where the dependency install writes a real directory,
|
|
214085
|
+
* structurally could not hold. The resolution happens in `collectSourcePaths`,
|
|
214086
|
+
* which stats a link to classify it and never follows one to read what is
|
|
214087
|
+
* behind it; the matcher below is unchanged and merely receives a truthful
|
|
214088
|
+
* `isDir`.
|
|
214089
|
+
*/
|
|
214090
|
+
/**
|
|
214091
|
+
* The exclusion set applied when `.kici/.kiciignore` is absent.
|
|
214092
|
+
*
|
|
214093
|
+
* `node_modules/` and `types/` are build outputs the tarball either omits or
|
|
214094
|
+
* regenerates; `.npmrc`, `package-lock.json` and `pnpm-lock.yaml` are rewritten
|
|
214095
|
+
* by the dependency install a run performs before it re-hashes;
|
|
214096
|
+
* `kici.lock.json` is the file the digest is written into.
|
|
214097
|
+
*/
|
|
214098
|
+
const KICI_DIGEST_DEFAULT_EXCLUSIONS = [
|
|
214099
|
+
"node_modules/",
|
|
214100
|
+
"types/",
|
|
214101
|
+
".npmrc",
|
|
214102
|
+
"package-lock.json",
|
|
214103
|
+
"pnpm-lock.yaml",
|
|
214104
|
+
"kici.lock.json"
|
|
214105
|
+
];
|
|
214106
|
+
/**
|
|
214107
|
+
* Excluded no matter what `.kiciignore` says.
|
|
214108
|
+
*
|
|
214109
|
+
* The compiler writes the computed digest INTO `.kici/kici.lock.json`, so a
|
|
214110
|
+
* digest covering that file would be an input to itself and no fixed point
|
|
214111
|
+
* would exist. This is arithmetic, not policy — there is no configuration under
|
|
214112
|
+
* which hashing it could work, so honoring a `.kiciignore` that omits it would
|
|
214113
|
+
* only produce a gate that rejects every run.
|
|
214114
|
+
*
|
|
214115
|
+
* The lock file's own integrity comes from elsewhere: the orchestrator fetches
|
|
214116
|
+
* it at the commit SHA, so provenance vouches for it, never `contentHash`.
|
|
214117
|
+
*/
|
|
214118
|
+
const KICI_DIGEST_FORCED_EXCLUSIONS = ["kici.lock.json"];
|
|
214119
|
+
/**
|
|
214120
|
+
* Paths a run rewrites inside `.kici/` before it re-hashes the tree.
|
|
214121
|
+
*
|
|
214122
|
+
* Under replace semantics a short `.kiciignore` silently re-includes these, and
|
|
214123
|
+
* the digest then changes on every run with nothing naming why. A compile whose
|
|
214124
|
+
* file omits one warns rather than failing: the customer keeps full control,
|
|
214125
|
+
* and the failure stops being mysterious.
|
|
214126
|
+
*/
|
|
214127
|
+
const KICI_RUN_REWRITTEN_PATHS = [
|
|
214128
|
+
"node_modules/",
|
|
214129
|
+
".npmrc",
|
|
214130
|
+
"package-lock.json"
|
|
214131
|
+
];
|
|
214132
|
+
/**
|
|
214133
|
+
* The `.kiciignore` file's name, relative to `.kici/`. Only the copy at the
|
|
214134
|
+
* root of `.kici/` is consulted; a nested one is ordinary hashed source.
|
|
214135
|
+
*/
|
|
214136
|
+
const KICI_IGNORE_FILENAME = ".kiciignore";
|
|
214137
|
+
/**
|
|
214138
|
+
* Hashed no matter what `.kiciignore` says — including when it names itself.
|
|
214139
|
+
*
|
|
214140
|
+
* The file declares which paths define a workflow's identity, so it is part of
|
|
214141
|
+
* that identity: editing it must move the digest and force a recompile. Were it
|
|
214142
|
+
* able to exclude itself, someone could change what a lock file attests to
|
|
214143
|
+
* without changing the lock file, which is the one outcome the digest exists to
|
|
214144
|
+
* prevent. Forced inclusion beats every exclusion, the forced ones included.
|
|
214145
|
+
*/
|
|
214146
|
+
const KICI_DIGEST_FORCED_INCLUSIONS = [KICI_IGNORE_FILENAME];
|
|
214147
|
+
/**
|
|
214148
|
+
* Split a `.kiciignore` file into patterns: trimmed, comment- and blank-free.
|
|
214149
|
+
*
|
|
214150
|
+
* Returns an empty array for a file that declares nothing, which under replace
|
|
214151
|
+
* semantics genuinely means "exclude nothing but the forced entry" — distinct
|
|
214152
|
+
* from an absent file, which means "use the defaults".
|
|
214153
|
+
*/
|
|
214154
|
+
function parseKiciIgnore(content) {
|
|
214155
|
+
const patterns = [];
|
|
214156
|
+
for (const rawLine of content.split("\n")) {
|
|
214157
|
+
const line = rawLine.trim();
|
|
214158
|
+
if (line.length === 0 || line.startsWith("#")) continue;
|
|
214159
|
+
patterns.push(line.startsWith("\\#") ? line.slice(1) : line);
|
|
214160
|
+
}
|
|
214161
|
+
return patterns;
|
|
214162
|
+
}
|
|
214163
|
+
/** Translate one gitignore-style pattern body into an anchored regular expression. */
|
|
214164
|
+
function globToRegExpSource(glob) {
|
|
214165
|
+
let out = "";
|
|
214166
|
+
for (let i = 0; i < glob.length; i++) {
|
|
214167
|
+
const ch = glob[i];
|
|
214168
|
+
if (ch === "*") {
|
|
214169
|
+
if (glob[i + 1] === "*") {
|
|
214170
|
+
out += ".*";
|
|
214171
|
+
i++;
|
|
214172
|
+
} else out += "[^/]*";
|
|
214173
|
+
} else if (ch === "?") out += "[^/]";
|
|
214174
|
+
else out += ch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
214175
|
+
}
|
|
214176
|
+
return out;
|
|
214177
|
+
}
|
|
214178
|
+
function compilePattern(pattern) {
|
|
214179
|
+
let body = pattern;
|
|
214180
|
+
const negated = body.startsWith("!");
|
|
214181
|
+
if (negated) body = body.slice(1);
|
|
214182
|
+
if (body.startsWith("\\!")) body = body.slice(1);
|
|
214183
|
+
if (body.length === 0) return null;
|
|
214184
|
+
const dirOnly = body.endsWith("/");
|
|
214185
|
+
if (dirOnly) body = body.slice(0, -1);
|
|
214186
|
+
if (body.length === 0) return null;
|
|
214187
|
+
const leadingSlash = body.startsWith("/");
|
|
214188
|
+
if (leadingSlash) body = body.slice(1);
|
|
214189
|
+
if (body.length === 0) return null;
|
|
214190
|
+
const anchored = leadingSlash || body.includes("/");
|
|
214191
|
+
const source = globToRegExpSource(body);
|
|
214192
|
+
return {
|
|
214193
|
+
re: new RegExp(anchored ? `^${source}$` : `(?:^|/)${source}$`),
|
|
214194
|
+
dirOnly,
|
|
214195
|
+
negated
|
|
214196
|
+
};
|
|
214197
|
+
}
|
|
214198
|
+
/**
|
|
214199
|
+
* Build a matcher over patterns already parsed from a `.kiciignore`.
|
|
214200
|
+
*
|
|
214201
|
+
* The returned predicate takes a POSIX path relative to `.kici/` and whether it
|
|
214202
|
+
* names a directory. It reports true when the path itself — or any directory
|
|
214203
|
+
* above it — is excluded, so a directory pattern covers its whole subtree.
|
|
214204
|
+
*/
|
|
214205
|
+
function buildKiciIgnoreMatcher(patterns) {
|
|
214206
|
+
const compiled = [];
|
|
214207
|
+
for (const pattern of patterns) {
|
|
214208
|
+
const entry = compilePattern(pattern);
|
|
214209
|
+
if (entry) compiled.push(entry);
|
|
214210
|
+
}
|
|
214211
|
+
if (compiled.length === 0) return () => false;
|
|
214212
|
+
/** Decide one concrete path, ancestors not considered. Last match wins. */
|
|
214213
|
+
const decideSelf = (relPath, isDir) => {
|
|
214214
|
+
let ignored = false;
|
|
214215
|
+
for (const { re, dirOnly, negated } of compiled) {
|
|
214216
|
+
if (dirOnly && !isDir) continue;
|
|
214217
|
+
if (re.test(relPath)) ignored = !negated;
|
|
214218
|
+
}
|
|
214219
|
+
return ignored;
|
|
214220
|
+
};
|
|
214221
|
+
return (relPath, isDir) => {
|
|
214222
|
+
const segments = relPath.split("/");
|
|
214223
|
+
for (let i = 1; i < segments.length; i++) if (decideSelf(segments.slice(0, i).join("/"), true)) return true;
|
|
214224
|
+
return decideSelf(relPath, isDir);
|
|
214225
|
+
};
|
|
214226
|
+
}
|
|
214227
|
+
/**
|
|
214228
|
+
* Which of the run-rewritten paths a pattern set fails to cover.
|
|
214229
|
+
*
|
|
214230
|
+
* Asks the matcher rather than comparing strings, so a broader pattern that
|
|
214231
|
+
* genuinely does exclude a path (`*`, `*.json`) is credited and does not warn.
|
|
214232
|
+
* Returned sorted, so the warning text is deterministic.
|
|
214233
|
+
*/
|
|
214234
|
+
function findUncoveredRunRewrittenPaths(patterns) {
|
|
214235
|
+
const matches = buildKiciIgnoreMatcher(patterns);
|
|
214236
|
+
const uncovered = [];
|
|
214237
|
+
for (const target of KICI_RUN_REWRITTEN_PATHS) {
|
|
214238
|
+
const isDir = target.endsWith("/");
|
|
214239
|
+
if (!matches(isDir ? target.slice(0, -1) : target, isDir)) uncovered.push(target);
|
|
214240
|
+
}
|
|
214241
|
+
return uncovered.sort();
|
|
214242
|
+
}
|
|
214243
|
+
/** The warning text for one run-rewritten path a `.kiciignore` omits. */
|
|
214244
|
+
function runRewrittenWarning(target) {
|
|
214245
|
+
return `.kici/.kiciignore does not exclude '${target}', which a run rewrites inside .kici/ before the agent re-hashes the tree. The workflow's contentHash will therefore change on every run and the drift gate will reject it. Add '${target}' to .kici/.kiciignore, or remove the file to fall back to the defaults.`;
|
|
214246
|
+
}
|
|
214247
|
+
//#endregion
|
|
214248
|
+
//#region ../core/dist/kici-source-digest.js
|
|
214249
|
+
init_crypto();
|
|
214250
|
+
/**
|
|
214251
|
+
* Resolve the exclusion rules for `kiciDir`, reading `.kici/.kiciignore`.
|
|
214252
|
+
*
|
|
214253
|
+
* Separate from `hashKiciSourceTree` because the compiler needs the warnings
|
|
214254
|
+
* without needing a digest — and because a caller that wants to explain the
|
|
214255
|
+
* exclusion set should not have to re-implement how it was chosen.
|
|
214256
|
+
*
|
|
214257
|
+
* An unreadable file falls back to the defaults: the digest's job is to be
|
|
214258
|
+
* computable, and a read failure that silently emptied the exclusion set would
|
|
214259
|
+
* make every subsequent run drift instead.
|
|
214260
|
+
*/
|
|
214261
|
+
async function loadKiciIgnoreRules(kiciDir) {
|
|
214262
|
+
let patterns = KICI_DIGEST_DEFAULT_EXCLUSIONS;
|
|
214263
|
+
let source = "default";
|
|
214264
|
+
try {
|
|
214265
|
+
patterns = parseKiciIgnore(await ro.readFile(path$1.join(kiciDir, KICI_IGNORE_FILENAME), "utf-8"));
|
|
214266
|
+
source = "file";
|
|
214267
|
+
} catch {}
|
|
214268
|
+
const missingRunRewritten = source === "file" ? findUncoveredRunRewrittenPaths(patterns) : [];
|
|
214269
|
+
return {
|
|
214270
|
+
source,
|
|
214271
|
+
patterns,
|
|
214272
|
+
forced: KICI_DIGEST_FORCED_EXCLUSIONS,
|
|
214273
|
+
missingRunRewritten,
|
|
214274
|
+
warnings: missingRunRewritten.map(runRewrittenWarning)
|
|
214275
|
+
};
|
|
214276
|
+
}
|
|
214277
|
+
/**
|
|
214278
|
+
* Whether the exclusion patterns should see this entry as a directory.
|
|
214279
|
+
*
|
|
214280
|
+
* `Dirent.isDirectory()` describes the link itself, so a symlinked
|
|
214281
|
+
* `.kici/node_modules` reads as a file and the directory-only `node_modules/`
|
|
214282
|
+
* pattern misses it. The compiler then hashes a `symlink:<target>` member that
|
|
214283
|
+
* no agent can hold: the tarball drops `.kici/node_modules` by prefix, and the
|
|
214284
|
+
* restore materialises a real directory the exclusion does match. The producer
|
|
214285
|
+
* and its verifier disagree on a tree nobody edited, and recompiling reproduces
|
|
214286
|
+
* the same lock.
|
|
214287
|
+
*
|
|
214288
|
+
* So classification resolves the link and the walk does not. An entry that
|
|
214289
|
+
* survives exclusion is still recorded as a member and still contributes its
|
|
214290
|
+
* target string; nothing here follows a link to read what is behind it, which
|
|
214291
|
+
* is what keeps the walk terminating and keeps the hashed set equal to what the
|
|
214292
|
+
* tarball carries.
|
|
214293
|
+
*
|
|
214294
|
+
* A link that cannot be resolved — dangling, cyclic (the kernel's own
|
|
214295
|
+
* `ELOOP`), or unreadable — is a file, which is what it contributes anyway.
|
|
214296
|
+
*
|
|
214297
|
+
* This is a deliberate deviation from gitignore, where `node_modules/` does not
|
|
214298
|
+
* match a symlinked `node_modules`. The purpose of the entry is "exclude the
|
|
214299
|
+
* dependency tree, whatever shape it takes on disk", and a link to the
|
|
214300
|
+
* dependency tree is the dependency tree.
|
|
214301
|
+
*/
|
|
214302
|
+
async function matchesAsDirectory(abs, entry) {
|
|
214303
|
+
if (entry.isDirectory()) return true;
|
|
214304
|
+
if (!entry.isSymbolicLink()) return false;
|
|
214305
|
+
try {
|
|
214306
|
+
return (await ro.stat(abs)).isDirectory();
|
|
214307
|
+
} catch {
|
|
214308
|
+
return false;
|
|
214309
|
+
}
|
|
214310
|
+
}
|
|
214311
|
+
/** Every file the source tarball carries, as `.kici/`-prefixed POSIX paths. */
|
|
214312
|
+
async function collectSourcePaths(kiciDir, rules) {
|
|
214313
|
+
const found = [];
|
|
214314
|
+
const isExcluded = buildKiciIgnoreMatcher(rules.patterns);
|
|
214315
|
+
const isForced = buildKiciIgnoreMatcher(rules.forced);
|
|
214316
|
+
async function walk(absDir, relDir) {
|
|
214317
|
+
const entries = await ro.readdir(absDir, { withFileTypes: true });
|
|
214318
|
+
for (const entry of entries) {
|
|
214319
|
+
const rel = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
214320
|
+
const member = `.kici/${rel}`;
|
|
214321
|
+
const abs = path$1.join(absDir, entry.name);
|
|
214322
|
+
const asDir = await matchesAsDirectory(abs, entry);
|
|
214323
|
+
if (!(!asDir && KICI_DIGEST_FORCED_INCLUSIONS.includes(rel)) && (isForced(rel, asDir) || isExcluded(rel, asDir))) continue;
|
|
214324
|
+
if (entry.isDirectory()) await walk(abs, rel);
|
|
214325
|
+
else found.push(member);
|
|
214326
|
+
}
|
|
214327
|
+
}
|
|
214328
|
+
await walk(kiciDir, "");
|
|
214329
|
+
return found.sort();
|
|
214330
|
+
}
|
|
214331
|
+
/**
|
|
214332
|
+
* Read one member's contribution. A symlink contributes its target; a file its
|
|
214333
|
+
* text. An unreadable entry contributes the empty string rather than throwing —
|
|
214334
|
+
* the digest's job is to change when the tree changes, and a read failure is
|
|
214335
|
+
* reported by the tar step that follows, not here.
|
|
214336
|
+
*/
|
|
214337
|
+
async function readMember(abs) {
|
|
214338
|
+
if ((await ro.lstat(abs)).isSymbolicLink()) return `symlink:${await ro.readlink(abs)}`;
|
|
214339
|
+
return ro.readFile(abs, "utf-8");
|
|
214340
|
+
}
|
|
214341
|
+
/**
|
|
214342
|
+
* SHA-256 over every file in `kiciDir`, excluding `.kici/node_modules/`.
|
|
214343
|
+
*
|
|
214344
|
+
* Returns the empty string when the directory does not exist, which the callers
|
|
214345
|
+
* treat exactly as they already treat an unreadable entry file: no hash, so no
|
|
214346
|
+
* drift gate.
|
|
214347
|
+
*/
|
|
214348
|
+
async function hashKiciSourceTree(kiciDir) {
|
|
214349
|
+
let members;
|
|
214350
|
+
try {
|
|
214351
|
+
members = await collectSourcePaths(kiciDir, await loadKiciIgnoreRules(kiciDir));
|
|
214352
|
+
} catch {
|
|
214353
|
+
return "";
|
|
214354
|
+
}
|
|
214355
|
+
const parts = [];
|
|
214356
|
+
for (const member of members) {
|
|
214357
|
+
const abs = path$1.join(kiciDir, member.slice(6));
|
|
214358
|
+
let content;
|
|
214359
|
+
try {
|
|
214360
|
+
content = await readMember(abs);
|
|
214361
|
+
} catch {
|
|
214362
|
+
content = "";
|
|
214363
|
+
}
|
|
214364
|
+
parts.push(`${member}\0${normalizeLineEndings(content)}\0`);
|
|
214365
|
+
}
|
|
214366
|
+
return sha256(parts.join(""));
|
|
214367
|
+
}
|
|
214368
|
+
/**
|
|
214369
|
+
* Every symlink the digest hashes, in member order.
|
|
214370
|
+
*
|
|
214371
|
+
* Reads the same member set the digest does, so a link an exclusion covers is
|
|
214372
|
+
* absent here for the same reason it is absent from the hash.
|
|
214373
|
+
*/
|
|
214374
|
+
async function collectSourceSymlinks(kiciDir, rules) {
|
|
214375
|
+
let members;
|
|
214376
|
+
try {
|
|
214377
|
+
members = await collectSourcePaths(kiciDir, rules ?? await loadKiciIgnoreRules(kiciDir));
|
|
214378
|
+
} catch {
|
|
214379
|
+
return [];
|
|
214380
|
+
}
|
|
214381
|
+
const found = [];
|
|
214382
|
+
for (const member of members) {
|
|
214383
|
+
const abs = path$1.join(kiciDir, member.slice(6));
|
|
214384
|
+
try {
|
|
214385
|
+
if ((await ro.lstat(abs)).isSymbolicLink()) found.push({
|
|
214386
|
+
member,
|
|
214387
|
+
target: await ro.readlink(abs)
|
|
214388
|
+
});
|
|
214389
|
+
} catch {}
|
|
214390
|
+
}
|
|
214391
|
+
return found;
|
|
214392
|
+
}
|
|
214393
|
+
/**
|
|
214394
|
+
* The sentence a drift error adds when the hashed tree carries symlinks.
|
|
214395
|
+
*
|
|
214396
|
+
* "Run 'kici compile'" is the right remedy for ordinary drift and is actively
|
|
214397
|
+
* misleading here: a symlink the tarball omits or extraction rewrites makes the
|
|
214398
|
+
* producer's hash unreachable on this side, so recompiling reproduces the same
|
|
214399
|
+
* lock forever. Naming the links turns an unrecoverable loop into something the
|
|
214400
|
+
* error text alone explains. Empty when the tree carries none, so an ordinary
|
|
214401
|
+
* drift error is unchanged.
|
|
214402
|
+
*/
|
|
214403
|
+
function hashedSymlinkDriftNote(symlinks) {
|
|
214404
|
+
if (symlinks.length === 0) return "";
|
|
214405
|
+
const named = symlinks.map(({ member, target }) => `${member} -> ${target}`).join(", ");
|
|
214406
|
+
return ` The hashed .kici/ tree carries ${symlinks.length} symlink${symlinks.length === 1 ? "" : "s"} (${named}). A symlink is hashed as its target string, not the bytes behind it, so a link the source tarball omits or extraction rewrites cannot reproduce the compiling machine's hash and recompiling will not change that. Remove it from .kici/, or exclude it in .kici/.kiciignore.`;
|
|
214407
|
+
}
|
|
214408
|
+
/**
|
|
214409
|
+
* Locate the `.kici` directory a workflow file lives under, or null when it
|
|
214410
|
+
* does not live under one (a config file outside the convention). Walks
|
|
214411
|
+
* ancestors rather than taking the directory as a parameter, so every entry
|
|
214412
|
+
* point that loads a workflow module resolves the same tree without threading
|
|
214413
|
+
* one more argument through each of them.
|
|
214414
|
+
*/
|
|
214415
|
+
function findKiciDir(entryPath) {
|
|
214416
|
+
let dir = path$1.dirname(path$1.resolve(entryPath));
|
|
214417
|
+
for (;;) {
|
|
214418
|
+
if (path$1.basename(dir) === ".kici") return dir;
|
|
214419
|
+
const parent = path$1.dirname(dir);
|
|
214420
|
+
if (parent === dir) return null;
|
|
214421
|
+
dir = parent;
|
|
214422
|
+
}
|
|
214423
|
+
}
|
|
214424
|
+
//#endregion
|
|
213608
214425
|
//#region src/execution/generator-context.ts
|
|
213609
214426
|
/**
|
|
213610
214427
|
* Build the context handed to a `DynamicJobFn`.
|
|
@@ -213658,12 +214475,17 @@ init_dist();
|
|
|
213658
214475
|
* ESM modules by resolved URL, so importing that path yields the workflow's live
|
|
213659
214476
|
* singleton, not a fresh copy.
|
|
213660
214477
|
*
|
|
213661
|
-
*
|
|
214478
|
+
* Both specifiers resolve to the same module-global maps — `internal.ts` and the
|
|
214479
|
+
* root barrel re-export the same `outputs.js` bindings — so the fallback below
|
|
214480
|
+
* changes which entry is imported, never which singleton is mutated.
|
|
214481
|
+
*
|
|
214482
|
+
* Falls back to the agent's bundled setters when neither resolves (mirrors
|
|
213662
214483
|
* `resolveSdkSetters` in the compiler's test runner).
|
|
213663
214484
|
*/
|
|
213664
214485
|
async function resolveWorkflowSdkSetters(workflowFilePath) {
|
|
213665
|
-
|
|
213666
|
-
|
|
214486
|
+
const req = createRequire(workflowFilePath);
|
|
214487
|
+
for (const specifier of ["@kici-dev/sdk/internal", "@kici-dev/sdk"]) try {
|
|
214488
|
+
const sdkEntry = req.resolve(specifier);
|
|
213667
214489
|
const sdk = await import(pathToFileURL(sdkEntry).href);
|
|
213668
214490
|
if (typeof sdk.setStepOutputsMap === "function" && typeof sdk.setStepRefMap === "function" && typeof sdk.setJobOutputsMap === "function") return {
|
|
213669
214491
|
setStepOutputsMap: sdk.setStepOutputsMap,
|
|
@@ -213677,8 +214499,8 @@ async function resolveWorkflowSdkSetters(workflowFilePath) {
|
|
|
213677
214499
|
setJobOutputsMap
|
|
213678
214500
|
};
|
|
213679
214501
|
}
|
|
213680
|
-
const AGENT_SDK_VERSION = "0.
|
|
213681
|
-
const AGENT_SDK_BUNDLE_HASH = "
|
|
214502
|
+
const AGENT_SDK_VERSION = "0.7.0";
|
|
214503
|
+
const AGENT_SDK_BUNDLE_HASH = "065963c7765dc8d87e04d45f57d7e15be1613da705e4ff3ec3742fd1408b7bf5";
|
|
213682
214504
|
/**
|
|
213683
214505
|
* Register the ESM loader hook that transforms `.ts` / `.tsx` files on the fly
|
|
213684
214506
|
* for subsequent dynamic `import()` calls. Idempotent at our level via the
|
|
@@ -213718,10 +214540,49 @@ function ensureLoaderHookRegistered() {
|
|
|
213718
214540
|
* with lockfiles compiled on Linux (LF).
|
|
213719
214541
|
*/
|
|
213720
214542
|
function computeContentHash(rawSource, assetDigest) {
|
|
213721
|
-
let input = `
|
|
214543
|
+
let input = `7:${normalizeLineEndings(rawSource)}`;
|
|
213722
214544
|
if (assetDigest !== void 0 && assetDigest.length > 0) input += `\0${normalizeLineEndings(assetDigest)}`;
|
|
213723
214545
|
return sha256(input);
|
|
213724
214546
|
}
|
|
214547
|
+
/**
|
|
214548
|
+
* The compile schema version the lock file records for `sourceFile`, or null
|
|
214549
|
+
* when the tree carries no readable lock (a `file://` in-place run, a workflow
|
|
214550
|
+
* outside the `.kici/` convention, a hand-built fixture).
|
|
214551
|
+
*
|
|
214552
|
+
* The source tarball carries `.kici/kici.lock.json` — the digest excludes it,
|
|
214553
|
+
* but `source-packer.ts` packs it — so the agent can read the producing
|
|
214554
|
+
* compiler's schema version from the tree it already has, with no wire field
|
|
214555
|
+
* to plumb and no protocol change.
|
|
214556
|
+
*
|
|
214557
|
+
* A malformed or unreadable lock returns null rather than throwing: this is a
|
|
214558
|
+
* diagnostic gate in front of the real hash check, so it must never convert a
|
|
214559
|
+
* bad lock into a worse error than the hash comparison already gives.
|
|
214560
|
+
*/
|
|
214561
|
+
async function readLockCompileSchemaVersion(kiciDir, sourceFile) {
|
|
214562
|
+
let parsed;
|
|
214563
|
+
try {
|
|
214564
|
+
parsed = JSON.parse(await ro.readFile(path$1.join(kiciDir, "kici.lock.json"), "utf-8"));
|
|
214565
|
+
} catch {
|
|
214566
|
+
return null;
|
|
214567
|
+
}
|
|
214568
|
+
const workflows = parsed?.workflows;
|
|
214569
|
+
if (!Array.isArray(workflows)) return null;
|
|
214570
|
+
const normalize = (p) => p.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
214571
|
+
const target = normalize(sourceFile);
|
|
214572
|
+
const versionOf = (w) => {
|
|
214573
|
+
const v = w?.compileSchemaVersion;
|
|
214574
|
+
return typeof v === "number" && Number.isFinite(v) && v > 0 ? v : null;
|
|
214575
|
+
};
|
|
214576
|
+
for (const w of workflows) {
|
|
214577
|
+
const file = w?.source?.file;
|
|
214578
|
+
if (typeof file === "string" && normalize(file) === target) return versionOf(w);
|
|
214579
|
+
}
|
|
214580
|
+
for (const w of workflows) {
|
|
214581
|
+
const v = versionOf(w);
|
|
214582
|
+
if (v !== null) return v;
|
|
214583
|
+
}
|
|
214584
|
+
return null;
|
|
214585
|
+
}
|
|
213725
214586
|
async function buildAssetDigestFromResolvedPaths(workDir, resolvedPaths) {
|
|
213726
214587
|
const parts = [];
|
|
213727
214588
|
for (const rel of resolvedPaths) {
|
|
@@ -213743,20 +214604,30 @@ async function buildAssetDigestFromResolvedPaths(workDir, resolvedPaths) {
|
|
|
213743
214604
|
* `node_modules/` the same way any `tsx`-style runner would — so host-repo
|
|
213744
214605
|
* helpers and `@kici-dev/sdk` Just Work.
|
|
213745
214606
|
*
|
|
213746
|
-
* When `expectedContentHash` is provided, verifies the
|
|
213747
|
-
* the hash in the lock file.
|
|
213748
|
-
*
|
|
213749
|
-
*
|
|
214607
|
+
* When `expectedContentHash` is provided, verifies the extracted `.kici/` tree
|
|
214608
|
+
* matches the hash in the lock file. It re-hashes the whole tree, not the entry
|
|
214609
|
+
* file alone, so an edit to an imported helper is caught — that was the gap
|
|
214610
|
+
* that let a warm cache restore a stale tarball and run the OLD helper green.
|
|
214611
|
+
* Drift produces a descriptive error that surfaces the baked agent SDK
|
|
214612
|
+
* fingerprint (useful when debugging "is the agent running a stale build?").
|
|
213750
214613
|
*/
|
|
213751
214614
|
async function loadWorkflowSource(workDir, sourceFile, expectedContentHash, resolvedHashFiles) {
|
|
213752
214615
|
ensureLoaderHookRegistered();
|
|
213753
214616
|
const filePath = path$1.join(workDir, sourceFile);
|
|
213754
214617
|
if (expectedContentHash) {
|
|
213755
|
-
const
|
|
214618
|
+
const kiciDir = findKiciDir(filePath);
|
|
214619
|
+
if (kiciDir) {
|
|
214620
|
+
const lockVersion = await readLockCompileSchemaVersion(kiciDir, sourceFile);
|
|
214621
|
+
if (lockVersion !== null && lockVersion !== 7) throw new Error(`kici.lock.json was compiled by an incompatible @kici-dev/compiler: the lock declares compile schema ${lockVersion}, this agent implements 7. The schema version is mixed into every contentHash, so recompiling cannot reconcile them. Align the versions: upgrade the agent to one implementing schema ${lockVersion}, or pin @kici-dev/compiler to a release implementing schema 7 and recompile (agent baked @kici-dev/sdk@${AGENT_SDK_VERSION} bundleHash=${AGENT_SDK_BUNDLE_HASH}).`);
|
|
214622
|
+
}
|
|
214623
|
+
const rawSource = (kiciDir ? await hashKiciSourceTree(kiciDir) : "") || await ro.readFile(filePath, "utf-8");
|
|
213756
214624
|
let assetDigest;
|
|
213757
214625
|
if (resolvedHashFiles?.length) assetDigest = await buildAssetDigestFromResolvedPaths(workDir, resolvedHashFiles);
|
|
213758
214626
|
const actualHash = computeContentHash(rawSource, assetDigest);
|
|
213759
|
-
if (actualHash !== expectedContentHash)
|
|
214627
|
+
if (actualHash !== expectedContentHash) {
|
|
214628
|
+
const symlinkNote = kiciDir ? hashedSymlinkDriftNote(await collectSourceSymlinks(kiciDir)) : "";
|
|
214629
|
+
throw new Error(`Lock file is out of date: workflow source changed without regenerating kici.lock.json (expected contentHash ${expectedContentHash}, got ${actualHash}, agent baked @kici-dev/sdk@${AGENT_SDK_VERSION} bundleHash=${AGENT_SDK_BUNDLE_HASH}). Run 'kici compile' and commit the updated lock file.${symlinkNote}`);
|
|
214630
|
+
}
|
|
213760
214631
|
}
|
|
213761
214632
|
return {
|
|
213762
214633
|
module: await import(pathToFileURL(filePath).href + `?t=${Date.now()}`),
|
|
@@ -213919,22 +214790,30 @@ function applyGlobalWorkflowEnv(repos) {
|
|
|
213919
214790
|
* `.kici/` source tarball restoration for execution agents.
|
|
213920
214791
|
*
|
|
213921
214792
|
* Downloads a pre-built `.kici/` source tarball from the orchestrator's cache
|
|
213922
|
-
* and
|
|
214793
|
+
* and installs it at `workDir/.kici` so the workflow entry point becomes
|
|
213923
214794
|
* importable. Mirrors the shape of `dep-restore.ts` but without the streaming
|
|
213924
214795
|
* optimization — source tarballs are tiny (kilobytes, not the hundreds of
|
|
213925
214796
|
* megabytes a `node_modules/` tarball carries).
|
|
213926
214797
|
*
|
|
213927
|
-
*
|
|
213928
|
-
*
|
|
213929
|
-
*
|
|
213930
|
-
*
|
|
213931
|
-
*
|
|
213932
|
-
* `
|
|
213933
|
-
* the
|
|
213934
|
-
*
|
|
213935
|
-
*
|
|
213936
|
-
*
|
|
213937
|
-
*
|
|
214798
|
+
* Two properties this path is responsible for, both of which it previously
|
|
214799
|
+
* lacked:
|
|
214800
|
+
*
|
|
214801
|
+
* **Verification.** `dispatch.sourceTarDigest` is the SHA-256 of the tarball's
|
|
214802
|
+
* own bytes, so the download is checked before anything is extracted — the same
|
|
214803
|
+
* contract `restoreDeps` has always had via `depsHash`. The older
|
|
214804
|
+
* `dispatch.sourceTarHash` field carries the workflow `contentHash` instead, so
|
|
214805
|
+
* it never could serve this purpose; it stays on the wire for older peers and
|
|
214806
|
+
* is deliberately not used as a verification input here. When no digest is
|
|
214807
|
+
* dispatched (an older orchestrator, or a source that did not come from the
|
|
214808
|
+
* content-addressed cache) the restore proceeds unverified rather than failing,
|
|
214809
|
+
* so a mixed-version rollout still runs.
|
|
214810
|
+
*
|
|
214811
|
+
* **Replacement, not overlay.** Extraction lands in a scratch directory and the
|
|
214812
|
+
* result REPLACES `workDir/.kici` wholesale, save for `node_modules/` — the one
|
|
214813
|
+
* directory the tarball deliberately omits, which the deps restore has already
|
|
214814
|
+
* written by the time this runs. Extracting over the existing tree left any file
|
|
214815
|
+
* the tarball no longer carries in place, so a helper the author deleted
|
|
214816
|
+
* survived every warm-cache run and kept being imported.
|
|
213938
214817
|
*/
|
|
213939
214818
|
init_index_min();
|
|
213940
214819
|
init_dist$2();
|
|
@@ -213950,7 +214829,14 @@ async function extractSourceTarball(data, targetDir) {
|
|
|
213950
214829
|
})).on("finish", resolve).on("error", reject);
|
|
213951
214830
|
});
|
|
213952
214831
|
}
|
|
213953
|
-
|
|
214832
|
+
/**
|
|
214833
|
+
* Download, verify, and install the `.kici/` source tree.
|
|
214834
|
+
*
|
|
214835
|
+
* @param workDir - Root of the cloned repository; `.kici` is replaced under it
|
|
214836
|
+
* @param sourceTarUrl - `http://`, `https://`, or `file://` URL to the tarball
|
|
214837
|
+
* @param sourceTarDigest - Expected SHA-256 of the tarball bytes, when known
|
|
214838
|
+
*/
|
|
214839
|
+
async function restoreSource(workDir, sourceTarUrl, sourceTarDigest) {
|
|
213954
214840
|
sourceTarUrl = resolveOrchestratorUrl(sourceTarUrl);
|
|
213955
214841
|
logger$1.info("Restoring .kici/ source from tarball", { sourceTarUrl });
|
|
213956
214842
|
const startTime = Date.now();
|
|
@@ -213960,11 +214846,34 @@ async function restoreSource(workDir, sourceTarUrl) {
|
|
|
213960
214846
|
data = await ro.readFile(localPath);
|
|
213961
214847
|
} else if (sourceTarUrl.startsWith("http://") || sourceTarUrl.startsWith("https://")) data = await downloadUrl(sourceTarUrl);
|
|
213962
214848
|
else throw new Error(`Unsupported source tarball URL scheme: ${sourceTarUrl}`);
|
|
213963
|
-
|
|
214849
|
+
if (sourceTarDigest) {
|
|
214850
|
+
const actual = createHash("sha256").update(data).digest("hex");
|
|
214851
|
+
if (actual !== sourceTarDigest) throw new Error(`Source tarball hash mismatch: expected ${sourceTarDigest}, got ${actual}. The restored source does not match what the orchestrator dispatched.`);
|
|
214852
|
+
}
|
|
214853
|
+
const kiciDir = path$1.join(workDir, ".kici");
|
|
214854
|
+
const scratch = path$1.join(workDir, `.kici.restore-${process.pid}-${Date.now()}`);
|
|
214855
|
+
try {
|
|
214856
|
+
await extractSourceTarball(data, scratch);
|
|
214857
|
+
const extracted = path$1.join(scratch, ".kici");
|
|
214858
|
+
const src = (await ro.stat(extracted).catch(() => null))?.isDirectory() ? extracted : scratch;
|
|
214859
|
+
const installedDeps = path$1.join(kiciDir, "node_modules");
|
|
214860
|
+
if (await ro.stat(installedDeps).catch(() => null)) await rename(installedDeps, path$1.join(src, "node_modules"));
|
|
214861
|
+
await rm(kiciDir, {
|
|
214862
|
+
recursive: true,
|
|
214863
|
+
force: true
|
|
214864
|
+
});
|
|
214865
|
+
await rename(src, kiciDir);
|
|
214866
|
+
} finally {
|
|
214867
|
+
await rm(scratch, {
|
|
214868
|
+
recursive: true,
|
|
214869
|
+
force: true
|
|
214870
|
+
}).catch(() => {});
|
|
214871
|
+
}
|
|
213964
214872
|
const durationMs = Date.now() - startTime;
|
|
213965
214873
|
logger$1.info(".kici/ source restored", {
|
|
213966
214874
|
sizeKB: (data.length / 1024).toFixed(2),
|
|
213967
|
-
durationMs
|
|
214875
|
+
durationMs,
|
|
214876
|
+
verified: sourceTarDigest !== void 0
|
|
213968
214877
|
});
|
|
213969
214878
|
}
|
|
213970
214879
|
//#endregion
|
|
@@ -214122,7 +215031,7 @@ init_oidc_token_relay();
|
|
|
214122
215031
|
init_dist$4();
|
|
214123
215032
|
init_download();
|
|
214124
215033
|
init_dep_restore();
|
|
214125
|
-
const AGENT_VERSION = "0.
|
|
215034
|
+
const AGENT_VERSION = "0.7.0";
|
|
214126
215035
|
process.on("uncaughtException", (err) => {
|
|
214127
215036
|
process.stderr.write(`[workflow-runner] UNCAUGHT EXCEPTION: ${err.message}\n`);
|
|
214128
215037
|
if (err.stack) process.stderr.write(`[workflow-runner] Stack: ${err.stack}\n`);
|
|
@@ -214194,7 +215103,12 @@ function installOutputCapture() {
|
|
|
214194
215103
|
line
|
|
214195
215104
|
});
|
|
214196
215105
|
}
|
|
214197
|
-
if (isForkMode)
|
|
215106
|
+
if (isForkMode) {
|
|
215107
|
+
if (!captureIsActive() || process.env.KICI_RUNNER_DEBUG_STDIO === "true") return origStdoutWrite(chunk, encodingOrCb, cb);
|
|
215108
|
+
const forkCallback = typeof encodingOrCb === "function" ? encodingOrCb : cb;
|
|
215109
|
+
if (forkCallback) forkCallback();
|
|
215110
|
+
return true;
|
|
215111
|
+
}
|
|
214198
215112
|
const callback = typeof encodingOrCb === "function" ? encodingOrCb : cb;
|
|
214199
215113
|
if (callback) callback();
|
|
214200
215114
|
return true;
|
|
@@ -214784,6 +215698,46 @@ function extractRepoIdentifier(repoUrl) {
|
|
|
214784
215698
|
* mint failure the statement is frozen + reported for later minting (deferred);
|
|
214785
215699
|
* the step still completes.
|
|
214786
215700
|
*/
|
|
215701
|
+
/**
|
|
215702
|
+
* The context a deferred attestation's frozen statement is built from.
|
|
215703
|
+
*
|
|
215704
|
+
* Prefers the orchestrator's `provenanceContext` — server truth, derived from
|
|
215705
|
+
* the same run row the mint reads, so the frozen statement is field-for-field
|
|
215706
|
+
* what a live mint would have produced and the orchestrator can cross-check it.
|
|
215707
|
+
*
|
|
215708
|
+
* Falls back to the agent's local view when an older orchestrator sent none.
|
|
215709
|
+
* That fallback disagrees with the claims by construction: `request.ref` is the
|
|
215710
|
+
* job's CHECKOUT ref, which for a pull request is the HEAD branch where the
|
|
215711
|
+
* claim is the BASE branch, and `request.workflowRef` is a global workflow's
|
|
215712
|
+
* CLONE ref where the claim is `<name>@<sha>`. So a statement built from it
|
|
215713
|
+
* fails the capture cross-check and the defer is dropped — a green job with no
|
|
215714
|
+
* attestation, rather than an unchecked statement the orchestrator signs.
|
|
215715
|
+
*/
|
|
215716
|
+
function buildLocalContext(request) {
|
|
215717
|
+
const ctx = request.provenanceContext;
|
|
215718
|
+
if (ctx) return {
|
|
215719
|
+
repository: ctx.repository ?? "",
|
|
215720
|
+
ref: ctx.ref ?? "",
|
|
215721
|
+
sha: ctx.sha,
|
|
215722
|
+
workflowRef: ctx.workflowRef ?? "",
|
|
215723
|
+
runId: ctx.runId,
|
|
215724
|
+
jobId: ctx.jobId,
|
|
215725
|
+
orgId: ctx.orgId,
|
|
215726
|
+
sourceOrigin: ctx.sourceOrigin,
|
|
215727
|
+
...ctx.provider ? { provider: ctx.provider } : {},
|
|
215728
|
+
issuer: ctx.issuer,
|
|
215729
|
+
orchestratorId: ctx.orchestratorId
|
|
215730
|
+
};
|
|
215731
|
+
return {
|
|
215732
|
+
repository: extractRepoIdentifier(request.repoUrl),
|
|
215733
|
+
ref: request.ref,
|
|
215734
|
+
sha: request.sha || null,
|
|
215735
|
+
workflowRef: request.workflowRef ?? request.workflowName,
|
|
215736
|
+
runId: request.runId,
|
|
215737
|
+
jobId: request.jobId,
|
|
215738
|
+
issuer: ""
|
|
215739
|
+
};
|
|
215740
|
+
}
|
|
214787
215741
|
function buildAttestProvenanceFn(request, workDir, getIdToken) {
|
|
214788
215742
|
return async (opts) => {
|
|
214789
215743
|
const subject = provenanceSubjectIsPath(opts.subject) ? {
|
|
@@ -214799,15 +215753,7 @@ function buildAttestProvenanceFn(request, workDir, getIdToken) {
|
|
|
214799
215753
|
"kici-agent": AGENT_VERSION,
|
|
214800
215754
|
"kici-orchestrator": "unknown"
|
|
214801
215755
|
},
|
|
214802
|
-
localContext:
|
|
214803
|
-
repository: extractRepoIdentifier(request.repoUrl),
|
|
214804
|
-
ref: request.ref,
|
|
214805
|
-
sha: request.sha || null,
|
|
214806
|
-
workflowRef: request.workflowRef ?? request.workflowName,
|
|
214807
|
-
runId: request.runId,
|
|
214808
|
-
jobId: request.jobId,
|
|
214809
|
-
issuer: request.provenanceIssuer ?? ""
|
|
214810
|
-
},
|
|
215756
|
+
localContext: buildLocalContext(request),
|
|
214811
215757
|
reportDeferred: async (report) => {
|
|
214812
215758
|
await relayProvenanceIpc({
|
|
214813
215759
|
op: "defer",
|
|
@@ -215112,20 +216058,6 @@ function buildStepSecrets(request, masker, onMaskerSecretsAdded) {
|
|
|
215112
216058
|
});
|
|
215113
216059
|
}
|
|
215114
216060
|
/**
|
|
215115
|
-
* Create a LogMasker initialized with all secret values from the request.
|
|
215116
|
-
*
|
|
215117
|
-
* Collects values from both flat secrets and all namespaced context secrets,
|
|
215118
|
-
* deduplicating before registration.
|
|
215119
|
-
*/
|
|
215120
|
-
function createSecretMasker(request) {
|
|
215121
|
-
const masker = new LogMasker();
|
|
215122
|
-
const allSecrets = {};
|
|
215123
|
-
if (request.secrets) Object.assign(allSecrets, request.secrets);
|
|
215124
|
-
if (request.namespacedSecrets) for (const contextSecrets of Object.values(request.namespacedSecrets)) Object.assign(allSecrets, contextSecrets);
|
|
215125
|
-
masker.registerSecrets(allSecrets);
|
|
215126
|
-
return masker;
|
|
215127
|
-
}
|
|
215128
|
-
/**
|
|
215129
216061
|
* Build a fresh zx `$` shell bound to the sandbox working directory and the
|
|
215130
216062
|
* sanitized environment (process.env was set by the parent via env-sanitizer
|
|
215131
216063
|
* before spawning this process). This is the single shell-construction code
|
|
@@ -215156,12 +216088,13 @@ function createSecretMasker(request) {
|
|
|
215156
216088
|
* step$.log would only set it on the function object and NOT propagate to the
|
|
215157
216089
|
* AsyncLocalStorage store that zx uses for ProcessPromise snapshots.
|
|
215158
216090
|
*/
|
|
215159
|
-
function buildSandboxShell(cwd, stepIndex, maskedSendFn) {
|
|
216091
|
+
function buildSandboxShell(cwd, stepIndex, maskedSendFn, signal) {
|
|
215160
216092
|
return $$1({
|
|
215161
216093
|
cwd,
|
|
215162
216094
|
env: process.env,
|
|
215163
216095
|
verbose: true,
|
|
215164
216096
|
quiet: false,
|
|
216097
|
+
...signal ? { signal } : {},
|
|
215165
216098
|
log: makeStreamingZxLog((line, stream) => maskedSendFn({
|
|
215166
216099
|
type: "log.line",
|
|
215167
216100
|
stepIndex,
|
|
@@ -215246,7 +216179,7 @@ function initialJobStatus(loopStatus) {
|
|
|
215246
216179
|
* inside this process with full shell access.
|
|
215247
216180
|
*/
|
|
215248
216181
|
function createSandboxStepContext(workDir, stepIndex, stepName, request, maskedSendFn, outputsMap, refMap, operatorSecretKeys, secretOutputs, jobOutputsMap, secrets, masker, signal, jobTempScope) {
|
|
215249
|
-
const step$ = buildSandboxShell(workDir, stepIndex, maskedSendFn);
|
|
216182
|
+
const step$ = buildSandboxShell(workDir, stepIndex, maskedSendFn, signal);
|
|
215250
216183
|
const log = createIpcLogger(stepIndex, stepName, maskedSendFn);
|
|
215251
216184
|
const rawPayload = rawPayloadFromEvent(request.event);
|
|
215252
216185
|
const kici = buildKiciApi(async (method, params) => {
|
|
@@ -215501,7 +216434,8 @@ async function installDependenciesIfNeeded(workflowDir, request) {
|
|
|
215501
216434
|
await installDeps(kiciDir, {
|
|
215502
216435
|
npmRegistries: request.npmRegistries,
|
|
215503
216436
|
installEnvSecrets: request.installEnvSecrets,
|
|
215504
|
-
jobIdShort: request.jobIdShort
|
|
216437
|
+
jobIdShort: request.jobIdShort,
|
|
216438
|
+
...request.allowInstallScripts ? { allowInstallScripts: true } : {}
|
|
215505
216439
|
});
|
|
215506
216440
|
trace("fallback install complete");
|
|
215507
216441
|
}
|
|
@@ -215526,7 +216460,8 @@ async function installDependenciesIfNeeded(workflowDir, request) {
|
|
|
215526
216460
|
await installDeps(kiciDir, {
|
|
215527
216461
|
npmRegistries: request.npmRegistries,
|
|
215528
216462
|
installEnvSecrets: request.installEnvSecrets,
|
|
215529
|
-
jobIdShort: request.jobIdShort
|
|
216463
|
+
jobIdShort: request.jobIdShort,
|
|
216464
|
+
...request.allowInstallScripts ? { allowInstallScripts: true } : {}
|
|
215530
216465
|
});
|
|
215531
216466
|
trace("installDeps() returned successfully");
|
|
215532
216467
|
} catch (depErr) {
|
|
@@ -215556,7 +216491,7 @@ async function restoreSourceTarballIfRequested(workflowRoot, request) {
|
|
|
215556
216491
|
stepIndex: -1,
|
|
215557
216492
|
line: "[workflow-runner] Restoring .kici/ source from cached tarball"
|
|
215558
216493
|
});
|
|
215559
|
-
await restoreSource(workflowRoot, request.sourceTarUrl);
|
|
216494
|
+
await restoreSource(workflowRoot, request.sourceTarUrl, request.sourceTarDigest);
|
|
215560
216495
|
trace("source tarball restored");
|
|
215561
216496
|
}
|
|
215562
216497
|
/**
|
|
@@ -216057,7 +216992,7 @@ function buildJobRuleCompletion(ruleResult, normalizedSteps) {
|
|
|
216057
216992
|
* success-skip). Returns false when the caller should continue to step
|
|
216058
216993
|
* execution.
|
|
216059
216994
|
*/
|
|
216060
|
-
async function maybeSkipJobOnRules(job, request, normalizedSteps, repos) {
|
|
216995
|
+
async function maybeSkipJobOnRules(job, request, normalizedSteps, send, repos) {
|
|
216061
216996
|
if (!job?.rules || job.rules.length === 0) return false;
|
|
216062
216997
|
const ev = request.event ?? {};
|
|
216063
216998
|
const ruleCtx = createRuleContext({
|
|
@@ -216076,7 +217011,7 @@ async function maybeSkipJobOnRules(job, request, normalizedSteps, repos) {
|
|
|
216076
217011
|
if (!completion) return false;
|
|
216077
217012
|
flushOutputCapture();
|
|
216078
217013
|
capturePrepareActive = false;
|
|
216079
|
-
|
|
217014
|
+
send(completion);
|
|
216080
217015
|
process.exit(0);
|
|
216081
217016
|
}
|
|
216082
217017
|
/**
|
|
@@ -216302,11 +217237,7 @@ async function main() {
|
|
|
216302
217237
|
const sourceDir = isGlobal ? join(workDir, "source") : workDir;
|
|
216303
217238
|
const masker = createSecretMasker(request);
|
|
216304
217239
|
const maskedSend = (msg) => {
|
|
216305
|
-
|
|
216306
|
-
...msg,
|
|
216307
|
-
line: masker.mask(msg.line)
|
|
216308
|
-
});
|
|
216309
|
-
else sendMessage(msg);
|
|
217240
|
+
sendMessage(maskMessageText(msg, masker));
|
|
216310
217241
|
};
|
|
216311
217242
|
const jobDeadline = armJobDeadline(request.jobTimeoutMs, (reason, timeoutMs) => {
|
|
216312
217243
|
jobTimedOut = true;
|
|
@@ -216341,7 +217272,7 @@ async function main() {
|
|
|
216341
217272
|
const jobHasRules = (job?.rules?.length ?? 0) > 0;
|
|
216342
217273
|
const anyStepHasRules = normalizedSteps.some((s) => (s.rules?.length ?? 0) > 0);
|
|
216343
217274
|
await resolveChangedFilesForRules(request, sourceDir, jobHasRules || anyStepHasRules);
|
|
216344
|
-
await maybeSkipJobOnRules(job, request, normalizedSteps, globalRepoInfo);
|
|
217275
|
+
await maybeSkipJobOnRules(job, request, normalizedSteps, maskedSend, globalRepoInfo);
|
|
216345
217276
|
if (aborted) abortAndExit("aborted after rules");
|
|
216346
217277
|
}
|
|
216347
217278
|
const jobHooks = collectJobHooks(job);
|
|
@@ -216450,7 +217381,8 @@ async function main() {
|
|
|
216450
217381
|
jobTimedOut,
|
|
216451
217382
|
jobTimeoutMs: request.jobTimeoutMs,
|
|
216452
217383
|
cancelFailureReason,
|
|
216453
|
-
driftDroppedJobs
|
|
217384
|
+
driftDroppedJobs,
|
|
217385
|
+
send: maskedSend
|
|
216454
217386
|
});
|
|
216455
217387
|
process.exit(finalStatus === ExecutionJobStatus.enum.success ? 0 : 1);
|
|
216456
217388
|
}
|
|
@@ -216463,7 +217395,7 @@ async function main() {
|
|
|
216463
217395
|
function emitJobComplete(args) {
|
|
216464
217396
|
const aggregatedOutputs = {};
|
|
216465
217397
|
for (const [stepName, outputs] of args.outputsMap) aggregatedOutputs[stepName] = outputs;
|
|
216466
|
-
|
|
217398
|
+
args.send({
|
|
216467
217399
|
type: "job.complete",
|
|
216468
217400
|
status: args.finalStatus,
|
|
216469
217401
|
stepResults: args.loopResult.stepResults,
|