@kici-dev/agent 0.6.1 → 0.8.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 +1530 -1666
- package/dist/workflow-runner-bundle.js +1129 -192
- 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
|
@@ -184447,7 +184447,7 @@ function meter() {
|
|
|
184447
184447
|
* Always incremented by 1 on every tick — guaranteed-visible proof
|
|
184448
184448
|
* that the cold-store subsystem is registered and running.
|
|
184449
184449
|
*
|
|
184450
|
-
* `result`
|
|
184450
|
+
* `result` is a {@link ColdStoreCycleResult}.
|
|
184451
184451
|
*/
|
|
184452
184452
|
function coldStoreArchiveCyclesTotal() {
|
|
184453
184453
|
if (!_archiveCyclesTotal) _archiveCyclesTotal = meter().createCounter("cold_store_archive_cycles_total", { description: "Completed archive cycles by db and outcome" });
|
|
@@ -184596,9 +184596,16 @@ function coldStorePurgeDurationSeconds() {
|
|
|
184596
184596
|
});
|
|
184597
184597
|
return _purgeDurationSeconds;
|
|
184598
184598
|
}
|
|
184599
|
-
var _meter, _archiveCyclesTotal, _archiveRowsTotal, _archiveBytesTotal, _archiveDurationSeconds, _rehydrateRequestsTotal, _rehydrateBytesTotal, _rehydrateDurationSeconds, _verifyFailuresTotal, _replayRowsTotal, _replayDurationSeconds, _purgeChunksTotal, _purgeBytesTotal, _purgeDurationSeconds;
|
|
184599
|
+
var _meter, _archiveCyclesTotal, _archiveRowsTotal, _archiveBytesTotal, _archiveDurationSeconds, _rehydrateRequestsTotal, _rehydrateBytesTotal, _rehydrateDurationSeconds, _verifyFailuresTotal, _replayRowsTotal, _replayDurationSeconds, _purgeChunksTotal, _purgeBytesTotal, _purgeDurationSeconds, ColdStoreCycleResult;
|
|
184600
184600
|
var init_metrics = __esmMin((() => {
|
|
184601
184601
|
init_metrics$1();
|
|
184602
|
+
init_zod();
|
|
184603
|
+
ColdStoreCycleResult = _enum([
|
|
184604
|
+
"disabled",
|
|
184605
|
+
"no_tables",
|
|
184606
|
+
"success",
|
|
184607
|
+
"failure"
|
|
184608
|
+
]);
|
|
184602
184609
|
}));
|
|
184603
184610
|
//#endregion
|
|
184604
184611
|
//#region ../shared/dist/cold-store/config.js
|
|
@@ -184788,14 +184795,14 @@ var init_cold_store$1 = __esmMin((() => {
|
|
|
184788
184795
|
summary.skipped.disabled += 1;
|
|
184789
184796
|
coldStoreArchiveCyclesTotal().add(1, {
|
|
184790
184797
|
db: this.db,
|
|
184791
|
-
result:
|
|
184798
|
+
result: ColdStoreCycleResult.enum.disabled
|
|
184792
184799
|
});
|
|
184793
184800
|
return summary;
|
|
184794
184801
|
}
|
|
184795
184802
|
if (this.adapters.size === 0) {
|
|
184796
184803
|
coldStoreArchiveCyclesTotal().add(1, {
|
|
184797
184804
|
db: this.db,
|
|
184798
|
-
result:
|
|
184805
|
+
result: ColdStoreCycleResult.enum.no_tables
|
|
184799
184806
|
});
|
|
184800
184807
|
summary.skipped.no_tables += 1;
|
|
184801
184808
|
this.log("info", "cold-store cycle: no adapters registered", {
|
|
@@ -184812,7 +184819,7 @@ var init_cold_store$1 = __esmMin((() => {
|
|
|
184812
184819
|
await this.processAdapter(adapter, summary);
|
|
184813
184820
|
summary.tablesProcessed += 1;
|
|
184814
184821
|
}
|
|
184815
|
-
const cycleResult = summary.rowsFailed > 0 ?
|
|
184822
|
+
const cycleResult = summary.rowsFailed > 0 ? ColdStoreCycleResult.enum.failure : summary.chunksWritten > 0 ? ColdStoreCycleResult.enum.success : ColdStoreCycleResult.enum.no_tables;
|
|
184816
184823
|
coldStoreArchiveCyclesTotal().add(1, {
|
|
184817
184824
|
db: this.db,
|
|
184818
184825
|
result: cycleResult
|
|
@@ -185747,6 +185754,7 @@ var init_lru = __esmMin((() => {
|
|
|
185747
185754
|
var init_cold_store = __esmMin((() => {
|
|
185748
185755
|
init_chunk_id();
|
|
185749
185756
|
init_chunk_encoder();
|
|
185757
|
+
init_metrics();
|
|
185750
185758
|
init_cold_store$1();
|
|
185751
185759
|
}));
|
|
185752
185760
|
//#endregion
|
|
@@ -185759,6 +185767,7 @@ var dist_exports$1 = /* @__PURE__ */ __exportAll({
|
|
|
185759
185767
|
COLD_BUCKET_NAMES: () => COLD_BUCKET_NAMES,
|
|
185760
185768
|
ChunkLru: () => ChunkLru,
|
|
185761
185769
|
ChunkRequestWaiter: () => ChunkRequestWaiter,
|
|
185770
|
+
ColdStoreCycleResult: () => ColdStoreCycleResult,
|
|
185762
185771
|
DEFAULT_TABLE_CONFIG: () => DEFAULT_TABLE_CONFIG,
|
|
185763
185772
|
DIAGNOSE_OVERALLS: () => DIAGNOSE_OVERALLS,
|
|
185764
185773
|
DIAGNOSE_STATUSES: () => DIAGNOSE_STATUSES,
|
|
@@ -185946,6 +185955,38 @@ var init_attestation_origin = __esmMin((() => {
|
|
|
185946
185955
|
"deferred",
|
|
185947
185956
|
"offline-backfill"
|
|
185948
185957
|
]);
|
|
185958
|
+
}));
|
|
185959
|
+
//#endregion
|
|
185960
|
+
//#region ../engine/dist/provenance/id-token-event-claims.js
|
|
185961
|
+
var provenanceContextSchema;
|
|
185962
|
+
var init_id_token_event_claims = __esmMin((() => {
|
|
185963
|
+
init_zod();
|
|
185964
|
+
provenanceContextSchema = object({
|
|
185965
|
+
/** `owner/repo`, the token's `repository` claim. */
|
|
185966
|
+
repository: string$1().nullable(),
|
|
185967
|
+
/** The branch the run PRESENTS — a pull request's BASE branch. */
|
|
185968
|
+
ref: string$1().nullable(),
|
|
185969
|
+
/** The run's commit SHA, the token's `sha` claim. */
|
|
185970
|
+
sha: string$1().nullable(),
|
|
185971
|
+
/**
|
|
185972
|
+
* The token's `workflow_ref` claim (`<workflow_name>@<sha>`) — NOT the git
|
|
185973
|
+
* ref used to clone a global workflow's repository. The statement's
|
|
185974
|
+
* `workflow.path` is compared against this.
|
|
185975
|
+
*/
|
|
185976
|
+
workflowRef: string$1().nullable(),
|
|
185977
|
+
runId: string$1(),
|
|
185978
|
+
jobId: string$1(),
|
|
185979
|
+
/** The customer's public org id, resolved server-side from the routing key. */
|
|
185980
|
+
orgId: string$1(),
|
|
185981
|
+
/** `triggered` or `run-remote`, derived from the run's local-working-tree flag. */
|
|
185982
|
+
sourceOrigin: string$1(),
|
|
185983
|
+
/** Informational source provider (github / gitlab / …). */
|
|
185984
|
+
provider: string$1().nullable(),
|
|
185985
|
+
/** The orchestrator's provenance issuer, for the statement's `builder.id`. */
|
|
185986
|
+
issuer: string$1(),
|
|
185987
|
+
/** This orchestrator's instance id, also for `builder.id`. */
|
|
185988
|
+
orchestratorId: string$1()
|
|
185989
|
+
}).passthrough();
|
|
185949
185990
|
})), heartbeatSchema, nackSchema;
|
|
185950
185991
|
var init_common = __esmMin((() => {
|
|
185951
185992
|
init_zod();
|
|
@@ -186562,6 +186603,7 @@ var init_access_log = __esmMin((() => {
|
|
|
186562
186603
|
"runs.list.read",
|
|
186563
186604
|
"runs.filters.read",
|
|
186564
186605
|
"sources.list.read",
|
|
186606
|
+
"admin_tokens.list.read",
|
|
186565
186607
|
"run.payload.read",
|
|
186566
186608
|
"run.orch_logs.read",
|
|
186567
186609
|
"step.logs.read",
|
|
@@ -186806,6 +186848,7 @@ var init_event_log = __esmMin((() => {
|
|
|
186806
186848
|
init_zod();
|
|
186807
186849
|
EventLogStatus = _enum([
|
|
186808
186850
|
"received",
|
|
186851
|
+
"shed",
|
|
186809
186852
|
"processed",
|
|
186810
186853
|
"duplicate",
|
|
186811
186854
|
"lockfile_missing",
|
|
@@ -186835,9 +186878,24 @@ var init_deployment_identity = __esmMin((() => {
|
|
|
186835
186878
|
adminInvocation: string$1().min(1).optional(),
|
|
186836
186879
|
adminPath: string$1().min(1).optional()
|
|
186837
186880
|
});
|
|
186881
|
+
}));
|
|
186882
|
+
//#endregion
|
|
186883
|
+
//#region ../engine/dist/protocol/messages/config-paths.js
|
|
186884
|
+
var ConfigPathsSchema;
|
|
186885
|
+
var init_config_paths = __esmMin((() => {
|
|
186886
|
+
init_zod();
|
|
186887
|
+
ConfigPathsSchema = object({
|
|
186888
|
+
/** The env file the service manager loads (`EnvironmentFile=` / `env_file:`). */
|
|
186889
|
+
envFile: string$1().min(1).optional(),
|
|
186890
|
+
/** The scaler YAML file, or the directory when only a directory is configured. */
|
|
186891
|
+
scalerConfig: string$1().min(1).optional(),
|
|
186892
|
+
/** The generated compose file, for a compose deployment. */
|
|
186893
|
+
composeFile: string$1().min(1).optional()
|
|
186894
|
+
});
|
|
186838
186895
|
})), SourceSubtype, OrchestratorMode, SourceProvider, sourceRegistrationSchema, acceptedSourceSchema, sourceRegistrationAckSchema, sourceDeregisterSchema, sourceDeregisterAckSchema;
|
|
186839
186896
|
var init_source_registration = __esmMin((() => {
|
|
186840
186897
|
init_deployment_identity();
|
|
186898
|
+
init_config_paths();
|
|
186841
186899
|
init_zod();
|
|
186842
186900
|
SourceSubtype = _enum([
|
|
186843
186901
|
"github_app",
|
|
@@ -186924,6 +186982,13 @@ var init_source_registration = __esmMin((() => {
|
|
|
186924
186982
|
* publish it, in which case the dashboard treats the shape as `unknown`.
|
|
186925
186983
|
*/
|
|
186926
186984
|
deployment: DeploymentIdentitySchema.optional(),
|
|
186985
|
+
/**
|
|
186986
|
+
* Where this orchestrator's own config files live on its host, so the
|
|
186987
|
+
* dashboard can point an operator straight at them. Optional: an
|
|
186988
|
+
* orchestrator that predates the field omits it, and each member is omitted
|
|
186989
|
+
* independently when that path is not knowable.
|
|
186990
|
+
*/
|
|
186991
|
+
configPaths: ConfigPathsSchema.optional(),
|
|
186927
186992
|
/** Whether this orchestrator has S3 log storage configured. Used for multi-orch pool validation. */
|
|
186928
186993
|
s3LogAccess: boolean$1().optional(),
|
|
186929
186994
|
/** Queue timeout in ms. Platform uses this (with margin) for safety-net GC of stale queued jobs. */
|
|
@@ -187107,6 +187172,27 @@ var init_types$3 = __esmMin((() => {
|
|
|
187107
187172
|
]);
|
|
187108
187173
|
}));
|
|
187109
187174
|
//#endregion
|
|
187175
|
+
//#region ../engine/dist/regex-flags.js
|
|
187176
|
+
/**
|
|
187177
|
+
* Drop `g` and `y` from a flag string. Every other ECMAScript flag —
|
|
187178
|
+
* `d` (hasIndices), `i`, `m`, `s`, `u`, `v` — is meaningful to a single
|
|
187179
|
+
* `.test()` and is preserved.
|
|
187180
|
+
*/
|
|
187181
|
+
function stripStatefulRegexFlags(flags) {
|
|
187182
|
+
return (flags ?? "").replace(STATEFUL_FLAGS, "");
|
|
187183
|
+
}
|
|
187184
|
+
/**
|
|
187185
|
+
* Same, but collapsing an empty result to `undefined` — the shape the SDK's
|
|
187186
|
+
* pattern types and the lock file use for "no flags".
|
|
187187
|
+
*/
|
|
187188
|
+
function normalizeRegexFlags(flags) {
|
|
187189
|
+
return stripStatefulRegexFlags(flags) || void 0;
|
|
187190
|
+
}
|
|
187191
|
+
var STATEFUL_FLAGS;
|
|
187192
|
+
var init_regex_flags = __esmMin((() => {
|
|
187193
|
+
STATEFUL_FLAGS = /[gy]/g;
|
|
187194
|
+
}));
|
|
187195
|
+
//#endregion
|
|
187110
187196
|
//#region ../engine/dist/labels-match.js
|
|
187111
187197
|
var LabelMatcher, HostTargetValue, HostTargetSelector;
|
|
187112
187198
|
var init_labels_match = __esmMin((() => {
|
|
@@ -187304,7 +187390,7 @@ var init_dashboard_global_workflows = __esmMin((() => {
|
|
|
187304
187390
|
settings: globalWorkflowSettingsSchema.optional(),
|
|
187305
187391
|
error: string$1().optional()
|
|
187306
187392
|
});
|
|
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;
|
|
187393
|
+
})), 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
187394
|
var init_dashboard = __esmMin((() => {
|
|
187309
187395
|
init_check_mode();
|
|
187310
187396
|
init_source_origin();
|
|
@@ -187780,6 +187866,40 @@ var init_dashboard = __esmMin((() => {
|
|
|
187780
187866
|
nextCursor: string$1().optional(),
|
|
187781
187867
|
error: string$1().optional()
|
|
187782
187868
|
});
|
|
187869
|
+
dashboardAdminTokenSummarySchema = object({
|
|
187870
|
+
id: string$1(),
|
|
187871
|
+
label: string$1(),
|
|
187872
|
+
/** `owner`, `admin` or `auditor` — the orchestrator's three fixed roles. */
|
|
187873
|
+
role: string$1(),
|
|
187874
|
+
/**
|
|
187875
|
+
* The intended holder the operator recorded at `token create --subject` (an
|
|
187876
|
+
* OIDC `sub` or an email), or null when nobody did. Null is the report's
|
|
187877
|
+
* `unlinked` finding.
|
|
187878
|
+
*/
|
|
187879
|
+
subject: string$1().nullable(),
|
|
187880
|
+
/**
|
|
187881
|
+
* The single routing key this token is restricted to, or null for an
|
|
187882
|
+
* unscoped token. Load-bearing for the report: a routing-key-scoped token is
|
|
187883
|
+
* refused outright on the secret, context, org-settings and trust-policy
|
|
187884
|
+
* routes, so it is materially less privileged than its role alone suggests.
|
|
187885
|
+
*/
|
|
187886
|
+
routingKey: string$1().nullable(),
|
|
187887
|
+
createdAt: string$1(),
|
|
187888
|
+
expiresAt: string$1().nullable(),
|
|
187889
|
+
lastUsedAt: string$1().nullable(),
|
|
187890
|
+
revoked: boolean$1()
|
|
187891
|
+
});
|
|
187892
|
+
dashboardAdminTokensListRequestSchema = object({
|
|
187893
|
+
type: literal("dashboard.admin-tokens.list"),
|
|
187894
|
+
requestId: string$1(),
|
|
187895
|
+
actor: actorPrincipalSchema
|
|
187896
|
+
});
|
|
187897
|
+
dashboardAdminTokensListResponseSchema = object({
|
|
187898
|
+
type: literal("dashboard.admin-tokens.list.response"),
|
|
187899
|
+
requestId: string$1(),
|
|
187900
|
+
tokens: array(dashboardAdminTokenSummarySchema),
|
|
187901
|
+
error: string$1().optional()
|
|
187902
|
+
});
|
|
187783
187903
|
runRerunRequestSchema = object({
|
|
187784
187904
|
type: literal("run.rerun.request"),
|
|
187785
187905
|
requestId: string$1(),
|
|
@@ -189047,7 +189167,8 @@ var init_dashboard = __esmMin((() => {
|
|
|
189047
189167
|
jobName: string$1(),
|
|
189048
189168
|
status: string$1(),
|
|
189049
189169
|
exitCode: number$1().nullable().optional(),
|
|
189050
|
-
errorMessage: string$1().nullable().optional()
|
|
189170
|
+
errorMessage: string$1().nullable().optional(),
|
|
189171
|
+
durationMs: number$1().nullable().optional()
|
|
189051
189172
|
})).optional(),
|
|
189052
189173
|
done: boolean$1().optional(),
|
|
189053
189174
|
error: string$1().optional()
|
|
@@ -189093,6 +189214,7 @@ var init_dashboard = __esmMin((() => {
|
|
|
189093
189214
|
dashboardRunsListRequestSchema,
|
|
189094
189215
|
dashboardRunsFiltersRequestSchema,
|
|
189095
189216
|
dashboardSourcesListRequestSchema,
|
|
189217
|
+
dashboardAdminTokensListRequestSchema,
|
|
189096
189218
|
runRerunRequestSchema,
|
|
189097
189219
|
manualScheduleRequestSchema,
|
|
189098
189220
|
runCancelRequestSchema,
|
|
@@ -189169,6 +189291,7 @@ var init_dashboard = __esmMin((() => {
|
|
|
189169
189291
|
dashboardRunsListResponseSchema,
|
|
189170
189292
|
dashboardRunsFiltersResponseSchema,
|
|
189171
189293
|
dashboardSourcesListResponseSchema,
|
|
189294
|
+
dashboardAdminTokensListResponseSchema,
|
|
189172
189295
|
runRerunResponseSchema,
|
|
189173
189296
|
manualScheduleResponseSchema,
|
|
189174
189297
|
runCancelResponseSchema,
|
|
@@ -189392,6 +189515,26 @@ var init_dashboard = __esmMin((() => {
|
|
|
189392
189515
|
adminInvocation: string$1().nullable(),
|
|
189393
189516
|
adminPath: string$1().nullable()
|
|
189394
189517
|
}),
|
|
189518
|
+
/**
|
|
189519
|
+
* Absolute host paths of this orchestrator's own config files, for an
|
|
189520
|
+
* operator who wants to inspect them.
|
|
189521
|
+
*
|
|
189522
|
+
* Optional, and every member nullable. The Platform always emits the object
|
|
189523
|
+
* — all-nulls when nothing is known — but the field is declared optional
|
|
189524
|
+
* because a customer-installed `kici` CLI validates this same schema and may
|
|
189525
|
+
* be pointed at a Platform that predates the field. Members are null rather
|
|
189526
|
+
* than absent so a reader never has to distinguish "not sent" from "not
|
|
189527
|
+
* known".
|
|
189528
|
+
*
|
|
189529
|
+
* Null for every connection served from the database rather than from a live
|
|
189530
|
+
* in-memory registry entry: these are live-host runtime paths and are not
|
|
189531
|
+
* persisted, the same treatment `deployment.adminInvocation` already gets.
|
|
189532
|
+
*/
|
|
189533
|
+
configPaths: object({
|
|
189534
|
+
envFile: string$1().nullable(),
|
|
189535
|
+
scalerConfig: string$1().nullable(),
|
|
189536
|
+
composeFile: string$1().nullable()
|
|
189537
|
+
}).optional(),
|
|
189395
189538
|
s3LogAccess: boolean$1().nullable().optional(),
|
|
189396
189539
|
agentCount: number$1(),
|
|
189397
189540
|
runningJobs: number$1(),
|
|
@@ -189732,7 +189875,7 @@ var init_dashboard_write_operations = __esmMin((() => {
|
|
|
189732
189875
|
category: "Held runs",
|
|
189733
189876
|
label: "Approve held run",
|
|
189734
189877
|
sensitivity: "dispatch",
|
|
189735
|
-
cliEquivalent: "kici-admin
|
|
189878
|
+
cliEquivalent: "kici-admin held-run approve"
|
|
189736
189879
|
},
|
|
189737
189880
|
{
|
|
189738
189881
|
name: "held_runs.reject",
|
|
@@ -189740,7 +189883,7 @@ var init_dashboard_write_operations = __esmMin((() => {
|
|
|
189740
189883
|
category: "Held runs",
|
|
189741
189884
|
label: "Reject held run",
|
|
189742
189885
|
sensitivity: "dispatch",
|
|
189743
|
-
cliEquivalent: "kici-admin
|
|
189886
|
+
cliEquivalent: "kici-admin held-run reject"
|
|
189744
189887
|
},
|
|
189745
189888
|
{
|
|
189746
189889
|
name: "event_dlq.retry",
|
|
@@ -189831,6 +189974,7 @@ var init_dashboard_write_operations = __esmMin((() => {
|
|
|
189831
189974
|
cliEquivalent: "kici-admin host remove"
|
|
189832
189975
|
}
|
|
189833
189976
|
]);
|
|
189977
|
+
Object.freeze(["held_runs.approve", "held_runs.reject"]);
|
|
189834
189978
|
DASHBOARD_WRITE_OPERATIONS_BY_NAME = new Map(DASHBOARD_WRITE_OPERATIONS.map((d) => [d.name, d]));
|
|
189835
189979
|
new Map(DASHBOARD_WRITE_OPERATIONS.map((d) => [d.wireMessageType, d]));
|
|
189836
189980
|
DashboardWritePolicyState = _enum([
|
|
@@ -191123,8 +191267,13 @@ var init_peer = __esmMin((() => {
|
|
|
191123
191267
|
providerContext: record(string$1(), unknown()).optional(),
|
|
191124
191268
|
/** Pre-signed source tarball download URL (cache hit). */
|
|
191125
191269
|
sourceTarUrl: string$1().optional(),
|
|
191126
|
-
/**
|
|
191270
|
+
/**
|
|
191271
|
+
* @deprecated Use `sourceTarDigest` — this carries the workflow
|
|
191272
|
+
* `contentHash`, not a hash of the tarball bytes.
|
|
191273
|
+
*/
|
|
191127
191274
|
sourceTarHash: string$1().optional(),
|
|
191275
|
+
/** SHA-256 of the source tarball's own bytes, for integrity verification. */
|
|
191276
|
+
sourceTarDigest: string$1().optional(),
|
|
191128
191277
|
/** Pre-signed dependency tarball download URL (cache hit). */
|
|
191129
191278
|
depsUrl: string$1().optional(),
|
|
191130
191279
|
/** Dependency tarball hash for cache keying. */
|
|
@@ -191346,8 +191495,9 @@ var init_peer = __esmMin((() => {
|
|
|
191346
191495
|
peerAgentTokenRevokeSchema,
|
|
191347
191496
|
peerScalerEventSchema
|
|
191348
191497
|
]);
|
|
191349
|
-
})), 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;
|
|
191498
|
+
})), 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, rateLimitWarningSchema, fleetBundleChunkSchema, fleetBundleErrorSchema, StepApprovalOutcome, stepApprovalPayloadSchema, stepApprovalRequestSchema, stepApprovalResolvedSchema;
|
|
191350
191499
|
var init_orchestrator_agent = __esmMin((() => {
|
|
191500
|
+
init_id_token_event_claims();
|
|
191351
191501
|
init_execution_status();
|
|
191352
191502
|
init_dsse();
|
|
191353
191503
|
init_types$4();
|
|
@@ -191413,8 +191563,19 @@ var init_orchestrator_agent = __esmMin((() => {
|
|
|
191413
191563
|
concurrencyWaitTimeoutMs: number().optional(),
|
|
191414
191564
|
/** URL or file:// path to a pre-packed `.kici/` source tarball. If present, agent extracts it into workDir instead of cloning the repo. */
|
|
191415
191565
|
sourceTarUrl: string$1().optional(),
|
|
191416
|
-
/**
|
|
191566
|
+
/**
|
|
191567
|
+
* @deprecated Use `sourceTarDigest`. Despite its name this carries the
|
|
191568
|
+
* workflow `contentHash`, not a hash of the tarball bytes, so an agent
|
|
191569
|
+
* could not verify a restored tarball against it. Kept on the wire for
|
|
191570
|
+
* older agents; removed at v1.0.0.
|
|
191571
|
+
*/
|
|
191417
191572
|
sourceTarHash: string$1().optional(),
|
|
191573
|
+
/**
|
|
191574
|
+
* SHA-256 of the source tarball's own bytes, for integrity verification
|
|
191575
|
+
* before extraction. The sibling of `depsHash`, which has always carried
|
|
191576
|
+
* the dependency tarball's real digest.
|
|
191577
|
+
*/
|
|
191578
|
+
sourceTarDigest: string$1().optional(),
|
|
191418
191579
|
/** URL or file:// path to pre-built dependency tarball. If present, agent extracts to .kici/node_modules/ instead of running install. */
|
|
191419
191580
|
depsUrl: string$1().optional(),
|
|
191420
191581
|
/** SHA-256 hash of the dependency tarball for integrity verification. */
|
|
@@ -191423,6 +191584,22 @@ var init_orchestrator_agent = __esmMin((() => {
|
|
|
191423
191584
|
requestId: string$1().optional(),
|
|
191424
191585
|
/** Base64-encoded X25519 public key for the workflow run (for encrypting secret outputs). */
|
|
191425
191586
|
runPublicKey: string$1().optional(),
|
|
191587
|
+
/**
|
|
191588
|
+
* The orchestrator's own view of the build, for a provenance statement the
|
|
191589
|
+
* agent has to freeze before its identity token exists (the deferred path).
|
|
191590
|
+
*
|
|
191591
|
+
* Every field is what `buildIdTokenClaims` derives from the run row, so a
|
|
191592
|
+
* frozen statement built from this is field-for-field what a live mint
|
|
191593
|
+
* would have produced — and the server can therefore cross-check it. The
|
|
191594
|
+
* agent's local guess is NOT: the job's checkout `ref` is a pull request's
|
|
191595
|
+
* HEAD branch where the claim is the BASE branch, and `workflowRef` here is
|
|
191596
|
+
* the `<name>@<sha>` claim rather than a global workflow's clone ref.
|
|
191597
|
+
*
|
|
191598
|
+
* Additive and optional: an older orchestrator omits it and the agent falls
|
|
191599
|
+
* back to its local guess. That fallback statement fails the capture
|
|
191600
|
+
* cross-check, so the defer is dropped rather than stored unchecked.
|
|
191601
|
+
*/
|
|
191602
|
+
provenanceContext: provenanceContextSchema.optional(),
|
|
191426
191603
|
/** Plain outputs from upstream jobs (keyed by job name, then by step name). Populated for downstream jobs with `needs` dependencies. */
|
|
191427
191604
|
upstreamJobOutputs: record(string$1(), record(string$1(), unknown())).optional(),
|
|
191428
191605
|
/** Terminal status of each upstream job (keyed by job name; per-child for fan-out). Powers `ctx.needs.<job>.status`. */
|
|
@@ -191783,7 +191960,18 @@ var init_orchestrator_agent = __esmMin((() => {
|
|
|
191783
191960
|
* and hashed it by the time it asks. Optional so an older agent that omits it
|
|
191784
191961
|
* still gets a usable (lockfile-keyed) URL during a mixed-version rollout.
|
|
191785
191962
|
*/
|
|
191786
|
-
depsHash: string$1().optional()
|
|
191963
|
+
depsHash: string$1().optional(),
|
|
191964
|
+
/**
|
|
191965
|
+
* SHA-256 of the source tarball about to be uploaded. Source uploads only.
|
|
191966
|
+
*
|
|
191967
|
+
* The source tarball is stored under its own content hash, so the
|
|
191968
|
+
* orchestrator needs it to sign the upload URL — the agent has already packed
|
|
191969
|
+
* and hashed it by the time it asks. Optional so an older agent that omits it
|
|
191970
|
+
* still gets a usable URL during a mixed-version rollout.
|
|
191971
|
+
*/
|
|
191972
|
+
sourceTarDigest: string$1().optional(),
|
|
191973
|
+
/** In-repo `workspace:` sibling closure digest; part of the dep pointer key. */
|
|
191974
|
+
siblingsDigest: string$1().optional()
|
|
191787
191975
|
});
|
|
191788
191976
|
cacheUploadResponseSchema = object({
|
|
191789
191977
|
type: literal("cache.upload.response"),
|
|
@@ -191800,7 +191988,11 @@ var init_orchestrator_agent = __esmMin((() => {
|
|
|
191800
191988
|
platform: string$1(),
|
|
191801
191989
|
arch: string$1(),
|
|
191802
191990
|
/** SHA-256 hash of the dependency tarball for integrity verification. Only present for deps uploads. */
|
|
191803
|
-
depsHash: string$1().optional()
|
|
191991
|
+
depsHash: string$1().optional(),
|
|
191992
|
+
/** SHA-256 of the source tarball's own bytes. Only present for source uploads. */
|
|
191993
|
+
sourceTarDigest: string$1().optional(),
|
|
191994
|
+
/** In-repo `workspace:` sibling closure digest; part of the dep pointer key. */
|
|
191995
|
+
siblingsDigest: string$1().optional()
|
|
191804
191996
|
});
|
|
191805
191997
|
cacheUserRestoreRequestSchema = object({
|
|
191806
191998
|
type: literal("cache.user.restore.request"),
|
|
@@ -191880,8 +192072,12 @@ var init_orchestrator_agent = __esmMin((() => {
|
|
|
191880
192072
|
subjectName: string$1(),
|
|
191881
192073
|
/** Primary subject digest (lowercase hex). */
|
|
191882
192074
|
subjectDigest: string$1(),
|
|
191883
|
-
/**
|
|
191884
|
-
audience
|
|
192075
|
+
/**
|
|
192076
|
+
* Requested token audience for the later mint. Bounded to match the LIVE
|
|
192077
|
+
* mint's `oidcTokenRequestParamsSchema` — the two are the same
|
|
192078
|
+
* agent-supplied value and had no reason to differ.
|
|
192079
|
+
*/
|
|
192080
|
+
audience: string$1().min(1).max(255),
|
|
191885
192081
|
/** Bundle media type. */
|
|
191886
192082
|
mediaType: string$1(),
|
|
191887
192083
|
/** SHA-256 of the frozen DSSE statement payload — the later-mint binding. */
|
|
@@ -192103,6 +192299,11 @@ var init_orchestrator_agent = __esmMin((() => {
|
|
|
192103
192299
|
/** Per-node cap on raw log bytes. */
|
|
192104
192300
|
maxBytes: number$1()
|
|
192105
192301
|
});
|
|
192302
|
+
rateLimitWarningSchema = object({
|
|
192303
|
+
type: literal("rate.limit.warning"),
|
|
192304
|
+
/** Estimated wait until the limiter admits another frame of that size. */
|
|
192305
|
+
retryAfterMs: number$1().optional()
|
|
192306
|
+
});
|
|
192106
192307
|
fleetBundleChunkSchema = object({
|
|
192107
192308
|
type: literal("fleet.bundle.chunk"),
|
|
192108
192309
|
requestId: string$1(),
|
|
@@ -192173,7 +192374,8 @@ var init_orchestrator_agent = __esmMin((() => {
|
|
|
192173
192374
|
agentAuthSuccessSchema,
|
|
192174
192375
|
agentAuthFailureSchema,
|
|
192175
192376
|
fleetLogsRequestSchema,
|
|
192176
|
-
stepApprovalResolvedSchema
|
|
192377
|
+
stepApprovalResolvedSchema,
|
|
192378
|
+
rateLimitWarningSchema
|
|
192177
192379
|
]);
|
|
192178
192380
|
discriminatedUnion("type", [
|
|
192179
192381
|
agentRegisterSchema,
|
|
@@ -202307,6 +202509,20 @@ var init_plan_type = __esmMin((() => {
|
|
|
202307
202509
|
PlanType.exclude(["free"]);
|
|
202308
202510
|
PlanType.options;
|
|
202309
202511
|
PlanType.enum.free, PlanType.enum.pro, PlanType.enum.team, PlanType.enum.business;
|
|
202512
|
+
})), StripeSubscriptionStatus;
|
|
202513
|
+
var init_subscription_status = __esmMin((() => {
|
|
202514
|
+
init_zod();
|
|
202515
|
+
StripeSubscriptionStatus = _enum([
|
|
202516
|
+
"incomplete",
|
|
202517
|
+
"incomplete_expired",
|
|
202518
|
+
"trialing",
|
|
202519
|
+
"active",
|
|
202520
|
+
"past_due",
|
|
202521
|
+
"canceled",
|
|
202522
|
+
"unpaid",
|
|
202523
|
+
"paused"
|
|
202524
|
+
]);
|
|
202525
|
+
StripeSubscriptionStatus.enum.active, StripeSubscriptionStatus.enum.trialing, StripeSubscriptionStatus.enum.past_due;
|
|
202310
202526
|
})), InfraAlertType;
|
|
202311
202527
|
var init_infra_alert = __esmMin((() => {
|
|
202312
202528
|
init_zod();
|
|
@@ -202325,6 +202541,7 @@ var init_dist$1 = __esmMin((() => {
|
|
|
202325
202541
|
init_check_mode();
|
|
202326
202542
|
init_source_origin();
|
|
202327
202543
|
init_attestation_origin();
|
|
202544
|
+
init_id_token_event_claims();
|
|
202328
202545
|
init_common();
|
|
202329
202546
|
init_actor();
|
|
202330
202547
|
init_pat_kind();
|
|
@@ -202339,11 +202556,13 @@ var init_dist$1 = __esmMin((() => {
|
|
|
202339
202556
|
init_run_events();
|
|
202340
202557
|
init_event_log();
|
|
202341
202558
|
init_deployment_identity();
|
|
202559
|
+
init_config_paths();
|
|
202342
202560
|
init_source_registration();
|
|
202343
202561
|
init_scaler_backend_type();
|
|
202344
202562
|
init_types$4();
|
|
202345
202563
|
init_presentation();
|
|
202346
202564
|
init_types$3();
|
|
202565
|
+
init_regex_flags();
|
|
202347
202566
|
init_labels_match();
|
|
202348
202567
|
init_concurrency_strategy();
|
|
202349
202568
|
init_inventory();
|
|
@@ -202385,6 +202604,7 @@ var init_dist$1 = __esmMin((() => {
|
|
|
202385
202604
|
init_expand$1();
|
|
202386
202605
|
init_name();
|
|
202387
202606
|
init_plan_type();
|
|
202607
|
+
init_subscription_status();
|
|
202388
202608
|
init_infra_alert();
|
|
202389
202609
|
}));
|
|
202390
202610
|
//#endregion
|
|
@@ -203456,6 +203676,25 @@ var init_outputs = __esmMin((() => {
|
|
|
203456
203676
|
function assertSecretName(value, field, subject = "git credential") {
|
|
203457
203677
|
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.`);
|
|
203458
203678
|
}
|
|
203679
|
+
/**
|
|
203680
|
+
* Every `<name>Secret` field in a credential map names a secret in qualified
|
|
203681
|
+
* `<context>:<secret-name>` form, and never carries the material itself.
|
|
203682
|
+
*
|
|
203683
|
+
* `subject` is the declaring surface as the author wrote it — `job('build')` or
|
|
203684
|
+
* `dynamicJob('reports')` — so a message names the call the author can find.
|
|
203685
|
+
* A `<name>Value` sibling is material by design and is not checked here.
|
|
203686
|
+
*/
|
|
203687
|
+
function validateGitCredentials(subject, credentials) {
|
|
203688
|
+
for (const [alias, ref] of Object.entries(credentials)) {
|
|
203689
|
+
const bag = ref;
|
|
203690
|
+
for (const [field, value] of Object.entries(bag)) {
|
|
203691
|
+
if (!field.endsWith("Secret") || typeof value !== "string") continue;
|
|
203692
|
+
assertSecretName(value, `${alias}.${field}`);
|
|
203693
|
+
const idx = value.indexOf(":");
|
|
203694
|
+
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})`);
|
|
203695
|
+
}
|
|
203696
|
+
}
|
|
203697
|
+
}
|
|
203459
203698
|
var init_git_types = __esmMin((() => {}));
|
|
203460
203699
|
//#endregion
|
|
203461
203700
|
//#region ../sdk/dist/job.js
|
|
@@ -203508,7 +203747,7 @@ function job(nameOrOptions, maybeOptions) {
|
|
|
203508
203747
|
if (options.runsOn !== void 0 && options.runsOnAll !== void 0) throw new Error(`job('${name}'): runsOn and runsOnAll are mutually exclusive`);
|
|
203509
203748
|
if (options.container !== void 0) validateContainerImageSource(name, options.container);
|
|
203510
203749
|
if (options.container && typeof options.container === "object" && options.container.auth) validateContainerAuth(name, options.container.auth);
|
|
203511
|
-
if (options.gitCredentials) validateGitCredentials(name
|
|
203750
|
+
if (options.gitCredentials) validateGitCredentials(`job('${name}')`, options.gitCredentials);
|
|
203512
203751
|
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`);
|
|
203513
203752
|
if (options.onUnreachable !== void 0 && options.runsOnAll === void 0) console.warn(`[kici] job('${name}'): onUnreachable is ignored without runsOnAll`);
|
|
203514
203753
|
if (options.includeUninitialized !== void 0 && options.runsOnAll === void 0) console.warn(`[kici] job('${name}'): includeUninitialized is ignored without runsOnAll`);
|
|
@@ -203626,17 +203865,6 @@ function validateContainerAuth(name, auth) {
|
|
|
203626
203865
|
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})`);
|
|
203627
203866
|
}
|
|
203628
203867
|
}
|
|
203629
|
-
function validateGitCredentials(name, credentials) {
|
|
203630
|
-
for (const [alias, ref] of Object.entries(credentials)) {
|
|
203631
|
-
const bag = ref;
|
|
203632
|
-
for (const [field, value] of Object.entries(bag)) {
|
|
203633
|
-
if (!field.endsWith("Secret") || typeof value !== "string") continue;
|
|
203634
|
-
assertSecretName(value, `${alias}.${field}`);
|
|
203635
|
-
const idx = value.indexOf(":");
|
|
203636
|
-
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})`);
|
|
203637
|
-
}
|
|
203638
|
-
}
|
|
203639
|
-
}
|
|
203640
203868
|
var init_job = __esmMin((() => {
|
|
203641
203869
|
init_outputs();
|
|
203642
203870
|
init_git_types();
|
|
@@ -204135,12 +204363,17 @@ var init_invoke = __esmMin((() => {
|
|
|
204135
204363
|
/**
|
|
204136
204364
|
* Convert a string or RegExp to a BranchPattern.
|
|
204137
204365
|
* Strings become glob patterns, RegExp becomes regex patterns.
|
|
204366
|
+
*
|
|
204367
|
+
* `g` and `y` are dropped from the author's flags: the matcher memoizes one
|
|
204368
|
+
* `RegExp` per (flags, pattern), and a sticky instance carries `lastIndex`
|
|
204369
|
+
* between calls — so `/^release-\d+/g` would match `release-1`, miss
|
|
204370
|
+
* `release-2`, and match `release-3`, silently skipping every second push.
|
|
204138
204371
|
*/
|
|
204139
204372
|
function toBranchPattern(input) {
|
|
204140
204373
|
if (input instanceof RegExp) return {
|
|
204141
204374
|
type: "regex",
|
|
204142
204375
|
pattern: input.source,
|
|
204143
|
-
flags: input.flags
|
|
204376
|
+
flags: normalizeRegexFlags(input.flags)
|
|
204144
204377
|
};
|
|
204145
204378
|
return {
|
|
204146
204379
|
type: "glob",
|
|
@@ -204155,6 +204388,7 @@ function asArray(value) {
|
|
|
204155
204388
|
}
|
|
204156
204389
|
var DEFAULT_PR_EVENTS;
|
|
204157
204390
|
var init_types$2 = __esmMin((() => {
|
|
204391
|
+
init_dist$1();
|
|
204158
204392
|
DEFAULT_PR_EVENTS = [
|
|
204159
204393
|
"opened",
|
|
204160
204394
|
"synchronize",
|
|
@@ -204294,12 +204528,16 @@ var init_tag = __esmMin((() => {
|
|
|
204294
204528
|
/**
|
|
204295
204529
|
* Convert a string or RegExp to a BodyMatchPattern.
|
|
204296
204530
|
* Strings become glob patterns, RegExp becomes regex patterns.
|
|
204531
|
+
*
|
|
204532
|
+
* `g` and `y` are dropped for the same reason as `toBranchPattern`: the reader
|
|
204533
|
+
* memoizes the compiled instance, so a sticky one alternates verdicts between
|
|
204534
|
+
* comments.
|
|
204297
204535
|
*/
|
|
204298
204536
|
function toBodyMatchPattern(input) {
|
|
204299
204537
|
if (input instanceof RegExp) return {
|
|
204300
204538
|
type: "regex",
|
|
204301
204539
|
pattern: input.source,
|
|
204302
|
-
flags: input.flags
|
|
204540
|
+
flags: normalizeRegexFlags(input.flags)
|
|
204303
204541
|
};
|
|
204304
204542
|
return {
|
|
204305
204543
|
type: "glob",
|
|
@@ -204333,6 +204571,7 @@ function comment(config) {
|
|
|
204333
204571
|
}
|
|
204334
204572
|
var init_comment = __esmMin((() => {
|
|
204335
204573
|
init_types$2();
|
|
204574
|
+
init_dist$1();
|
|
204336
204575
|
}));
|
|
204337
204576
|
//#endregion
|
|
204338
204577
|
//#region ../sdk/dist/triggers/review.js
|
|
@@ -204882,6 +205121,27 @@ function defineDispatchInputs(map) {
|
|
|
204882
205121
|
}
|
|
204883
205122
|
var init_dispatch_inputs = __esmMin((() => {}));
|
|
204884
205123
|
//#endregion
|
|
205124
|
+
//#region ../sdk/dist/triggers/index.js
|
|
205125
|
+
var init_triggers = __esmMin((() => {
|
|
205126
|
+
init_types$2();
|
|
205127
|
+
init_pr();
|
|
205128
|
+
init_push();
|
|
205129
|
+
init_tag();
|
|
205130
|
+
init_comment();
|
|
205131
|
+
init_review();
|
|
205132
|
+
init_review_comment();
|
|
205133
|
+
init_release();
|
|
205134
|
+
init_dispatch();
|
|
205135
|
+
init_create();
|
|
205136
|
+
init_delete();
|
|
205137
|
+
init_status();
|
|
205138
|
+
init_workflow_run();
|
|
205139
|
+
init_fork();
|
|
205140
|
+
init_star();
|
|
205141
|
+
init_watch();
|
|
205142
|
+
init_webhook();
|
|
205143
|
+
}));
|
|
205144
|
+
//#endregion
|
|
204885
205145
|
//#region ../sdk/dist/hooks/index.js
|
|
204886
205146
|
/**
|
|
204887
205147
|
* Normalize HookInput to extract run function and optional timeout.
|
|
@@ -205236,12 +205496,16 @@ function isDynamicJobFn(item) {
|
|
|
205236
205496
|
* Two forms:
|
|
205237
205497
|
* - **Function form** (event-only): `dynamicJob('shards', async ({ ctx }) => [...])`.
|
|
205238
205498
|
* Dispatched at webhook time; deterministic from `ctx.event` alone.
|
|
205239
|
-
* - **Options form
|
|
205240
|
-
*
|
|
205241
|
-
* evaluated with the upstreams' frozen outputs
|
|
205499
|
+
* - **Options form**: `dynamicJob('reports', { needs, generate, gitCredentials })`.
|
|
205500
|
+
* With `needs` it is result-aware — deferred until every job/group in `needs`
|
|
205501
|
+
* completes, then `generate` is evaluated with the upstreams' frozen outputs
|
|
205502
|
+
* available as `ctx.needs`. `needs` is optional: without it the generator is
|
|
205503
|
+
* dispatched at webhook time, as the function form is. `gitCredentials`
|
|
205504
|
+
* declares the named credentials every generated job inherits, and only the
|
|
205505
|
+
* options form can carry it.
|
|
205242
205506
|
*
|
|
205243
205507
|
* @param groupName - The group name (must match what static jobs reference)
|
|
205244
|
-
* @param fnOrConfig - The generator function, or a
|
|
205508
|
+
* @param fnOrConfig - The generator function, or a `{ needs, generate, gitCredentials }` config
|
|
205245
205509
|
*/
|
|
205246
205510
|
function dynamicJob(groupName, fnOrConfig) {
|
|
205247
205511
|
const isConfig = typeof fnOrConfig !== "function";
|
|
@@ -205250,10 +205514,17 @@ function dynamicJob(groupName, fnOrConfig) {
|
|
|
205250
205514
|
value: groupName,
|
|
205251
205515
|
enumerable: false
|
|
205252
205516
|
});
|
|
205253
|
-
if (isConfig) Object.defineProperty(tagged, DYNAMIC_JOB_NEEDS_TAG, {
|
|
205517
|
+
if (isConfig && fnOrConfig.needs) Object.defineProperty(tagged, DYNAMIC_JOB_NEEDS_TAG, {
|
|
205254
205518
|
value: fnOrConfig.needs,
|
|
205255
205519
|
enumerable: false
|
|
205256
205520
|
});
|
|
205521
|
+
if (isConfig && fnOrConfig.gitCredentials) {
|
|
205522
|
+
validateGitCredentials(`dynamicJob('${groupName}')`, fnOrConfig.gitCredentials);
|
|
205523
|
+
Object.defineProperty(tagged, DYNAMIC_JOB_GIT_CREDENTIALS_TAG, {
|
|
205524
|
+
value: fnOrConfig.gitCredentials,
|
|
205525
|
+
enumerable: false
|
|
205526
|
+
});
|
|
205527
|
+
}
|
|
205257
205528
|
return tagged;
|
|
205258
205529
|
}
|
|
205259
205530
|
/**
|
|
@@ -205270,10 +205541,19 @@ function getDynamicJobGroup(fn) {
|
|
|
205270
205541
|
function getDynamicJobNeeds(fn) {
|
|
205271
205542
|
return fn[DYNAMIC_JOB_NEEDS_TAG];
|
|
205272
205543
|
}
|
|
205273
|
-
|
|
205544
|
+
/**
|
|
205545
|
+
* Read the named git credentials a generator declared.
|
|
205546
|
+
* Returns undefined for the bare function form, which cannot declare any.
|
|
205547
|
+
*/
|
|
205548
|
+
function getDynamicJobGitCredentials(fn) {
|
|
205549
|
+
return fn[DYNAMIC_JOB_GIT_CREDENTIALS_TAG];
|
|
205550
|
+
}
|
|
205551
|
+
var DYNAMIC_JOB_GROUP_TAG, DYNAMIC_JOB_NEEDS_TAG, DYNAMIC_JOB_GIT_CREDENTIALS_TAG;
|
|
205274
205552
|
var init_types$1 = __esmMin((() => {
|
|
205553
|
+
init_git_types();
|
|
205275
205554
|
DYNAMIC_JOB_GROUP_TAG = Symbol.for("kici:dynamicJobGroup");
|
|
205276
205555
|
DYNAMIC_JOB_NEEDS_TAG = Symbol.for("kici:dynamicJobNeeds");
|
|
205556
|
+
DYNAMIC_JOB_GIT_CREDENTIALS_TAG = Symbol.for("kici:dynamicJobGitCredentials");
|
|
205277
205557
|
}));
|
|
205278
205558
|
//#endregion
|
|
205279
205559
|
//#region ../sdk/dist/needs-context.js
|
|
@@ -205590,6 +205870,7 @@ var dist_exports = /* @__PURE__ */ __exportAll({
|
|
|
205590
205870
|
CacheSpecSchema: () => CacheSpecSchema,
|
|
205591
205871
|
ChangedFilesUnavailableError: () => ChangedFilesUnavailableError,
|
|
205592
205872
|
DYNAMIC_GROUP_TAG: () => DYNAMIC_GROUP_TAG,
|
|
205873
|
+
DYNAMIC_JOB_GIT_CREDENTIALS_TAG: () => DYNAMIC_JOB_GIT_CREDENTIALS_TAG,
|
|
205593
205874
|
DYNAMIC_JOB_GROUP_TAG: () => DYNAMIC_JOB_GROUP_TAG,
|
|
205594
205875
|
DYNAMIC_JOB_NEEDS_TAG: () => DYNAMIC_JOB_NEEDS_TAG,
|
|
205595
205876
|
SCALER_EVENT_NAMES: () => SCALER_EVENT_NAMES,
|
|
@@ -205628,6 +205909,7 @@ var dist_exports = /* @__PURE__ */ __exportAll({
|
|
|
205628
205909
|
flattenStepInputs: () => flattenStepInputs,
|
|
205629
205910
|
fork: () => fork,
|
|
205630
205911
|
genericWebhook: () => genericWebhook,
|
|
205912
|
+
getDynamicJobGitCredentials: () => getDynamicJobGitCredentials,
|
|
205631
205913
|
getDynamicJobGroup: () => getDynamicJobGroup,
|
|
205632
205914
|
getDynamicJobNeeds: () => getDynamicJobNeeds,
|
|
205633
205915
|
getJobOutputsMap: () => getJobOutputsMap,
|
|
@@ -205736,6 +206018,7 @@ var init_dist = __esmMin((() => {
|
|
|
205736
206018
|
init_schedule();
|
|
205737
206019
|
init_lifecycle();
|
|
205738
206020
|
init_dispatch_inputs();
|
|
206021
|
+
init_triggers();
|
|
205739
206022
|
init_hooks();
|
|
205740
206023
|
init_rule();
|
|
205741
206024
|
init_context();
|
|
@@ -205756,6 +206039,21 @@ var init_dist = __esmMin((() => {
|
|
|
205756
206039
|
init_zod();
|
|
205757
206040
|
}));
|
|
205758
206041
|
//#endregion
|
|
206042
|
+
//#region ../sdk/dist/internal.js
|
|
206043
|
+
init_dist();
|
|
206044
|
+
init_api_types();
|
|
206045
|
+
init_approval();
|
|
206046
|
+
init_cache_types();
|
|
206047
|
+
init_filter_context();
|
|
206048
|
+
init_outputs();
|
|
206049
|
+
init_context();
|
|
206050
|
+
init_evaluator();
|
|
206051
|
+
init_rules();
|
|
206052
|
+
init_needs_context();
|
|
206053
|
+
init_secrets();
|
|
206054
|
+
init_expand();
|
|
206055
|
+
init_matrix();
|
|
206056
|
+
//#endregion
|
|
205759
206057
|
//#region ../../node_modules/.pnpm/jose@6.2.10/node_modules/jose/dist/webapi/lib/buffer_utils.js
|
|
205760
206058
|
const encoder = new TextEncoder();
|
|
205761
206059
|
const decoder = new TextDecoder();
|
|
@@ -206250,13 +206548,21 @@ async function generateKeyPair(alg, options) {
|
|
|
206250
206548
|
}
|
|
206251
206549
|
//#endregion
|
|
206252
206550
|
//#region ../engine/dist/provenance/statement-hash.js
|
|
206253
|
-
init_dist();
|
|
206254
206551
|
/**
|
|
206255
|
-
* Lowercase-hex SHA-256 of the DSSE statement payload bytes.
|
|
206256
|
-
*
|
|
206257
|
-
*
|
|
206258
|
-
*
|
|
206259
|
-
*
|
|
206552
|
+
* Lowercase-hex SHA-256 of the DSSE statement payload bytes.
|
|
206553
|
+
*
|
|
206554
|
+
* One of the two bindings a deferred OIDC mint commits to: the later token
|
|
206555
|
+
* carries this hash as a claim, so the identity cannot be re-bound to a
|
|
206556
|
+
* different frozen statement at retry time.
|
|
206557
|
+
*
|
|
206558
|
+
* It is a binding, not a substitute for the build-context cross-check. The
|
|
206559
|
+
* verifier requires BOTH for a non-live origin — the hash proves the statement
|
|
206560
|
+
* has not been swapped, and the cross-check proves the statement agrees with
|
|
206561
|
+
* the run the token names. A hash-only rule verified a bundle whose statement
|
|
206562
|
+
* claimed a release SHA the build never touched.
|
|
206563
|
+
*
|
|
206564
|
+
* Browser-safe: `crypto.subtle` only, so the verifier (dashboard + CLI) can
|
|
206565
|
+
* recompute it.
|
|
206260
206566
|
*/
|
|
206261
206567
|
async function computeStatementHash(payload) {
|
|
206262
206568
|
const digest = await crypto.subtle.digest("SHA-256", payload);
|
|
@@ -206418,7 +206724,12 @@ inTotoStatementSchema.extend({
|
|
|
206418
206724
|
* for a deferred attestation (no minted identity token yet). Marks
|
|
206419
206725
|
* `attestationOrigin: 'deferred'` in the internal parameters. The caller
|
|
206420
206726
|
* DSSE-signs the returned statement immediately and computes its statement hash
|
|
206421
|
-
* — the
|
|
206727
|
+
* — one of the two bindings the later OIDC mint commits to.
|
|
206728
|
+
*
|
|
206729
|
+
* Emits the same fields `buildProvenanceStatement` emits, so a statement frozen
|
|
206730
|
+
* from an orchestrator-supplied context is field-for-field what a live mint
|
|
206731
|
+
* would have produced. That is what lets the orchestrator cross-check the
|
|
206732
|
+
* statement against its own run row before signing anything that commits to it.
|
|
206422
206733
|
*/
|
|
206423
206734
|
function buildLocalProvenanceStatement(input) {
|
|
206424
206735
|
const c = input.context;
|
|
@@ -206432,11 +206743,14 @@ function buildLocalProvenanceStatement(input) {
|
|
|
206432
206743
|
predicate: {
|
|
206433
206744
|
buildDefinition: {
|
|
206434
206745
|
buildType: KICI_WORKFLOW_BUILD_TYPE,
|
|
206435
|
-
externalParameters: {
|
|
206436
|
-
|
|
206437
|
-
|
|
206438
|
-
|
|
206439
|
-
|
|
206746
|
+
externalParameters: {
|
|
206747
|
+
workflow: {
|
|
206748
|
+
repository: c.repository,
|
|
206749
|
+
ref: c.ref,
|
|
206750
|
+
path: c.workflowRef
|
|
206751
|
+
},
|
|
206752
|
+
...c.provider ? { provider: c.provider } : {}
|
|
206753
|
+
},
|
|
206440
206754
|
internalParameters: {
|
|
206441
206755
|
...c.sha ? { commit: c.sha } : {},
|
|
206442
206756
|
runId: c.runId,
|
|
@@ -206448,7 +206762,7 @@ function buildLocalProvenanceStatement(input) {
|
|
|
206448
206762
|
},
|
|
206449
206763
|
runDetails: {
|
|
206450
206764
|
builder: {
|
|
206451
|
-
id: `${c.issuer}/orchestrator
|
|
206765
|
+
id: `${c.issuer}/orchestrator/${c.orchestratorId ?? "unknown"}`,
|
|
206452
206766
|
version: input.builderVersions
|
|
206453
206767
|
},
|
|
206454
206768
|
metadata: {
|
|
@@ -210731,15 +211045,6 @@ function createArtifactsApi(workDir, transport, roots) {
|
|
|
210731
211045
|
}
|
|
210732
211046
|
//#endregion
|
|
210733
211047
|
//#region src/execution/sandbox/log-masker.ts
|
|
210734
|
-
/**
|
|
210735
|
-
* Secret value masking for log lines.
|
|
210736
|
-
*
|
|
210737
|
-
* Replaces all occurrences of registered secret values with '***' in log output.
|
|
210738
|
-
* Used by the workflow runner to prevent secret leaks in IPC log messages.
|
|
210739
|
-
*
|
|
210740
|
-
* Performance: Builds a single combined regex from all secret values, so each
|
|
210741
|
-
* log line is scanned in a single pass (not O(secrets * lines)).
|
|
210742
|
-
*/
|
|
210743
211048
|
/** Minimum length for a secret value to be maskable (avoids false positives). */
|
|
210744
211049
|
const MIN_MASK_LENGTH = 3;
|
|
210745
211050
|
/**
|
|
@@ -210770,18 +211075,38 @@ var LogMasker = class {
|
|
|
210770
211075
|
* Authorization: Basic headers, base64-encoded config values).
|
|
210771
211076
|
* Values are sorted by length descending so longer values are matched first
|
|
210772
211077
|
* (prevents partial masking when one secret is a substring of another).
|
|
211078
|
+
*
|
|
211079
|
+
* Multi-line values additionally register each of their individual lines.
|
|
211080
|
+
* Log output is split into lines before it reaches the masker, so a value
|
|
211081
|
+
* containing a newline can never match as a whole — a PEM private key or a
|
|
211082
|
+
* kubeconfig would otherwise stream in clear text. Two consequences of the
|
|
211083
|
+
* per-line registration are deliberate:
|
|
211084
|
+
*
|
|
211085
|
+
* - `MIN_MASK_LENGTH` is 3, so short structural lines of a structured secret
|
|
211086
|
+
* are registered too. A `---` YAML separator or a bare `{` from a
|
|
211087
|
+
* service-account JSON is masked wherever it appears in that job's logs.
|
|
211088
|
+
* - PEM header and footer lines (`-----BEGIN OPENSSH PRIVATE KEY-----`) are
|
|
211089
|
+
* not secret on their own and are masked as a side effect.
|
|
211090
|
+
*
|
|
211091
|
+
* Both are strictly safer than leaking the body, and no heuristic separates a
|
|
211092
|
+
* structural line from a body line without risking the reverse mistake.
|
|
210773
211093
|
*/
|
|
210774
211094
|
registerSecrets(secrets) {
|
|
210775
211095
|
const seen = /* @__PURE__ */ new Set();
|
|
210776
211096
|
const values = [];
|
|
210777
|
-
|
|
210778
|
-
seen.
|
|
210779
|
-
|
|
210780
|
-
|
|
211097
|
+
const add = (candidate) => {
|
|
211098
|
+
if (candidate.length < MIN_MASK_LENGTH || seen.has(candidate)) return;
|
|
211099
|
+
seen.add(candidate);
|
|
211100
|
+
values.push(candidate);
|
|
211101
|
+
const b64 = Buffer.from(candidate).toString("base64");
|
|
210781
211102
|
if (b64.length >= MIN_MASK_LENGTH && !seen.has(b64)) {
|
|
210782
211103
|
seen.add(b64);
|
|
210783
211104
|
values.push(b64);
|
|
210784
211105
|
}
|
|
211106
|
+
};
|
|
211107
|
+
for (const value of Object.values(secrets)) {
|
|
211108
|
+
add(value);
|
|
211109
|
+
if (value.includes("\n")) for (const rawLine of value.split("\n")) add(rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine);
|
|
210785
211110
|
}
|
|
210786
211111
|
if (values.length === 0) {
|
|
210787
211112
|
this.pattern = null;
|
|
@@ -210807,6 +211132,57 @@ var LogMasker = class {
|
|
|
210807
211132
|
return this.pattern !== null;
|
|
210808
211133
|
}
|
|
210809
211134
|
};
|
|
211135
|
+
/**
|
|
211136
|
+
* Create a LogMasker initialized with all secret values from the request.
|
|
211137
|
+
*
|
|
211138
|
+
* Collects values from both flat secrets and all namespaced context secrets,
|
|
211139
|
+
* deduplicating before registration.
|
|
211140
|
+
*
|
|
211141
|
+
* Both the runner child and the agent-side fork runner build a masker from the
|
|
211142
|
+
* same request, so the crash tail the agent assembles from the child's stderr is
|
|
211143
|
+
* masked with the same value set the child used for its own log lines.
|
|
211144
|
+
*/
|
|
211145
|
+
function createSecretMasker(request) {
|
|
211146
|
+
const masker = new LogMasker();
|
|
211147
|
+
const allSecrets = {};
|
|
211148
|
+
if (request.secrets) Object.assign(allSecrets, request.secrets);
|
|
211149
|
+
if (request.namespacedSecrets) for (const contextSecrets of Object.values(request.namespacedSecrets)) Object.assign(allSecrets, contextSecrets);
|
|
211150
|
+
masker.registerSecrets(allSecrets);
|
|
211151
|
+
return masker;
|
|
211152
|
+
}
|
|
211153
|
+
/**
|
|
211154
|
+
* Mask every operator-visible text field of an outbound runner message.
|
|
211155
|
+
*
|
|
211156
|
+
* Each message type carrying free text is named here, so a new text-bearing
|
|
211157
|
+
* message type is a visible omission rather than a silent leak. `step.complete`
|
|
211158
|
+
* error text and the `job.complete` failure reason are persisted on the step and
|
|
211159
|
+
* run rows the dashboard renders, so they need the same masking `log.line` gets.
|
|
211160
|
+
*
|
|
211161
|
+
* Returns the message unchanged when no secrets are registered.
|
|
211162
|
+
*/
|
|
211163
|
+
function maskMessageText(msg, masker) {
|
|
211164
|
+
if (!masker.hasSecrets()) return msg;
|
|
211165
|
+
switch (msg.type) {
|
|
211166
|
+
case "log.line": return {
|
|
211167
|
+
...msg,
|
|
211168
|
+
line: masker.mask(msg.line)
|
|
211169
|
+
};
|
|
211170
|
+
case "step.complete": return msg.error ? {
|
|
211171
|
+
...msg,
|
|
211172
|
+
error: {
|
|
211173
|
+
...msg.error,
|
|
211174
|
+
message: masker.mask(msg.error.message)
|
|
211175
|
+
}
|
|
211176
|
+
} : msg;
|
|
211177
|
+
case "job.complete": {
|
|
211178
|
+
const masked = { ...msg };
|
|
211179
|
+
if (masked.error !== void 0) masked.error = masker.mask(masked.error);
|
|
211180
|
+
if (masked.droppedJobs) masked.droppedJobs = masked.droppedJobs.map((j) => masker.mask(j));
|
|
211181
|
+
return masked;
|
|
211182
|
+
}
|
|
211183
|
+
default: return msg;
|
|
211184
|
+
}
|
|
211185
|
+
}
|
|
210810
211186
|
//#endregion
|
|
210811
211187
|
//#region src/execution/sandbox/env-delta.ts
|
|
210812
211188
|
/**
|
|
@@ -210988,18 +211364,23 @@ async function executeHook(opts) {
|
|
|
210988
211364
|
step_type: `hook:${hookType}`
|
|
210989
211365
|
});
|
|
210990
211366
|
const startTime = Date.now();
|
|
211367
|
+
const abortController = new AbortController();
|
|
211368
|
+
let rejectTimeout = () => {};
|
|
211369
|
+
const timeoutRace = new Promise((_, reject) => {
|
|
211370
|
+
rejectTimeout = reject;
|
|
211371
|
+
});
|
|
211372
|
+
const timeoutId = setTimeout(() => {
|
|
211373
|
+
rejectTimeout(/* @__PURE__ */ new Error(`Hook '${normalized.name}' timed out after ${timeoutMs}ms`));
|
|
211374
|
+
abortController.abort();
|
|
211375
|
+
}, timeoutMs);
|
|
210991
211376
|
const mergedCtx = {
|
|
210992
211377
|
...stepContext,
|
|
210993
|
-
outcome
|
|
211378
|
+
outcome,
|
|
211379
|
+
signal: AbortSignal.any([...stepContext.signal ? [stepContext.signal] : [], abortController.signal]),
|
|
211380
|
+
...typeof stepContext.$ === "function" ? { $: stepContext.$({ signal: abortController.signal }) } : {}
|
|
210994
211381
|
};
|
|
210995
|
-
const abortController = new AbortController();
|
|
210996
|
-
const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);
|
|
210997
211382
|
try {
|
|
210998
|
-
await Promise.race([normalized.run(mergedCtx),
|
|
210999
|
-
abortController.signal.addEventListener("abort", () => {
|
|
211000
|
-
reject(/* @__PURE__ */ new Error(`Hook '${normalized.name}' timed out after ${timeoutMs}ms`));
|
|
211001
|
-
});
|
|
211002
|
-
})]);
|
|
211383
|
+
await Promise.race([normalized.run(mergedCtx), timeoutRace]);
|
|
211003
211384
|
clearTimeout(timeoutId);
|
|
211004
211385
|
sendIpc({
|
|
211005
211386
|
type: "step.complete",
|
|
@@ -211141,7 +211522,6 @@ async function runParallelGroup(node, opts) {
|
|
|
211141
211522
|
}
|
|
211142
211523
|
//#endregion
|
|
211143
211524
|
//#region src/execution/sandbox/step-loop.ts
|
|
211144
|
-
init_dist();
|
|
211145
211525
|
init_dist$1();
|
|
211146
211526
|
init_idempotency();
|
|
211147
211527
|
init_dist$4();
|
|
@@ -211268,12 +211648,12 @@ const STEP_FAILURE_LOG_MAX_CHARS = 8192;
|
|
|
211268
211648
|
* One consequence to know: a `$({ quiet: true })` command's output is kept out
|
|
211269
211649
|
* of the log by the `verbose` gate in `streaming-zx-log.ts`, but when such a
|
|
211270
211650
|
* command FAILS, zx packs its captured output into the thrown error's message —
|
|
211271
|
-
* which this function then writes to the log.
|
|
211272
|
-
*
|
|
211273
|
-
*
|
|
211274
|
-
*
|
|
211275
|
-
*
|
|
211276
|
-
* has never been told about is not.
|
|
211651
|
+
* which this function then writes to the log. Surfacing it is the whole point:
|
|
211652
|
+
* a quiet command that fails is exactly the failure an operator cannot
|
|
211653
|
+
* otherwise diagnose. The same text also travels on `step.complete` for the step
|
|
211654
|
+
* row the dashboard renders, and both copies pass the masker — `maskMessageText`
|
|
211655
|
+
* masks `step.complete.error.message` alongside `log.line`. Registered secret
|
|
211656
|
+
* values are masked; anything the masker has never been told about is not.
|
|
211277
211657
|
*/
|
|
211278
211658
|
function emitStepFailureLog(stepName, stepIndex, message, sendFn) {
|
|
211279
211659
|
const prefix = `[kici] Step '${stepName}' failed: `;
|
|
@@ -211310,16 +211690,29 @@ async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, output
|
|
|
211310
211690
|
});
|
|
211311
211691
|
const startTime = Date.now();
|
|
211312
211692
|
const abortController = new AbortController();
|
|
211313
|
-
|
|
211693
|
+
let timedOut = false;
|
|
211694
|
+
let rejectTimeout = () => {};
|
|
211695
|
+
const timeoutRace = new Promise((_, reject) => {
|
|
211696
|
+
rejectTimeout = reject;
|
|
211697
|
+
});
|
|
211698
|
+
const timeoutId = setTimeout(() => {
|
|
211699
|
+
timedOut = true;
|
|
211700
|
+
rejectTimeout(/* @__PURE__ */ new Error(`Step '${step.name}' timed out after ${timeoutMs}ms`));
|
|
211701
|
+
abortController.abort();
|
|
211702
|
+
opts.abortStep?.(stepIndex);
|
|
211703
|
+
}, timeoutMs);
|
|
211314
211704
|
const stepAbortSignal = opts.getStepAbortSignal?.(stepIndex);
|
|
211705
|
+
const stepPromise = runStepWithCheckMode(step, stepIndex, ctx, checkMode, sendFn, opts);
|
|
211706
|
+
let stepSettled = false;
|
|
211707
|
+
const stepSettledPromise = stepPromise.then(() => {
|
|
211708
|
+
stepSettled = true;
|
|
211709
|
+
}, () => {
|
|
211710
|
+
stepSettled = true;
|
|
211711
|
+
});
|
|
211315
211712
|
try {
|
|
211316
211713
|
const phase = await Promise.race([
|
|
211317
|
-
|
|
211318
|
-
|
|
211319
|
-
abortController.signal.addEventListener("abort", () => {
|
|
211320
|
-
reject(/* @__PURE__ */ new Error(`Step '${step.name}' timed out after ${timeoutMs}ms`));
|
|
211321
|
-
});
|
|
211322
|
-
}),
|
|
211714
|
+
stepPromise,
|
|
211715
|
+
timeoutRace,
|
|
211323
211716
|
new Promise((_, reject) => {
|
|
211324
211717
|
if (!jobDeadlineSignal) return;
|
|
211325
211718
|
if (jobDeadlineSignal.aborted) {
|
|
@@ -211336,7 +211729,10 @@ async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, output
|
|
|
211336
211729
|
reject(new StepCancelledError(step.name));
|
|
211337
211730
|
return;
|
|
211338
211731
|
}
|
|
211339
|
-
stepAbortSignal.addEventListener("abort", () =>
|
|
211732
|
+
stepAbortSignal.addEventListener("abort", () => {
|
|
211733
|
+
if (timedOut) return;
|
|
211734
|
+
reject(new StepCancelledError(step.name));
|
|
211735
|
+
});
|
|
211340
211736
|
})
|
|
211341
211737
|
]);
|
|
211342
211738
|
clearTimeout(timeoutId);
|
|
@@ -211368,6 +211764,15 @@ async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, output
|
|
|
211368
211764
|
clearTimeout(timeoutId);
|
|
211369
211765
|
const durationMs = Date.now() - startTime;
|
|
211370
211766
|
const error = e instanceof Error ? e : new Error(String(e));
|
|
211767
|
+
if (timedOut && !stepSettled) {
|
|
211768
|
+
await Promise.race([stepSettledPromise, delayUnref(STEP_ABORT_GRACE_MS)]);
|
|
211769
|
+
if (!stepSettled) sendFn({
|
|
211770
|
+
type: "log.line",
|
|
211771
|
+
stepIndex,
|
|
211772
|
+
line: `[timeout] Step '${step.name}' did not stop within ${STEP_ABORT_GRACE_MS}ms of its abort signal; continuing without it.`,
|
|
211773
|
+
stream: LogStream.enum.stderr
|
|
211774
|
+
});
|
|
211775
|
+
}
|
|
211371
211776
|
if (e instanceof StepCancelledError) {
|
|
211372
211777
|
const secretsAccessed = getSecretsAccessLog?.(stepIndex);
|
|
211373
211778
|
emitSecretMountEvents(getSecretMountRecords?.(stepIndex), stepIndex, sendFn);
|
|
@@ -211416,6 +211821,17 @@ async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, output
|
|
|
211416
211821
|
}
|
|
211417
211822
|
}
|
|
211418
211823
|
/**
|
|
211824
|
+
* How long a timed-out step is given to unwind after its abort signal fires,
|
|
211825
|
+
* before the loop reports that the step ignored it and moves on.
|
|
211826
|
+
*/
|
|
211827
|
+
const STEP_ABORT_GRACE_MS = 2e3;
|
|
211828
|
+
/** A timer-backed delay that never keeps the process alive on its own. */
|
|
211829
|
+
function delayUnref(ms) {
|
|
211830
|
+
return new Promise((resolve) => {
|
|
211831
|
+
setTimeout(resolve, ms).unref?.();
|
|
211832
|
+
});
|
|
211833
|
+
}
|
|
211834
|
+
/**
|
|
211419
211835
|
* Emit one `step.secret_mount` IPC event per `mountFile` / `exposeFile` call
|
|
211420
211836
|
* the step performed. Called from both the success and failure paths so the
|
|
211421
211837
|
* orchestrator's audit trail records every mount regardless of step outcome.
|
|
@@ -212858,10 +213274,12 @@ function redactNpmOutput(input, tokens) {
|
|
|
212858
213274
|
*
|
|
212859
213275
|
* `nodeLinker: node-modules` makes berry lay down a real `node_modules` tree
|
|
212860
213276
|
* (no PnP `.pnp.cjs`), so the agent's packer / restore / sibling-walk /
|
|
212861
|
-
* workflow-loader work unchanged. `enableScripts: false`
|
|
212862
|
-
*
|
|
212863
|
-
*
|
|
212864
|
-
* `--ignore-scripts`.
|
|
213277
|
+
* workflow-loader work unchanged. `enableScripts: false` keeps dependency
|
|
213278
|
+
* lifecycle scripts from running at all — for every install, not only one
|
|
213279
|
+
* against a private registry — the same security model as npm/pnpm/classic
|
|
213280
|
+
* `--ignore-scripts`. An operator opts back in with
|
|
213281
|
+
* `KICI_ALLOW_INSTALL_SCRIPTS=true`, which arrives here as
|
|
213282
|
+
* `ignoreScripts: false`.
|
|
212865
213283
|
*
|
|
212866
213284
|
* Reuses the same `ApplyNpmRegistryConfigArgs` / `ApplyNpmRegistryConfigResult`
|
|
212867
213285
|
* shapes as the npm overlay so `dep-installer` can pick either by flavor.
|
|
@@ -212910,8 +213328,8 @@ async function applyYarnrcBerryConfig(args) {
|
|
|
212910
213328
|
};
|
|
212911
213329
|
const tokenEnv = {};
|
|
212912
213330
|
const tokensForRedaction = [];
|
|
213331
|
+
if (args.ignoreScripts !== false) merged.enableScripts = false;
|
|
212913
213332
|
if (hasPrivateRegistry) {
|
|
212914
|
-
merged.enableScripts = false;
|
|
212915
213333
|
const npmScopes = { ...doc.npmScopes ?? {} };
|
|
212916
213334
|
for (let i = 0; i < registries.length; i++) {
|
|
212917
213335
|
const reg = registries[i];
|
|
@@ -213249,9 +213667,11 @@ function isAbsoluteRel(rel) {
|
|
|
213249
213667
|
* Security: the install runs with an isolated per-invocation cache/store
|
|
213250
213668
|
* directory to prevent cache poisoning across build jobs — a malicious
|
|
213251
213669
|
* package.json in one repo cannot taint the cache used by subsequent builds.
|
|
213252
|
-
* The
|
|
213253
|
-
*
|
|
213254
|
-
*
|
|
213670
|
+
* The install runs with `--ignore-scripts` for every package manager. A
|
|
213671
|
+
* lifecycle script in a committed `package.json` is customer code the agent
|
|
213672
|
+
* never agreed to execute: it would run wherever the install runs, which for a
|
|
213673
|
+
* step-child install is the process holding the job's secrets. Operators who
|
|
213674
|
+
* genuinely need it set `KICI_ALLOW_INSTALL_SCRIPTS=true` on the agent.
|
|
213255
213675
|
*/
|
|
213256
213676
|
init_tmp();
|
|
213257
213677
|
init_dist$2();
|
|
@@ -213289,9 +213709,11 @@ async function detectKiciYarnFlavor(repoRoot, kiciDir) {
|
|
|
213289
213709
|
* between build jobs; the directory is removed after installation.
|
|
213290
213710
|
*
|
|
213291
213711
|
* If `opts.npmRegistries` / `opts.installEnvSecrets` is provided, a job-scoped
|
|
213292
|
-
* `.kici/.npmrc` overlay is synthesized for the install
|
|
213293
|
-
*
|
|
213294
|
-
*
|
|
213712
|
+
* `.kici/.npmrc` overlay is synthesized for the install and restored in
|
|
213713
|
+
* `finally`.
|
|
213714
|
+
*
|
|
213715
|
+
* Lifecycle scripts are disabled for every package manager unless the operator
|
|
213716
|
+
* set `opts.allowInstallScripts`.
|
|
213295
213717
|
*
|
|
213296
213718
|
* @param kiciDir - Path to the `.kici/` directory containing package.json.
|
|
213297
213719
|
* @param opts - Optional registry / installEnv / repoRoot configuration.
|
|
@@ -213313,13 +213735,15 @@ async function installDeps(kiciDir, opts = {}) {
|
|
|
213313
213735
|
yarnFlavor
|
|
213314
213736
|
});
|
|
213315
213737
|
const startTime = Date.now();
|
|
213316
|
-
const
|
|
213738
|
+
const ignoreScripts = opts.allowInstallScripts !== true;
|
|
213739
|
+
const baseEnv = opts.baseEnv ?? process.env;
|
|
213317
213740
|
const isBerry = packageManager === PackageManager.Yarn && yarnFlavor === YarnFlavor.Berry;
|
|
213318
213741
|
const registryConfig = isBerry ? await applyYarnrcBerryConfig({
|
|
213319
213742
|
kiciDir,
|
|
213320
213743
|
npmRegistries: opts.npmRegistries,
|
|
213321
213744
|
installEnvSecrets: opts.installEnvSecrets,
|
|
213322
|
-
jobIdShort: opts.jobIdShort ?? "00000000"
|
|
213745
|
+
jobIdShort: opts.jobIdShort ?? "00000000",
|
|
213746
|
+
ignoreScripts
|
|
213323
213747
|
}) : await applyNpmRegistryConfig({
|
|
213324
213748
|
kiciDir,
|
|
213325
213749
|
npmRegistries: opts.npmRegistries,
|
|
@@ -213329,22 +213753,26 @@ async function installDeps(kiciDir, opts = {}) {
|
|
|
213329
213753
|
try {
|
|
213330
213754
|
if (packageManager === PackageManager.Pnpm) await runPnpmInstall({
|
|
213331
213755
|
kiciDir,
|
|
213332
|
-
|
|
213333
|
-
registryConfig
|
|
213756
|
+
ignoreScripts,
|
|
213757
|
+
registryConfig,
|
|
213758
|
+
baseEnv
|
|
213334
213759
|
});
|
|
213335
213760
|
else if (isBerry) await runYarnBerryInstall({
|
|
213336
213761
|
kiciDir,
|
|
213337
|
-
registryConfig
|
|
213762
|
+
registryConfig,
|
|
213763
|
+
baseEnv
|
|
213338
213764
|
});
|
|
213339
213765
|
else if (packageManager === PackageManager.Yarn) await runYarnInstall({
|
|
213340
213766
|
kiciDir,
|
|
213341
|
-
|
|
213342
|
-
registryConfig
|
|
213767
|
+
ignoreScripts,
|
|
213768
|
+
registryConfig,
|
|
213769
|
+
baseEnv
|
|
213343
213770
|
});
|
|
213344
213771
|
else await runNpmInstall({
|
|
213345
213772
|
kiciDir,
|
|
213346
|
-
|
|
213347
|
-
registryConfig
|
|
213773
|
+
ignoreScripts,
|
|
213774
|
+
registryConfig,
|
|
213775
|
+
baseEnv
|
|
213348
213776
|
});
|
|
213349
213777
|
} catch (e) {
|
|
213350
213778
|
const tokens = registryConfig.tokensForRedaction;
|
|
@@ -213354,8 +213782,8 @@ async function installDeps(kiciDir, opts = {}) {
|
|
|
213354
213782
|
} finally {
|
|
213355
213783
|
await registryConfig.cleanup();
|
|
213356
213784
|
}
|
|
213357
|
-
if (packageManager === PackageManager.Pnpm && await kiciHasLocalProtocolDeps(kiciDir)) await buildWorkspaceClosure(repoRoot);
|
|
213358
|
-
if (packageManager === PackageManager.Yarn) await buildYarnWorkspaceClosure(repoRoot, kiciDir, yarnFlavor);
|
|
213785
|
+
if (packageManager === PackageManager.Pnpm && await kiciHasLocalProtocolDeps(kiciDir)) await buildWorkspaceClosure(repoRoot, baseEnv);
|
|
213786
|
+
if (packageManager === PackageManager.Yarn) await buildYarnWorkspaceClosure(repoRoot, kiciDir, yarnFlavor, baseEnv);
|
|
213359
213787
|
const durationMs = Date.now() - startTime;
|
|
213360
213788
|
process.stderr.write(`[dep-installer:trace] install complete: ${durationMs}ms\n`);
|
|
213361
213789
|
logger$2.info("Deps installed inline", {
|
|
@@ -213363,20 +213791,26 @@ async function installDeps(kiciDir, opts = {}) {
|
|
|
213363
213791
|
durationMs
|
|
213364
213792
|
});
|
|
213365
213793
|
}
|
|
213366
|
-
/**
|
|
213367
|
-
|
|
213368
|
-
|
|
213794
|
+
/**
|
|
213795
|
+
* Build the Node binary directory onto PATH so spawned tools find `node`.
|
|
213796
|
+
*
|
|
213797
|
+
* `baseEnv` is the caller's declared environment for the subprocess. It
|
|
213798
|
+
* defaults to `process.env` because inside the runner child that IS the
|
|
213799
|
+
* sanitized job environment; an agent-process caller passes a sanitized base.
|
|
213800
|
+
*/
|
|
213801
|
+
function envWithNodeOnPath(extraEnv, nodeDir, baseEnv = process.env) {
|
|
213802
|
+
const { NODE_ENV: _NODE_ENV, ...restEnv } = baseEnv;
|
|
213369
213803
|
return {
|
|
213370
213804
|
...restEnv,
|
|
213371
213805
|
...extraEnv,
|
|
213372
|
-
PATH: `${nodeDir}${process.platform === "win32" ? ";" : ":"}${
|
|
213806
|
+
PATH: `${nodeDir}${process.platform === "win32" ? ";" : ":"}${restEnv.PATH ?? ""}`
|
|
213373
213807
|
};
|
|
213374
213808
|
}
|
|
213375
213809
|
/** Run `npm install` in `.kici/` with an isolated cache directory. */
|
|
213376
213810
|
async function runNpmInstall(args) {
|
|
213377
213811
|
const { npmCliPath, nodeExe, nodeDir } = resolveNpm();
|
|
213378
213812
|
const { path: cacheDir, cleanup } = await makeTempDir("npm-cache");
|
|
213379
|
-
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir);
|
|
213813
|
+
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir, args.baseEnv);
|
|
213380
213814
|
const buildArgs = (...prefix) => {
|
|
213381
213815
|
const a = [
|
|
213382
213816
|
...prefix,
|
|
@@ -213386,7 +213820,7 @@ async function runNpmInstall(args) {
|
|
|
213386
213820
|
"--no-audit",
|
|
213387
213821
|
"--no-fund"
|
|
213388
213822
|
];
|
|
213389
|
-
if (args.
|
|
213823
|
+
if (args.ignoreScripts) a.push("--ignore-scripts");
|
|
213390
213824
|
return a;
|
|
213391
213825
|
};
|
|
213392
213826
|
try {
|
|
@@ -213415,7 +213849,7 @@ async function runPnpmInstall(args) {
|
|
|
213415
213849
|
await assertPnpmAvailable();
|
|
213416
213850
|
const { nodeDir } = resolveNpm();
|
|
213417
213851
|
const { path: storeDir, cleanup } = await makeTempDir("pnpm-store");
|
|
213418
|
-
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir);
|
|
213852
|
+
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir, args.baseEnv);
|
|
213419
213853
|
const argv = [
|
|
213420
213854
|
"install",
|
|
213421
213855
|
`--config.store-dir=${storeDir}`,
|
|
@@ -213424,7 +213858,7 @@ async function runPnpmInstall(args) {
|
|
|
213424
213858
|
"--config.side-effects-cache=false",
|
|
213425
213859
|
PNPM_IGNORE_BUILD_GATE_ARG
|
|
213426
213860
|
];
|
|
213427
|
-
if (args.
|
|
213861
|
+
if (args.ignoreScripts) argv.push("--ignore-scripts");
|
|
213428
213862
|
try {
|
|
213429
213863
|
process.stderr.write(`[dep-installer:trace] running: pnpm ${argv.join(" ")}\n`);
|
|
213430
213864
|
await execFileAsync("pnpm", argv, {
|
|
@@ -213438,7 +213872,7 @@ async function runPnpmInstall(args) {
|
|
|
213438
213872
|
}
|
|
213439
213873
|
}
|
|
213440
213874
|
/** Pure: argv for `yarn install` with an isolated cache folder. */
|
|
213441
|
-
function buildYarnInstallArgs(cacheDir,
|
|
213875
|
+
function buildYarnInstallArgs(cacheDir, ignoreScripts) {
|
|
213442
213876
|
const a = [
|
|
213443
213877
|
"install",
|
|
213444
213878
|
"--cache-folder",
|
|
@@ -213446,7 +213880,7 @@ function buildYarnInstallArgs(cacheDir, hasPrivateRegistry) {
|
|
|
213446
213880
|
"--non-interactive",
|
|
213447
213881
|
"--no-progress"
|
|
213448
213882
|
];
|
|
213449
|
-
if (
|
|
213883
|
+
if (ignoreScripts) a.push("--ignore-scripts");
|
|
213450
213884
|
return a;
|
|
213451
213885
|
}
|
|
213452
213886
|
/**
|
|
@@ -213461,8 +213895,8 @@ async function runYarnInstall(args) {
|
|
|
213461
213895
|
await assertYarnAvailable();
|
|
213462
213896
|
const { nodeDir } = resolveNpm();
|
|
213463
213897
|
const { path: cacheDir, cleanup } = await makeTempDir("yarn-cache");
|
|
213464
|
-
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir);
|
|
213465
|
-
const argv = buildYarnInstallArgs(cacheDir, args.
|
|
213898
|
+
const env = envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir, args.baseEnv);
|
|
213899
|
+
const argv = buildYarnInstallArgs(cacheDir, args.ignoreScripts);
|
|
213466
213900
|
try {
|
|
213467
213901
|
process.stderr.write(`[dep-installer:trace] running: yarn ${argv.join(" ")}\n`);
|
|
213468
213902
|
await execFileAsync("yarn", argv, {
|
|
@@ -213492,7 +213926,7 @@ async function runYarnBerryInstall(args) {
|
|
|
213492
213926
|
await assertYarnAvailable();
|
|
213493
213927
|
const { nodeDir } = resolveNpm();
|
|
213494
213928
|
const env = {
|
|
213495
|
-
...envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir),
|
|
213929
|
+
...envWithNodeOnPath(args.registryConfig.extraEnv, nodeDir, args.baseEnv),
|
|
213496
213930
|
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0"
|
|
213497
213931
|
};
|
|
213498
213932
|
const argv = buildYarnBerryInstallArgs();
|
|
@@ -213523,11 +213957,11 @@ async function assertYarnAvailable() {
|
|
|
213523
213957
|
* Deep cross-sibling build chains may build out of strict topological order —
|
|
213524
213958
|
* real `.kici` closures are shallow.
|
|
213525
213959
|
*/
|
|
213526
|
-
async function buildYarnWorkspaceClosure(repoRoot, kiciDir, yarnFlavor) {
|
|
213960
|
+
async function buildYarnWorkspaceClosure(repoRoot, kiciDir, yarnFlavor, baseEnv) {
|
|
213527
213961
|
const siblings = await collectInRepoSiblings(repoRoot, kiciDir, resolveYarnNodeModulesRoot(repoRoot, kiciDir));
|
|
213528
213962
|
if (siblings.length === 0) return;
|
|
213529
213963
|
const { nodeDir } = resolveNpm();
|
|
213530
|
-
const env = envWithNodeOnPath({}, nodeDir);
|
|
213964
|
+
const env = envWithNodeOnPath({}, nodeDir, baseEnv);
|
|
213531
213965
|
for (const rel of [...siblings].reverse()) {
|
|
213532
213966
|
const sibDir = join(repoRoot, rel);
|
|
213533
213967
|
if (!await siblingHasBuildScript(sibDir)) continue;
|
|
@@ -213571,9 +214005,9 @@ async function siblingHasBuildScript(sibDir) {
|
|
|
213571
214005
|
* the subprocess stderr/stdout is folded into the thrown error so the job's
|
|
213572
214006
|
* failure message names the real cause instead of a bare "Command failed".
|
|
213573
214007
|
*/
|
|
213574
|
-
async function buildWorkspaceClosure(repoRoot) {
|
|
214008
|
+
async function buildWorkspaceClosure(repoRoot, baseEnv) {
|
|
213575
214009
|
const { nodeDir } = resolveNpm();
|
|
213576
|
-
const env = envWithNodeOnPath({}, nodeDir);
|
|
214010
|
+
const env = envWithNodeOnPath({}, nodeDir, baseEnv);
|
|
213577
214011
|
const argv = [
|
|
213578
214012
|
"--filter",
|
|
213579
214013
|
"{.kici}^...",
|
|
@@ -213616,6 +214050,394 @@ function logSubprocessStreams(e, tokens) {
|
|
|
213616
214050
|
if (e && typeof e === "object" && "stderr" in e) process.stderr.write(`[dep-installer:trace] stderr: ${redactNpmOutput(String(e.stderr), tokens).slice(0, 500)}\n`);
|
|
213617
214051
|
}
|
|
213618
214052
|
//#endregion
|
|
214053
|
+
//#region ../core/dist/kici-ignore.js
|
|
214054
|
+
/**
|
|
214055
|
+
* `.kici/.kiciignore` — the declared list of paths the source digest excludes.
|
|
214056
|
+
*
|
|
214057
|
+
* The digest names a workflow's identity, so *which files it covers* is part of
|
|
214058
|
+
* that identity's definition. Before this file that definition was three
|
|
214059
|
+
* hard-coded constants, and it was wrong: the agent rewrites
|
|
214060
|
+
* `.kici/package-lock.json` (`npm install`, deliberately not `npm ci`, so a
|
|
214061
|
+
* resolved URL baked into the lock file cannot point at the wrong registry) and
|
|
214062
|
+
* `.kici/.npmrc` (a managed auth block, applied for one install and restored
|
|
214063
|
+
* after) inside the very tree whose digest it must reproduce. Neither was
|
|
214064
|
+
* excluded, so the drift gate rejected runs whose source had not changed.
|
|
214065
|
+
*
|
|
214066
|
+
* Making the set declarative rather than hard-coded is what lets a customer
|
|
214067
|
+
* whose own tooling writes into `.kici/` fix the same class of problem without
|
|
214068
|
+
* a release.
|
|
214069
|
+
*
|
|
214070
|
+
* ## Not to be confused with the repo-root `.kiciignore`
|
|
214071
|
+
*
|
|
214072
|
+
* A repo **root** `.kiciignore` already exists and is unrelated: it selects
|
|
214073
|
+
* which working-tree files a `kici run --remote` overlay uploads
|
|
214074
|
+
* (`compiler/src/remote/uploader.ts`, glob-matched, relative to the repo root).
|
|
214075
|
+
* This one lives at `.kici/.kiciignore`, is matched relative to `.kici/`, and
|
|
214076
|
+
* affects only the digest. The two files never read each other.
|
|
214077
|
+
*
|
|
214078
|
+
* ## Semantics
|
|
214079
|
+
*
|
|
214080
|
+
* gitignore-style, and deliberately implemented here rather than delegated to a
|
|
214081
|
+
* glob library: a glob matcher reads `node_modules/` as a directory named
|
|
214082
|
+
* `node_modules` and NOT as everything beneath it, which would silently fail to
|
|
214083
|
+
* exclude the exact paths this file exists to exclude. The digest is a
|
|
214084
|
+
* compat-protected identity input, so its matching rules are spelled out and
|
|
214085
|
+
* tested rather than inherited.
|
|
214086
|
+
*
|
|
214087
|
+
* - Blank lines and `#` comments are dropped; `\#` escapes a leading `#`.
|
|
214088
|
+
* - A trailing `/` makes a pattern directory-only.
|
|
214089
|
+
* - A pattern containing a slash is anchored at `.kici/`; a bare name matches
|
|
214090
|
+
* at any depth.
|
|
214091
|
+
* - `*` matches within one segment, `**` across segments, `?` one character.
|
|
214092
|
+
* - A leading `!` re-includes; the last matching pattern wins.
|
|
214093
|
+
* - A path under an ignored directory is ignored.
|
|
214094
|
+
*
|
|
214095
|
+
* One deliberate deviation from gitignore: a **symlink to a directory counts as
|
|
214096
|
+
* a directory**, so `node_modules/` covers a symlinked `node_modules`. `git`
|
|
214097
|
+
* treats a symlink as a file and would not. The purpose of the entry is
|
|
214098
|
+
* "exclude the dependency tree, whatever shape it takes on disk" — and treating
|
|
214099
|
+
* the link as a file hashed a `symlink:<target>` member into the identity that
|
|
214100
|
+
* the agent's own tree, where the dependency install writes a real directory,
|
|
214101
|
+
* structurally could not hold. The resolution happens in `collectSourcePaths`,
|
|
214102
|
+
* which stats a link to classify it and never follows one to read what is
|
|
214103
|
+
* behind it; the matcher below is unchanged and merely receives a truthful
|
|
214104
|
+
* `isDir`.
|
|
214105
|
+
*/
|
|
214106
|
+
/**
|
|
214107
|
+
* The exclusion set applied when `.kici/.kiciignore` is absent.
|
|
214108
|
+
*
|
|
214109
|
+
* `node_modules/` and `types/` are build outputs the tarball either omits or
|
|
214110
|
+
* regenerates; `.npmrc`, `package-lock.json` and `pnpm-lock.yaml` are rewritten
|
|
214111
|
+
* by the dependency install a run performs before it re-hashes;
|
|
214112
|
+
* `kici.lock.json` is the file the digest is written into.
|
|
214113
|
+
*/
|
|
214114
|
+
const KICI_DIGEST_DEFAULT_EXCLUSIONS = [
|
|
214115
|
+
"node_modules/",
|
|
214116
|
+
"types/",
|
|
214117
|
+
".npmrc",
|
|
214118
|
+
"package-lock.json",
|
|
214119
|
+
"pnpm-lock.yaml",
|
|
214120
|
+
"kici.lock.json"
|
|
214121
|
+
];
|
|
214122
|
+
/**
|
|
214123
|
+
* Excluded no matter what `.kiciignore` says.
|
|
214124
|
+
*
|
|
214125
|
+
* The compiler writes the computed digest INTO `.kici/kici.lock.json`, so a
|
|
214126
|
+
* digest covering that file would be an input to itself and no fixed point
|
|
214127
|
+
* would exist. This is arithmetic, not policy — there is no configuration under
|
|
214128
|
+
* which hashing it could work, so honoring a `.kiciignore` that omits it would
|
|
214129
|
+
* only produce a gate that rejects every run.
|
|
214130
|
+
*
|
|
214131
|
+
* The lock file's own integrity comes from elsewhere: the orchestrator fetches
|
|
214132
|
+
* it at the commit SHA, so provenance vouches for it, never `contentHash`.
|
|
214133
|
+
*/
|
|
214134
|
+
const KICI_DIGEST_FORCED_EXCLUSIONS = ["kici.lock.json"];
|
|
214135
|
+
/**
|
|
214136
|
+
* Paths a run rewrites inside `.kici/` before it re-hashes the tree.
|
|
214137
|
+
*
|
|
214138
|
+
* Under replace semantics a short `.kiciignore` silently re-includes these, and
|
|
214139
|
+
* the digest then changes on every run with nothing naming why. A compile whose
|
|
214140
|
+
* file omits one warns rather than failing: the customer keeps full control,
|
|
214141
|
+
* and the failure stops being mysterious.
|
|
214142
|
+
*/
|
|
214143
|
+
const KICI_RUN_REWRITTEN_PATHS = [
|
|
214144
|
+
"node_modules/",
|
|
214145
|
+
".npmrc",
|
|
214146
|
+
"package-lock.json"
|
|
214147
|
+
];
|
|
214148
|
+
/**
|
|
214149
|
+
* The `.kiciignore` file's name, relative to `.kici/`. Only the copy at the
|
|
214150
|
+
* root of `.kici/` is consulted; a nested one is ordinary hashed source.
|
|
214151
|
+
*/
|
|
214152
|
+
const KICI_IGNORE_FILENAME = ".kiciignore";
|
|
214153
|
+
/**
|
|
214154
|
+
* Hashed no matter what `.kiciignore` says — including when it names itself.
|
|
214155
|
+
*
|
|
214156
|
+
* The file declares which paths define a workflow's identity, so it is part of
|
|
214157
|
+
* that identity: editing it must move the digest and force a recompile. Were it
|
|
214158
|
+
* able to exclude itself, someone could change what a lock file attests to
|
|
214159
|
+
* without changing the lock file, which is the one outcome the digest exists to
|
|
214160
|
+
* prevent. Forced inclusion beats every exclusion, the forced ones included.
|
|
214161
|
+
*/
|
|
214162
|
+
const KICI_DIGEST_FORCED_INCLUSIONS = [KICI_IGNORE_FILENAME];
|
|
214163
|
+
/**
|
|
214164
|
+
* Split a `.kiciignore` file into patterns: trimmed, comment- and blank-free.
|
|
214165
|
+
*
|
|
214166
|
+
* Returns an empty array for a file that declares nothing, which under replace
|
|
214167
|
+
* semantics genuinely means "exclude nothing but the forced entry" — distinct
|
|
214168
|
+
* from an absent file, which means "use the defaults".
|
|
214169
|
+
*/
|
|
214170
|
+
function parseKiciIgnore(content) {
|
|
214171
|
+
const patterns = [];
|
|
214172
|
+
for (const rawLine of content.split("\n")) {
|
|
214173
|
+
const line = rawLine.trim();
|
|
214174
|
+
if (line.length === 0 || line.startsWith("#")) continue;
|
|
214175
|
+
patterns.push(line.startsWith("\\#") ? line.slice(1) : line);
|
|
214176
|
+
}
|
|
214177
|
+
return patterns;
|
|
214178
|
+
}
|
|
214179
|
+
/** Translate one gitignore-style pattern body into an anchored regular expression. */
|
|
214180
|
+
function globToRegExpSource(glob) {
|
|
214181
|
+
let out = "";
|
|
214182
|
+
for (let i = 0; i < glob.length; i++) {
|
|
214183
|
+
const ch = glob[i];
|
|
214184
|
+
if (ch === "*") {
|
|
214185
|
+
if (glob[i + 1] === "*") {
|
|
214186
|
+
out += ".*";
|
|
214187
|
+
i++;
|
|
214188
|
+
} else out += "[^/]*";
|
|
214189
|
+
} else if (ch === "?") out += "[^/]";
|
|
214190
|
+
else out += ch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
214191
|
+
}
|
|
214192
|
+
return out;
|
|
214193
|
+
}
|
|
214194
|
+
function compilePattern(pattern) {
|
|
214195
|
+
let body = pattern;
|
|
214196
|
+
const negated = body.startsWith("!");
|
|
214197
|
+
if (negated) body = body.slice(1);
|
|
214198
|
+
if (body.startsWith("\\!")) body = body.slice(1);
|
|
214199
|
+
if (body.length === 0) return null;
|
|
214200
|
+
const dirOnly = body.endsWith("/");
|
|
214201
|
+
if (dirOnly) body = body.slice(0, -1);
|
|
214202
|
+
if (body.length === 0) return null;
|
|
214203
|
+
const leadingSlash = body.startsWith("/");
|
|
214204
|
+
if (leadingSlash) body = body.slice(1);
|
|
214205
|
+
if (body.length === 0) return null;
|
|
214206
|
+
const anchored = leadingSlash || body.includes("/");
|
|
214207
|
+
const source = globToRegExpSource(body);
|
|
214208
|
+
return {
|
|
214209
|
+
re: new RegExp(anchored ? `^${source}$` : `(?:^|/)${source}$`),
|
|
214210
|
+
dirOnly,
|
|
214211
|
+
negated
|
|
214212
|
+
};
|
|
214213
|
+
}
|
|
214214
|
+
/**
|
|
214215
|
+
* Build a matcher over patterns already parsed from a `.kiciignore`.
|
|
214216
|
+
*
|
|
214217
|
+
* The returned predicate takes a POSIX path relative to `.kici/` and whether it
|
|
214218
|
+
* names a directory. It reports true when the path itself — or any directory
|
|
214219
|
+
* above it — is excluded, so a directory pattern covers its whole subtree.
|
|
214220
|
+
*/
|
|
214221
|
+
function buildKiciIgnoreMatcher(patterns) {
|
|
214222
|
+
const compiled = [];
|
|
214223
|
+
for (const pattern of patterns) {
|
|
214224
|
+
const entry = compilePattern(pattern);
|
|
214225
|
+
if (entry) compiled.push(entry);
|
|
214226
|
+
}
|
|
214227
|
+
if (compiled.length === 0) return () => false;
|
|
214228
|
+
/** Decide one concrete path, ancestors not considered. Last match wins. */
|
|
214229
|
+
const decideSelf = (relPath, isDir) => {
|
|
214230
|
+
let ignored = false;
|
|
214231
|
+
for (const { re, dirOnly, negated } of compiled) {
|
|
214232
|
+
if (dirOnly && !isDir) continue;
|
|
214233
|
+
if (re.test(relPath)) ignored = !negated;
|
|
214234
|
+
}
|
|
214235
|
+
return ignored;
|
|
214236
|
+
};
|
|
214237
|
+
return (relPath, isDir) => {
|
|
214238
|
+
const segments = relPath.split("/");
|
|
214239
|
+
for (let i = 1; i < segments.length; i++) if (decideSelf(segments.slice(0, i).join("/"), true)) return true;
|
|
214240
|
+
return decideSelf(relPath, isDir);
|
|
214241
|
+
};
|
|
214242
|
+
}
|
|
214243
|
+
/**
|
|
214244
|
+
* Which of the run-rewritten paths a pattern set fails to cover.
|
|
214245
|
+
*
|
|
214246
|
+
* Asks the matcher rather than comparing strings, so a broader pattern that
|
|
214247
|
+
* genuinely does exclude a path (`*`, `*.json`) is credited and does not warn.
|
|
214248
|
+
* Returned sorted, so the warning text is deterministic.
|
|
214249
|
+
*/
|
|
214250
|
+
function findUncoveredRunRewrittenPaths(patterns) {
|
|
214251
|
+
const matches = buildKiciIgnoreMatcher(patterns);
|
|
214252
|
+
const uncovered = [];
|
|
214253
|
+
for (const target of KICI_RUN_REWRITTEN_PATHS) {
|
|
214254
|
+
const isDir = target.endsWith("/");
|
|
214255
|
+
if (!matches(isDir ? target.slice(0, -1) : target, isDir)) uncovered.push(target);
|
|
214256
|
+
}
|
|
214257
|
+
return uncovered.sort();
|
|
214258
|
+
}
|
|
214259
|
+
/** The warning text for one run-rewritten path a `.kiciignore` omits. */
|
|
214260
|
+
function runRewrittenWarning(target) {
|
|
214261
|
+
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.`;
|
|
214262
|
+
}
|
|
214263
|
+
//#endregion
|
|
214264
|
+
//#region ../core/dist/kici-source-digest.js
|
|
214265
|
+
init_crypto();
|
|
214266
|
+
/**
|
|
214267
|
+
* Resolve the exclusion rules for `kiciDir`, reading `.kici/.kiciignore`.
|
|
214268
|
+
*
|
|
214269
|
+
* Separate from `hashKiciSourceTree` because the compiler needs the warnings
|
|
214270
|
+
* without needing a digest — and because a caller that wants to explain the
|
|
214271
|
+
* exclusion set should not have to re-implement how it was chosen.
|
|
214272
|
+
*
|
|
214273
|
+
* An unreadable file falls back to the defaults: the digest's job is to be
|
|
214274
|
+
* computable, and a read failure that silently emptied the exclusion set would
|
|
214275
|
+
* make every subsequent run drift instead.
|
|
214276
|
+
*/
|
|
214277
|
+
async function loadKiciIgnoreRules(kiciDir) {
|
|
214278
|
+
let patterns = KICI_DIGEST_DEFAULT_EXCLUSIONS;
|
|
214279
|
+
let source = "default";
|
|
214280
|
+
try {
|
|
214281
|
+
patterns = parseKiciIgnore(await ro.readFile(path$1.join(kiciDir, KICI_IGNORE_FILENAME), "utf-8"));
|
|
214282
|
+
source = "file";
|
|
214283
|
+
} catch {}
|
|
214284
|
+
const missingRunRewritten = source === "file" ? findUncoveredRunRewrittenPaths(patterns) : [];
|
|
214285
|
+
return {
|
|
214286
|
+
source,
|
|
214287
|
+
patterns,
|
|
214288
|
+
forced: KICI_DIGEST_FORCED_EXCLUSIONS,
|
|
214289
|
+
missingRunRewritten,
|
|
214290
|
+
warnings: missingRunRewritten.map(runRewrittenWarning)
|
|
214291
|
+
};
|
|
214292
|
+
}
|
|
214293
|
+
/**
|
|
214294
|
+
* Whether the exclusion patterns should see this entry as a directory.
|
|
214295
|
+
*
|
|
214296
|
+
* `Dirent.isDirectory()` describes the link itself, so a symlinked
|
|
214297
|
+
* `.kici/node_modules` reads as a file and the directory-only `node_modules/`
|
|
214298
|
+
* pattern misses it. The compiler then hashes a `symlink:<target>` member that
|
|
214299
|
+
* no agent can hold: the tarball drops `.kici/node_modules` by prefix, and the
|
|
214300
|
+
* restore materialises a real directory the exclusion does match. The producer
|
|
214301
|
+
* and its verifier disagree on a tree nobody edited, and recompiling reproduces
|
|
214302
|
+
* the same lock.
|
|
214303
|
+
*
|
|
214304
|
+
* So classification resolves the link and the walk does not. An entry that
|
|
214305
|
+
* survives exclusion is still recorded as a member and still contributes its
|
|
214306
|
+
* target string; nothing here follows a link to read what is behind it, which
|
|
214307
|
+
* is what keeps the walk terminating and keeps the hashed set equal to what the
|
|
214308
|
+
* tarball carries.
|
|
214309
|
+
*
|
|
214310
|
+
* A link that cannot be resolved — dangling, cyclic (the kernel's own
|
|
214311
|
+
* `ELOOP`), or unreadable — is a file, which is what it contributes anyway.
|
|
214312
|
+
*
|
|
214313
|
+
* This is a deliberate deviation from gitignore, where `node_modules/` does not
|
|
214314
|
+
* match a symlinked `node_modules`. The purpose of the entry is "exclude the
|
|
214315
|
+
* dependency tree, whatever shape it takes on disk", and a link to the
|
|
214316
|
+
* dependency tree is the dependency tree.
|
|
214317
|
+
*/
|
|
214318
|
+
async function matchesAsDirectory(abs, entry) {
|
|
214319
|
+
if (entry.isDirectory()) return true;
|
|
214320
|
+
if (!entry.isSymbolicLink()) return false;
|
|
214321
|
+
try {
|
|
214322
|
+
return (await ro.stat(abs)).isDirectory();
|
|
214323
|
+
} catch {
|
|
214324
|
+
return false;
|
|
214325
|
+
}
|
|
214326
|
+
}
|
|
214327
|
+
/** Every file the source tarball carries, as `.kici/`-prefixed POSIX paths. */
|
|
214328
|
+
async function collectSourcePaths(kiciDir, rules) {
|
|
214329
|
+
const found = [];
|
|
214330
|
+
const isExcluded = buildKiciIgnoreMatcher(rules.patterns);
|
|
214331
|
+
const isForced = buildKiciIgnoreMatcher(rules.forced);
|
|
214332
|
+
async function walk(absDir, relDir) {
|
|
214333
|
+
const entries = await ro.readdir(absDir, { withFileTypes: true });
|
|
214334
|
+
for (const entry of entries) {
|
|
214335
|
+
const rel = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
214336
|
+
const member = `.kici/${rel}`;
|
|
214337
|
+
const abs = path$1.join(absDir, entry.name);
|
|
214338
|
+
const asDir = await matchesAsDirectory(abs, entry);
|
|
214339
|
+
if (!(!asDir && KICI_DIGEST_FORCED_INCLUSIONS.includes(rel)) && (isForced(rel, asDir) || isExcluded(rel, asDir))) continue;
|
|
214340
|
+
if (entry.isDirectory()) await walk(abs, rel);
|
|
214341
|
+
else found.push(member);
|
|
214342
|
+
}
|
|
214343
|
+
}
|
|
214344
|
+
await walk(kiciDir, "");
|
|
214345
|
+
return found.sort();
|
|
214346
|
+
}
|
|
214347
|
+
/**
|
|
214348
|
+
* Read one member's contribution. A symlink contributes its target; a file its
|
|
214349
|
+
* text. An unreadable entry contributes the empty string rather than throwing —
|
|
214350
|
+
* the digest's job is to change when the tree changes, and a read failure is
|
|
214351
|
+
* reported by the tar step that follows, not here.
|
|
214352
|
+
*/
|
|
214353
|
+
async function readMember(abs) {
|
|
214354
|
+
if ((await ro.lstat(abs)).isSymbolicLink()) return `symlink:${await ro.readlink(abs)}`;
|
|
214355
|
+
return ro.readFile(abs, "utf-8");
|
|
214356
|
+
}
|
|
214357
|
+
/**
|
|
214358
|
+
* SHA-256 over every file in `kiciDir`, excluding `.kici/node_modules/`.
|
|
214359
|
+
*
|
|
214360
|
+
* Returns the empty string when the directory does not exist, which the callers
|
|
214361
|
+
* treat exactly as they already treat an unreadable entry file: no hash, so no
|
|
214362
|
+
* drift gate.
|
|
214363
|
+
*/
|
|
214364
|
+
async function hashKiciSourceTree(kiciDir) {
|
|
214365
|
+
let members;
|
|
214366
|
+
try {
|
|
214367
|
+
members = await collectSourcePaths(kiciDir, await loadKiciIgnoreRules(kiciDir));
|
|
214368
|
+
} catch {
|
|
214369
|
+
return "";
|
|
214370
|
+
}
|
|
214371
|
+
const parts = [];
|
|
214372
|
+
for (const member of members) {
|
|
214373
|
+
const abs = path$1.join(kiciDir, member.slice(6));
|
|
214374
|
+
let content;
|
|
214375
|
+
try {
|
|
214376
|
+
content = await readMember(abs);
|
|
214377
|
+
} catch {
|
|
214378
|
+
content = "";
|
|
214379
|
+
}
|
|
214380
|
+
parts.push(`${member}\0${normalizeLineEndings(content)}\0`);
|
|
214381
|
+
}
|
|
214382
|
+
return sha256(parts.join(""));
|
|
214383
|
+
}
|
|
214384
|
+
/**
|
|
214385
|
+
* Every symlink the digest hashes, in member order.
|
|
214386
|
+
*
|
|
214387
|
+
* Reads the same member set the digest does, so a link an exclusion covers is
|
|
214388
|
+
* absent here for the same reason it is absent from the hash.
|
|
214389
|
+
*/
|
|
214390
|
+
async function collectSourceSymlinks(kiciDir, rules) {
|
|
214391
|
+
let members;
|
|
214392
|
+
try {
|
|
214393
|
+
members = await collectSourcePaths(kiciDir, rules ?? await loadKiciIgnoreRules(kiciDir));
|
|
214394
|
+
} catch {
|
|
214395
|
+
return [];
|
|
214396
|
+
}
|
|
214397
|
+
const found = [];
|
|
214398
|
+
for (const member of members) {
|
|
214399
|
+
const abs = path$1.join(kiciDir, member.slice(6));
|
|
214400
|
+
try {
|
|
214401
|
+
if ((await ro.lstat(abs)).isSymbolicLink()) found.push({
|
|
214402
|
+
member,
|
|
214403
|
+
target: await ro.readlink(abs)
|
|
214404
|
+
});
|
|
214405
|
+
} catch {}
|
|
214406
|
+
}
|
|
214407
|
+
return found;
|
|
214408
|
+
}
|
|
214409
|
+
/**
|
|
214410
|
+
* The sentence a drift error adds when the hashed tree carries symlinks.
|
|
214411
|
+
*
|
|
214412
|
+
* "Run 'kici compile'" is the right remedy for ordinary drift and is actively
|
|
214413
|
+
* misleading here: a symlink the tarball omits or extraction rewrites makes the
|
|
214414
|
+
* producer's hash unreachable on this side, so recompiling reproduces the same
|
|
214415
|
+
* lock forever. Naming the links turns an unrecoverable loop into something the
|
|
214416
|
+
* error text alone explains. Empty when the tree carries none, so an ordinary
|
|
214417
|
+
* drift error is unchanged.
|
|
214418
|
+
*/
|
|
214419
|
+
function hashedSymlinkDriftNote(symlinks) {
|
|
214420
|
+
if (symlinks.length === 0) return "";
|
|
214421
|
+
const named = symlinks.map(({ member, target }) => `${member} -> ${target}`).join(", ");
|
|
214422
|
+
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.`;
|
|
214423
|
+
}
|
|
214424
|
+
/**
|
|
214425
|
+
* Locate the `.kici` directory a workflow file lives under, or null when it
|
|
214426
|
+
* does not live under one (a config file outside the convention). Walks
|
|
214427
|
+
* ancestors rather than taking the directory as a parameter, so every entry
|
|
214428
|
+
* point that loads a workflow module resolves the same tree without threading
|
|
214429
|
+
* one more argument through each of them.
|
|
214430
|
+
*/
|
|
214431
|
+
function findKiciDir(entryPath) {
|
|
214432
|
+
let dir = path$1.dirname(path$1.resolve(entryPath));
|
|
214433
|
+
for (;;) {
|
|
214434
|
+
if (path$1.basename(dir) === ".kici") return dir;
|
|
214435
|
+
const parent = path$1.dirname(dir);
|
|
214436
|
+
if (parent === dir) return null;
|
|
214437
|
+
dir = parent;
|
|
214438
|
+
}
|
|
214439
|
+
}
|
|
214440
|
+
//#endregion
|
|
213619
214441
|
//#region src/execution/generator-context.ts
|
|
213620
214442
|
/**
|
|
213621
214443
|
* Build the context handed to a `DynamicJobFn`.
|
|
@@ -213669,12 +214491,17 @@ init_dist();
|
|
|
213669
214491
|
* ESM modules by resolved URL, so importing that path yields the workflow's live
|
|
213670
214492
|
* singleton, not a fresh copy.
|
|
213671
214493
|
*
|
|
213672
|
-
*
|
|
214494
|
+
* Both specifiers resolve to the same module-global maps — `internal.ts` and the
|
|
214495
|
+
* root barrel re-export the same `outputs.js` bindings — so the fallback below
|
|
214496
|
+
* changes which entry is imported, never which singleton is mutated.
|
|
214497
|
+
*
|
|
214498
|
+
* Falls back to the agent's bundled setters when neither resolves (mirrors
|
|
213673
214499
|
* `resolveSdkSetters` in the compiler's test runner).
|
|
213674
214500
|
*/
|
|
213675
214501
|
async function resolveWorkflowSdkSetters(workflowFilePath) {
|
|
213676
|
-
|
|
213677
|
-
|
|
214502
|
+
const req = createRequire(workflowFilePath);
|
|
214503
|
+
for (const specifier of ["@kici-dev/sdk/internal", "@kici-dev/sdk"]) try {
|
|
214504
|
+
const sdkEntry = req.resolve(specifier);
|
|
213678
214505
|
const sdk = await import(pathToFileURL(sdkEntry).href);
|
|
213679
214506
|
if (typeof sdk.setStepOutputsMap === "function" && typeof sdk.setStepRefMap === "function" && typeof sdk.setJobOutputsMap === "function") return {
|
|
213680
214507
|
setStepOutputsMap: sdk.setStepOutputsMap,
|
|
@@ -213688,8 +214515,8 @@ async function resolveWorkflowSdkSetters(workflowFilePath) {
|
|
|
213688
214515
|
setJobOutputsMap
|
|
213689
214516
|
};
|
|
213690
214517
|
}
|
|
213691
|
-
const AGENT_SDK_VERSION = "0.
|
|
213692
|
-
const AGENT_SDK_BUNDLE_HASH = "
|
|
214518
|
+
const AGENT_SDK_VERSION = "0.8.0";
|
|
214519
|
+
const AGENT_SDK_BUNDLE_HASH = "065963c7765dc8d87e04d45f57d7e15be1613da705e4ff3ec3742fd1408b7bf5";
|
|
213693
214520
|
/**
|
|
213694
214521
|
* Register the ESM loader hook that transforms `.ts` / `.tsx` files on the fly
|
|
213695
214522
|
* for subsequent dynamic `import()` calls. Idempotent at our level via the
|
|
@@ -213729,10 +214556,49 @@ function ensureLoaderHookRegistered() {
|
|
|
213729
214556
|
* with lockfiles compiled on Linux (LF).
|
|
213730
214557
|
*/
|
|
213731
214558
|
function computeContentHash(rawSource, assetDigest) {
|
|
213732
|
-
let input = `
|
|
214559
|
+
let input = `7:${normalizeLineEndings(rawSource)}`;
|
|
213733
214560
|
if (assetDigest !== void 0 && assetDigest.length > 0) input += `\0${normalizeLineEndings(assetDigest)}`;
|
|
213734
214561
|
return sha256(input);
|
|
213735
214562
|
}
|
|
214563
|
+
/**
|
|
214564
|
+
* The compile schema version the lock file records for `sourceFile`, or null
|
|
214565
|
+
* when the tree carries no readable lock (a `file://` in-place run, a workflow
|
|
214566
|
+
* outside the `.kici/` convention, a hand-built fixture).
|
|
214567
|
+
*
|
|
214568
|
+
* The source tarball carries `.kici/kici.lock.json` — the digest excludes it,
|
|
214569
|
+
* but `source-packer.ts` packs it — so the agent can read the producing
|
|
214570
|
+
* compiler's schema version from the tree it already has, with no wire field
|
|
214571
|
+
* to plumb and no protocol change.
|
|
214572
|
+
*
|
|
214573
|
+
* A malformed or unreadable lock returns null rather than throwing: this is a
|
|
214574
|
+
* diagnostic gate in front of the real hash check, so it must never convert a
|
|
214575
|
+
* bad lock into a worse error than the hash comparison already gives.
|
|
214576
|
+
*/
|
|
214577
|
+
async function readLockCompileSchemaVersion(kiciDir, sourceFile) {
|
|
214578
|
+
let parsed;
|
|
214579
|
+
try {
|
|
214580
|
+
parsed = JSON.parse(await ro.readFile(path$1.join(kiciDir, "kici.lock.json"), "utf-8"));
|
|
214581
|
+
} catch {
|
|
214582
|
+
return null;
|
|
214583
|
+
}
|
|
214584
|
+
const workflows = parsed?.workflows;
|
|
214585
|
+
if (!Array.isArray(workflows)) return null;
|
|
214586
|
+
const normalize = (p) => p.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
214587
|
+
const target = normalize(sourceFile);
|
|
214588
|
+
const versionOf = (w) => {
|
|
214589
|
+
const v = w?.compileSchemaVersion;
|
|
214590
|
+
return typeof v === "number" && Number.isFinite(v) && v > 0 ? v : null;
|
|
214591
|
+
};
|
|
214592
|
+
for (const w of workflows) {
|
|
214593
|
+
const file = w?.source?.file;
|
|
214594
|
+
if (typeof file === "string" && normalize(file) === target) return versionOf(w);
|
|
214595
|
+
}
|
|
214596
|
+
for (const w of workflows) {
|
|
214597
|
+
const v = versionOf(w);
|
|
214598
|
+
if (v !== null) return v;
|
|
214599
|
+
}
|
|
214600
|
+
return null;
|
|
214601
|
+
}
|
|
213736
214602
|
async function buildAssetDigestFromResolvedPaths(workDir, resolvedPaths) {
|
|
213737
214603
|
const parts = [];
|
|
213738
214604
|
for (const rel of resolvedPaths) {
|
|
@@ -213754,20 +214620,30 @@ async function buildAssetDigestFromResolvedPaths(workDir, resolvedPaths) {
|
|
|
213754
214620
|
* `node_modules/` the same way any `tsx`-style runner would — so host-repo
|
|
213755
214621
|
* helpers and `@kici-dev/sdk` Just Work.
|
|
213756
214622
|
*
|
|
213757
|
-
* When `expectedContentHash` is provided, verifies the
|
|
213758
|
-
* the hash in the lock file.
|
|
213759
|
-
*
|
|
213760
|
-
*
|
|
214623
|
+
* When `expectedContentHash` is provided, verifies the extracted `.kici/` tree
|
|
214624
|
+
* matches the hash in the lock file. It re-hashes the whole tree, not the entry
|
|
214625
|
+
* file alone, so an edit to an imported helper is caught — that was the gap
|
|
214626
|
+
* that let a warm cache restore a stale tarball and run the OLD helper green.
|
|
214627
|
+
* Drift produces a descriptive error that surfaces the baked agent SDK
|
|
214628
|
+
* fingerprint (useful when debugging "is the agent running a stale build?").
|
|
213761
214629
|
*/
|
|
213762
214630
|
async function loadWorkflowSource(workDir, sourceFile, expectedContentHash, resolvedHashFiles) {
|
|
213763
214631
|
ensureLoaderHookRegistered();
|
|
213764
214632
|
const filePath = path$1.join(workDir, sourceFile);
|
|
213765
214633
|
if (expectedContentHash) {
|
|
213766
|
-
const
|
|
214634
|
+
const kiciDir = findKiciDir(filePath);
|
|
214635
|
+
if (kiciDir) {
|
|
214636
|
+
const lockVersion = await readLockCompileSchemaVersion(kiciDir, sourceFile);
|
|
214637
|
+
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}).`);
|
|
214638
|
+
}
|
|
214639
|
+
const rawSource = (kiciDir ? await hashKiciSourceTree(kiciDir) : "") || await ro.readFile(filePath, "utf-8");
|
|
213767
214640
|
let assetDigest;
|
|
213768
214641
|
if (resolvedHashFiles?.length) assetDigest = await buildAssetDigestFromResolvedPaths(workDir, resolvedHashFiles);
|
|
213769
214642
|
const actualHash = computeContentHash(rawSource, assetDigest);
|
|
213770
|
-
if (actualHash !== expectedContentHash)
|
|
214643
|
+
if (actualHash !== expectedContentHash) {
|
|
214644
|
+
const symlinkNote = kiciDir ? hashedSymlinkDriftNote(await collectSourceSymlinks(kiciDir)) : "";
|
|
214645
|
+
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}`);
|
|
214646
|
+
}
|
|
213771
214647
|
}
|
|
213772
214648
|
return {
|
|
213773
214649
|
module: await import(pathToFileURL(filePath).href + `?t=${Date.now()}`),
|
|
@@ -213930,22 +214806,30 @@ function applyGlobalWorkflowEnv(repos) {
|
|
|
213930
214806
|
* `.kici/` source tarball restoration for execution agents.
|
|
213931
214807
|
*
|
|
213932
214808
|
* Downloads a pre-built `.kici/` source tarball from the orchestrator's cache
|
|
213933
|
-
* and
|
|
214809
|
+
* and installs it at `workDir/.kici` so the workflow entry point becomes
|
|
213934
214810
|
* importable. Mirrors the shape of `dep-restore.ts` but without the streaming
|
|
213935
214811
|
* optimization — source tarballs are tiny (kilobytes, not the hundreds of
|
|
213936
214812
|
* megabytes a `node_modules/` tarball carries).
|
|
213937
214813
|
*
|
|
213938
|
-
*
|
|
213939
|
-
*
|
|
213940
|
-
*
|
|
213941
|
-
*
|
|
213942
|
-
*
|
|
213943
|
-
* `
|
|
213944
|
-
* the
|
|
213945
|
-
*
|
|
213946
|
-
*
|
|
213947
|
-
*
|
|
213948
|
-
*
|
|
214814
|
+
* Two properties this path is responsible for, both of which it previously
|
|
214815
|
+
* lacked:
|
|
214816
|
+
*
|
|
214817
|
+
* **Verification.** `dispatch.sourceTarDigest` is the SHA-256 of the tarball's
|
|
214818
|
+
* own bytes, so the download is checked before anything is extracted — the same
|
|
214819
|
+
* contract `restoreDeps` has always had via `depsHash`. The older
|
|
214820
|
+
* `dispatch.sourceTarHash` field carries the workflow `contentHash` instead, so
|
|
214821
|
+
* it never could serve this purpose; it stays on the wire for older peers and
|
|
214822
|
+
* is deliberately not used as a verification input here. When no digest is
|
|
214823
|
+
* dispatched (an older orchestrator, or a source that did not come from the
|
|
214824
|
+
* content-addressed cache) the restore proceeds unverified rather than failing,
|
|
214825
|
+
* so a mixed-version rollout still runs.
|
|
214826
|
+
*
|
|
214827
|
+
* **Replacement, not overlay.** Extraction lands in a scratch directory and the
|
|
214828
|
+
* result REPLACES `workDir/.kici` wholesale, save for `node_modules/` — the one
|
|
214829
|
+
* directory the tarball deliberately omits, which the deps restore has already
|
|
214830
|
+
* written by the time this runs. Extracting over the existing tree left any file
|
|
214831
|
+
* the tarball no longer carries in place, so a helper the author deleted
|
|
214832
|
+
* survived every warm-cache run and kept being imported.
|
|
213949
214833
|
*/
|
|
213950
214834
|
init_index_min();
|
|
213951
214835
|
init_dist$2();
|
|
@@ -213961,7 +214845,14 @@ async function extractSourceTarball(data, targetDir) {
|
|
|
213961
214845
|
})).on("finish", resolve).on("error", reject);
|
|
213962
214846
|
});
|
|
213963
214847
|
}
|
|
213964
|
-
|
|
214848
|
+
/**
|
|
214849
|
+
* Download, verify, and install the `.kici/` source tree.
|
|
214850
|
+
*
|
|
214851
|
+
* @param workDir - Root of the cloned repository; `.kici` is replaced under it
|
|
214852
|
+
* @param sourceTarUrl - `http://`, `https://`, or `file://` URL to the tarball
|
|
214853
|
+
* @param sourceTarDigest - Expected SHA-256 of the tarball bytes, when known
|
|
214854
|
+
*/
|
|
214855
|
+
async function restoreSource(workDir, sourceTarUrl, sourceTarDigest) {
|
|
213965
214856
|
sourceTarUrl = resolveOrchestratorUrl(sourceTarUrl);
|
|
213966
214857
|
logger$1.info("Restoring .kici/ source from tarball", { sourceTarUrl });
|
|
213967
214858
|
const startTime = Date.now();
|
|
@@ -213971,11 +214862,34 @@ async function restoreSource(workDir, sourceTarUrl) {
|
|
|
213971
214862
|
data = await ro.readFile(localPath);
|
|
213972
214863
|
} else if (sourceTarUrl.startsWith("http://") || sourceTarUrl.startsWith("https://")) data = await downloadUrl(sourceTarUrl);
|
|
213973
214864
|
else throw new Error(`Unsupported source tarball URL scheme: ${sourceTarUrl}`);
|
|
213974
|
-
|
|
214865
|
+
if (sourceTarDigest) {
|
|
214866
|
+
const actual = createHash("sha256").update(data).digest("hex");
|
|
214867
|
+
if (actual !== sourceTarDigest) throw new Error(`Source tarball hash mismatch: expected ${sourceTarDigest}, got ${actual}. The restored source does not match what the orchestrator dispatched.`);
|
|
214868
|
+
}
|
|
214869
|
+
const kiciDir = path$1.join(workDir, ".kici");
|
|
214870
|
+
const scratch = path$1.join(workDir, `.kici.restore-${process.pid}-${Date.now()}`);
|
|
214871
|
+
try {
|
|
214872
|
+
await extractSourceTarball(data, scratch);
|
|
214873
|
+
const extracted = path$1.join(scratch, ".kici");
|
|
214874
|
+
const src = (await ro.stat(extracted).catch(() => null))?.isDirectory() ? extracted : scratch;
|
|
214875
|
+
const installedDeps = path$1.join(kiciDir, "node_modules");
|
|
214876
|
+
if (await ro.stat(installedDeps).catch(() => null)) await rename(installedDeps, path$1.join(src, "node_modules"));
|
|
214877
|
+
await rm(kiciDir, {
|
|
214878
|
+
recursive: true,
|
|
214879
|
+
force: true
|
|
214880
|
+
});
|
|
214881
|
+
await rename(src, kiciDir);
|
|
214882
|
+
} finally {
|
|
214883
|
+
await rm(scratch, {
|
|
214884
|
+
recursive: true,
|
|
214885
|
+
force: true
|
|
214886
|
+
}).catch(() => {});
|
|
214887
|
+
}
|
|
213975
214888
|
const durationMs = Date.now() - startTime;
|
|
213976
214889
|
logger$1.info(".kici/ source restored", {
|
|
213977
214890
|
sizeKB: (data.length / 1024).toFixed(2),
|
|
213978
|
-
durationMs
|
|
214891
|
+
durationMs,
|
|
214892
|
+
verified: sourceTarDigest !== void 0
|
|
213979
214893
|
});
|
|
213980
214894
|
}
|
|
213981
214895
|
//#endregion
|
|
@@ -214133,7 +215047,7 @@ init_oidc_token_relay();
|
|
|
214133
215047
|
init_dist$4();
|
|
214134
215048
|
init_download();
|
|
214135
215049
|
init_dep_restore();
|
|
214136
|
-
const AGENT_VERSION = "0.
|
|
215050
|
+
const AGENT_VERSION = "0.8.0";
|
|
214137
215051
|
process.on("uncaughtException", (err) => {
|
|
214138
215052
|
process.stderr.write(`[workflow-runner] UNCAUGHT EXCEPTION: ${err.message}\n`);
|
|
214139
215053
|
if (err.stack) process.stderr.write(`[workflow-runner] Stack: ${err.stack}\n`);
|
|
@@ -214205,7 +215119,12 @@ function installOutputCapture() {
|
|
|
214205
215119
|
line
|
|
214206
215120
|
});
|
|
214207
215121
|
}
|
|
214208
|
-
if (isForkMode)
|
|
215122
|
+
if (isForkMode) {
|
|
215123
|
+
if (!captureIsActive() || process.env.KICI_RUNNER_DEBUG_STDIO === "true") return origStdoutWrite(chunk, encodingOrCb, cb);
|
|
215124
|
+
const forkCallback = typeof encodingOrCb === "function" ? encodingOrCb : cb;
|
|
215125
|
+
if (forkCallback) forkCallback();
|
|
215126
|
+
return true;
|
|
215127
|
+
}
|
|
214209
215128
|
const callback = typeof encodingOrCb === "function" ? encodingOrCb : cb;
|
|
214210
215129
|
if (callback) callback();
|
|
214211
215130
|
return true;
|
|
@@ -214795,6 +215714,46 @@ function extractRepoIdentifier(repoUrl) {
|
|
|
214795
215714
|
* mint failure the statement is frozen + reported for later minting (deferred);
|
|
214796
215715
|
* the step still completes.
|
|
214797
215716
|
*/
|
|
215717
|
+
/**
|
|
215718
|
+
* The context a deferred attestation's frozen statement is built from.
|
|
215719
|
+
*
|
|
215720
|
+
* Prefers the orchestrator's `provenanceContext` — server truth, derived from
|
|
215721
|
+
* the same run row the mint reads, so the frozen statement is field-for-field
|
|
215722
|
+
* what a live mint would have produced and the orchestrator can cross-check it.
|
|
215723
|
+
*
|
|
215724
|
+
* Falls back to the agent's local view when an older orchestrator sent none.
|
|
215725
|
+
* That fallback disagrees with the claims by construction: `request.ref` is the
|
|
215726
|
+
* job's CHECKOUT ref, which for a pull request is the HEAD branch where the
|
|
215727
|
+
* claim is the BASE branch, and `request.workflowRef` is a global workflow's
|
|
215728
|
+
* CLONE ref where the claim is `<name>@<sha>`. So a statement built from it
|
|
215729
|
+
* fails the capture cross-check and the defer is dropped — a green job with no
|
|
215730
|
+
* attestation, rather than an unchecked statement the orchestrator signs.
|
|
215731
|
+
*/
|
|
215732
|
+
function buildLocalContext(request) {
|
|
215733
|
+
const ctx = request.provenanceContext;
|
|
215734
|
+
if (ctx) return {
|
|
215735
|
+
repository: ctx.repository ?? "",
|
|
215736
|
+
ref: ctx.ref ?? "",
|
|
215737
|
+
sha: ctx.sha,
|
|
215738
|
+
workflowRef: ctx.workflowRef ?? "",
|
|
215739
|
+
runId: ctx.runId,
|
|
215740
|
+
jobId: ctx.jobId,
|
|
215741
|
+
orgId: ctx.orgId,
|
|
215742
|
+
sourceOrigin: ctx.sourceOrigin,
|
|
215743
|
+
...ctx.provider ? { provider: ctx.provider } : {},
|
|
215744
|
+
issuer: ctx.issuer,
|
|
215745
|
+
orchestratorId: ctx.orchestratorId
|
|
215746
|
+
};
|
|
215747
|
+
return {
|
|
215748
|
+
repository: extractRepoIdentifier(request.repoUrl),
|
|
215749
|
+
ref: request.ref,
|
|
215750
|
+
sha: request.sha || null,
|
|
215751
|
+
workflowRef: request.workflowRef ?? request.workflowName,
|
|
215752
|
+
runId: request.runId,
|
|
215753
|
+
jobId: request.jobId,
|
|
215754
|
+
issuer: ""
|
|
215755
|
+
};
|
|
215756
|
+
}
|
|
214798
215757
|
function buildAttestProvenanceFn(request, workDir, getIdToken) {
|
|
214799
215758
|
return async (opts) => {
|
|
214800
215759
|
const subject = provenanceSubjectIsPath(opts.subject) ? {
|
|
@@ -214810,15 +215769,7 @@ function buildAttestProvenanceFn(request, workDir, getIdToken) {
|
|
|
214810
215769
|
"kici-agent": AGENT_VERSION,
|
|
214811
215770
|
"kici-orchestrator": "unknown"
|
|
214812
215771
|
},
|
|
214813
|
-
localContext:
|
|
214814
|
-
repository: extractRepoIdentifier(request.repoUrl),
|
|
214815
|
-
ref: request.ref,
|
|
214816
|
-
sha: request.sha || null,
|
|
214817
|
-
workflowRef: request.workflowRef ?? request.workflowName,
|
|
214818
|
-
runId: request.runId,
|
|
214819
|
-
jobId: request.jobId,
|
|
214820
|
-
issuer: request.provenanceIssuer ?? ""
|
|
214821
|
-
},
|
|
215772
|
+
localContext: buildLocalContext(request),
|
|
214822
215773
|
reportDeferred: async (report) => {
|
|
214823
215774
|
await relayProvenanceIpc({
|
|
214824
215775
|
op: "defer",
|
|
@@ -215123,20 +216074,6 @@ function buildStepSecrets(request, masker, onMaskerSecretsAdded) {
|
|
|
215123
216074
|
});
|
|
215124
216075
|
}
|
|
215125
216076
|
/**
|
|
215126
|
-
* Create a LogMasker initialized with all secret values from the request.
|
|
215127
|
-
*
|
|
215128
|
-
* Collects values from both flat secrets and all namespaced context secrets,
|
|
215129
|
-
* deduplicating before registration.
|
|
215130
|
-
*/
|
|
215131
|
-
function createSecretMasker(request) {
|
|
215132
|
-
const masker = new LogMasker();
|
|
215133
|
-
const allSecrets = {};
|
|
215134
|
-
if (request.secrets) Object.assign(allSecrets, request.secrets);
|
|
215135
|
-
if (request.namespacedSecrets) for (const contextSecrets of Object.values(request.namespacedSecrets)) Object.assign(allSecrets, contextSecrets);
|
|
215136
|
-
masker.registerSecrets(allSecrets);
|
|
215137
|
-
return masker;
|
|
215138
|
-
}
|
|
215139
|
-
/**
|
|
215140
216077
|
* Build a fresh zx `$` shell bound to the sandbox working directory and the
|
|
215141
216078
|
* sanitized environment (process.env was set by the parent via env-sanitizer
|
|
215142
216079
|
* before spawning this process). This is the single shell-construction code
|
|
@@ -215167,12 +216104,13 @@ function createSecretMasker(request) {
|
|
|
215167
216104
|
* step$.log would only set it on the function object and NOT propagate to the
|
|
215168
216105
|
* AsyncLocalStorage store that zx uses for ProcessPromise snapshots.
|
|
215169
216106
|
*/
|
|
215170
|
-
function buildSandboxShell(cwd, stepIndex, maskedSendFn) {
|
|
216107
|
+
function buildSandboxShell(cwd, stepIndex, maskedSendFn, signal) {
|
|
215171
216108
|
return $$1({
|
|
215172
216109
|
cwd,
|
|
215173
216110
|
env: process.env,
|
|
215174
216111
|
verbose: true,
|
|
215175
216112
|
quiet: false,
|
|
216113
|
+
...signal ? { signal } : {},
|
|
215176
216114
|
log: makeStreamingZxLog((line, stream) => maskedSendFn({
|
|
215177
216115
|
type: "log.line",
|
|
215178
216116
|
stepIndex,
|
|
@@ -215257,7 +216195,7 @@ function initialJobStatus(loopStatus) {
|
|
|
215257
216195
|
* inside this process with full shell access.
|
|
215258
216196
|
*/
|
|
215259
216197
|
function createSandboxStepContext(workDir, stepIndex, stepName, request, maskedSendFn, outputsMap, refMap, operatorSecretKeys, secretOutputs, jobOutputsMap, secrets, masker, signal, jobTempScope) {
|
|
215260
|
-
const step$ = buildSandboxShell(workDir, stepIndex, maskedSendFn);
|
|
216198
|
+
const step$ = buildSandboxShell(workDir, stepIndex, maskedSendFn, signal);
|
|
215261
216199
|
const log = createIpcLogger(stepIndex, stepName, maskedSendFn);
|
|
215262
216200
|
const rawPayload = rawPayloadFromEvent(request.event);
|
|
215263
216201
|
const kici = buildKiciApi(async (method, params) => {
|
|
@@ -215512,7 +216450,8 @@ async function installDependenciesIfNeeded(workflowDir, request) {
|
|
|
215512
216450
|
await installDeps(kiciDir, {
|
|
215513
216451
|
npmRegistries: request.npmRegistries,
|
|
215514
216452
|
installEnvSecrets: request.installEnvSecrets,
|
|
215515
|
-
jobIdShort: request.jobIdShort
|
|
216453
|
+
jobIdShort: request.jobIdShort,
|
|
216454
|
+
...request.allowInstallScripts ? { allowInstallScripts: true } : {}
|
|
215516
216455
|
});
|
|
215517
216456
|
trace("fallback install complete");
|
|
215518
216457
|
}
|
|
@@ -215537,7 +216476,8 @@ async function installDependenciesIfNeeded(workflowDir, request) {
|
|
|
215537
216476
|
await installDeps(kiciDir, {
|
|
215538
216477
|
npmRegistries: request.npmRegistries,
|
|
215539
216478
|
installEnvSecrets: request.installEnvSecrets,
|
|
215540
|
-
jobIdShort: request.jobIdShort
|
|
216479
|
+
jobIdShort: request.jobIdShort,
|
|
216480
|
+
...request.allowInstallScripts ? { allowInstallScripts: true } : {}
|
|
215541
216481
|
});
|
|
215542
216482
|
trace("installDeps() returned successfully");
|
|
215543
216483
|
} catch (depErr) {
|
|
@@ -215567,7 +216507,7 @@ async function restoreSourceTarballIfRequested(workflowRoot, request) {
|
|
|
215567
216507
|
stepIndex: -1,
|
|
215568
216508
|
line: "[workflow-runner] Restoring .kici/ source from cached tarball"
|
|
215569
216509
|
});
|
|
215570
|
-
await restoreSource(workflowRoot, request.sourceTarUrl);
|
|
216510
|
+
await restoreSource(workflowRoot, request.sourceTarUrl, request.sourceTarDigest);
|
|
215571
216511
|
trace("source tarball restored");
|
|
215572
216512
|
}
|
|
215573
216513
|
/**
|
|
@@ -216068,7 +217008,7 @@ function buildJobRuleCompletion(ruleResult, normalizedSteps) {
|
|
|
216068
217008
|
* success-skip). Returns false when the caller should continue to step
|
|
216069
217009
|
* execution.
|
|
216070
217010
|
*/
|
|
216071
|
-
async function maybeSkipJobOnRules(job, request, normalizedSteps, repos) {
|
|
217011
|
+
async function maybeSkipJobOnRules(job, request, normalizedSteps, send, repos) {
|
|
216072
217012
|
if (!job?.rules || job.rules.length === 0) return false;
|
|
216073
217013
|
const ev = request.event ?? {};
|
|
216074
217014
|
const ruleCtx = createRuleContext({
|
|
@@ -216087,7 +217027,7 @@ async function maybeSkipJobOnRules(job, request, normalizedSteps, repos) {
|
|
|
216087
217027
|
if (!completion) return false;
|
|
216088
217028
|
flushOutputCapture();
|
|
216089
217029
|
capturePrepareActive = false;
|
|
216090
|
-
|
|
217030
|
+
send(completion);
|
|
216091
217031
|
process.exit(0);
|
|
216092
217032
|
}
|
|
216093
217033
|
/**
|
|
@@ -216313,11 +217253,7 @@ async function main() {
|
|
|
216313
217253
|
const sourceDir = isGlobal ? join(workDir, "source") : workDir;
|
|
216314
217254
|
const masker = createSecretMasker(request);
|
|
216315
217255
|
const maskedSend = (msg) => {
|
|
216316
|
-
|
|
216317
|
-
...msg,
|
|
216318
|
-
line: masker.mask(msg.line)
|
|
216319
|
-
});
|
|
216320
|
-
else sendMessage(msg);
|
|
217256
|
+
sendMessage(maskMessageText(msg, masker));
|
|
216321
217257
|
};
|
|
216322
217258
|
const jobDeadline = armJobDeadline(request.jobTimeoutMs, (reason, timeoutMs) => {
|
|
216323
217259
|
jobTimedOut = true;
|
|
@@ -216352,7 +217288,7 @@ async function main() {
|
|
|
216352
217288
|
const jobHasRules = (job?.rules?.length ?? 0) > 0;
|
|
216353
217289
|
const anyStepHasRules = normalizedSteps.some((s) => (s.rules?.length ?? 0) > 0);
|
|
216354
217290
|
await resolveChangedFilesForRules(request, sourceDir, jobHasRules || anyStepHasRules);
|
|
216355
|
-
await maybeSkipJobOnRules(job, request, normalizedSteps, globalRepoInfo);
|
|
217291
|
+
await maybeSkipJobOnRules(job, request, normalizedSteps, maskedSend, globalRepoInfo);
|
|
216356
217292
|
if (aborted) abortAndExit("aborted after rules");
|
|
216357
217293
|
}
|
|
216358
217294
|
const jobHooks = collectJobHooks(job);
|
|
@@ -216461,7 +217397,8 @@ async function main() {
|
|
|
216461
217397
|
jobTimedOut,
|
|
216462
217398
|
jobTimeoutMs: request.jobTimeoutMs,
|
|
216463
217399
|
cancelFailureReason,
|
|
216464
|
-
driftDroppedJobs
|
|
217400
|
+
driftDroppedJobs,
|
|
217401
|
+
send: maskedSend
|
|
216465
217402
|
});
|
|
216466
217403
|
process.exit(finalStatus === ExecutionJobStatus.enum.success ? 0 : 1);
|
|
216467
217404
|
}
|
|
@@ -216474,7 +217411,7 @@ async function main() {
|
|
|
216474
217411
|
function emitJobComplete(args) {
|
|
216475
217412
|
const aggregatedOutputs = {};
|
|
216476
217413
|
for (const [stepName, outputs] of args.outputsMap) aggregatedOutputs[stepName] = outputs;
|
|
216477
|
-
|
|
217414
|
+
args.send({
|
|
216478
217415
|
type: "job.complete",
|
|
216479
217416
|
status: args.finalStatus,
|
|
216480
217417
|
stepResults: args.loopResult.stepResults,
|