@kici-dev/orchestrator 0.1.16 → 0.1.18

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.
Files changed (83) hide show
  1. package/dist/agent/dispatcher.d.ts +27 -1
  2. package/dist/agent/host-roster-reaper.d.ts +42 -0
  3. package/dist/agent/host-roster.d.ts +110 -0
  4. package/dist/agent/registry.d.ts +65 -2
  5. package/dist/app.d.ts +3 -0
  6. package/dist/approvals/apply-decision.d.ts +11 -1
  7. package/dist/cache/pending-inits.d.ts +5 -0
  8. package/dist/cli/api-client.d.ts +11 -0
  9. package/dist/cli/commands/host.d.ts +13 -0
  10. package/dist/cli/commands/local-hook.d.ts +15 -0
  11. package/dist/cli/commands/local-trigger.d.ts +24 -0
  12. package/dist/cli/commands/remote-source.d.ts +17 -0
  13. package/dist/cli/commands/shared/versioned-upgrade.d.ts +37 -0
  14. package/dist/cli/kici-admin.d.ts +13 -0
  15. package/dist/cli/service/compose.d.ts +2 -1
  16. package/dist/cli/service/index.d.ts +2 -2
  17. package/dist/cli/service/instance/manifest.d.ts +12 -0
  18. package/dist/cli/service/instance/resolve.d.ts +12 -2
  19. package/dist/cli/service/launchd.d.ts +4 -1
  20. package/dist/cli/service/platform-detect.d.ts +5 -0
  21. package/dist/cli/service/systemd.d.ts +2 -1
  22. package/dist/cli/service/types.d.ts +37 -0
  23. package/dist/cli/service/windows.d.ts +2 -1
  24. package/dist/cli.js +1340 -198
  25. package/dist/cluster/coordinator.d.ts +5 -1
  26. package/dist/config/schema.d.ts +4 -0
  27. package/dist/config/types.d.ts +9 -0
  28. package/dist/config.d.ts +6 -4
  29. package/dist/dashboard/handler.d.ts +26 -2
  30. package/dist/dashboard/needs-edges.d.ts +13 -0
  31. package/dist/db/migrations/035_pending_workflow_contexts.d.ts +11 -0
  32. package/dist/db/migrations/036_attestations.d.ts +15 -0
  33. package/dist/db/migrations/037_generic_sources_provider_type_local.d.ts +19 -0
  34. package/dist/db/migrations/038_remote_sources.d.ts +14 -0
  35. package/dist/db/migrations/039_host_roster.d.ts +19 -0
  36. package/dist/db/migrations/040_runsonall_pin.d.ts +4 -0
  37. package/dist/db/migrations/041_wave_gated.d.ts +4 -0
  38. package/dist/db/migrations/042_dispatch_queue_patterns.d.ts +4 -0
  39. package/dist/db/types.d.ts +144 -10
  40. package/dist/diagnostics/fleet-collector.d.ts +1 -1
  41. package/dist/entry-helpers.d.ts +7 -29
  42. package/dist/environments/held-runs.d.ts +33 -0
  43. package/dist/index.js +1 -0
  44. package/dist/lockfile-redos-guard.d.ts +19 -0
  45. package/dist/metrics/prometheus.d.ts +17 -2
  46. package/dist/metrics/scheduled-jobs.d.ts +2 -2
  47. package/dist/orchestrator-core.d.ts +94 -1
  48. package/dist/pipeline/dispatch-matched-workflow.d.ts +60 -2
  49. package/dist/pipeline/install-secrets-resolver.d.ts +32 -5
  50. package/dist/pipeline/needs-scheduler.d.ts +12 -10
  51. package/dist/pipeline/pending-workflow-context.d.ts +44 -0
  52. package/dist/pipeline/processor.d.ts +31 -4
  53. package/dist/pipeline/remote-source-store.d.ts +21 -0
  54. package/dist/pipeline/resume-workflow.d.ts +26 -0
  55. package/dist/pipeline/wave-scheduler.d.ts +60 -0
  56. package/dist/providers/local/index.d.ts +33 -0
  57. package/dist/providers/local/local-source-config.d.ts +17 -0
  58. package/dist/providers/{internal → local}/lock-file-fetcher.d.ts +6 -6
  59. package/dist/providers/{internal → local}/normalizer.d.ts +28 -28
  60. package/dist/providers/{internal → local}/repo-url-builder.d.ts +6 -6
  61. package/dist/queue/job-queue.d.ts +61 -2
  62. package/dist/reporting/execution-tracker.d.ts +61 -0
  63. package/dist/routes/admin-events.d.ts +3 -4
  64. package/dist/routes/uploads.d.ts +36 -26
  65. package/dist/server.js +53755 -50888
  66. package/dist/sources/build-platform-sources.d.ts +4 -2
  67. package/dist/stale-detector/stale-run-detector.d.ts +14 -1
  68. package/dist/standalone.js +17234 -15725
  69. package/dist/webhook/generic-sources-listener.d.ts +4 -0
  70. package/dist/webhook/generic-sources.d.ts +37 -11
  71. package/dist/webhook/register-source-bundle.d.ts +14 -4
  72. package/dist/ws/agent-handler.d.ts +17 -0
  73. package/dist/ws/dashboard-dispatch-guard.d.ts +21 -0
  74. package/dist/ws/dashboard-env-handler.d.ts +13 -1
  75. package/dist/ws/oidc-token-relay.d.ts +59 -0
  76. package/dist/ws/platform-client.d.ts +30 -1
  77. package/dist/ws/test-relay-handlers.d.ts +112 -0
  78. package/installer-image-digests.json +3 -3
  79. package/package.json +22 -22
  80. package/sbom.spdx.json +3228 -6633
  81. package/dist/providers/internal/index.d.ts +0 -32
  82. package/dist/routes/test-trigger.d.ts +0 -41
  83. package/dist/ws/observer-handler.d.ts +0 -42
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Local filesystem (`file://`) source provider implementations.
3
+ *
4
+ * Provides a complete provider bundle for cloning a git repository that is
5
+ * already present on the agent's filesystem, with no remote forge, no webhook
6
+ * signature verification, and no network clone. Uses file:// URLs for cloning
7
+ * and reads lock files directly from the filesystem.
8
+ *
9
+ * Classes:
10
+ * - LocalWebhookNormalizer -> WebhookNormalizer (extracts from custom headers)
11
+ * - LocalLockFileFetcher -> LockFileFetcher (reads from local filesystem)
12
+ * - LocalRepoUrlBuilder -> RepoUrlBuilder (returns file:// URLs)
13
+ */
14
+ import type { ProviderBundle } from '../../provider-registry.js';
15
+ export { LocalWebhookNormalizer } from './normalizer.js';
16
+ export { LocalLockFileFetcher } from './lock-file-fetcher.js';
17
+ export { LocalRepoUrlBuilder } from './repo-url-builder.js';
18
+ /**
19
+ * Create a ProviderBundle for a local filesystem (`file://`) source.
20
+ *
21
+ * Provides normalizer, lock file fetcher, and repo URL builder.
22
+ * Clone token provider and changed files fetcher are null since
23
+ * file:// URLs need no auth and local events don't track changed files.
24
+ *
25
+ * @param opts.repoBasePath - Base directory where the repo(s) live on disk
26
+ * @param opts.cloneUrlBase - Optional URL base for clone operations (e.g. git://host/path)
27
+ * @returns Complete ProviderBundle for the local provider
28
+ */
29
+ export declare function createLocalProviderBundle(opts: {
30
+ repoBasePath: string;
31
+ cloneUrlBase?: string;
32
+ }): ProviderBundle;
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Configuration for a local filesystem (`file://`) source, stored as JSONB in
3
+ * `generic_webhook_sources.git_config` and discriminated from universal-git
4
+ * config by the row's `provider_type='local'`.
5
+ *
6
+ * A local source clones a git repository that already exists on the agent's
7
+ * filesystem (host path for bare-metal scalers, image-bundled / rootfs /
8
+ * bind-mounted path for container + Firecracker scalers). The orchestrator does
9
+ * not verify in-agent reachability — that is the operator's responsibility.
10
+ */
11
+ import { z } from 'zod';
12
+ export declare const LocalSourceConfigSchema: z.ZodObject<{
13
+ repoBasePath: z.ZodString;
14
+ cloneUrlBase: z.ZodOptional<z.ZodString>;
15
+ }, z.core.$strict>;
16
+ export type LocalSourceConfig = z.infer<typeof LocalSourceConfigSchema>;
17
+ //# sourceMappingURL=local-source-config.d.ts.map
@@ -1,22 +1,22 @@
1
1
  /**
2
- * Internal lock file fetcher for E2E tests.
2
+ * Local filesystem lock file fetcher.
3
3
  *
4
4
  * Implements the LockFileFetcher interface from @kici-dev/engine by reading
5
- * kici.lock.json directly from the local filesystem. Used when the internal
5
+ * kici.lock.json directly from the local filesystem. Used when the local
6
6
  * provider processes webhooks for repos accessible via file:// URLs.
7
7
  */
8
8
  import type { LockFileFetcher, LockFile } from '@kici-dev/engine';
9
9
  /**
10
- * Internal provider implementation of LockFileFetcher.
10
+ * Local provider implementation of LockFileFetcher.
11
11
  *
12
12
  * Reads kici.lock.json from the local filesystem at the path derived from
13
13
  * the repoIdentifier (which is expected to be a file:// URL or a local path).
14
14
  */
15
- export declare class InternalLockFileFetcher implements LockFileFetcher {
15
+ export declare class LocalLockFileFetcher implements LockFileFetcher {
16
16
  private readonly repoBasePath;
17
- readonly provider: "internal";
17
+ readonly provider: "local";
18
18
  /**
19
- * @param repoBasePath - Base directory for test repos. When repoIdentifier
19
+ * @param repoBasePath - Base directory for the repo(s). When repoIdentifier
20
20
  * starts with 'file://', it is stripped and used as-is. Otherwise
21
21
  * repoBasePath is used as the root.
22
22
  */
@@ -1,25 +1,25 @@
1
1
  /**
2
- * Internal webhook normalizer for E2E tests.
2
+ * Local filesystem source webhook normalizer.
3
3
  *
4
- * Implements the WebhookNormalizer interface from @kici-dev/engine for internal
5
- * webhook sources. Used by the provider-agnostic E2E test suite to trigger the
6
- * full webhook processing pipeline without GitHub dependencies.
7
- *
8
- * Verification is always skipped (internal sources use verification='none').
9
- * Event type and routing key are extracted from custom headers.
4
+ * Implements the WebhookNormalizer interface from @kici-dev/engine for local
5
+ * (`file://`) sources. Drives the full webhook processing pipeline without a
6
+ * remote forge: signature verification is skipped (local sources use
7
+ * verification='none'), and event type / routing key are extracted from custom
8
+ * headers.
10
9
  */
11
10
  import type { WebhookNormalizer, SimulatedEvent, AccessCacheInvalidation } from '@kici-dev/engine';
12
11
  /**
13
- * Internal provider implementation of WebhookNormalizer.
12
+ * Local provider implementation of WebhookNormalizer.
14
13
  *
15
- * Maps internal webhook headers and payloads to KiCI's universal SimulatedEvent format.
16
- * Designed for E2E tests where payloads use normalized provider-agnostic structures
14
+ * Maps the synthetic webhook headers and payloads sent to a local source into
15
+ * KiCI's universal SimulatedEvent format. Payloads use the same GitHub-shaped
16
+ * structure the trigger CLI / post-receive hook build
17
17
  * (e.g., {ref: 'refs/heads/master', repository: {full_name: 'test/repo'}}).
18
18
  */
19
- export declare class InternalWebhookNormalizer implements WebhookNormalizer {
20
- readonly provider: "internal";
19
+ export declare class LocalWebhookNormalizer implements WebhookNormalizer {
20
+ readonly provider: "local";
21
21
  /**
22
- * Extract routing key from internal webhook headers.
22
+ * Extract routing key from the local-source webhook headers.
23
23
  *
24
24
  * Checks x-kici-routing-key first (explicit routing), then falls back
25
25
  * to x-kici-source-id (generic source ID format).
@@ -38,42 +38,42 @@ export declare class InternalWebhookNormalizer implements WebhookNormalizer {
38
38
  /**
39
39
  * Verify signature -- always returns true.
40
40
  *
41
- * Internal sources use verification='none'. Actual verification is not
42
- * needed for E2E test infrastructure where both sender and receiver
43
- * are controlled by the test harness.
41
+ * Local sources use verification='none' there is no remote forge to sign
42
+ * the payload. The operator is responsible for only registering repos they
43
+ * trust (see docs/user/providers/local-file.md).
44
44
  */
45
45
  verifySignature(_body: string, _headers: Record<string, string>, _secret: string): boolean;
46
46
  /**
47
- * Extract repository identifier from an internal webhook payload.
47
+ * Extract repository identifier from a local-source webhook payload.
48
48
  *
49
- * Internal events mimic GitHub-shaped payloads in E2E tests,
50
- * so we extract from payload.repository.full_name if present.
49
+ * Local-source events use GitHub-shaped payloads, so we extract from
50
+ * payload.repository.full_name if present.
51
51
  */
52
52
  extractRepoIdentifier(payload: unknown): string | null;
53
53
  /**
54
- * Extract ref from an internal webhook payload.
54
+ * Extract ref from a local-source webhook payload.
55
55
  *
56
- * Internal events use GitHub-shaped payloads, so extraction logic
56
+ * Local-source events use GitHub-shaped payloads, so extraction logic
57
57
  * mirrors GitHub's: push -> payload.after, PR -> payload.pull_request.head.sha.
58
58
  */
59
59
  extractRef(eventType: string, payload: unknown): string;
60
60
  /**
61
- * Extract credentials -- internal sources carry no provider credentials.
61
+ * Extract credentials -- local sources carry no provider credentials.
62
62
  */
63
63
  extractCredentials(_payload: unknown): Record<string, unknown>;
64
64
  /**
65
- * Normalize an internal webhook event into a SimulatedEvent.
65
+ * Normalize a local-source webhook event into a SimulatedEvent.
66
66
  *
67
67
  * Extracts branch information from payload.ref (stripping refs/heads/ prefix)
68
- * and preserves the raw payload for trigger matching. Falls back to '__internal__'
68
+ * and preserves the raw payload for trigger matching. Falls back to '__local__'
69
69
  * when no ref is present.
70
70
  */
71
71
  normalizeEvent(eventType: string, _action: string | null, payload: unknown): SimulatedEvent | null;
72
72
  /**
73
- * Map membership-related internal webhook events to ContributorCache
73
+ * Map membership-related local-source webhook events to ContributorCache
74
74
  * invalidations.
75
75
  *
76
- * Internal E2E payloads are GitHub-shaped by design, so the mapping
76
+ * Local-source payloads are GitHub-shaped by design, so the mapping
77
77
  * mirrors the GitHub normalizer exactly:
78
78
  *
79
79
  * - `member`: repo-user
@@ -81,8 +81,8 @@ export declare class InternalWebhookNormalizer implements WebhookNormalizer {
81
81
  * - `membership`: user-in-org
82
82
  * - `team` (repo-scoped actions only): repo
83
83
  *
84
- * This lets Bucket C E2E tests exercise the invalidation path through
85
- * `sendInternalWebhook()` without requiring a real GitHub App.
84
+ * This lets membership-invalidation paths be exercised through
85
+ * `sendLocalWebhook()` without requiring a real GitHub App.
86
86
  */
87
87
  getAccessCacheInvalidations(eventType: string, _action: string | null, payload: unknown): AccessCacheInvalidation[];
88
88
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Internal repo URL builder for E2E tests.
2
+ * Local filesystem repo URL builder.
3
3
  *
4
4
  * Implements the RepoUrlBuilder interface from @kici-dev/engine by returning
5
5
  * file:// URLs for local filesystem access, or network URLs (git://, http://)
@@ -7,16 +7,16 @@
7
7
  */
8
8
  import type { RepoUrlBuilder } from '@kici-dev/engine';
9
9
  /**
10
- * Internal provider implementation of RepoUrlBuilder.
10
+ * Local provider implementation of RepoUrlBuilder.
11
11
  *
12
12
  * Builds clone URLs for repository access. When cloneUrlBase is set (e.g. to
13
13
  * a git:// daemon URL), uses it for clone URLs so remote agents on different
14
14
  * machines can clone. Otherwise falls back to file:// URLs for local access.
15
15
  */
16
- export declare class InternalRepoUrlBuilder implements RepoUrlBuilder {
16
+ export declare class LocalRepoUrlBuilder implements RepoUrlBuilder {
17
17
  private readonly repoBasePath;
18
18
  private readonly cloneUrlBase?;
19
- readonly provider: "internal";
19
+ readonly provider: "local";
20
20
  /**
21
21
  * @param repoBasePath - Base directory where test repos live on disk
22
22
  * @param cloneUrlBase - Optional URL base for clone operations (e.g. git://host/path).
@@ -34,8 +34,8 @@ export declare class InternalRepoUrlBuilder implements RepoUrlBuilder {
34
34
  /**
35
35
  * Build a raw file URL -- returns empty string.
36
36
  *
37
- * Internal repos have no web UI, so raw file URLs are not applicable.
38
- * The lock file is fetched via InternalLockFileFetcher (filesystem) instead.
37
+ * Local repos have no web UI, so raw file URLs are not applicable.
38
+ * The lock file is fetched via LocalLockFileFetcher (filesystem) instead.
39
39
  */
40
40
  buildRawFileUrl(_repoIdentifier: string, _ref: string, _path: string): string;
41
41
  }
@@ -1,5 +1,5 @@
1
1
  import { type Kysely } from 'kysely';
2
- import type { ResourceRequest } from '@kici-dev/engine';
2
+ import { type LabelMatcher, type ResourceRequest } from '@kici-dev/engine';
3
3
  import type { Database } from '../db/types.js';
4
4
  /** Info about an expired dispatch_queue entry, returned by markExpired(). */
5
5
  export interface ExpiredJobInfo {
@@ -92,12 +92,26 @@ export interface QueuedJobInput {
92
92
  requestId?: string;
93
93
  /** Labels that the dispatched agent must NOT have. */
94
94
  excludeLabels?: string[];
95
+ /** Regex matchers the agent's labels must satisfy (JS post-filter on the exact @> prefilter). */
96
+ runsOnPatterns?: LabelMatcher[];
97
+ /** Regex matchers that disqualify an agent (JS post-filter). */
98
+ excludePatterns?: LabelMatcher[];
95
99
  /**
96
100
  * Per-job resource request and limit (K8s-style). Drives scaler cap accounting
97
101
  * (`requests`) and kernel-side enforcement on the spawned agent (`limits`).
98
102
  * Stored inside `jobConfig` JSON; this typed field is a convenience for callers.
99
103
  */
100
104
  resources?: ResourceRequest;
105
+ /**
106
+ * For a runsOnAll host-fanout child: the agent this job is pinned to. The
107
+ * dispatcher routes it only to that agent; the drain never hands it to another.
108
+ */
109
+ pinnedAgentId?: string;
110
+ /**
111
+ * For a pinned child: which orchestrator instance owns the pinned agent's live
112
+ * WS (null = not currently connected). Used by the cross-cluster pin reroute.
113
+ */
114
+ connectedInstanceId?: string | null;
101
115
  }
102
116
  /**
103
117
  * Full queued job as stored in the database.
@@ -135,11 +149,17 @@ export interface QueuedJob {
135
149
  requestId?: string;
136
150
  /** Labels that the dispatched agent must NOT have. */
137
151
  excludeLabels: string[];
152
+ /** Regex matchers the agent's labels must satisfy (JS post-filter on the exact @> prefilter). */
153
+ runsOnPatterns: LabelMatcher[];
154
+ /** Regex matchers that disqualify an agent (JS post-filter). */
155
+ excludePatterns: LabelMatcher[];
138
156
  /**
139
157
  * Per-job resource request and limit (K8s-style). Materialized from `jobConfig.resources`
140
158
  * by `rowToQueuedJob` so callers can read it without re-parsing the JSON column.
141
159
  */
142
160
  resources?: ResourceRequest;
161
+ /** For a runsOnAll host-fanout child: the agent this job is pinned to. */
162
+ pinnedAgentId?: string;
143
163
  }
144
164
  /**
145
165
  * DB-backed FIFO job dispatch queue using Kysely (PostgreSQL only).
@@ -188,7 +208,46 @@ export declare class JobQueue {
188
208
  * (empty for static / non-scaler agents).
189
209
  * @returns The matching job, or null if none found.
190
210
  */
191
- dequeueForLabels(agentLabels: string[], agentMandatoryLabels?: string[]): Promise<QueuedJob | null>;
211
+ dequeueForLabels(agentLabels: string[], agentMandatoryLabels?: string[], agentId?: string): Promise<QueuedJob | null>;
212
+ /**
213
+ * Build the shared drain WHERE chain (status / expiry / exact-label @> /
214
+ * exclude-label / pin / mandatory-label gate) common to both drain passes.
215
+ * The pattern columns are NOT filtered here — each pass adds its own
216
+ * pattern-free / pattern-bearing guard on top.
217
+ */
218
+ private drainBaseQuery;
219
+ /**
220
+ * Fast path: claim the oldest pending pattern-free row. The
221
+ * `runs_on_patterns = '[]' AND exclude_patterns = '[]'` guard restricts this
222
+ * pass to rows that need no JS post-filter, so the single-row atomic claim
223
+ * (FOR UPDATE SKIP LOCKED) keeps the original hot-path semantics intact.
224
+ */
225
+ private claimPatternFree;
226
+ /**
227
+ * Pattern path: load a small batch of pattern-bearing candidate rows, apply
228
+ * the JS regex post-filter (matcherSatisfiedBy), and atomically claim the
229
+ * first match by id with a conditional `status = Pending` guard. The claim is
230
+ * a conditional UPDATE rather than relying on the SELECT lock alone because
231
+ * the JS filter runs after the per-statement lock window has closed, so two
232
+ * agents could both pass the filter for the same row; the `where status =
233
+ * Pending` makes exactly one of them win. The claim transitions the row to
234
+ * Dispatched, matching the value the caller-side markDispatched would set
235
+ * (which then re-sets it idempotently).
236
+ */
237
+ private claimWithPatterns;
238
+ /**
239
+ * Atomically claim the oldest pending job pinned to a specific agent. Used by
240
+ * the eager pin drain when the pinned agent (re)registers or frees a slot —
241
+ * the host-fanout analog of `dispatchBoundJob`'s eager path. Ignores the exact
242
+ * label gate: the pin was resolved against the roster at materialize time.
243
+ *
244
+ * Still applies the JS regex post-filter (`jobPatternsSatisfiedBy`) when
245
+ * `agentLabels` is supplied, mirroring `dequeueById`: a pinned child whose
246
+ * `runsOn`/`exclude` patterns no longer match the agent's current labels must
247
+ * not be claimed. The single matching authority is the engine's
248
+ * `matcherSatisfiedBy` (never a Postgres `~`).
249
+ */
250
+ dequeueByPinnedAgent(agentId: string, agentLabels?: string[]): Promise<QueuedJob | null>;
192
251
  /**
193
252
  * Atomically claim a specific pending job by ID, validating it still
194
253
  * matches the agent's labels and isn't expired.
@@ -202,6 +202,12 @@ export declare class ExecutionTracker {
202
202
  jobName: string;
203
203
  matrixValues?: Record<string, unknown>;
204
204
  runsOnLabels?: string[];
205
+ baseJobName?: string;
206
+ variantKind?: string;
207
+ variantLabel?: string;
208
+ waveGated?: boolean;
209
+ waveMaxParallel?: number;
210
+ waveFailFast?: boolean;
205
211
  }>, routingKey?: string,
206
212
  /** Secret context names dispatched with jobs (for context-disable job lookup). */
207
213
  dispatchedContexts?: string[],
@@ -237,6 +243,13 @@ export declare class ExecutionTracker {
237
243
  * which peer owns the downstream dispatch.
238
244
  */
239
245
  findSyntheticJobId(runId: string, jobName: string): Promise<string | undefined>;
246
+ /**
247
+ * Find the synthetic deferred-eval placeholder job ID for a result-aware
248
+ * dynamic generator's eval job. Mirrors {@link findSyntheticJobId} but keys on
249
+ * the `dynamic-eval-pending-<evalJobName>-` prefix that registerDeferredEvalJob
250
+ * uses, so dispatchEvalJob can swap it for the real eval job id.
251
+ */
252
+ findDynamicEvalSyntheticId(runId: string, evalJobName: string): Promise<string | undefined>;
240
253
  /**
241
254
  * Run `fn` while holding a per-run lock, serializing the run-mutating methods
242
255
  * (`onJobStatus`, `addJobsToRun`) so a status reply cannot interleave with the
@@ -258,6 +271,9 @@ export declare class ExecutionTracker {
258
271
  jobName: string;
259
272
  matrixValues?: Record<string, unknown>;
260
273
  runsOnLabels?: string[];
274
+ baseJobName?: string;
275
+ variantKind?: string;
276
+ variantLabel?: string;
261
277
  }>, dispatchedContexts?: string[],
262
278
  /** Synthetic job ID to replace (e.g. needs-pending-deploy-{uuid}). */
263
279
  replaceSyntheticId?: string): Promise<void>;
@@ -339,6 +355,18 @@ export declare class ExecutionTracker {
339
355
  * so recursive skip propagation terminates naturally.
340
356
  */
341
357
  private runSchedulerHook;
358
+ /**
359
+ * Rolling-wave hook: fires beside the needs-scheduler when a fan-out child of
360
+ * a bounded wave (`maxParallel` set) reaches terminal. Reads the completed
361
+ * child's row to recover the base + wave policy, asks {@link evaluateWave}
362
+ * what to do, then performs it:
363
+ *
364
+ * - `release`: clear the next held sibling's `wave_gated` flag and fire the
365
+ * onJobReady callback (the existing ready→dispatch path).
366
+ * - `skip-remaining`: mark every still-held sibling `skipped` (failFast).
367
+ * - `noop`: nothing — a later terminal will free the next slot.
368
+ */
369
+ private runWaveSchedulerHook;
342
370
  /**
343
371
  * Phase 9: stuck-jobs invariant check ( Layer 3).
344
372
  * Before declaring a run complete, verify no stuck jobs exist. If any are
@@ -395,6 +423,39 @@ export declare class ExecutionTracker {
395
423
  triggerEvent?: string;
396
424
  commitMessage?: string;
397
425
  }): Promise<void>;
426
+ /**
427
+ * Record a run paused at the workflow install gate (a `registries:` /
428
+ * `installEnv:` protection rule returned hold / wait / queue). Writes an
429
+ * `execution_runs` row in the `held` state — alive and resumable — so the
430
+ * dashboard run list surfaces the paused workflow. No jobs are tracked: the
431
+ * workflow-scoped held_runs row + pending workflow context (written by the
432
+ * caller) keep the run from being counted complete. Idempotent on runId.
433
+ */
434
+ recordRunHeld(args: {
435
+ runId: string;
436
+ workflowName: string;
437
+ provider: string;
438
+ repoIdentifier: string;
439
+ ref: string;
440
+ sha: string;
441
+ deliveryId: string | null;
442
+ providerContext: Record<string, unknown>;
443
+ routingKey: string;
444
+ environmentName?: string;
445
+ reason: string;
446
+ triggerEvent?: string;
447
+ commitMessage?: string;
448
+ }): Promise<void>;
449
+ /**
450
+ * Flip a `held` run back to `pending` so the resumed dispatch can proceed
451
+ * into job dispatch. Returns true when a held row was found and updated.
452
+ */
453
+ resumeHeldRun(runId: string): Promise<boolean>;
454
+ /**
455
+ * Cancel a held run (reviewer rejected the install gate). Flips the held row
456
+ * to `cancelled` and fires the status-change forward so Platform projects it.
457
+ */
458
+ cancelHeldRun(runId: string, reason: string): Promise<void>;
398
459
  /**
399
460
  * Mark a run as failed immediately with a reason message.
400
461
  *
@@ -26,15 +26,14 @@ interface AdminEventRouteDeps {
26
26
  rbac: RbacEnforcer;
27
27
  /**
28
28
  * The in-process bundle registry. The POST /generic-sources handler
29
- * registers an internal / universal-git bundle into this registry
29
+ * registers a local / universal-git bundle into this registry
30
30
  * immediately after the source row lands in the DB, so the next
31
31
  * webhook against that source resolves the right normalizer without
32
32
  * waiting for an orchestrator restart.
33
33
  */
34
34
  providerRegistry: ProviderRegistry;
35
- /** Needed by `registerProviderBundleForSource` to gate internal-bundle
36
- * registration on `canServeGenericProviderType` and read the
37
- * `internalProviderRepoPath` / `internalProviderCloneUrl` config. */
35
+ /** Passed through to `registerProviderBundleForSource` (universal-git bundle
36
+ * build reads cluster config; local bundles read the row's own git_config). */
38
37
  config: AppConfig;
39
38
  /** Required for universal-git source registration — `null` is allowed;
40
39
  * rows with `git_config` are skipped + metric-bumped in that case. */
@@ -1,38 +1,48 @@
1
1
  /**
2
- * REST endpoints for test run upload management.
2
+ * Test-run upload provisioning.
3
3
  *
4
- * POST /api/v1/uploads/init - Initialize an upload (returns signed URL + public key)
5
- * GET /api/v1/uploads/:uploadId/status - Check upload status
6
- *
7
- * Uploads support the repo state transfer mechanism: CLI uploads an encrypted
8
- * tarball of changed files to S3 via a pre-signed URL, then references the
9
- * upload ID in the test trigger request.
4
+ * Mints an upload record and a presigned PUT URL + ephemeral X25519 public key
5
+ * for the overlay tarball. The developer encrypts the tarball with the returned
6
+ * public key and PUTs it directly to the object store; a test trigger then
7
+ * references the upload id. This is invoked by the Platform-first
8
+ * `test.relay.uploads.init` relay handler the developer never reaches the
9
+ * orchestrator's HTTP API directly.
10
10
  */
11
- import { Hono } from 'hono';
12
- import type { AppConfig } from '../config.js';
13
- import type { TokenManager } from '../secrets/token-manager.js';
14
11
  import type { Kysely } from 'kysely';
15
12
  import type { Database } from '../db/types.js';
16
13
  import type { CacheStorage } from '../storage/types.js';
17
- /**
18
- * Dependencies for upload routes.
19
- */
20
- export interface UploadRouteDeps {
21
- config: AppConfig;
14
+ /** Parameters for {@link initTestUpload}. */
15
+ export interface InitTestUploadParams {
16
+ routingKey: string;
17
+ sha?: string;
18
+ fileCount?: number;
19
+ compressedSize?: number;
20
+ /** PAT/actor identity that owns this upload, written to `test_uploads.created_by`. */
21
+ createdBy?: string | null;
22
+ /**
23
+ * When true, presign with the host-facing internal endpoint. When false (the
24
+ * Platform-relayed path), presign with the external/dev-reachable endpoint so
25
+ * a developer on a different network can PUT directly to the object store.
26
+ */
27
+ internal?: boolean;
28
+ }
29
+ /** Result of {@link initTestUpload}. */
30
+ export interface InitTestUploadResult {
31
+ uploadId: string;
32
+ signedUrl: string;
33
+ publicKey: string;
34
+ expiresIn: number;
35
+ }
36
+ /** Dependencies for {@link initTestUpload}. */
37
+ export interface InitTestUploadDeps {
22
38
  db: Kysely<Database>;
23
- tokenManager?: TokenManager;
24
39
  cacheStorage?: CacheStorage;
25
40
  }
26
- /** Hono env type for upload routes with context variables. */
27
- type UploadEnv = {
28
- Variables: {
29
- userId: string;
30
- routingKey: string | null;
31
- };
32
- };
33
41
  /**
34
- * Create Hono routes for upload management endpoints.
42
+ * Mint an upload record and return a presigned PUT URL + ephemeral X25519
43
+ * public key. The encryption keypair is generated per upload; the private key
44
+ * is stored orchestrator-side for post-PUT decryption. The `internal` flag
45
+ * selects the host-facing vs the external/dev-reachable presign endpoint.
35
46
  */
36
- export declare function createUploadRoutes(deps: UploadRouteDeps): Hono<UploadEnv>;
37
- export {};
47
+ export declare function initTestUpload(deps: InitTestUploadDeps, params: InitTestUploadParams): Promise<InitTestUploadResult>;
38
48
  //# sourceMappingURL=uploads.d.ts.map