@kici-dev/orchestrator 0.1.27 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__test-helpers__/mock-db.d.ts +2 -0
- package/dist/agent/dispatcher.d.ts +52 -2
- package/dist/agent/host-roster-reaper.d.ts +1 -2
- package/dist/agent/host-roster.d.ts +34 -0
- package/dist/agent/ownership-refusal.d.ts +12 -0
- package/dist/agent/ownership-tracker.d.ts +64 -16
- package/dist/agent/token-store.d.ts +13 -0
- package/dist/agent-packaging/availability.d.ts +27 -0
- package/dist/agent-packaging/build-package.d.ts +16 -0
- package/dist/agent-packaging/download.d.ts +30 -0
- package/dist/agent-packaging/index.d.ts +9 -0
- package/dist/agent-packaging/install-closure.d.ts +30 -0
- package/dist/agent-packaging/node-binary.d.ts +26 -0
- package/dist/agent-packaging/refresh.d.ts +50 -0
- package/dist/agent-packaging/store.d.ts +15 -0
- package/dist/agent-packaging/upload.d.ts +24 -0
- package/dist/app-on-error.d.ts +29 -0
- package/dist/app.d.ts +56 -4
- package/dist/artifacts/artifact-store.d.ts +233 -0
- package/dist/artifacts/failure-messages.d.ts +46 -0
- package/dist/audit/access-log.d.ts +18 -1
- package/dist/cache/dep-cache.d.ts +15 -0
- package/dist/cache/user-cache.d.ts +37 -0
- package/dist/cancel/cancel-run.d.ts +10 -3
- package/dist/cancel/dashboard-cancel-handler.d.ts +19 -0
- package/dist/cli/api-client.d.ts +22 -1
- package/dist/cli/commands/agent-package.d.ts +34 -0
- package/dist/cli/commands/check-run.d.ts +26 -0
- package/dist/cli/commands/cluster-settings.d.ts +55 -0
- package/dist/cli/commands/dashboard-encryption-key.d.ts +3 -0
- package/dist/cli/commands/db-backup.d.ts +71 -0
- package/dist/cli/commands/orchestrator-service/drain.d.ts +31 -0
- package/dist/cli/commands/orchestrator-service/index.d.ts +7 -4
- package/dist/cli/commands/orchestrator-service/install-env.d.ts +58 -0
- package/dist/cli/commands/secret.d.ts +45 -1
- package/dist/cli/commands/shared/admin-cli-access-log.d.ts +46 -0
- package/dist/cli/commands/shared/versioned-upgrade.d.ts +65 -2
- package/dist/cli/commands/signing-key.d.ts +3 -0
- package/dist/cli/commands/source.d.ts +12 -0
- package/dist/cli/commands/token.d.ts +9 -0
- package/dist/cli/commands/trust-policy.d.ts +48 -0
- package/dist/cli/join.d.ts +1 -1
- package/dist/cli/service/index.d.ts +2 -2
- package/dist/cli/service/instance/manifest.d.ts +17 -0
- package/dist/cli/service/instance/resolve.d.ts +11 -0
- package/dist/cli/wizard/orchestrator-wizard.d.ts +39 -11
- package/dist/cli.js +9818 -3408
- package/dist/cluster/cluster-settings-reader.d.ts +78 -0
- package/dist/cluster/coordinator.d.ts +81 -9
- package/dist/cluster/join-client.d.ts +1 -1
- package/dist/cluster/leader-gated-scheduler.d.ts +50 -0
- package/dist/cluster/orphan-recovery.d.ts +14 -0
- package/dist/cluster/peer-client.d.ts +12 -2
- package/dist/cluster/peer-handler.d.ts +15 -4
- package/dist/cluster/peer-registry.d.ts +12 -0
- package/dist/cluster/verified-issuer-poller.d.ts +11 -0
- package/dist/cluster/verified-issuer.d.ts +63 -0
- package/dist/cold-store/tables/access-log.d.ts +1 -1
- package/dist/cold-store/tables/event-log.d.ts +1 -1
- package/dist/cold-store/tables/execution-jobs.d.ts +1 -1
- package/dist/cold-store/tables/execution-runs.d.ts +1 -1
- package/dist/cold-store/tables/execution-steps.d.ts +1 -1
- package/dist/cold-store/tables/secret-audit-log.d.ts +1 -1
- package/dist/config/loader.d.ts +1 -1
- package/dist/config/resolver.d.ts +2 -1
- package/dist/config/schema.d.ts +8 -6
- package/dist/config/types.d.ts +3 -2
- package/dist/config.d.ts +363 -53
- package/dist/contexts/context-store.d.ts +21 -8
- package/dist/contexts/glob-specificity.d.ts +30 -0
- package/dist/contexts/held-runs.d.ts +51 -19
- package/dist/contexts/protection/aggregate.d.ts +2 -2
- package/dist/contexts/protection/concurrency-gate.d.ts +0 -3
- package/dist/contexts/protection/reviewer-gate.d.ts +0 -3
- package/dist/contexts/protection/satisfiability.d.ts +1 -1
- package/dist/contexts/protection/trust-gate.d.ts +0 -3
- package/dist/contexts/protection/wait-timer-gate.d.ts +0 -3
- package/dist/cron/cron-scheduler.d.ts +24 -4
- package/dist/cron/cron-store.d.ts +21 -7
- package/dist/dashboard/attestation-filters.d.ts +92 -74
- package/dist/dashboard/handler.d.ts +35 -1
- package/dist/db/migration-test-harness.d.ts +41 -0
- package/dist/db/migrations/069_reroute_tunables.d.ts +21 -0
- package/dist/db/migrations/070_execution_runs_failure_class.d.ts +4 -0
- package/dist/db/migrations/071_batch_accumulation.d.ts +26 -0
- package/dist/db/migrations/072_dispatch_queue_run_id_index.d.ts +15 -0
- package/dist/db/migrations/073_org_settings_ingest_concurrency.d.ts +13 -0
- package/dist/db/migrations/074_normalize_zero_concurrency_limit.d.ts +13 -0
- package/dist/db/migrations/075_ingest_overflow_buffer.d.ts +16 -0
- package/dist/db/migrations/076_artifacts.d.ts +19 -0
- package/dist/db/migrations/077_backup_runs.d.ts +13 -0
- package/dist/db/migrations/078_org_settings_backup_staleness.d.ts +12 -0
- package/dist/db/migrations/079_org_settings_scaler_spawn_timeout.d.ts +14 -0
- package/dist/db/migrations/080_cluster_settings.d.ts +17 -0
- package/dist/db/migrations/081_org_settings_queue_timeout.d.ts +19 -0
- package/dist/db/migrations/082_host_s3_reachable.d.ts +16 -0
- package/dist/db/migrations/083_org_settings_artifact_caps.d.ts +18 -0
- package/dist/db/migrations/084_orchestrator_signing_keys.d.ts +20 -0
- package/dist/db/migrations/085_cluster_settings_reroute_flap_grace_ms.d.ts +12 -0
- package/dist/db/migrations/086_cluster_settings_max_fanout_hosts.d.ts +12 -0
- package/dist/db/migrations/087_cluster_settings_event_router_rate_limit.d.ts +12 -0
- package/dist/db/migrations/088_cluster_settings_cache_max_tarball_bytes.d.ts +12 -0
- package/dist/db/migrations/089_cluster_settings_cache_ttl_days.d.ts +12 -0
- package/dist/db/migrations/090_cluster_settings_concurrency_wait_timeout_ms.d.ts +12 -0
- package/dist/db/migrations/091_cluster_settings_agent_token_ttl_ms.d.ts +12 -0
- package/dist/db/migrations/092_cluster_settings_version.d.ts +16 -0
- package/dist/db/migrations/093_org_settings_sandbox_allowlist.d.ts +4 -0
- package/dist/db/migrations/094_dashboard_encryption_keys.d.ts +21 -0
- package/dist/db/migrations/095_dashboard_write_policy_tristate.d.ts +20 -0
- package/dist/db/migrations/096_multi_schedule_cron_last_fired.d.ts +4 -0
- package/dist/db/migrations/097_execution_runs_pr_number.d.ts +4 -0
- package/dist/db/migrations/098_execution_runs_customer_id.d.ts +4 -0
- package/dist/db/migrations/099_cluster_settings_dashboard_verified_issuer.d.ts +21 -0
- package/dist/db/migrations/100_held_runs_hold_type_vocabulary.d.ts +31 -0
- package/dist/db/migrations/101_contexts_hold_expiry_drop_default.d.ts +31 -0
- package/dist/db/migrations/102_dispatch_queue_agent_id.d.ts +20 -0
- package/dist/db/migrations/103_cluster_settings_ownership_db_check_timeout_ms.d.ts +20 -0
- package/dist/db/migrations/104_check_run_terminal_sent.d.ts +22 -0
- package/dist/db/migrations/105_org_trust_policy.d.ts +19 -0
- package/dist/db/migrations/106_cluster_settings_check_run_tracking_ttl_days.d.ts +18 -0
- package/dist/db/migrations/107_check_run_tracking_updated_at_index.d.ts +23 -0
- package/dist/db/repos/dashboard-encryption-keys-repo.d.ts +48 -0
- package/dist/db/repos/signing-keys-repo.d.ts +34 -0
- package/dist/db/types.d.ts +421 -7
- package/dist/deployment/deployment-identity.d.ts +87 -2
- package/dist/diagnostics/checks/backup.d.ts +18 -0
- package/dist/diagnostics/checks/index.d.ts +2 -1
- package/dist/diagnostics/fleet-collector.d.ts +1 -1
- package/dist/drain/drain-controller.d.ts +52 -0
- package/dist/events/batch-accumulator.d.ts +63 -0
- package/dist/events/circuit-breaker.d.ts +9 -3
- package/dist/events/event-emitter.d.ts +31 -0
- package/dist/events/event-retry-scanner.d.ts +12 -2
- package/dist/events/event-router.d.ts +29 -0
- package/dist/firecracker/host-network.d.ts +2 -0
- package/dist/firecracker/net-interfaces.d.ts +14 -0
- package/dist/github-app-name-refresher/github-app-name-refresher.d.ts +18 -8
- package/dist/index.d.ts +2 -0
- package/dist/index.js +604 -16
- package/dist/lockfile-cache.d.ts +4 -0
- package/dist/lockfile-validate.d.ts +16 -9
- package/dist/metrics/prometheus.d.ts +112 -15
- package/dist/oidc/aws-kms-signer.d.ts +37 -0
- package/dist/oidc/command-signer.d.ts +23 -0
- package/dist/oidc/db-signer.d.ts +48 -0
- package/dist/oidc/id-token-claims.d.ts +22 -9
- package/dist/oidc/jwt.d.ts +5 -2
- package/dist/oidc/local-dev-signer.d.ts +1 -1
- package/dist/oidc/oidc-mint-registration.d.ts +27 -11
- package/dist/oidc/orchestrator-mint.d.ts +97 -0
- package/dist/oidc/orchestrator-signer-factory.d.ts +33 -0
- package/dist/oidc/reconcile-signing-key.d.ts +21 -0
- package/dist/oidc/signer.d.ts +25 -0
- package/dist/oidc/signing-key-status.d.ts +23 -0
- package/dist/orchestrator-core.d.ts +42 -2
- package/dist/pipeline/decorating-secret-resolver.d.ts +3 -2
- package/dist/pipeline/dispatch-matched-workflow.d.ts +41 -0
- package/dist/pipeline/install-secrets-resolver.d.ts +12 -1
- package/dist/pipeline/manual-schedule.d.ts +20 -13
- package/dist/pipeline/pending-workflow-context.d.ts +1 -1
- package/dist/pipeline/process-webhook.d.ts +57 -2
- package/dist/pipeline/processor.d.ts +44 -8
- package/dist/pipeline/rerun.d.ts +12 -13
- package/dist/pipeline/resolve-sandbox-grant.d.ts +28 -0
- package/dist/pipeline/route-or-dispatch-jobs.d.ts +71 -0
- package/dist/pipeline/sandbox-allowlist-reader.d.ts +33 -0
- package/dist/policy/dashboard-write-policy.d.ts +9 -4
- package/dist/provenance/backfill-run.d.ts +1 -0
- package/dist/provenance/trust-root.d.ts +13 -0
- package/dist/providers/generic/normalizer.d.ts +1 -1
- package/dist/providers/github/auth.d.ts +1 -1
- package/dist/providers/github/changed-files.d.ts +14 -6
- package/dist/providers/github/check-status-poster.d.ts +19 -3
- package/dist/providers/github/contributor-resolver.d.ts +1 -1
- package/dist/providers/github/lock-file.d.ts +1 -1
- package/dist/providers/github/normalizer.d.ts +1 -1
- package/dist/providers/github/repo-url.d.ts +1 -1
- package/dist/providers/local/lock-file-fetcher.d.ts +1 -1
- package/dist/providers/local/normalizer.d.ts +1 -1
- package/dist/providers/local/repo-url-builder.d.ts +1 -1
- package/dist/providers/universal-git/changed-files.d.ts +8 -7
- package/dist/providers/universal-git/clone-token.d.ts +1 -1
- package/dist/providers/universal-git/config.d.ts +4 -4
- package/dist/providers/universal-git/index.d.ts +5 -1
- package/dist/providers/universal-git/lock-file.d.ts +6 -1
- package/dist/providers/universal-git/normalizer.d.ts +1 -1
- package/dist/providers/universal-git/repo-url.d.ts +1 -1
- package/dist/queue/cleanup.d.ts +69 -8
- package/dist/queue/job-queue.d.ts +94 -14
- package/dist/queue/scheduled-job.d.ts +2 -2
- package/dist/registration/registration-index.d.ts +1 -1
- package/dist/reporting/agent-failure-category.d.ts +4 -1
- package/dist/reporting/check-run-reporter.d.ts +34 -14
- package/dist/reporting/check-run-tracking-store.d.ts +70 -8
- package/dist/reporting/execution-tracker.d.ts +156 -10
- package/dist/reporting/fs-log-storage.d.ts +31 -2
- package/dist/reporting/job-check-run-completion.d.ts +50 -0
- package/dist/reporting/log-chunk-sink.d.ts +47 -0
- package/dist/reporting/log-retention.d.ts +16 -0
- package/dist/reporting/log-storage.d.ts +64 -4
- package/dist/reporting/log-writer.d.ts +22 -7
- package/dist/reporting/peer-log-normalize.d.ts +14 -0
- package/dist/reporting/run-aggregator.d.ts +32 -32
- package/dist/reporting/s3-log-storage.d.ts +102 -8
- package/dist/routes/admin-auth.d.ts +90 -0
- package/dist/routes/admin-cluster-settings.d.ts +31 -0
- package/dist/routes/admin-events.d.ts +3 -0
- package/dist/routes/admin-orchestrator-drain.d.ts +35 -0
- package/dist/routes/admin-runs.d.ts +9 -0
- package/dist/routes/admin-sources.d.ts +7 -0
- package/dist/routes/admin-trust-policy.d.ts +49 -0
- package/dist/routes/admin.d.ts +15 -1
- package/dist/routes/github-webhook.d.ts +9 -3
- package/dist/routes/provenance-oidc.d.ts +38 -0
- package/dist/routes/verify-attestation.d.ts +21 -0
- package/dist/scaler/bare-metal-backend.d.ts +9 -2
- package/dist/scaler/config.d.ts +25 -4
- package/dist/scaler/container-backend.d.ts +9 -1
- package/dist/scaler/firecracker-backend.d.ts +29 -2
- package/dist/scaler/index.d.ts +1 -1
- package/dist/scaler/manager.d.ts +166 -8
- package/dist/scaler/pending-scale-sweeper.d.ts +35 -0
- package/dist/scaler/types.d.ts +75 -6
- package/dist/secrets/backend-registry.d.ts +32 -1
- package/dist/secrets/dashboard-encryption-key.d.ts +48 -0
- package/dist/secrets/ephemeral-keys.d.ts +16 -0
- package/dist/secrets/pg-secret-store.d.ts +10 -0
- package/dist/secrets/rbac.d.ts +5 -1
- package/dist/secrets/routing-key-scope.d.ts +6 -0
- package/dist/secrets/scope-routing.d.ts +126 -0
- package/dist/secrets/secret-resolver.d.ts +1 -14
- package/dist/secrets/token-manager.d.ts +5 -1
- package/dist/security/contributor-cache.d.ts +9 -1
- package/dist/security/trust-policy-gate.d.ts +117 -0
- package/dist/security/trust-policy-store.d.ts +78 -0
- package/dist/server.js +35769 -25052
- package/dist/sources/source-manager.d.ts +24 -0
- package/dist/stale-detector/stale-run-detector.d.ts +7 -0
- package/dist/standalone.js +34831 -25317
- package/dist/storage/filesystem.d.ts +17 -0
- package/dist/storage/key-discriminator.d.ts +20 -0
- package/dist/storage/s3.d.ts +2 -0
- package/dist/storage/types.d.ts +16 -0
- package/dist/webhook/dedup.d.ts +9 -1
- package/dist/webhook/event-log.d.ts +13 -2
- package/dist/webhook/generic-sources-listener.d.ts +4 -0
- package/dist/webhook/ingest-admission.d.ts +140 -0
- package/dist/webhook/ingest-overflow-buffer.d.ts +27 -0
- package/dist/webhook/ingest-overflow-replayer.d.ts +49 -0
- package/dist/webhook/ingest-overflow-types.d.ts +73 -0
- package/dist/webhook/loop-lag-source.d.ts +40 -0
- package/dist/webhook/observed-relay-guard.d.ts +40 -0
- package/dist/webhook/org-ingest-cap-reader.d.ts +24 -0
- package/dist/webhook/register-source-bundle.d.ts +3 -0
- package/dist/webhook/relay-reinject.d.ts +43 -0
- package/dist/worker/in-memory-job-queue.d.ts +8 -3
- package/dist/worker-core.d.ts +10 -0
- package/dist/ws/agent-api-registry.d.ts +20 -0
- package/dist/ws/agent-handler.d.ts +63 -7
- package/dist/ws/bringup-api.d.ts +99 -0
- package/dist/ws/dashboard-context-handler.d.ts +28 -10
- package/dist/ws/dashboard-run-state-handler.d.ts +31 -0
- package/dist/ws/failure-messages.d.ts +29 -0
- package/dist/ws/oidc-token-relay.d.ts +9 -0
- package/dist/ws/ownership-gate.d.ts +22 -0
- package/dist/ws/platform-client.d.ts +96 -1
- package/dist/ws/replay-chunker.d.ts +37 -0
- package/installer-image-digests.json +3 -3
- package/package.json +22 -21
- package/sbom.spdx.json +1240 -1788
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { type Kysely } from 'kysely';
|
|
15
15
|
import type { Database } from '../db/types.js';
|
|
16
|
-
import { ExecutionRunStatus, type InitFailure, ScalerEventType } from '@kici-dev/engine';
|
|
16
|
+
import { ExecutionRunStatus, type InitFailure, RunFailureClass, ScalerEventType, OrchLogPhase } from '@kici-dev/engine';
|
|
17
17
|
import type { ObserverRegistry } from '../ws/observer-registry.js';
|
|
18
18
|
import type { LogStorage } from './log-storage.js';
|
|
19
19
|
import type { JobQueue } from '../queue/job-queue.js';
|
|
@@ -50,6 +50,12 @@ export interface ExecutionContext {
|
|
|
50
50
|
triggerActorUsername?: string | null;
|
|
51
51
|
/** Immutable provider user id of the triggering actor (preferred for resolution). */
|
|
52
52
|
triggerActorUserId?: string | null;
|
|
53
|
+
/**
|
|
54
|
+
* Why a terminal run failed (`RunFailureClass`). Set on terminal-failed /
|
|
55
|
+
* cancelled runs; forwarded to the Platform on `execution.status` so managed
|
|
56
|
+
* subscriptions can match on it. Null/undefined for success or non-terminal.
|
|
57
|
+
*/
|
|
58
|
+
failureClass?: RunFailureClass | null;
|
|
53
59
|
/** Workflow-level concurrency config from the lock file. */
|
|
54
60
|
concurrency?: {
|
|
55
61
|
cancelInProgress?: boolean;
|
|
@@ -68,6 +74,8 @@ interface WorkflowCompleteCallbackData {
|
|
|
68
74
|
}>;
|
|
69
75
|
routingKey?: string;
|
|
70
76
|
repo: string;
|
|
77
|
+
/** Why the run failed (`RunFailureClass`); carried onto the `__workflow_complete` event. */
|
|
78
|
+
failureClass?: string;
|
|
71
79
|
}
|
|
72
80
|
/** Data passed to the onJobComplete callback. */
|
|
73
81
|
interface JobCompleteCallbackData {
|
|
@@ -78,6 +86,11 @@ interface JobCompleteCallbackData {
|
|
|
78
86
|
routingKey?: string;
|
|
79
87
|
repo: string;
|
|
80
88
|
workflowName: string;
|
|
89
|
+
/**
|
|
90
|
+
* Job payload carried by the status update (step results, error, duration).
|
|
91
|
+
* Feeds the enriched check-run summary and the failure description.
|
|
92
|
+
*/
|
|
93
|
+
data?: Record<string, unknown>;
|
|
81
94
|
}
|
|
82
95
|
export interface ExecutionTrackerDeps {
|
|
83
96
|
db: Kysely<Database>;
|
|
@@ -145,12 +158,37 @@ export interface ExecutionTrackerDeps {
|
|
|
145
158
|
metadata?: Record<string, unknown>;
|
|
146
159
|
durationMs?: number | null;
|
|
147
160
|
}) => void;
|
|
161
|
+
/**
|
|
162
|
+
* Optional live forward of orchestration/provisioning log lines to Platform,
|
|
163
|
+
* fired in addition to persisting them to `logStorage`. Platform fans these
|
|
164
|
+
* out to run-detail browser subscribers as `orch-log.lines`. Best-effort:
|
|
165
|
+
* a push failure must never break dispatch.
|
|
166
|
+
*
|
|
167
|
+
* SECURITY: like `onRunEventEmit`, this payload MUST NOT carry an `orgId`.
|
|
168
|
+
* The Platform attributes the tenant from `authState.orgId`.
|
|
169
|
+
*/
|
|
170
|
+
onOrchLog?: (chunk: {
|
|
171
|
+
runId: string;
|
|
172
|
+
jobId: string;
|
|
173
|
+
phase: OrchLogPhase;
|
|
174
|
+
lines: string[];
|
|
175
|
+
ts: number;
|
|
176
|
+
}) => void;
|
|
148
177
|
/** Optional log storage for writing per-job orchestration logs (JSONL). */
|
|
149
178
|
logStorage?: LogStorage;
|
|
150
179
|
/** Org ID for this orchestrator instance (used in run.event emission). */
|
|
151
180
|
orgId?: string;
|
|
152
181
|
/** Optional job queue for cascading run failures to dispatch_queue entries. */
|
|
153
182
|
jobQueue?: JobQueue;
|
|
183
|
+
/**
|
|
184
|
+
* Resolves a routing key to its owning org (customer_id), used to populate
|
|
185
|
+
* `execution_runs.customer_id` at insert time so the concurrency-gate running
|
|
186
|
+
* count is scoped per tenant. Injected (bound to `(rk) => resolveOrgId(db, rk)`
|
|
187
|
+
* at construction) to keep the tracker free of a pipeline import cycle. When
|
|
188
|
+
* absent — or when a run has no routing key — the row falls back to the
|
|
189
|
+
* `'__default__'` column default (the no-source fallback org).
|
|
190
|
+
*/
|
|
191
|
+
resolveOrgId?: (routingKey: string) => Promise<string>;
|
|
154
192
|
}
|
|
155
193
|
export declare class ExecutionTracker {
|
|
156
194
|
private readonly db;
|
|
@@ -163,9 +201,11 @@ export declare class ExecutionTracker {
|
|
|
163
201
|
private readonly onExecutionStatusChange?;
|
|
164
202
|
private readonly onJobStatusChange?;
|
|
165
203
|
private readonly onRunEventEmit?;
|
|
204
|
+
private readonly onOrchLog?;
|
|
166
205
|
private readonly logStorage?;
|
|
167
206
|
private readonly orgId?;
|
|
168
207
|
private readonly jobQueue?;
|
|
208
|
+
private readonly resolveOrgIdFn?;
|
|
169
209
|
private readonly runs;
|
|
170
210
|
/**
|
|
171
211
|
* Per-run async-mutex chain. `onJobStatus` and `addJobsToRun` mutate the same
|
|
@@ -200,6 +240,12 @@ export declare class ExecutionTracker {
|
|
|
200
240
|
/** Callback fired when a job's needs are satisfied and it's ready for dispatch. */
|
|
201
241
|
onJobReadyCallback?: (runId: string, jobName: string) => Promise<void>;
|
|
202
242
|
constructor(deps: ExecutionTrackerDeps);
|
|
243
|
+
/**
|
|
244
|
+
* Resolve the owning org for an `execution_runs` row from its routing key.
|
|
245
|
+
* Falls back to the `'__default__'` org (the column default) when the run has
|
|
246
|
+
* no routing key or no resolver is injected (single-tenant / test contexts).
|
|
247
|
+
*/
|
|
248
|
+
private resolveCustomerId;
|
|
203
249
|
/**
|
|
204
250
|
* Register a callback fired when a job's needs become satisfied.
|
|
205
251
|
* The processor sets this to dispatch newly-ready jobs to agents.
|
|
@@ -254,7 +300,9 @@ export declare class ExecutionTracker {
|
|
|
254
300
|
/** Immutable provider user id of the triggering actor. */
|
|
255
301
|
triggerActorUserId?: string | null,
|
|
256
302
|
/** Agent provenance label when triggered through an agent credential. */
|
|
257
|
-
triggeredByAgentLabel?: string | null
|
|
303
|
+
triggeredByAgentLabel?: string | null,
|
|
304
|
+
/** Pull-request number for PR-triggered runs; null/omitted for non-PR runs. */
|
|
305
|
+
prNumber?: number | null): Promise<void>;
|
|
258
306
|
/**
|
|
259
307
|
* Upsert one execution_jobs row per dispatched job (idempotent on
|
|
260
308
|
* (run_id, job_id) to tolerate a race with an early `onJobStatus`). The
|
|
@@ -438,12 +486,17 @@ export declare class ExecutionTracker {
|
|
|
438
486
|
*/
|
|
439
487
|
onBuildFailedBeforeTracking(runId: string, workflowName: string, provider: string, repoIdentifier: string, ref: string, sha: string, deliveryId: string | null, providerContext: Record<string, unknown>, routingKey: string, triggerEvent?: string, commitMessage?: string, failureReason?: string, initFailure?: InitFailure): Promise<void>;
|
|
440
488
|
/**
|
|
441
|
-
*
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
* for this runId
|
|
489
|
+
* Write a `failed` execution_runs row directly for an init failure, and drop
|
|
490
|
+
* any in-memory run so the recorded failure is the run's final word. Also
|
|
491
|
+
* writes the structured init_failure signal and fires onExecutionStatusChange
|
|
492
|
+
* so Platform's projection picks it up via the normal forward path.
|
|
493
|
+
*
|
|
494
|
+
* An existing row for this runId is overwritten while it is still live —
|
|
495
|
+
* dispatch may already have registered the run before init failed — and left
|
|
496
|
+
* alone once it is terminal, so a run that genuinely finished is never
|
|
497
|
+
* rewritten as failed. When the guard leaves the row alone, the failure
|
|
498
|
+
* metric and the Platform forward are suppressed with it: this orchestrator
|
|
499
|
+
* did not record the init failure, so it must not report one.
|
|
447
500
|
*
|
|
448
501
|
* Closes the silent pre-run-failure gap — without this helper, secret /
|
|
449
502
|
* install-secret / all-jobs-rejected early-exits in dispatch-matched-workflow
|
|
@@ -469,7 +522,11 @@ export declare class ExecutionTracker {
|
|
|
469
522
|
* `execution_runs` row in the `held` state — alive and resumable — so the
|
|
470
523
|
* dashboard run list surfaces the paused workflow. No jobs are tracked: the
|
|
471
524
|
* workflow-scoped held_runs row + pending workflow context (written by the
|
|
472
|
-
* caller) keep the run from being counted complete
|
|
525
|
+
* caller) keep the run from being counted complete, and any in-memory run is
|
|
526
|
+
* dropped so jobs registered before the gate cannot roll it up.
|
|
527
|
+
*
|
|
528
|
+
* An existing row for this runId is flipped to `held` while it is still live
|
|
529
|
+
* and left alone once it is terminal — a finished run is never re-held.
|
|
473
530
|
*/
|
|
474
531
|
recordRunHeld(args: {
|
|
475
532
|
runId: string;
|
|
@@ -485,6 +542,13 @@ export declare class ExecutionTracker {
|
|
|
485
542
|
reason: string;
|
|
486
543
|
triggerEvent?: string;
|
|
487
544
|
commitMessage?: string;
|
|
545
|
+
/**
|
|
546
|
+
* Pull-request number for PR-triggered holds; null/omitted for non-PR runs.
|
|
547
|
+
* Stamped so PR-scoped `/kici approve|reject` (which joins `execution_runs`
|
|
548
|
+
* on `pr_number`) can attribute the held run to its PR — a NULL leaves a
|
|
549
|
+
* security hold fail-closed unreachable by the comment path.
|
|
550
|
+
*/
|
|
551
|
+
prNumber?: number | null;
|
|
488
552
|
}): Promise<void>;
|
|
489
553
|
/**
|
|
490
554
|
* Flip a `held` run back to `pending` so the resumed dispatch can proceed
|
|
@@ -524,6 +588,54 @@ export declare class ExecutionTracker {
|
|
|
524
588
|
* Check if all tracked jobs in a run have reached terminal state.
|
|
525
589
|
*/
|
|
526
590
|
isRunComplete(runId: string): boolean;
|
|
591
|
+
/**
|
|
592
|
+
* Take a token holding a run open while some of its jobs are still to be
|
|
593
|
+
* registered. Returns true when a token was taken, false for an unknown or
|
|
594
|
+
* already-completed run (nothing to hold).
|
|
595
|
+
*
|
|
596
|
+
* Three registration windows need this. A run whose source-pack `__build__`
|
|
597
|
+
* job is dispatched first is registered with that job ALONE, and its real
|
|
598
|
+
* jobs are only dispatched once the build finishes. A deferred init job and a
|
|
599
|
+
* deferred dynamic entry each register their jobs from a fire-and-forget task
|
|
600
|
+
* that outlives the dispatch call. In every case, without a token the already
|
|
601
|
+
* -registered jobs reaching a terminal state satisfies {@link isRunComplete},
|
|
602
|
+
* so the run is finalized early: a terminal run status is written, the
|
|
603
|
+
* provider check is posted, and the status is forwarded to the Platform — all
|
|
604
|
+
* before a single real job has run.
|
|
605
|
+
*
|
|
606
|
+
* The hold is a counter on the in-memory run, read only by
|
|
607
|
+
* {@link isRunComplete} — deliberately NOT a synthetic entry in the run's job
|
|
608
|
+
* map. The job map is what every outward projection enumerates (the Platform
|
|
609
|
+
* `state.replay` snapshot and its `execution_jobs` mirror, the reported job
|
|
610
|
+
* count, the workflow-complete job results, the active-run summary), so a
|
|
611
|
+
* marker parked there would surface as a phantom pending job on the dashboard
|
|
612
|
+
* and an off-by-one job count for the length of the window. A counter holds
|
|
613
|
+
* the completion check open without being visible to any of them, and it
|
|
614
|
+
* writes no `execution_jobs` row, so it never surfaces as a phantom job.
|
|
615
|
+
* A token whose holder never settles keeps the run `running` until the stale
|
|
616
|
+
* detector reaps it — the same backstop that covers a job that never
|
|
617
|
+
* reports.
|
|
618
|
+
*
|
|
619
|
+
* Each token must be paired with exactly one {@link releasePendingJobsHold}.
|
|
620
|
+
*/
|
|
621
|
+
holdRunForPendingJobs(runId: string): boolean;
|
|
622
|
+
/**
|
|
623
|
+
* Drop one token taken by {@link holdRunForPendingJobs}, and finalize the run
|
|
624
|
+
* if dropping the LAST one left every remaining job terminal.
|
|
625
|
+
*
|
|
626
|
+
* The re-evaluation matters for the same reason it does in `addJobsToRun`:
|
|
627
|
+
* once the last token is gone nothing else drives a completion check (no
|
|
628
|
+
* further `job.status` messages are coming for already-finished jobs), so a
|
|
629
|
+
* run whose jobs all finished while a token was held would otherwise hang in
|
|
630
|
+
* `running` forever. Only the last release may finalize — while any other
|
|
631
|
+
* token is outstanding more jobs are still on their way.
|
|
632
|
+
*
|
|
633
|
+
* Idempotent — a no-op when no token is outstanding. The count clamps at
|
|
634
|
+
* zero so a stray extra release cannot drive it negative, which would
|
|
635
|
+
* permanently un-hold the run and stop a genuinely outstanding token from
|
|
636
|
+
* holding it open.
|
|
637
|
+
*/
|
|
638
|
+
releasePendingJobsHold(runId: string): Promise<void>;
|
|
527
639
|
/**
|
|
528
640
|
* Get the current overall status of a run.
|
|
529
641
|
*/
|
|
@@ -594,6 +706,7 @@ export declare class ExecutionTracker {
|
|
|
594
706
|
triggeredBy?: string | null;
|
|
595
707
|
triggeredByAgentLabel?: string | null;
|
|
596
708
|
failureReason?: string;
|
|
709
|
+
failureClass?: RunFailureClass;
|
|
597
710
|
jobCount: number;
|
|
598
711
|
startedAt: number;
|
|
599
712
|
completedAt?: number;
|
|
@@ -669,13 +782,35 @@ export declare class ExecutionTracker {
|
|
|
669
782
|
* stale detector when memRun is still tracked locally — writes the
|
|
670
783
|
* execution_runs terminal row, fires Platform-forwarding +
|
|
671
784
|
* workflow-complete callbacks, and schedules in-memory pruning.
|
|
785
|
+
*
|
|
786
|
+
* The terminal write is clobber-guarded. If the guard rejects it (the run
|
|
787
|
+
* already finished through the normal path), only the pruning happens: the
|
|
788
|
+
* recomputed status is not this orchestrator's record of the run, so neither
|
|
789
|
+
* the callbacks nor the completion metrics may claim it.
|
|
672
790
|
*/
|
|
673
791
|
private completeRunFromMemoryState;
|
|
792
|
+
/**
|
|
793
|
+
* Drop a finished run's in-memory state after a grace period, so a late
|
|
794
|
+
* status/heartbeat arriving just after completion still finds its run.
|
|
795
|
+
*
|
|
796
|
+
* This is the single owner of run-lifetime state teardown, so every
|
|
797
|
+
* run-lifetime map is cleared here — including `testRunIds`: a run finished by
|
|
798
|
+
* the stale detector rather than the normal completion path would otherwise
|
|
799
|
+
* leave its observer-broadcast marker behind for the process's lifetime.
|
|
800
|
+
* (`runLockTails` is deliberately not touched: it is keyed by runId but scoped
|
|
801
|
+
* to a lock's holders, and `withRunLock` drops its own entry on release.)
|
|
802
|
+
*/
|
|
803
|
+
private scheduleRunPrune;
|
|
674
804
|
/**
|
|
675
805
|
* Path B helper: complete a run using DB rows only (crash recovery /
|
|
676
806
|
* pruned runs). Loads jobs + run row from execution_jobs/execution_runs,
|
|
677
807
|
* checks the all-terminal predicate + run-state guard, then writes the
|
|
678
808
|
* terminal row and fires Platform-forwarding callbacks.
|
|
809
|
+
*
|
|
810
|
+
* The run-state read and the terminal write are separate statements, so the
|
|
811
|
+
* write carries its own clobber guard. If that guard rejects it — a
|
|
812
|
+
* concurrent normal completion landed in between — the callbacks and the
|
|
813
|
+
* completion metrics are suppressed with it.
|
|
679
814
|
*/
|
|
680
815
|
private completeRunFromDbFallback;
|
|
681
816
|
/**
|
|
@@ -714,10 +849,21 @@ export declare class ExecutionTracker {
|
|
|
714
849
|
*
|
|
715
850
|
* Per locked decision:
|
|
716
851
|
* - success ONLY if ALL jobs pass (skipped jobs count as success)
|
|
717
|
-
* - failed if ANY job
|
|
852
|
+
* - failed if ANY job ended in a failure status (see `STATUS_FAILURE_CLASS`)
|
|
718
853
|
* - cancelled if ANY job cancelled (and none failed)
|
|
719
854
|
*/
|
|
720
855
|
private computeRunStatus;
|
|
856
|
+
/**
|
|
857
|
+
* Derive the failure class from a terminal run's job statuses. Null for
|
|
858
|
+
* success (and any non-terminal status). `timed_out_stale` / `drift_dropped`
|
|
859
|
+
* / `unroutable` jobs — a job that never dispatched, whose `runsOn` matched no
|
|
860
|
+
* agent, whose agent went silent, or that was dropped by a topology reroute —
|
|
861
|
+
* are infra-class and collapse to `timed_out`; any other `failed` run is a
|
|
862
|
+
* `step_failure` (a job actually ran and failed). `never_started` (init
|
|
863
|
+
* failure) and `dead_orchestrator` (Platform-detected) are stamped by their
|
|
864
|
+
* own paths, not here.
|
|
865
|
+
*/
|
|
866
|
+
private computeFailureClass;
|
|
721
867
|
}
|
|
722
868
|
export {};
|
|
723
869
|
//# sourceMappingURL=execution-tracker.d.ts.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Filesystem-backed log storage.
|
|
3
3
|
*
|
|
4
|
-
* Stores execution logs as files on disk.
|
|
5
|
-
*
|
|
4
|
+
* Stores execution logs as files on disk. Lifecycle is operator-managed (the
|
|
5
|
+
* cleanup retention sweep runs on the S3 backend only). Supports append-only
|
|
6
|
+
* writes (JSONL accumulation) and cursor-based pagination.
|
|
6
7
|
*
|
|
7
8
|
* File layout:
|
|
8
9
|
* {basePath}/executions/{runId}/job-{name}/step-{index}.log
|
|
@@ -14,6 +15,27 @@ interface FilesystemLogStorageOptions {
|
|
|
14
15
|
export declare class FilesystemLogStorage implements LogStorage {
|
|
15
16
|
private readonly basePath;
|
|
16
17
|
constructor(options: FilesystemLogStorageOptions);
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a logical log path to an absolute filesystem path, refusing
|
|
20
|
+
* anything that would address a file outside the storage root.
|
|
21
|
+
*
|
|
22
|
+
* Two checks, deliberately both. `assertSafeLogPath` is the effective gate on
|
|
23
|
+
* POSIX: once a path is relative and carries no `..` segment, `resolve()`
|
|
24
|
+
* cannot produce anything outside the root, so the comparison below is
|
|
25
|
+
* unreachable there. It is kept as a second, independent statement of the
|
|
26
|
+
* same invariant — it is what catches a resolver with extra escape hatches
|
|
27
|
+
* (win32 resolves a drive-relative `C:foo` against that drive's own cwd, not
|
|
28
|
+
* against the root), and it means relaxing the logical check cannot silently
|
|
29
|
+
* reopen an escape.
|
|
30
|
+
*
|
|
31
|
+
* Both checks are lexical — neither resolves symlinks. A symlink planted
|
|
32
|
+
* inside the log root still redirects a logically-clean path outside it;
|
|
33
|
+
* containing that would need a `realpath` on every call, and the log root is
|
|
34
|
+
* the orchestrator user's own directory.
|
|
35
|
+
*
|
|
36
|
+
* Every verb on this class funnels through here, so a new caller is
|
|
37
|
+
* contained on arrival.
|
|
38
|
+
*/
|
|
17
39
|
private fullPath;
|
|
18
40
|
/**
|
|
19
41
|
* Ensure the directory for a given file path exists.
|
|
@@ -33,9 +55,16 @@ export declare class FilesystemLogStorage implements LogStorage {
|
|
|
33
55
|
*/
|
|
34
56
|
private listRecursive;
|
|
35
57
|
append(path: string, data: string): Promise<void>;
|
|
58
|
+
appendStreaming(path: string, data: string): Promise<void>;
|
|
59
|
+
finalize(_path: string): Promise<void>;
|
|
36
60
|
read(path: string, options?: LogReadOptions): Promise<LogReadResult>;
|
|
37
61
|
exists(path: string): Promise<boolean>;
|
|
38
62
|
list(prefix: string): Promise<string[]>;
|
|
63
|
+
listWithMetadata(prefix: string): Promise<Array<{
|
|
64
|
+
path: string;
|
|
65
|
+
lastModified: Date;
|
|
66
|
+
}>>;
|
|
67
|
+
deleteMany(paths: string[]): Promise<number>;
|
|
39
68
|
}
|
|
40
69
|
export {};
|
|
41
70
|
//# sourceMappingURL=fs-log-storage.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type CheckRunReporter } from './check-run-reporter.js';
|
|
2
|
+
import type { ExecutionContext } from './execution-tracker.js';
|
|
3
|
+
/**
|
|
4
|
+
* Job-terminal signal this module reacts to. Shaped after the payload the
|
|
5
|
+
* execution tracker hands its `onJobComplete` hook — its primary caller — plus
|
|
6
|
+
* the agent-supplied `data` blob used to build the enriched check-run summary.
|
|
7
|
+
*/
|
|
8
|
+
export interface JobCheckRunCompletionInput {
|
|
9
|
+
runId: string;
|
|
10
|
+
jobId: string;
|
|
11
|
+
jobName: string;
|
|
12
|
+
/** Raw job status string as recorded by the tracker. */
|
|
13
|
+
status: string;
|
|
14
|
+
/** Agent-supplied job payload (step results, error, duration). */
|
|
15
|
+
data?: Record<string, unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Explicit check-run description, overriding the conclusion mapper's default
|
|
18
|
+
* wording. A caller that already holds the specific reason a job ended the way
|
|
19
|
+
* it did passes it here — the queue-expiry sweep's `unroutable` message, for
|
|
20
|
+
* instance, is the only statement of WHICH `runsOn` selectors went unmatched,
|
|
21
|
+
* and the mapper's generic phrasing cannot name them.
|
|
22
|
+
*/
|
|
23
|
+
description?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Collaborators needed to post a job's terminal check-run conclusion. */
|
|
26
|
+
export interface JobCheckRunCompletionDeps {
|
|
27
|
+
checkRunReporter: Pick<CheckRunReporter, 'updateJobStatus'>;
|
|
28
|
+
/** Resolves the run's provider/repo/sha context — `ExecutionTracker.getExecutionContext`. */
|
|
29
|
+
getExecutionContext: (runId: string) => ExecutionContext | undefined;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Post the terminal conclusion for a job's check run.
|
|
33
|
+
*
|
|
34
|
+
* Every job that reaches a terminal state must resolve the check run that
|
|
35
|
+
* `setPending()` created for it at trigger match. A check run left `queued` is
|
|
36
|
+
* not cosmetic: branch protection that requires it can never be satisfied, and
|
|
37
|
+
* a developer reads it as "still running" indefinitely with nothing to point at.
|
|
38
|
+
*
|
|
39
|
+
* A non-terminal status is a no-op — a check run is completed once, when the job
|
|
40
|
+
* stops.
|
|
41
|
+
*
|
|
42
|
+
* Never throws. Its callers are hot paths whose remaining work matters more than
|
|
43
|
+
* this report: the tracker's terminal-job hook runs inside `onJobStatus`, ahead
|
|
44
|
+
* of the job-complete event emission and the needs/wave scheduler hooks, and the
|
|
45
|
+
* expiry sweep runs it per job inside a loop. `buildJobFailureDescription` reads
|
|
46
|
+
* an agent-supplied payload, so a malformed `stepResults` entry is enough to
|
|
47
|
+
* raise — and a check-run report is not worth stalling a run's scheduling for.
|
|
48
|
+
*/
|
|
49
|
+
export declare function reportJobCheckRunCompletion(deps: JobCheckRunCompletionDeps, input: JobCheckRunCompletionInput): void;
|
|
50
|
+
//# sourceMappingURL=job-check-run-completion.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The single place that decides what happens to a step-log chunk.
|
|
3
|
+
*
|
|
4
|
+
* Both ingresses feed this sink: the local agent WebSocket handler and the
|
|
5
|
+
* coordinator's peer handler, which receives chunks relayed by a worker
|
|
6
|
+
* orchestrator. Keeping the policy in one place is what makes the two paths
|
|
7
|
+
* behave identically — a worker-dispatched job's logs are persisted, counted
|
|
8
|
+
* and forwarded exactly like a locally-dispatched one's.
|
|
9
|
+
*
|
|
10
|
+
* The storage key is derived from `executionTracker.getJobName`, the same call
|
|
11
|
+
* `ExecutionTracker.onStepStatus` uses to fill `execution_steps.log_path`, so
|
|
12
|
+
* the reader and the writer cannot disagree about the naming rule.
|
|
13
|
+
*/
|
|
14
|
+
import type { LogStream } from '@kici-dev/engine';
|
|
15
|
+
import type { LogWriter } from './log-writer.js';
|
|
16
|
+
import type { StepLogBuffer } from './step-log-buffer.js';
|
|
17
|
+
/** A chunk whose lines all share one timestamp and one originating stream. */
|
|
18
|
+
export interface NormalizedLogChunk {
|
|
19
|
+
runId: string;
|
|
20
|
+
jobId: string;
|
|
21
|
+
stepIndex: number;
|
|
22
|
+
lines: string[];
|
|
23
|
+
timestamp: number;
|
|
24
|
+
stream?: LogStream;
|
|
25
|
+
}
|
|
26
|
+
/** Which ingress produced the chunk. Stamped onto the two log counters. */
|
|
27
|
+
export type LogChunkSource = 'local' | 'peer';
|
|
28
|
+
export interface LogChunkSinkDeps {
|
|
29
|
+
/** Ingress this sink instance serves; becomes the `source` metric attribute. */
|
|
30
|
+
source: LogChunkSource;
|
|
31
|
+
/** Short in-memory tail feeding the GitHub check-run summary. */
|
|
32
|
+
stepLogBuffer?: StepLogBuffer;
|
|
33
|
+
/** Durable step-log persistence. Absent when the orchestrator has no database. */
|
|
34
|
+
logWriter?: LogWriter;
|
|
35
|
+
/** Resolves the job name that names the storage path. */
|
|
36
|
+
executionTracker?: {
|
|
37
|
+
getJobName(runId: string, jobId: string): string | undefined;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Forward to the Platform for browser fan-out. Absent in independent mode.
|
|
41
|
+
* The caller wraps the chunk in the `log.chunk` envelope, which keeps this
|
|
42
|
+
* module free of Platform-protocol knowledge.
|
|
43
|
+
*/
|
|
44
|
+
forwardToPlatform?: (chunk: NormalizedLogChunk) => void;
|
|
45
|
+
}
|
|
46
|
+
export declare function createLogChunkSink(deps: LogChunkSinkDeps): (chunk: NormalizedLogChunk) => void;
|
|
47
|
+
//# sourceMappingURL=log-chunk-sink.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LogStorage } from './log-storage.js';
|
|
2
|
+
/**
|
|
3
|
+
* Delete step-log objects older than `ttlDays` in one bulk pass.
|
|
4
|
+
*
|
|
5
|
+
* Step-log objects are write-once (appended during the run, untouched after
|
|
6
|
+
* finalize), so last-modified time is the correct expiry basis — a recent or
|
|
7
|
+
* still-running run's objects were all written within the window and survive.
|
|
8
|
+
* `ttlDays <= 0` disables the sweep (returns 0). Expired objects are collected
|
|
9
|
+
* then bulk-deleted via `deleteMany` (S3 DeleteObjects), so a large sweep is a
|
|
10
|
+
* handful of API calls, not one-per-object. A delete failure is logged and the
|
|
11
|
+
* cleanup tick is not aborted.
|
|
12
|
+
*
|
|
13
|
+
* @returns Number of objects deleted.
|
|
14
|
+
*/
|
|
15
|
+
export declare function pruneExpiredLogs(storage: LogStorage, ttlDays: number, now?: Date): Promise<number>;
|
|
16
|
+
//# sourceMappingURL=log-retention.d.ts.map
|
|
@@ -2,14 +2,33 @@
|
|
|
2
2
|
* Log storage abstraction for the orchestrator.
|
|
3
3
|
*
|
|
4
4
|
* Provides a pluggable backend interface for persisting execution logs.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Retention on the S3 backend is bounded by the orchestrator cleanup sweep
|
|
6
|
+
* (`KICI_STEP_LOG_TTL_DAYS`, default 90; 0 disables it); the filesystem backend
|
|
7
|
+
* leaves lifecycle to the operator.
|
|
7
8
|
*
|
|
8
9
|
* Backends: FilesystemLogStorage, S3LogStorage
|
|
9
10
|
*
|
|
10
11
|
* Log file layout: executions/{runId}/job-{name}/step-{index}.log
|
|
11
12
|
* Format: JSONL (one JSON object per line with timestamp, level, message, metadata)
|
|
12
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* Assert that a logical log path is safe to hand to a storage backend.
|
|
16
|
+
*
|
|
17
|
+
* The invariant every backend relies on: a log path is relative, contains no
|
|
18
|
+
* `..` segment, and carries no NUL byte. Both backends call this at their own
|
|
19
|
+
* boundary, so a malformed key is refused identically whichever backend the
|
|
20
|
+
* operator configured, and a new caller is guarded on arrival rather than by
|
|
21
|
+
* remembering to validate.
|
|
22
|
+
*
|
|
23
|
+
* The `..` test is per-segment, not a substring match: a job name such as
|
|
24
|
+
* `job-a..b` is legitimate and is accepted.
|
|
25
|
+
*
|
|
26
|
+
* `FilesystemLogStorage.fullPath()` re-states the same invariant physically,
|
|
27
|
+
* comparing the resolved absolute path against the resolved root. Both checks
|
|
28
|
+
* are lexical: neither resolves symlinks, so a symlink planted inside the log
|
|
29
|
+
* root still redirects a logically-clean path through it.
|
|
30
|
+
*/
|
|
31
|
+
export declare function assertSafeLogPath(path: string): void;
|
|
13
32
|
/**
|
|
14
33
|
* Read result from LogStorage.read().
|
|
15
34
|
* Supports cursor-based pagination for large log files.
|
|
@@ -34,17 +53,54 @@ export interface LogReadOptions {
|
|
|
34
53
|
/**
|
|
35
54
|
* Pluggable log storage interface.
|
|
36
55
|
*
|
|
37
|
-
* All operations are async.
|
|
56
|
+
* All operations are async. Retention is enforced by the cleanup sweep on the
|
|
57
|
+
* S3 backend (see `pruneExpiredLogs`); the filesystem backend is operator-managed.
|
|
38
58
|
*/
|
|
39
59
|
export interface LogStorage {
|
|
40
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Append lines to a log file, immediately durable. Creates the file (and
|
|
62
|
+
* directories) if it doesn't exist. Used by single-shot writers (webhook
|
|
63
|
+
* payloads, rerun payloads, orchestration logs) that write once and expect
|
|
64
|
+
* the content to be readable without any subsequent finalize call.
|
|
65
|
+
*/
|
|
41
66
|
append(path: string, data: string): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* High-frequency append-only log write. Durability is guaranteed by a
|
|
69
|
+
* subsequent finalize(path) (called at run completion). S3 buffers per key
|
|
70
|
+
* and seals immutable segments on a size/time threshold; the filesystem
|
|
71
|
+
* appends immediately (appendFile is already durable).
|
|
72
|
+
*/
|
|
73
|
+
appendStreaming(path: string, data: string): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Seal any buffered-but-unwritten content for a path. S3 flushes its current
|
|
76
|
+
* tail segment; the filesystem is a no-op. Safe on any path — a no-op when
|
|
77
|
+
* nothing is buffered.
|
|
78
|
+
*/
|
|
79
|
+
finalize(path: string): Promise<void>;
|
|
42
80
|
/** Read log file content. Returns { data, cursor, complete } for pagination. */
|
|
43
81
|
read(path: string, options?: LogReadOptions): Promise<LogReadResult>;
|
|
44
82
|
/** Check if a log file exists. */
|
|
45
83
|
exists(path: string): Promise<boolean>;
|
|
46
84
|
/** List files matching a prefix (directory listing). */
|
|
47
85
|
list(prefix: string): Promise<string[]>;
|
|
86
|
+
/**
|
|
87
|
+
* List every physical object under a prefix with its last-modified time,
|
|
88
|
+
* `path` relative to the storage prefix. Unlike {@link LogStorage.list} this
|
|
89
|
+
* does NOT collapse S3 segment objects (`.../seg-NNNNNN`) to their logical
|
|
90
|
+
* step path — the retention sweep must see and delete each physical object,
|
|
91
|
+
* and each segment carries its own last-modified time. Step-log objects are
|
|
92
|
+
* write-once, so last-modified is the correct expiry basis.
|
|
93
|
+
*/
|
|
94
|
+
listWithMetadata(prefix: string): Promise<Array<{
|
|
95
|
+
path: string;
|
|
96
|
+
lastModified: Date;
|
|
97
|
+
}>>;
|
|
98
|
+
/**
|
|
99
|
+
* Bulk-delete objects by their storage-relative path (as returned by
|
|
100
|
+
* {@link LogStorage.listWithMetadata}). S3 uses `DeleteObjects` (≤1000
|
|
101
|
+
* keys/call, chunked); missing keys are no-ops. Returns the count deleted.
|
|
102
|
+
*/
|
|
103
|
+
deleteMany(paths: string[]): Promise<number>;
|
|
48
104
|
}
|
|
49
105
|
/**
|
|
50
106
|
* Configuration for creating a log storage backend.
|
|
@@ -61,6 +117,10 @@ type LogStorageConfig = {
|
|
|
61
117
|
endpoint?: string;
|
|
62
118
|
/** Use path-style access instead of virtual-hosted-style (required for most S3-compatible services) */
|
|
63
119
|
forcePathStyle?: boolean;
|
|
120
|
+
/** Seal a step-log segment once the in-memory buffer reaches this many bytes. */
|
|
121
|
+
segmentFlushBytes: number;
|
|
122
|
+
/** Seal a step-log segment once the oldest buffered byte reaches this age (ms). */
|
|
123
|
+
segmentFlushMs: number;
|
|
64
124
|
};
|
|
65
125
|
/**
|
|
66
126
|
* Factory function to create the appropriate LogStorage backend.
|
|
@@ -5,10 +5,14 @@
|
|
|
5
5
|
* and metadata fields. Lines are written to the standard log path layout:
|
|
6
6
|
* executions/{runId}/job-{name}/step-{index}.log
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Step-log writes go through the high-frequency `appendStreaming` verb: the
|
|
9
|
+
* filesystem backend appends immediately (no buffering); the S3 backend buffers
|
|
10
|
+
* per step key and seals immutable append-only segments. `drain(runId)` awaits
|
|
11
|
+
* the in-flight appends and then finalizes each step path so the S3 tail
|
|
12
|
+
* segment is sealed before a reader declares the stream complete.
|
|
10
13
|
*/
|
|
11
14
|
import type { LogStorage } from './log-storage.js';
|
|
15
|
+
import { LogStream } from '@kici-dev/engine';
|
|
12
16
|
import type { ObserverRegistry } from '../ws/observer-registry.js';
|
|
13
17
|
interface LogWriterDeps {
|
|
14
18
|
logStorage: LogStorage;
|
|
@@ -32,13 +36,22 @@ export declare class LogWriter {
|
|
|
32
36
|
* disk, dropping it from a blocking `kici run remote` follow.
|
|
33
37
|
*/
|
|
34
38
|
private readonly pendingAppends;
|
|
39
|
+
/**
|
|
40
|
+
* Log paths written for each run, populated in `appendChunk`. `drain(runId)`
|
|
41
|
+
* finalizes each one so the S3 backend seals its buffered tail segment before
|
|
42
|
+
* a reader declares the stream complete. The filesystem backend's `finalize`
|
|
43
|
+
* is a no-op.
|
|
44
|
+
*/
|
|
45
|
+
private readonly runPaths;
|
|
35
46
|
constructor(deps: LogWriterDeps);
|
|
36
47
|
/**
|
|
37
48
|
* Await every in-flight log append for `runId` that was registered before
|
|
38
|
-
* this call
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
49
|
+
* this call, then seal each step log's buffered tail segment so the durable
|
|
50
|
+
* S3 record is complete. Settles even if an append rejected (errors are
|
|
51
|
+
* already logged by `appendChunk`); the point is ordering, not error
|
|
52
|
+
* propagation. New appends started after this call are not waited on — a
|
|
53
|
+
* terminal run emits no further chunks, so the snapshot taken at call time is
|
|
54
|
+
* complete.
|
|
42
55
|
*/
|
|
43
56
|
drain(runId: string): Promise<void>;
|
|
44
57
|
/**
|
|
@@ -57,8 +70,10 @@ export declare class LogWriter {
|
|
|
57
70
|
* @param timestamp - Timestamp from the agent message (epoch ms)
|
|
58
71
|
* @param jobId - Job ID for observer broadcasting (optional)
|
|
59
72
|
* @param stepName - Step name for observer broadcasting (optional)
|
|
73
|
+
* @param stream - Which subprocess stream the lines came from. Absent means
|
|
74
|
+
* the agent did not report one, which is recorded as `stdout`.
|
|
60
75
|
*/
|
|
61
|
-
appendChunk(runId: string, jobName: string, stepIndex: number, lines: string[], timestamp: number, jobId?: string, stepName?: string): Promise<void>;
|
|
76
|
+
appendChunk(runId: string, jobName: string, stepIndex: number, lines: string[], timestamp: number, jobId?: string, stepName?: string, stream?: LogStream): Promise<void>;
|
|
62
77
|
}
|
|
63
78
|
export {};
|
|
64
79
|
//# sourceMappingURL=log-writer.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a worker-relayed `peer.log.chunk` into the chunk shape the log sink
|
|
3
|
+
* consumes.
|
|
4
|
+
*
|
|
5
|
+
* The peer frame carries a timestamp and an originating stream per line, while
|
|
6
|
+
* the sink (and `LogWriter.appendChunk` beneath it) takes one of each per
|
|
7
|
+
* chunk. Grouping consecutive lines that agree on both is lossless and keeps
|
|
8
|
+
* line order intact. A worker today gives every line in a chunk the same
|
|
9
|
+
* timestamp and stream, so this yields a single group in practice.
|
|
10
|
+
*/
|
|
11
|
+
import type { PeerLogChunk } from '@kici-dev/engine';
|
|
12
|
+
import type { NormalizedLogChunk } from './log-chunk-sink.js';
|
|
13
|
+
export declare function normalizePeerLogChunk(chunk: PeerLogChunk): NormalizedLogChunk[];
|
|
14
|
+
//# sourceMappingURL=peer-log-normalize.d.ts.map
|