@kici-dev/orchestrator 0.1.15 → 0.1.17

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 (94) hide show
  1. package/dist/agent/dispatcher.d.ts +18 -1
  2. package/dist/app.d.ts +25 -0
  3. package/dist/approvals/apply-decision.d.ts +70 -0
  4. package/dist/approvals/approval-resolver.d.ts +66 -0
  5. package/dist/approvals/step-approval-bridge.d.ts +72 -0
  6. package/dist/approvals/team-membership-lookup.d.ts +13 -0
  7. package/dist/cache/pending-inits.d.ts +5 -0
  8. package/dist/cache/user-cache.d.ts +1 -1
  9. package/dist/cli/api-client.d.ts +35 -0
  10. package/dist/cli/commands/firecracker/index.d.ts +11 -0
  11. package/dist/cli/commands/firecracker/provision.d.ts +13 -0
  12. package/dist/cli/commands/firecracker/teardown.d.ts +3 -0
  13. package/dist/cli/commands/firecracker/verify.d.ts +3 -0
  14. package/dist/cli/commands/local-hook.d.ts +15 -0
  15. package/dist/cli/commands/local-trigger.d.ts +24 -0
  16. package/dist/cli/commands/remote-source.d.ts +17 -0
  17. package/dist/cli/commands/scaler.d.ts +18 -0
  18. package/dist/cli/commands/shared/versioned-upgrade.d.ts +14 -0
  19. package/dist/cli/kici-admin.d.ts +23 -1
  20. package/dist/cli/service/image-digests.d.ts +21 -0
  21. package/dist/cli.js +4320 -490
  22. package/dist/cluster/peer-client.d.ts +23 -1
  23. package/dist/cluster/peer-handler.d.ts +9 -1
  24. package/dist/config.d.ts +0 -4
  25. package/dist/dashboard/handler.d.ts +26 -2
  26. package/dist/dashboard/needs-edges.d.ts +13 -0
  27. package/dist/db/migrations/033_org_settings_approval.d.ts +19 -0
  28. package/dist/db/migrations/034_held_runs_generalize.d.ts +24 -0
  29. package/dist/db/migrations/035_pending_workflow_contexts.d.ts +11 -0
  30. package/dist/db/migrations/036_attestations.d.ts +15 -0
  31. package/dist/db/migrations/037_generic_sources_provider_type_local.d.ts +19 -0
  32. package/dist/db/migrations/038_remote_sources.d.ts +14 -0
  33. package/dist/db/types.d.ts +135 -11
  34. package/dist/diagnostics/bundle-writer.d.ts +1 -17
  35. package/dist/diagnostics/checks/firecracker-network.d.ts +13 -0
  36. package/dist/diagnostics/checks/index.d.ts +2 -1
  37. package/dist/diagnostics/fleet-collector.d.ts +52 -0
  38. package/dist/diagnostics/fleet-constants.d.ts +8 -0
  39. package/dist/diagnostics/fleet-selection.d.ts +15 -0
  40. package/dist/diagnostics/fleet-topology.d.ts +47 -0
  41. package/dist/diagnostics/fleet-wiring.d.ts +60 -0
  42. package/dist/entry-helpers.d.ts +7 -29
  43. package/dist/environments/held-runs.d.ts +93 -1
  44. package/dist/firecracker/host-network.d.ts +83 -0
  45. package/dist/firecracker/persist.d.ts +14 -0
  46. package/dist/index.js +25 -6
  47. package/dist/metrics/prometheus.d.ts +4 -2
  48. package/dist/orchestrator-core.d.ts +57 -1
  49. package/dist/pipeline/dispatch-matched-workflow.d.ts +18 -1
  50. package/dist/pipeline/install-secrets-resolver.d.ts +32 -5
  51. package/dist/pipeline/needs-scheduler.d.ts +12 -10
  52. package/dist/pipeline/pending-workflow-context.d.ts +44 -0
  53. package/dist/pipeline/processor.d.ts +28 -4
  54. package/dist/pipeline/remote-source-store.d.ts +21 -0
  55. package/dist/pipeline/resume-workflow.d.ts +26 -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/reporting/execution-tracker.d.ts +33 -0
  62. package/dist/routes/admin-events.d.ts +3 -4
  63. package/dist/routes/fleet.d.ts +20 -0
  64. package/dist/routes/uploads.d.ts +36 -26
  65. package/dist/scaler/config.d.ts +4 -2
  66. package/dist/scaler/disk-guard.d.ts +27 -0
  67. package/dist/scaler/firecracker-backend.d.ts +18 -0
  68. package/dist/scaler/manager.d.ts +6 -0
  69. package/dist/scaler/nftables.d.ts +4 -0
  70. package/dist/scaler/reap-orphans.d.ts +27 -0
  71. package/dist/scaler/types.d.ts +2 -0
  72. package/dist/server.js +26929 -23511
  73. package/dist/sources/build-platform-sources.d.ts +4 -2
  74. package/dist/stale-detector/stale-run-detector.d.ts +32 -1
  75. package/dist/standalone.js +16941 -15329
  76. package/dist/storage/filesystem.d.ts +2 -1
  77. package/dist/storage/s3.d.ts +2 -1
  78. package/dist/storage/types.d.ts +7 -0
  79. package/dist/webhook/generic-sources-listener.d.ts +4 -0
  80. package/dist/webhook/generic-sources.d.ts +37 -11
  81. package/dist/webhook/register-source-bundle.d.ts +14 -4
  82. package/dist/ws/agent-handler.d.ts +49 -0
  83. package/dist/ws/dashboard-dispatch-guard.d.ts +21 -0
  84. package/dist/ws/dashboard-env-handler.d.ts +47 -0
  85. package/dist/ws/fleet-agent-collector.d.ts +23 -0
  86. package/dist/ws/oidc-token-relay.d.ts +59 -0
  87. package/dist/ws/platform-client.d.ts +30 -1
  88. package/dist/ws/test-relay-handlers.d.ts +112 -0
  89. package/installer-image-digests.json +7 -0
  90. package/package.json +6 -5
  91. package/sbom.spdx.json +112 -57
  92. package/dist/providers/internal/index.d.ts +0 -32
  93. package/dist/routes/test-trigger.d.ts +0 -41
  94. package/dist/ws/observer-handler.d.ts +0 -42
@@ -8,10 +8,13 @@
8
8
  *
9
9
  * 1. Parses every qualified ref and groups by environment name.
10
10
  * 2. Fires the per-environment protection-rule pipeline once per unique env
11
- * (branch / trust / concurrency / reviewer / wait-timer). Any non-pass
12
- * result rejects the whole workflow dispatch v1 does not yet support
13
- * workflow-scoped held-runs, so a `hold` from the reviewer-gate is
14
- * surfaced as a reject with a clear reason.
11
+ * (branch / trust / concurrency / reviewer / wait-timer). A `reject`
12
+ * result fails the whole workflow dispatch with a clear reason; a
13
+ * `hold` / `wait` / `queue` result returns a structured `hold` decision
14
+ * so the caller can pause the workflow dispatch as a workflow-scoped held
15
+ * run and resume it when the gate clears. On the resume path the caller
16
+ * sets `skipProtectionGate` so the gate (already satisfied) is bypassed
17
+ * and secrets are resolved directly.
15
18
  * 3. Resolves secrets per environment via `secretResolver.resolveForJob`
16
19
  * (which writes its own audit log lines).
17
20
  * 4. Validates each registry URL scheme: HTTPS always allowed; `http://`
@@ -26,7 +29,7 @@
26
29
  * Pure helper: no imports of the dispatch giant. Tested in isolation in
27
30
  * `install-secrets-resolver.test.ts`.
28
31
  */
29
- import type { LockRegistry } from '@kici-dev/engine';
32
+ import type { ApproverClause, LockRegistry } from '@kici-dev/engine';
30
33
  import type { TrustResolution } from '../security/trust-resolver.js';
31
34
  import type { SecretResolver } from '../secrets/secret-resolver.js';
32
35
  import type { EnvironmentStore } from '../environments/environment-store.js';
@@ -47,6 +50,18 @@ export interface ResolveInstallSecretsArgs {
47
50
  environmentStore: EnvironmentStore | undefined;
48
51
  secretResolver: SecretResolver | undefined;
49
52
  protectionContext: JobDispatchContext;
53
+ /**
54
+ * Resume path: skip the protection-rule gate (already satisfied) and resolve
55
+ * secrets directly. The untrusted-contributor strip still runs first.
56
+ */
57
+ skipProtectionGate?: boolean;
58
+ }
59
+ /** Normalized requirement carried on a `hold` decision. */
60
+ export interface InstallHoldRequirement {
61
+ clauses: ApproverClause[];
62
+ /** ISO timestamp after which the hold expires. */
63
+ expiresAt: string;
64
+ reason: string;
50
65
  }
51
66
  export type ResolveInstallSecretsResult = {
52
67
  decision: 'pass';
@@ -56,6 +71,18 @@ export type ResolveInstallSecretsResult = {
56
71
  } | {
57
72
  decision: 'reject';
58
73
  reason: string;
74
+ } | {
75
+ decision: 'hold';
76
+ /** The gate action that paused the dispatch. */
77
+ action: 'hold' | 'wait' | 'queue';
78
+ /** The environment whose install gate held. */
79
+ envName: string;
80
+ /** Resolved environment id (for the held row). */
81
+ environmentId: string;
82
+ /** Discriminates the release trigger: 'reviewer' | 'wait_timer' | 'concurrency' | 'security'. */
83
+ holdType: string;
84
+ queueType: 'environment' | 'security';
85
+ requirement: InstallHoldRequirement;
59
86
  };
60
87
  /** Parse `<environment>:<secret-name>`. Returns null on malformed input. */
61
88
  export declare function parseQualifiedSecretRef(ref: string): {
@@ -19,25 +19,28 @@
19
19
  */
20
20
  import type { Kysely } from 'kysely';
21
21
  import type { Database } from '../db/types.js';
22
- import type { NeedsEntry, NeedsGroupEntry } from '@kici-dev/engine';
22
+ import type { MaterializedJob } from '@kici-dev/engine';
23
23
  /** Result of evaluating downstream jobs after an upstream completes. */
24
24
  export interface SchedulerResult {
25
25
  jobName: string;
26
26
  action: 'dispatch' | 'skip';
27
27
  reason?: string;
28
28
  }
29
- /** Minimal lock job shape needed by insertEdgesForRun. */
30
- interface LockJobLike {
31
- readonly name: string;
32
- readonly needs: readonly (string | NeedsEntry | NeedsGroupEntry)[];
33
- readonly dependsOnGroups?: readonly string[];
34
- }
35
29
  /**
36
30
  * Insert dependency edges at run start for static-to-static needs.
31
+ *
32
+ * Operates on materialized jobs (matrix-expanded children) and an expansion map
33
+ * (base job name -> the expanded child names). An upstream reference to a base
34
+ * name `test` that fanned into `test (a)` / `test (b)` produces one edge per
35
+ * child, so a downstream `needs: ['test']` waits for ALL children to terminate.
36
+ * Each materialized child likewise inherits the full upstream edge set under its
37
+ * own expanded name.
38
+ *
37
39
  * NeedsGroupEntry items are skipped (resolved later via resolveGroupEdges).
38
- * Root jobs (no needs, no dependsOnGroups) are marked needs_satisfied=true.
40
+ * Root jobs (no needs, no dependsOnGroups) are marked needs_satisfied=true,
41
+ * keyed by expanded name.
39
42
  */
40
- export declare function insertEdgesForRun(db: Kysely<Database>, runId: string, lockJobs: readonly LockJobLike[]): Promise<void>;
43
+ export declare function insertEdgesForRun(db: Kysely<Database>, runId: string, jobs: readonly MaterializedJob[], expansionMap: ReadonlyMap<string, readonly string[]>): Promise<void>;
41
44
  /**
42
45
  * Resolve dynamic group edges after eval completion.
43
46
  *
@@ -81,5 +84,4 @@ export declare function checkSchedulerInvariant(db: Kysely<Database>, runId: str
81
84
  * due to failure propagation. Only follows edges where if_failed='skip'.
82
85
  */
83
86
  export declare function getFailurePropagationTargets(db: Kysely<Database>, runId: string, failedJobName: string): Promise<string[]>;
84
- export {};
85
87
  //# sourceMappingURL=needs-scheduler.d.ts.map
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Pending workflow dispatch context store — the workflow-level analogue of the
3
+ * `pending_job_contexts` store. Persists the serializable inputs of a
4
+ * `WorkflowDispatchContext` so a workflow whose install gate held can be
5
+ * resumed (reviewer approve, wait-timer expiry, concurrency slot free) by
6
+ * rebuilding the dispatch context and re-running `dispatchMatchedWorkflow`.
7
+ *
8
+ * Only the serializable inputs are stored — the live `deps` (ProcessingDeps)
9
+ * and `bundle` (ProviderBundle) are NOT persisted; they are rebuilt from the
10
+ * live orchestrator on resume.
11
+ *
12
+ * Writes to both an in-memory Map (fast read on the same process) and the DB
13
+ * (crash recovery + cross-orchestrator read), mirroring the pending-job store.
14
+ */
15
+ import type { Kysely } from 'kysely';
16
+ import type { Database } from '../db/types.js';
17
+ import type { WorkflowDispatchContext } from './dispatch-matched-workflow.js';
18
+ /**
19
+ * The serializable subset of a `WorkflowDispatchContext` — everything except
20
+ * the live `deps` and `bundle`, which are rebuilt on resume. Every field here
21
+ * is JSON-safe (the event, payload, and lock file already ride the WS protocol
22
+ * as JSON).
23
+ */
24
+ export type SerializableWorkflowDispatchInputs = Omit<WorkflowDispatchContext, 'deps' | 'bundle'>;
25
+ /** Extract the serializable inputs from a live dispatch context. */
26
+ export declare function toSerializableInputs(ctx: WorkflowDispatchContext): SerializableWorkflowDispatchInputs;
27
+ /** Persist the pending workflow context to the in-memory Map and the DB. */
28
+ export declare function storePendingWorkflowContext(db: Kysely<Database> | undefined, inputs: SerializableWorkflowDispatchInputs): Promise<void>;
29
+ /** Load the pending workflow context by run id (memory first, then DB). */
30
+ export declare function loadPendingWorkflowContext(db: Kysely<Database> | undefined, runId: string): Promise<SerializableWorkflowDispatchInputs | null>;
31
+ /** Delete the pending workflow context from the in-memory Map and the DB. */
32
+ export declare function deletePendingWorkflowContext(db: Kysely<Database> | undefined, runId: string): Promise<void>;
33
+ /**
34
+ * Restore the in-memory Map from the DB on startup, skipping rows whose run has
35
+ * already reached a terminal state. Mirrors `restorePendingJobContexts`.
36
+ * Returns the number of restored contexts.
37
+ */
38
+ export declare function restorePendingWorkflowContexts(db: Kysely<Database>): Promise<number>;
39
+ /**
40
+ * Clear all entries from the in-memory pending workflow contexts Map.
41
+ * @internal Exported for testing only.
42
+ */
43
+ export declare function clearPendingWorkflowContextsMap(): void;
44
+ //# sourceMappingURL=pending-workflow-context.d.ts.map
@@ -30,9 +30,11 @@ import type { CheckRunReporter } from '../reporting/check-run-reporter.js';
30
30
  import type { ExecutionTracker } from '../reporting/execution-tracker.js';
31
31
  import type { AgentRegistry } from '../agent/registry.js';
32
32
  import type { RunCoordinator } from '../cluster/coordinator.js';
33
+ import type { TeamMembershipLookup } from '../approvals/team-membership-lookup.js';
33
34
  import type { LogStorage } from '../reporting/log-storage.js';
34
35
  import type { SecretResolver } from '../secrets/secret-resolver.js';
35
36
  import type { ContributorCache } from '../security/contributor-cache.js';
37
+ import type { AccessLogWriter } from '../audit/access-log.js';
36
38
  import type { LockFile as FullLockFile, LockWorkflow, SimulatedEvent, WebhookNormalizer } from '@kici-dev/engine';
37
39
  import { LockFileParseError } from '@kici-dev/engine';
38
40
  import type { EventRouter } from '../events/event-router.js';
@@ -110,7 +112,7 @@ export declare function isRootJob(lockJob: LockJob): boolean;
110
112
  * Why this exists
111
113
  * ----------------
112
114
  * The webhook pipeline binds `lockFileFetcher` to the inbound webhook's
113
- * provider bundle. When an internal-sourced webhook (e.g., the staging
115
+ * provider bundle. When a local-sourced webhook (e.g., the staging
114
116
  * stg-ha-smoke failover-dispatch test) arrives for a repo whose lock file
115
117
  * is only accessible via a different provider (e.g., github), the inbound
116
118
  * fetcher returns null and trigger matching silently drops the webhook.
@@ -141,7 +143,7 @@ export declare function isRootJob(lockJob: LockJob): boolean;
141
143
  * -----------
142
144
  * Each fallback fetcher is invoked with the REGISTRATION'S
143
145
  * `providerContext`, NOT the inbound normalizer's credentials. This is
144
- * load-bearing: the InternalWebhookNormalizer returns `{}` as
146
+ * load-bearing: the LocalWebhookNormalizer returns `{}` as
145
147
  * credentials, which would never satisfy a github fetcher that requires
146
148
  * `installationId`. The registration carries the correct credentials
147
149
  * because it was created via the owning provider's source.
@@ -176,8 +178,10 @@ export declare function resolveLockFileWithFallback(args: {
176
178
  * Resolve the customer/org ID for a routing key.
177
179
  *
178
180
  * Checks the `sources` table first (GitHub App sources), then
179
- * `generic_webhook_sources` (generic webhook sources). Falls back to
180
- * '__default__' if neither table has the routing key.
181
+ * `generic_webhook_sources` (generic webhook sources), then `remote_sources`
182
+ * (the auto-provisioned anchor for Platform-relayed `kici run remote`, routing
183
+ * key `remote:<orgId>`). Falls back to '__default__' if none of the three
184
+ * tables has the routing key.
181
185
  */
182
186
  export declare function resolveOrgId(db: Kysely<Database>, routingKey: string): Promise<string>;
183
187
  /**
@@ -213,6 +217,17 @@ export declare function extractCommitMessage(event: string, payload: unknown): s
213
217
  * Build a human-readable summary for a security hold check.
214
218
  */
215
219
  export declare function buildSecurityHoldSummary(reason: string, tier: string, contributorUsername?: string): string;
220
+ /**
221
+ * Build the pending check-run description for a job/workflow approval hold,
222
+ * naming the clauses an approver must satisfy. `{team:X}` renders as
223
+ * "team X", `{user:Y}` as "user Y". An empty clause list (any eligible
224
+ * reviewer satisfies the hold) falls back to a generic message.
225
+ */
226
+ export declare function summarizeApprovalClauses(clauses: ReadonlyArray<{
227
+ team: string;
228
+ } | {
229
+ user: string;
230
+ }>): string;
216
231
  /**
217
232
  * Dependencies for the processing pipeline.
218
233
  * All injected for testability. Fully provider-agnostic.
@@ -283,6 +298,12 @@ export interface ProcessingDeps {
283
298
  identityLinks?: IdentityLink[];
284
299
  /** ci_trust permission levels per user ID from Platform push. Optional -- defaults to empty. */
285
300
  orgMemberPermissions?: Map<string, PermissionLevel>;
301
+ /**
302
+ * Team-membership lookup pushed from the Platform (team name → member set).
303
+ * Consumed by the approval resolver to satisfy `{team}` approval clauses.
304
+ * Optional -- defaults to "no teams".
305
+ */
306
+ teamMembershipLookup?: TeamMembershipLookup;
286
307
  /** Global workflow policy for org-level permission enforcement. Optional -- if not set, global workflows are unrestricted. */
287
308
  globalWorkflowPolicy?: GlobalWorkflowPolicy;
288
309
  /** Inbound webhook delivery log writer. Optional -- if not set, deliveries are not persisted to event_log. */
@@ -296,6 +317,9 @@ export interface ProcessingDeps {
296
317
  * created in server.ts and threaded through both the Platform-relay WS
297
318
  * path and the generic webhook HTTP path. */
298
319
  contributorCache?: ContributorCache;
320
+ /** Access-log writer for the orchestrator audit stream. Optional -- if not
321
+ * set, hold-creation audit rows (`held_run.request`) are skipped. */
322
+ accessLogWriter?: AccessLogWriter;
299
323
  }
300
324
  /**
301
325
  * Check if any trigger in the lock file workflows uses path filters.
@@ -0,0 +1,21 @@
1
+ import type { Kysely } from 'kysely';
2
+ import type { Database, RemoteSourceRow } from '../db/types.js';
3
+ /**
4
+ * Deterministic routing key for an org's Platform-relayed remote runs. A
5
+ * Platform-relayed `test.trigger` carries this key; `resolveOrgId` maps it back
6
+ * to the canonical org id via the `remote_sources` row.
7
+ */
8
+ export declare function remoteRoutingKeyFor(orgId: string): string;
9
+ /**
10
+ * Idempotently upsert the `remote_sources` anchor for an org. Called on every
11
+ * Platform (re)connect once the orchestrator learns its canonical org id from
12
+ * `auth.success`. The unique `(customer_id)` constraint makes this a safe
13
+ * self-heal — re-running updates `cluster_id` if the cluster identity changed.
14
+ */
15
+ export declare function provisionRemoteSource(db: Kysely<Database>, params: {
16
+ orgId: string;
17
+ clusterId: string | null;
18
+ }): Promise<void>;
19
+ /** Read the auto-provisioned remote-source row for an org, if it exists. */
20
+ export declare function getRemoteSource(db: Kysely<Database>, orgId: string): Promise<RemoteSourceRow | undefined>;
21
+ //# sourceMappingURL=remote-source-store.d.ts.map
@@ -0,0 +1,26 @@
1
+ import type { Kysely } from 'kysely';
2
+ import type { Database } from '../db/types.js';
3
+ import type { ProcessingDeps } from './processor.js';
4
+ import type { ReleaseSignal } from '../environments/held-runs.js';
5
+ import { type WorkflowDispatchContext } from './dispatch-matched-workflow.js';
6
+ import { type SerializableWorkflowDispatchInputs } from './pending-workflow-context.js';
7
+ /**
8
+ * Rebuild a live `WorkflowDispatchContext` from the persisted serializable
9
+ * inputs by re-attaching the orchestrator's live `deps` and reconstructing the
10
+ * provider `bundle` from the live registry (keyed by the stored routing key).
11
+ * Returns null when the provider bundle can no longer be resolved.
12
+ */
13
+ export declare function rebuildWorkflowDispatchContext(inputs: SerializableWorkflowDispatchInputs, deps: ProcessingDeps): WorkflowDispatchContext | null;
14
+ /**
15
+ * Resume a released workflow install-gate hold. Loads the pending context,
16
+ * rebuilds the dispatch context, and re-dispatches with the gate skipped. On a
17
+ * lost pending context (or unresolvable provider bundle) the run is failed
18
+ * loudly rather than silently dropped.
19
+ */
20
+ export declare function resumeWorkflow(signal: ReleaseSignal, deps: ProcessingDeps, db: Kysely<Database> | undefined): Promise<void>;
21
+ /**
22
+ * Cancel a rejected workflow install-gate hold: mark the run cancelled and drop
23
+ * the pending context.
24
+ */
25
+ export declare function rejectWorkflow(runId: string, deps: ProcessingDeps, db: Kysely<Database> | undefined, reason: string): Promise<void>;
26
+ //# sourceMappingURL=resume-workflow.d.ts.map
@@ -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
  }
@@ -395,6 +395,39 @@ export declare class ExecutionTracker {
395
395
  triggerEvent?: string;
396
396
  commitMessage?: string;
397
397
  }): Promise<void>;
398
+ /**
399
+ * Record a run paused at the workflow install gate (a `registries:` /
400
+ * `installEnv:` protection rule returned hold / wait / queue). Writes an
401
+ * `execution_runs` row in the `held` state — alive and resumable — so the
402
+ * dashboard run list surfaces the paused workflow. No jobs are tracked: the
403
+ * workflow-scoped held_runs row + pending workflow context (written by the
404
+ * caller) keep the run from being counted complete. Idempotent on runId.
405
+ */
406
+ recordRunHeld(args: {
407
+ runId: string;
408
+ workflowName: string;
409
+ provider: string;
410
+ repoIdentifier: string;
411
+ ref: string;
412
+ sha: string;
413
+ deliveryId: string | null;
414
+ providerContext: Record<string, unknown>;
415
+ routingKey: string;
416
+ environmentName?: string;
417
+ reason: string;
418
+ triggerEvent?: string;
419
+ commitMessage?: string;
420
+ }): Promise<void>;
421
+ /**
422
+ * Flip a `held` run back to `pending` so the resumed dispatch can proceed
423
+ * into job dispatch. Returns true when a held row was found and updated.
424
+ */
425
+ resumeHeldRun(runId: string): Promise<boolean>;
426
+ /**
427
+ * Cancel a held run (reviewer rejected the install gate). Flips the held row
428
+ * to `cancelled` and fires the status-change forward so Platform projects it.
429
+ */
430
+ cancelHeldRun(runId: string, reason: string): Promise<void>;
398
431
  /**
399
432
  * Mark a run as failed immediately with a reason message.
400
433
  *
@@ -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. */
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Fleet log-collection admin routes.
3
+ *
4
+ * GET /admin/fleet-topology — enumerate the cluster (no fan-out) for
5
+ * `debug-bundle --fleet --list` / `--pick`.
6
+ * POST /admin/fleet-bundle — drive the recursive fan-out and stream the
7
+ * assembled nested ZIP back as an octet-stream.
8
+ *
9
+ * Both are protected by the same Bearer admin-token auth as the other admin
10
+ * routes (token-manager validation).
11
+ */
12
+ import { Hono } from 'hono';
13
+ import type { TokenManager } from '../secrets/token-manager.js';
14
+ import type { FleetRoutesDeps } from '../app.js';
15
+ export interface FleetAdminRouteDeps {
16
+ fleet: FleetRoutesDeps;
17
+ tokenManager: TokenManager;
18
+ }
19
+ export declare function createFleetRoutes(deps: FleetAdminRouteDeps): Hono;
20
+ //# sourceMappingURL=fleet.d.ts.map