@kici-dev/orchestrator 0.9.0 → 0.10.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__/test-db.d.ts +14 -1
- package/dist/agent/dispatcher.d.ts +137 -41
- package/dist/agent/registry.d.ts +79 -2
- package/dist/app.d.ts +2 -0
- package/dist/cancel/cancel-held-run.d.ts +68 -0
- package/dist/cancel/cancel-route-answer.d.ts +21 -0
- package/dist/cancel/cancel-run.d.ts +32 -0
- package/dist/cli/api-client.d.ts +3 -0
- package/dist/cli/commands/context.d.ts +3 -1
- package/dist/cli/commands/runs.d.ts +7 -0
- package/dist/cli/commands/shared/unbound-context-warning.d.ts +43 -0
- package/dist/cli/commands/signing-key.d.ts +22 -1
- package/dist/cli.js +1306 -739
- package/dist/cluster/cluster-settings-reader.d.ts +1 -1
- package/dist/cluster/coordinator.d.ts +2 -0
- package/dist/cluster/live-coordinator-peer.d.ts +13 -0
- package/dist/config.d.ts +9 -0
- package/dist/contexts/context-store.d.ts +3 -2
- package/dist/contexts/protection/satisfiability.d.ts +6 -3
- package/dist/db/migrations/145_execution_runs_workflow_provenance.d.ts +4 -0
- package/dist/db/migrations/146_pending_job_contexts_context_resolution.d.ts +4 -0
- package/dist/db/migrations/147_sealed_job_secrets.d.ts +19 -0
- package/dist/db/migrations/148_cluster_settings_sealed_secrets_retry_backoff_ms.d.ts +21 -0
- package/dist/db/migrations/149_workflow_registrations_dep_cache_key.d.ts +4 -0
- package/dist/db/migrations/150_execution_runs_status_epoch.d.ts +18 -0
- package/dist/db/migrations/151_workflow_registrations_dep_cache_key_sha.d.ts +4 -0
- package/dist/db/repos/signing-keys-repo.d.ts +48 -3
- package/dist/db/types.d.ts +81 -1
- package/dist/git/clone-token-auth.d.ts +7 -0
- package/dist/git/dispatch-git-auth.d.ts +63 -0
- package/dist/lockfile-validate.d.ts +4 -2
- package/dist/metrics/prometheus.d.ts +1 -1
- package/dist/oidc/id-token-claims.d.ts +10 -0
- package/dist/oidc/oidc-mint-registration.d.ts +2 -0
- package/dist/oidc/orchestrator-mint.d.ts +25 -4
- package/dist/oidc/reconcile-signing-key.d.ts +31 -6
- package/dist/oidc/resolve-signer.d.ts +12 -6
- package/dist/pipeline/decorating-secret-resolver.d.ts +5 -3
- package/dist/pipeline/dispatch-matched-workflow.d.ts +105 -13
- package/dist/pipeline/dispatch-matched-workflow.test-helpers.d.ts +121 -0
- package/dist/pipeline/global-dispatch-identity.d.ts +76 -0
- package/dist/pipeline/global-dispatch.d.ts +91 -0
- package/dist/pipeline/global-eval-round.d.ts +56 -4
- package/dist/pipeline/global-round-hold.d.ts +41 -0
- package/dist/pipeline/held-context-data.d.ts +143 -0
- package/dist/pipeline/held-round-registrations.d.ts +40 -0
- package/dist/pipeline/install-gate.d.ts +93 -0
- package/dist/pipeline/install-secrets-resolver.d.ts +28 -13
- package/dist/pipeline/job-contexts.d.ts +21 -6
- package/dist/pipeline/pending-workflow-context.d.ts +15 -3
- package/dist/pipeline/process-webhook.d.ts +38 -1
- package/dist/pipeline/processor.d.ts +20 -0
- package/dist/pipeline/rerun-global.d.ts +28 -0
- package/dist/pipeline/rerun-inputs.d.ts +88 -0
- package/dist/pipeline/rerun.d.ts +25 -2
- package/dist/pipeline/resume-workflow.d.ts +36 -2
- package/dist/pipeline/unbound-job-context.d.ts +34 -0
- package/dist/pipeline/workflow-lock-at-sha.d.ts +67 -0
- package/dist/provenance/backfill-run.d.ts +5 -0
- package/dist/providers/github/check-status-poster.d.ts +4 -16
- package/dist/queue/job-queue.d.ts +127 -10
- package/dist/queue/terminalize-unroutable.d.ts +37 -4
- package/dist/registration/registration-index.d.ts +13 -1
- package/dist/registration/registration-store.d.ts +10 -1
- package/dist/reporting/execution-tracker.d.ts +149 -8
- package/dist/reporting/log-chunk-sink.d.ts +1 -1
- package/dist/reporting/log-writer.d.ts +14 -0
- package/dist/reporting/step-log-path.d.ts +8 -0
- package/dist/reporting/step-log-reader.d.ts +21 -3
- package/dist/routes/admin-signing-keys.d.ts +33 -0
- package/dist/scaler/agent-fit.d.ts +159 -0
- package/dist/scaler/container-routing.d.ts +16 -16
- package/dist/scaler/manager.d.ts +23 -17
- package/dist/secrets/context-secret-resolver.d.ts +1 -1
- package/dist/secrets/index.d.ts +1 -1
- package/dist/secrets/job-secret-gate.d.ts +25 -3
- package/dist/secrets/job-secret-seal.d.ts +36 -0
- package/dist/secrets/master-key-rotation.d.ts +7 -3
- package/dist/secrets/secret-resolver.d.ts +42 -23
- package/dist/server.js +27732 -23477
- package/dist/standalone.js +58270 -53199
- package/dist/worker/in-memory-job-queue.d.ts +12 -3
- package/dist/worker/reroute-dispatch.d.ts +35 -0
- package/dist/ws/agent-handler.d.ts +23 -10
- package/dist/ws/git-credential-relay.d.ts +16 -3
- package/installer-image-digests.json +3 -3
- package/package.json +4 -4
- package/sbom.spdx.json +53 -53
|
@@ -59,6 +59,7 @@ import { type Context as EngineContext } from '@kici-dev/engine';
|
|
|
59
59
|
import type { OrchestratorMode, WorkflowDecision } from '@kici-dev/engine';
|
|
60
60
|
import type { TrustPolicyStore } from '../security/trust-policy-store.js';
|
|
61
61
|
import type { TrustDirectoryStore } from '../security/trust-directory-store.js';
|
|
62
|
+
import { type ContextDataDeps, type DeferredContextResolution } from './held-context-data.js';
|
|
62
63
|
/**
|
|
63
64
|
* Pending dispatch context for jobs gated by the needs scheduler.
|
|
64
65
|
* Keyed by `${runId}:${jobName}`. Populated at processWebhook time for all
|
|
@@ -76,6 +77,19 @@ interface PendingJobContext {
|
|
|
76
77
|
* to an agent.
|
|
77
78
|
*/
|
|
78
79
|
invoke?: InvokeGateParams;
|
|
80
|
+
/**
|
|
81
|
+
* Set for a job bound to a context: what `dispatchReadyJob` resolves the
|
|
82
|
+
* job's context variables and secrets from when the job leaves its pending
|
|
83
|
+
* context, since `jobInput` is stored without them. Absent for a job with no
|
|
84
|
+
* bound context, which dispatches its stored input unchanged.
|
|
85
|
+
*/
|
|
86
|
+
contextResolution?: DeferredContextResolution;
|
|
87
|
+
/**
|
|
88
|
+
* Set when the stored row's sealed secret fields could not be decrypted:
|
|
89
|
+
* `dispatchReadyJob` fails the job with this reason instead of dispatching
|
|
90
|
+
* it without them.
|
|
91
|
+
*/
|
|
92
|
+
secretsUnavailable?: string;
|
|
79
93
|
}
|
|
80
94
|
/**
|
|
81
95
|
* Register an eval gate and return a promise that resolves when the scheduler
|
|
@@ -580,6 +594,12 @@ export interface ReadyDispatchGateDeps {
|
|
|
580
594
|
accessLogWriter?: Pick<AccessLogWriter, 'record'>;
|
|
581
595
|
/** The routing key the audit row is attributed to, when the call site knows it. */
|
|
582
596
|
routingKey?: string | null;
|
|
597
|
+
/**
|
|
598
|
+
* The stores a released held job's context variables and secrets resolve
|
|
599
|
+
* against. Called at release time, so a call site whose stores are built after
|
|
600
|
+
* it is wired can still supply them.
|
|
601
|
+
*/
|
|
602
|
+
contextData?: () => ContextDataDeps;
|
|
583
603
|
}
|
|
584
604
|
/** What {@link resolveRunConcurrency} needs to evaluate the concurrency gate. */
|
|
585
605
|
export interface RunConcurrency {
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { OriginalRunRow, RerunDeps } from './rerun.js';
|
|
2
|
+
/** True when the run executed a workflow defined in another repository. */
|
|
3
|
+
export declare function isCrossRepoGlobalRun(originalRun: OriginalRunRow): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Re-run an organization-wide run against the commits it recorded: the source
|
|
6
|
+
* repository at `sha`, and the workflow repository at `workflow_sha`.
|
|
7
|
+
*
|
|
8
|
+
* The workflow is read from the workflow repository's lock file at the recorded
|
|
9
|
+
* commit, through that repository's registration — its provider bundle and
|
|
10
|
+
* provider context — never from the registration's current lock entry, which
|
|
11
|
+
* may have moved on. The run goes through the same pipeline a first global run
|
|
12
|
+
* does, so contexts, holds, approval gates, and the workflow repository's
|
|
13
|
+
* credentials apply exactly as they did.
|
|
14
|
+
*
|
|
15
|
+
* Refuses, naming the workflow repository, when the run recorded no workflow
|
|
16
|
+
* commit or the workflow is no longer registered there. Nothing falls back to a
|
|
17
|
+
* current lock file.
|
|
18
|
+
*/
|
|
19
|
+
export declare function rerunCrossRepoGlobalRun(opts: {
|
|
20
|
+
originalRun: OriginalRunRow;
|
|
21
|
+
triggeredBy: string | null;
|
|
22
|
+
triggeredByAgentLabel: string | null;
|
|
23
|
+
deps: RerunDeps;
|
|
24
|
+
requestId: string;
|
|
25
|
+
}): Promise<{
|
|
26
|
+
newRunId: string;
|
|
27
|
+
}>;
|
|
28
|
+
//# sourceMappingURL=rerun-global.d.ts.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { SimulatedEvent } from '@kici-dev/engine';
|
|
2
|
+
import type { ProviderRegistry } from '../provider-registry.js';
|
|
3
|
+
import type { TrustPolicyOutcome } from '../security/trust-policy-gate.js';
|
|
4
|
+
import type { WebhookInfo } from '../webhook/handler.js';
|
|
5
|
+
import type { OriginalRunRow, RerunDeps } from './rerun.js';
|
|
6
|
+
/**
|
|
7
|
+
* Load the original webhook payload from object storage. Returns
|
|
8
|
+
* null for cron/schedule runs (no payload was stored) or when the payload
|
|
9
|
+
* cannot be parsed.
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadWebhookPayload(originalRunId: string, deps: Pick<RerunDeps, 'logStorage'>): Promise<Record<string, unknown> | null>;
|
|
12
|
+
/** The provider bundle + context a re-run resolves out of the original run's row. */
|
|
13
|
+
export interface RerunProviderBinding {
|
|
14
|
+
providerBundle: NonNullable<ReturnType<ProviderRegistry['getByRoutingKey']>>;
|
|
15
|
+
providerContext: Record<string, unknown>;
|
|
16
|
+
routingKey: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the provider bundle and stored provider context for a run being
|
|
20
|
+
* re-run.
|
|
21
|
+
*
|
|
22
|
+
* Shared by the workflow re-run and the evaluation-round re-run so the two
|
|
23
|
+
* cannot disagree about which source a run belongs to, or about how its
|
|
24
|
+
* `provider_context` column is parsed.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveRerunProviderBinding(originalRun: OriginalRunRow, deps: Pick<RerunDeps, 'providerRegistry'>): RerunProviderBinding;
|
|
27
|
+
/** How a refusal to rebuild a re-run's inputs names the run and the way forward. */
|
|
28
|
+
export interface RerunRefusal {
|
|
29
|
+
/** The sentence's opening, naming the run: `Cannot re-run …`. */
|
|
30
|
+
subject: string;
|
|
31
|
+
/** How the run relates to its event: `was deciding` for a round, `ran for` for a run. */
|
|
32
|
+
eventVerb: string;
|
|
33
|
+
/** What the caller can do instead. */
|
|
34
|
+
remedy: string;
|
|
35
|
+
}
|
|
36
|
+
/** The refusal wording for a global evaluation round. */
|
|
37
|
+
export declare function roundRefusal(originalRun: OriginalRunRow): RerunRefusal;
|
|
38
|
+
/**
|
|
39
|
+
* The provider event name + action the original delivery carried.
|
|
40
|
+
*
|
|
41
|
+
* `execution_runs` records neither: the event name arrives in a provider header,
|
|
42
|
+
* not in the payload, so it cannot be recovered from the stored payload either.
|
|
43
|
+
* The delivery's own `event_log` row is where it lives, and a round's run row
|
|
44
|
+
* carries the delivery id that addresses it. That row is written at the end of
|
|
45
|
+
* the delivery, after the round's failure is recorded, so it is present for
|
|
46
|
+
* every re-run an operator can actually reach.
|
|
47
|
+
*
|
|
48
|
+
* Addressed by `(org_id, delivery_id)` — the table's own uniqueness — never by
|
|
49
|
+
* the delivery id alone. A generic source's delivery id is taken verbatim from
|
|
50
|
+
* a sender-supplied header, so one tenant can choose an id another tenant's
|
|
51
|
+
* round already carries; a lookup by id alone would then re-evaluate one org's
|
|
52
|
+
* global workflows against another org's event shape, and with no `ORDER BY`
|
|
53
|
+
* the row it picked would not even be stable.
|
|
54
|
+
*/
|
|
55
|
+
export declare function loadDeliveryEventName(originalRun: OriginalRunRow, deps: Pick<RerunDeps, 'db'>, refusal?: RerunRefusal): Promise<{
|
|
56
|
+
event: string;
|
|
57
|
+
action: string | null;
|
|
58
|
+
}>;
|
|
59
|
+
/** The verdict an approved security hold stands for. */
|
|
60
|
+
export declare const RELEASED_DECISION: TrustPolicyOutcome;
|
|
61
|
+
/**
|
|
62
|
+
* The normalized form of the delivery the round was deciding.
|
|
63
|
+
*
|
|
64
|
+
* Read-only, and it throws — so it runs before the requestId claim, never
|
|
65
|
+
* inside the re-evaluation.
|
|
66
|
+
*/
|
|
67
|
+
export declare function normalizeRoundEvent(originalRun: OriginalRunRow, providerBundle: NonNullable<ReturnType<ProviderRegistry['getByRoutingKey']>>, delivery: {
|
|
68
|
+
event: string;
|
|
69
|
+
action: string | null;
|
|
70
|
+
}, payload: Record<string, unknown>, refusal?: RerunRefusal): SimulatedEvent;
|
|
71
|
+
/**
|
|
72
|
+
* Stamp the re-evaluated event with the source repository's changed files.
|
|
73
|
+
*
|
|
74
|
+
* Unconditional, unlike the delivery path's fetch: that path skips the fetch
|
|
75
|
+
* when no trigger in the source repo's lock file uses path patterns, and a
|
|
76
|
+
* scoped re-evaluation has no such lock file to read. An error carries
|
|
77
|
+
* `unavailable`, which every path filter downstream already treats
|
|
78
|
+
* conservatively.
|
|
79
|
+
*/
|
|
80
|
+
export declare function withChangedFiles(opts: {
|
|
81
|
+
event: SimulatedEvent;
|
|
82
|
+
bundle: NonNullable<ReturnType<ProviderRegistry['getByRoutingKey']>>;
|
|
83
|
+
info: WebhookInfo;
|
|
84
|
+
payload: Record<string, unknown>;
|
|
85
|
+
credentials: Record<string, unknown>;
|
|
86
|
+
repoIdentifier: string;
|
|
87
|
+
}): Promise<SimulatedEvent>;
|
|
88
|
+
//# sourceMappingURL=rerun-inputs.d.ts.map
|
package/dist/pipeline/rerun.d.ts
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* It goes directly to: lock file parse -> job expansion -> dispatch.
|
|
11
11
|
*/
|
|
12
12
|
import { type ColdStore } from '@kici-dev/shared';
|
|
13
|
-
import type { Kysely } from 'kysely';
|
|
14
|
-
import type { Database } from '../db/types.js';
|
|
13
|
+
import type { Kysely, Selectable } from 'kysely';
|
|
14
|
+
import type { Database, ExecutionRunTable } from '../db/types.js';
|
|
15
15
|
import type { LogStorage } from '../reporting/log-storage.js';
|
|
16
16
|
import type { ProviderRegistry } from '../provider-registry.js';
|
|
17
17
|
import type { Dispatcher } from '../agent/dispatcher.js';
|
|
@@ -78,6 +78,12 @@ export interface RerunDeps {
|
|
|
78
78
|
*/
|
|
79
79
|
processingDeps?: (() => ProcessingDeps) | null;
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Original run row from execution_runs (selectAll).
|
|
83
|
+
* Aliased to the Kysely Selectable so all column names stay typed
|
|
84
|
+
* as the underlying schema.
|
|
85
|
+
*/
|
|
86
|
+
export type OriginalRunRow = Selectable<ExecutionRunTable>;
|
|
81
87
|
export declare function handleRerun(originalRunId: string, triggeredBy: string | null, triggeredByAgentLabel: string | null, deps: RerunDeps,
|
|
82
88
|
/**
|
|
83
89
|
* Platform-minted `requestId` for this rerun. Stable across an HA relay
|
|
@@ -104,4 +110,21 @@ routingKeyHint?: string): Promise<{
|
|
|
104
110
|
* deterministically instead of racing the microtask queue.
|
|
105
111
|
*/
|
|
106
112
|
export declare function settlePendingRoundReevaluations(): Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Release a held global evaluation round: re-evaluate the round of its workflow
|
|
115
|
+
* repository from the stored webhook payload with the approved event treated as
|
|
116
|
+
* passing, dispatch what it admits, and settle the held run.
|
|
117
|
+
*
|
|
118
|
+
* Reached from the security queue's workflow release (`resumeWorkflow`), which
|
|
119
|
+
* recognises the held round by its run row. The held row stores no dispatch
|
|
120
|
+
* context: everything is rebuilt from the row, the payload and the delivery's
|
|
121
|
+
* event log entry — the same reconstruction a re-run of a failed round uses —
|
|
122
|
+
* so the release does not depend on how a workflow dispatch context would be
|
|
123
|
+
* replayed.
|
|
124
|
+
*
|
|
125
|
+
* Never throws. A round that cannot be rebuilt or decided fails the held run
|
|
126
|
+
* with the reason, which is the record an approver can read. A release that
|
|
127
|
+
* cannot claim the held row — another release already did — does nothing.
|
|
128
|
+
*/
|
|
129
|
+
export declare function releaseHeldGlobalEvalRound(runId: string, deps: ProcessingDeps): Promise<void>;
|
|
107
130
|
//# sourceMappingURL=rerun.d.ts.map
|
|
@@ -1,16 +1,42 @@
|
|
|
1
1
|
import type { Kysely } from 'kysely';
|
|
2
2
|
import type { Database } from '../db/types.js';
|
|
3
3
|
import type { ProcessingDeps } from './processor.js';
|
|
4
|
+
import type { ProviderBundle } from '../provider-registry.js';
|
|
4
5
|
import type { ReleaseSignal } from '../contexts/held-runs.js';
|
|
5
6
|
import { type WorkflowDispatchContext } from './dispatch-matched-workflow.js';
|
|
6
7
|
import { type SerializableWorkflowDispatchInputs } from './pending-workflow-context.js';
|
|
8
|
+
import type { GlobalDispatchIdentity } from './global-dispatch-identity.js';
|
|
7
9
|
import { type SecurityCheckHold } from './security-hold-check.js';
|
|
10
|
+
/**
|
|
11
|
+
* A global run's identity rebuilt from its stored hold, before the workflow
|
|
12
|
+
* repository's credentials are minted. It has no `workflowCredentials`, so a
|
|
13
|
+
* context carrying it is not a {@link WorkflowDispatchContext}.
|
|
14
|
+
*/
|
|
15
|
+
export type UnmintedGlobalIdentity = Omit<GlobalDispatchIdentity, 'workflowCredentials' | 'workflowBundle'> & {
|
|
16
|
+
readonly workflowBundle: ProviderBundle;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* A dispatch context rebuilt from a stored hold. `dispatchMatchedWorkflow`
|
|
20
|
+
* does not accept it: only {@link withWorkflowRepoCredentials} turns it into a
|
|
21
|
+
* {@link WorkflowDispatchContext}, so a resume cannot dispatch a global run
|
|
22
|
+
* without a freshly minted clone token.
|
|
23
|
+
*/
|
|
24
|
+
export type RebuiltWorkflowDispatchContext = Omit<WorkflowDispatchContext, 'global'> & {
|
|
25
|
+
global?: UnmintedGlobalIdentity;
|
|
26
|
+
};
|
|
8
27
|
/**
|
|
9
28
|
* Rebuild a live `WorkflowDispatchContext` from the persisted serializable
|
|
10
29
|
* inputs by re-attaching the orchestrator's live `deps` and reconstructing the
|
|
11
30
|
* provider `bundle` from the live registry (keyed by the stored routing key).
|
|
12
31
|
* Returns null when the provider bundle can no longer be resolved.
|
|
13
32
|
*
|
|
33
|
+
* A global run's identity is rebuilt from what was stored at hold time — the
|
|
34
|
+
* workflow commit, branch and provider context it was held at — never from the
|
|
35
|
+
* live registration, which may have moved to a newer commit while the run
|
|
36
|
+
* waited. Its workflow bundle is looked up by the stored workflow routing key,
|
|
37
|
+
* and it has no credentials until {@link withWorkflowRepoCredentials} mints
|
|
38
|
+
* them. Returns null when the workflow bundle can no longer be resolved either.
|
|
39
|
+
*
|
|
14
40
|
* The key it looks the bundle up by is `effectiveRoutingKey ?? info.routingKey`
|
|
15
41
|
* — the post-overlay key, the same one `setupDispatchContext` builds
|
|
16
42
|
* `setup.info` from and the same one the run row records. `info.routingKey` is
|
|
@@ -19,7 +45,13 @@ import { type SecurityCheckHold } from './security-hold-check.js';
|
|
|
19
45
|
* than the source the workflow belongs to: the resumed run would get the wrong
|
|
20
46
|
* app, the wrong credentials, and a check poster writing to the wrong place.
|
|
21
47
|
*/
|
|
22
|
-
export declare function rebuildWorkflowDispatchContext(inputs: SerializableWorkflowDispatchInputs, deps: ProcessingDeps):
|
|
48
|
+
export declare function rebuildWorkflowDispatchContext(inputs: SerializableWorkflowDispatchInputs, deps: ProcessingDeps): RebuiltWorkflowDispatchContext | null;
|
|
49
|
+
/**
|
|
50
|
+
* The rebuilt context with fresh credentials for a global run's workflow
|
|
51
|
+
* repository, minted by its bundle the way the first dispatch minted them. A
|
|
52
|
+
* same-repo context gets no global identity. Throws when the mint fails.
|
|
53
|
+
*/
|
|
54
|
+
export declare function withWorkflowRepoCredentials(rebuilt: RebuiltWorkflowDispatchContext): Promise<WorkflowDispatchContext>;
|
|
23
55
|
/**
|
|
24
56
|
* Resume a released workflow-scoped hold. Loads the pending context, rebuilds
|
|
25
57
|
* the dispatch context, and re-dispatches. On a lost pending context (or
|
|
@@ -55,5 +87,7 @@ export declare function resumeWorkflow(signal: ReleaseSignal, deps: ProcessingDe
|
|
|
55
87
|
* a rejection this declines to report (an install gate, or a commit whose other
|
|
56
88
|
* holds are still pending) leaves the caller free to decide for itself.
|
|
57
89
|
*/
|
|
58
|
-
export declare function rejectWorkflow(hold: SecurityCheckHold, deps: ProcessingDeps, db: Kysely<Database> | undefined, reason: string
|
|
90
|
+
export declare function rejectWorkflow(hold: SecurityCheckHold, deps: ProcessingDeps, db: Kysely<Database> | undefined, reason: string, opts?: {
|
|
91
|
+
runHeld?: boolean;
|
|
92
|
+
}): Promise<boolean>;
|
|
59
93
|
//# sourceMappingURL=resume-workflow.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dispatch-time warning for a job context that has no scope binding.
|
|
3
|
+
*
|
|
4
|
+
* A job receives a context's secrets only through the context's scope
|
|
5
|
+
* bindings, so a context with none delivers no secret, and nothing fails until
|
|
6
|
+
* a step reads one. This names each such context in the orchestrator log when
|
|
7
|
+
* the job's context data resolves. Advisory only: it runs after resolution, a
|
|
8
|
+
* failed lookup logs nothing, and the dispatch proceeds unchanged.
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@kici-dev/engine';
|
|
11
|
+
import type { SecretResolverApi } from '../secrets/secret-resolver.js';
|
|
12
|
+
/** The warning line's message; the structured fields carry the identities. */
|
|
13
|
+
export declare const UNBOUND_JOB_CONTEXT_MESSAGE = "Job binds a context that has no scope binding; it receives no secrets from it";
|
|
14
|
+
/**
|
|
15
|
+
* Warn once for every context in `entries` that resolved no secret and has no
|
|
16
|
+
* scope binding. A context that resolved at least one secret is skipped without
|
|
17
|
+
* a lookup, so a bound context costs nothing here.
|
|
18
|
+
*/
|
|
19
|
+
export declare function warnUnboundJobContexts(args: {
|
|
20
|
+
secretResolver: SecretResolverApi | undefined;
|
|
21
|
+
orgId: string;
|
|
22
|
+
entries: ReadonlyArray<{
|
|
23
|
+
name: string;
|
|
24
|
+
env: Context;
|
|
25
|
+
}>;
|
|
26
|
+
/** Per-context resolved secrets, keyed by the declared name; empty maps are absent. */
|
|
27
|
+
resolvedByName: Readonly<Record<string, Record<string, string>>> | undefined;
|
|
28
|
+
log: {
|
|
29
|
+
runId: string;
|
|
30
|
+
workflow: string;
|
|
31
|
+
job: string;
|
|
32
|
+
};
|
|
33
|
+
}): Promise<void>;
|
|
34
|
+
//# sourceMappingURL=unbound-job-context.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load a workflow repository's lock entries as they were at a recorded commit.
|
|
3
|
+
*
|
|
4
|
+
* An organization-wide run records the commit of the repository that defines
|
|
5
|
+
* its workflow (`execution_runs.workflow_sha`). Two paths act on such a run
|
|
6
|
+
* later, after that repository may have moved on: the re-run of a finished
|
|
7
|
+
* global run, and the release of a held evaluation round. Both must run the
|
|
8
|
+
* workflow version the run was created against, never the registration's
|
|
9
|
+
* current one. This module is the one place that resolves it.
|
|
10
|
+
*/
|
|
11
|
+
import type { LockFile, LockWorkflow } from '@kici-dev/engine';
|
|
12
|
+
import type { ProviderRegistry } from '../provider-registry.js';
|
|
13
|
+
import type { RegisteredWorkflow } from '../registration/registration-index.js';
|
|
14
|
+
import { type DepCacheKey } from '@kici-dev/shared';
|
|
15
|
+
/**
|
|
16
|
+
* Whether a lock entry is an organization-wide workflow: a trigger carries
|
|
17
|
+
* `repos:`. The re-run and the held-round release both judge a workflow by its
|
|
18
|
+
* entry at the recorded commit through this one rule, never by the current
|
|
19
|
+
* registration.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isOrganizationWideEntry(entry: LockWorkflow): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* The whole lock file of a registration's repository at `sha`, fetched through
|
|
24
|
+
* the registration's bundle. Throws, naming the repository, when it cannot be
|
|
25
|
+
* read.
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadWorkflowLockFileAtSha(args: {
|
|
28
|
+
registration: WorkflowLockRegistration;
|
|
29
|
+
sha: string;
|
|
30
|
+
providerRegistry: Pick<ProviderRegistry, 'getByRoutingKey'>;
|
|
31
|
+
}): Promise<LockFile>;
|
|
32
|
+
/** The registration fields that address a workflow repository's lock file. */
|
|
33
|
+
export type WorkflowLockRegistration = Pick<RegisteredWorkflow, 'repoIdentifier' | 'workflowName' | 'routingKey' | 'providerContext' | 'commitSha' | 'lockEntry' | 'lockfileHash' | 'siblingsDigest'>;
|
|
34
|
+
/** One workflow's lock entry at a commit, with the dependency-cache key of the lock file it comes from. */
|
|
35
|
+
export interface WorkflowLockEntryAtSha {
|
|
36
|
+
lockEntry: LockWorkflow;
|
|
37
|
+
depCacheKey: DepCacheKey;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The lock entries of `registrations` at `sha`, keyed by workflow name.
|
|
41
|
+
*
|
|
42
|
+
* Every registration must belong to the same workflow repository. When each
|
|
43
|
+
* registration is already at `sha`, its stored lock entry is that version and
|
|
44
|
+
* nothing is fetched. Otherwise the lock file is fetched at `sha` through the
|
|
45
|
+
* registration's own provider bundle, with the registration's provider context
|
|
46
|
+
* — the same fetcher a per-repository re-run reads its own lock file with. A
|
|
47
|
+
* workflow the lock file at `sha` does not define is absent from the result.
|
|
48
|
+
*
|
|
49
|
+
* Throws, naming the workflow repository, when the lock file cannot be read:
|
|
50
|
+
* the caller refuses rather than falling back to any other version.
|
|
51
|
+
*/
|
|
52
|
+
export declare function loadWorkflowLockEntriesAtSha(args: {
|
|
53
|
+
registrations: readonly WorkflowLockRegistration[];
|
|
54
|
+
sha: string;
|
|
55
|
+
providerRegistry: Pick<ProviderRegistry, 'getByRoutingKey'>;
|
|
56
|
+
}): Promise<Map<string, LockWorkflow>>;
|
|
57
|
+
/**
|
|
58
|
+
* The lock entry of one registered workflow at `sha`, with the dependency-cache
|
|
59
|
+
* key of the lock file it comes from. Throws, naming the workflow repository,
|
|
60
|
+
* when the lock file at `sha` does not define it.
|
|
61
|
+
*/
|
|
62
|
+
export declare function loadWorkflowLockEntryAtSha(args: {
|
|
63
|
+
registration: WorkflowLockRegistration;
|
|
64
|
+
sha: string;
|
|
65
|
+
providerRegistry: Pick<ProviderRegistry, 'getByRoutingKey'>;
|
|
66
|
+
}): Promise<WorkflowLockEntryAtSha>;
|
|
67
|
+
//# sourceMappingURL=workflow-lock-at-sha.d.ts.map
|
|
@@ -20,6 +20,11 @@ export interface BackfillRunRow {
|
|
|
20
20
|
started_at: Date | null;
|
|
21
21
|
completed_at: Date | null;
|
|
22
22
|
duration_ms: number | null;
|
|
23
|
+
/**
|
|
24
|
+
* The run's status generation. Forwarded so the Platform mirror records the
|
|
25
|
+
* generation the backfilled status belongs to. Absent reads as 0.
|
|
26
|
+
*/
|
|
27
|
+
status_epoch?: number | null;
|
|
23
28
|
}
|
|
24
29
|
/** The local `execution_jobs` fields the backfill needs. */
|
|
25
30
|
export interface BackfillJobRow {
|
|
@@ -4,12 +4,11 @@
|
|
|
4
4
|
* Posts check statuses to GitHub PRs using the Checks API for:
|
|
5
5
|
* - Security holds (pending): "KiCI Security" — "Held for approval"
|
|
6
6
|
* - Workflow modifications (neutral): "KiCI: Workflow changes"
|
|
7
|
-
* - Org globals skipped by the trust policy (neutral): "KiCI: Organization workflows"
|
|
8
7
|
* - Org global evaluation failed (failure): "KiCI: Organization workflow evaluation"
|
|
9
8
|
* - Approved runs (success) / Rejected/expired runs (failure)
|
|
10
9
|
*
|
|
11
|
-
* `postWorkflowModificationCheck
|
|
12
|
-
*
|
|
10
|
+
* `postWorkflowModificationCheck` and `postGlobalEvalFailedCheck` are
|
|
11
|
+
* interface-backed (the `CheckStatusPoster`
|
|
13
12
|
* contract in `@kici-dev/engine`) and post on their own check names so none of
|
|
14
13
|
* them overwrites the security-hold check.
|
|
15
14
|
*
|
|
@@ -38,23 +37,12 @@ export declare class GitHubCheckStatusPoster implements CheckStatusPoster {
|
|
|
38
37
|
* conflict with the security hold check. Always posted as neutral/completed.
|
|
39
38
|
*/
|
|
40
39
|
postWorkflowModificationCheck(repoIdentifier: string, commitSha: string, modifications: WorkflowModification[], credentials: unknown): Promise<void>;
|
|
41
|
-
/**
|
|
42
|
-
* Post the informational check recording that the org's global workflows were
|
|
43
|
-
* skipped because the trust policy held or rejected the event.
|
|
44
|
-
*
|
|
45
|
-
* Uses a separate check name ("KiCI: Organization workflows") so it never
|
|
46
|
-
* overwrites the security-hold check: that one is a single named run per
|
|
47
|
-
* commit which the hold posts as pending and approve / reject later complete,
|
|
48
|
-
* so writing this notice onto it would resolve the hold's check to neutral
|
|
49
|
-
* while the run is still held. Always posted as neutral/completed.
|
|
50
|
-
*/
|
|
51
|
-
postGlobalWorkflowsSkippedCheck(repoIdentifier: string, commitSha: string, summary: string, credentials: unknown): Promise<void>;
|
|
52
40
|
/**
|
|
53
41
|
* Post the check recording that the pre-run evaluation of the organization's
|
|
54
42
|
* global workflows failed, so none of the workflows it was deciding on ran.
|
|
55
43
|
*
|
|
56
|
-
* Its own check name ("KiCI: Organization workflow evaluation")
|
|
57
|
-
*
|
|
44
|
+
* Its own check name ("KiCI: Organization workflow evaluation") because the
|
|
45
|
+
* security-hold check is a single named run
|
|
58
46
|
* per commit which a hold posts as pending and approve / reject later
|
|
59
47
|
* complete, so writing this through `postCheckStatus` would resolve a
|
|
60
48
|
* still-held run's check and unblock a branch protection rule that requires
|
|
@@ -2,6 +2,7 @@ import { type Kysely, type SqlBool } from 'kysely';
|
|
|
2
2
|
import { type CanonicalLabel, type CanonicalMatcher, type LabelMatcher, type ResourceRequest } from '@kici-dev/engine';
|
|
3
3
|
import type { Database } from '../db/types.js';
|
|
4
4
|
import type { ClusterSettingsReader } from '../cluster/cluster-settings-reader.js';
|
|
5
|
+
import { JobContainerNeed } from '../scaler/agent-fit.js';
|
|
5
6
|
/**
|
|
6
7
|
* The ownership predicate, in its two forms. Both come from here so they cannot
|
|
7
8
|
* drift apart, because they answer the same question from opposite directions.
|
|
@@ -35,6 +36,8 @@ export interface OwnershipPredicateOptions {
|
|
|
35
36
|
*/
|
|
36
37
|
livePeerIds?: readonly string[];
|
|
37
38
|
}
|
|
39
|
+
/** Recorded on a job whose direct dispatch was refused because its run stopped. */
|
|
40
|
+
export declare const STOPPED_RUN_DISPATCH_REASON = "the run stopped before this job was dispatched";
|
|
38
41
|
/**
|
|
39
42
|
* Strict selection: the rows a cluster-wide sweep may recover. Excludes a NULL
|
|
40
43
|
* owner and excludes any row whose owner is still live.
|
|
@@ -67,6 +70,12 @@ export interface ExpiredJobInfo {
|
|
|
67
70
|
runsOnPatterns: CanonicalMatcher[];
|
|
68
71
|
excludeLabels: CanonicalLabel[];
|
|
69
72
|
excludePatterns: CanonicalMatcher[];
|
|
73
|
+
/**
|
|
74
|
+
* What the job's `container:` asks of the agent that runs it. A container job
|
|
75
|
+
* reaches only an agent that can start one, so an agent that merely matches
|
|
76
|
+
* its labels may still be no route at all. Absent reads as no container.
|
|
77
|
+
*/
|
|
78
|
+
container?: JobContainerNeed;
|
|
70
79
|
}
|
|
71
80
|
/**
|
|
72
81
|
* A pending job as the unroutable probe sees it: the same routing facts the
|
|
@@ -109,11 +118,23 @@ export declare enum DispatchQueueStatus {
|
|
|
109
118
|
Expired = "expired",
|
|
110
119
|
Recovering = "recovering"
|
|
111
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* The `dispatch_queue` statuses a row never leaves. `cancelled` is not a
|
|
123
|
+
* {@link DispatchQueueStatus}, but existing databases hold rows the test-run
|
|
124
|
+
* cancel stamped with it; those rows are terminal too, so the prune and the
|
|
125
|
+
* sealed-secrets scrub cover them.
|
|
126
|
+
*/
|
|
127
|
+
export declare const TERMINAL_DISPATCH_STATUSES: string[];
|
|
112
128
|
/**
|
|
113
129
|
* Maximum delivery attempts for a single dispatch_queue job. A job whose
|
|
114
130
|
* `dispatch_attempts` reaches this value is failed permanently instead of
|
|
115
131
|
* being requeued again. Bounds requeue loops from repeated job.reject /
|
|
116
|
-
* pre-start agent loss
|
|
132
|
+
* pre-start agent loss. A busy job.reject is not counted while the agent keeps
|
|
133
|
+
* reporting an empty slot between rejections, but is counted once the agent is
|
|
134
|
+
* re-picked only because its busy hold expired, so this cap still bounds an
|
|
135
|
+
* agent whose job count is stuck. `expires_at` is no backstop for this: a
|
|
136
|
+
* job dispatched straight to an agent carries none, and requeue does not set
|
|
137
|
+
* one.
|
|
117
138
|
*/
|
|
118
139
|
export declare const MAX_DISPATCH_ATTEMPTS = 5;
|
|
119
140
|
/**
|
|
@@ -231,6 +252,12 @@ export interface QueuedJob {
|
|
|
231
252
|
resources?: ResourceRequest;
|
|
232
253
|
/** For a runsOnAll host-fanout child: the agent this job is pinned to. */
|
|
233
254
|
pinnedAgentId?: string;
|
|
255
|
+
/**
|
|
256
|
+
* Set when the row's sealed secret fields could not be decrypted. The
|
|
257
|
+
* dispatcher never sends such a job: it puts the job back so a coordinator
|
|
258
|
+
* holding the key can take it, or fails it with this reason when none can.
|
|
259
|
+
*/
|
|
260
|
+
secretsUnavailable?: string;
|
|
234
261
|
}
|
|
235
262
|
/**
|
|
236
263
|
* The resource shape a job declares, read from the one location that always
|
|
@@ -272,6 +299,19 @@ export declare class JobQueue {
|
|
|
272
299
|
* gauge callback never issues an extra COUNT per scrape.
|
|
273
300
|
*/
|
|
274
301
|
private breakdownCache;
|
|
302
|
+
/**
|
|
303
|
+
* Jobs this coordinator put back because it could not open their sealed
|
|
304
|
+
* secrets (sealed with a master key it does not hold), keyed to the time it
|
|
305
|
+
* may claim them again. Until then every claim path and the re-drive readers
|
|
306
|
+
* skip them, so during a rolling key rotation a coordinator that holds the
|
|
307
|
+
* key can take the job in that window. After the back-off this coordinator
|
|
308
|
+
* can claim the job again, and each claim spends another attempt, so a job
|
|
309
|
+
* no coordinator can open still reaches {@link MAX_DISPATCH_ATTEMPTS} and
|
|
310
|
+
* fails. Bounded; an evicted entry only ends its back-off early.
|
|
311
|
+
*/
|
|
312
|
+
private readonly deferredUnopenable;
|
|
313
|
+
/** The configured default for `sealed_secrets_retry_backoff_ms`. */
|
|
314
|
+
private readonly sealedSecretsRetryBackoffMs;
|
|
275
315
|
constructor(db: Kysely<Database>, options: {
|
|
276
316
|
maxDepth: number;
|
|
277
317
|
defaultTimeoutMs: number;
|
|
@@ -293,6 +333,11 @@ export declare class JobQueue {
|
|
|
293
333
|
* recovery grace period.
|
|
294
334
|
*/
|
|
295
335
|
ownershipGraceMs?: number;
|
|
336
|
+
/**
|
|
337
|
+
* Default back-off before this coordinator claims again a job whose
|
|
338
|
+
* sealed secrets it could not open; `cluster_settings` overrides it.
|
|
339
|
+
*/
|
|
340
|
+
sealedSecretsRetryBackoffMs?: number;
|
|
296
341
|
});
|
|
297
342
|
/** Ownership predicate inputs, in the shape both predicate builders take. */
|
|
298
343
|
private ownershipOptions;
|
|
@@ -324,12 +369,14 @@ export declare class JobQueue {
|
|
|
324
369
|
* @param agentMandatoryLabels Mandatory labels the spawning scaler declared
|
|
325
370
|
* (empty for static / non-scaler agents).
|
|
326
371
|
* @param canServe Optional extra predicate applied to every candidate row
|
|
327
|
-
* before it is claimed. Labels cannot express whether
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
372
|
+
* before it is claimed. Labels cannot express whether an agent may run a
|
|
373
|
+
* job — a pre-spawned agent's fixed shape, a container job on an agent with
|
|
374
|
+
* no runtime, an agent started inside another job's image — so a caller
|
|
375
|
+
* that has to answer that supplies it here. The row must never be claimed
|
|
376
|
+
* and put back, because a claim-then-release both strands it as Dispatched
|
|
377
|
+
* for a window and burns a dispatch attempt. Supplying it opts out of the
|
|
378
|
+
* single-statement fast path, so pass it only for an agent that actually
|
|
379
|
+
* needs the check.
|
|
333
380
|
* @returns The matching job, or null if none found.
|
|
334
381
|
*/
|
|
335
382
|
dequeueForLabels(agentLabels: string[], agentMandatoryLabels?: string[], agentId?: string, canServe?: (job: QueuedJob) => boolean): Promise<QueuedJob | null>;
|
|
@@ -491,12 +538,38 @@ export declare class JobQueue {
|
|
|
491
538
|
* it can avoid double-dispatching an already-present job. The conflicting row
|
|
492
539
|
* keeps the owner the winning writer recorded.
|
|
493
540
|
*
|
|
494
|
-
*
|
|
541
|
+
* A job whose run has already stopped is refused, with the same stopped-run
|
|
542
|
+
* guard the queue drain applies (`notTerminalRunSql`). The row is still
|
|
543
|
+
* written, then settled `expired` in a guarded update — the state a cancel
|
|
544
|
+
* leaves a queued job of its run in — and the job's `execution_jobs` row, if
|
|
545
|
+
* one exists yet, is cancelled. `runStopped` tells the caller not to send it.
|
|
546
|
+
* Checking after the insert rather than before closes the race with a cancel:
|
|
547
|
+
* a cancel that lands after the check sees the dispatched row and cancels it
|
|
548
|
+
* through the agent, and one that lands before is seen by the check.
|
|
549
|
+
*
|
|
550
|
+
* @returns The job ID, whether a new row was inserted (false = row already
|
|
551
|
+
* existed), and whether the dispatch was refused because the run stopped.
|
|
495
552
|
*/
|
|
496
553
|
insertDispatched(job: QueuedJobInput, agentId: string): Promise<{
|
|
497
554
|
id: string;
|
|
498
555
|
inserted: boolean;
|
|
556
|
+
runStopped?: boolean;
|
|
499
557
|
}>;
|
|
558
|
+
/**
|
|
559
|
+
* Settle a claimed row whose run stopped after the claim: expire it and
|
|
560
|
+
* cancel its pending job row, the way the cancel path ends a stopped run's
|
|
561
|
+
* work. {@link requeue} refuses a stopped run's row, so a caller that claimed
|
|
562
|
+
* a job to put it back uses this when that requeue returns null; otherwise
|
|
563
|
+
* the row stays dispatched to no agent until a sweep finds it. Returns
|
|
564
|
+
* whether it settled the row: false when the row is no longer dispatched or
|
|
565
|
+
* its run is still going.
|
|
566
|
+
*/
|
|
567
|
+
settleClaimOfStoppedRun(id: string, runId: string): Promise<boolean>;
|
|
568
|
+
/**
|
|
569
|
+
* Settle a dispatched row `expired` when its run has stopped, and cancel the
|
|
570
|
+
* job's `execution_jobs` row. Returns whether it did.
|
|
571
|
+
*/
|
|
572
|
+
private expireDispatchOfStoppedRun;
|
|
500
573
|
/**
|
|
501
574
|
* Mark a job as dispatched and record the agent it went to.
|
|
502
575
|
*
|
|
@@ -555,6 +628,15 @@ export declare class JobQueue {
|
|
|
555
628
|
* @returns Number of affected rows.
|
|
556
629
|
*/
|
|
557
630
|
cancelByRunId(runId: string): Promise<number>;
|
|
631
|
+
/**
|
|
632
|
+
* Drop the sealed secret fields of every terminal row. A completed, failed or
|
|
633
|
+
* expired row is never dispatched again (a requeue moves only a dispatched
|
|
634
|
+
* row), so its secrets have no reader left; the row stays for its retention
|
|
635
|
+
* window without them.
|
|
636
|
+
*
|
|
637
|
+
* @returns Number of rows scrubbed.
|
|
638
|
+
*/
|
|
639
|
+
scrubTerminalSealedSecrets(): Promise<number>;
|
|
558
640
|
/**
|
|
559
641
|
* Delete terminal dispatch_queue rows older than `retentionDays`.
|
|
560
642
|
*
|
|
@@ -739,10 +821,17 @@ export declare class JobQueue {
|
|
|
739
821
|
* job started. Only flips rows still in 'dispatched' — a job that was
|
|
740
822
|
* concurrently completed / failed / cancelled is left untouched.
|
|
741
823
|
*
|
|
742
|
-
*
|
|
824
|
+
* `countAttempt: false` leaves the counter alone — used for a busy rejection
|
|
825
|
+
* from an agent still tearing down its previous job (see
|
|
826
|
+
* {@link MAX_DISPATCH_ATTEMPTS} for when a busy rejection does count).
|
|
827
|
+
*
|
|
828
|
+
* @returns the dispatch_attempts after the requeue, or null when the row
|
|
743
829
|
* was not in 'dispatched' state (nothing requeued).
|
|
744
830
|
*/
|
|
745
|
-
requeue(jobId: string
|
|
831
|
+
requeue(jobId: string, opts?: {
|
|
832
|
+
countAttempt?: boolean;
|
|
833
|
+
provisioningError?: string;
|
|
834
|
+
}): Promise<number | null>;
|
|
746
835
|
/**
|
|
747
836
|
* Requeue a dispatched job **only** when its ack genuinely never landed.
|
|
748
837
|
*
|
|
@@ -876,6 +965,34 @@ export declare class JobQueue {
|
|
|
876
965
|
* dispatched, cancelled, expired, or claimed by another coordinator.
|
|
877
966
|
*/
|
|
878
967
|
claimUnroutable(id: string): Promise<boolean>;
|
|
968
|
+
/**
|
|
969
|
+
* Leave a job alone for the sealed-secrets back-off: the dispatcher calls
|
|
970
|
+
* this when it puts back a job whose sealed secrets this coordinator could not
|
|
971
|
+
* open. See {@link deferredUnopenable}.
|
|
972
|
+
*/
|
|
973
|
+
deferUnopenable(jobId: string): Promise<void>;
|
|
974
|
+
/** Whether `jobId` is still inside its sealed-secrets back-off on this coordinator. */
|
|
975
|
+
isDeferredUnopenable(jobId: string): boolean;
|
|
976
|
+
/**
|
|
977
|
+
* Claim a specific pending job for no agent: Pending -> Dispatched, with no
|
|
978
|
+
* `agent_id`. The dispatcher calls this for a job whose sealed secrets this
|
|
979
|
+
* coordinator cannot open, when a re-drive would otherwise scale for it, so
|
|
980
|
+
* the job gets the put-back or fail decision a drain claim would give it.
|
|
981
|
+
*
|
|
982
|
+
* The guards of the other claim paths apply: the row is still pending and
|
|
983
|
+
* unexpired, its run has not stopped, and it is outside this coordinator's
|
|
984
|
+
* sealed-secrets back-off. The `status = Pending` guard is the arbiter, so a
|
|
985
|
+
* concurrent claimant makes this return false.
|
|
986
|
+
*
|
|
987
|
+
* @returns whether this call claimed the row.
|
|
988
|
+
*/
|
|
989
|
+
claimUnopenableById(jobId: string): Promise<boolean>;
|
|
990
|
+
/**
|
|
991
|
+
* The query condition that skips jobs still inside their sealed-secrets
|
|
992
|
+
* back-off, or undefined when none is (the common case adds no predicate).
|
|
993
|
+
* Expired entries are dropped here, which is what lets a job be claimed again.
|
|
994
|
+
*/
|
|
995
|
+
private deferredFilter;
|
|
879
996
|
/**
|
|
880
997
|
* Convert a DB row to a QueuedJob object.
|
|
881
998
|
* Handles both auto-parsed JSONB arrays (from pg driver) and JSON strings (from tests).
|