@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
@@ -13,7 +13,7 @@
13
13
  * data file is byte-identical to what the agent receives over HTTP. TTL is
14
14
  * enforced lazily on access — same model as the S3 backend.
15
15
  */
16
- import type { CacheStorage } from './types.js';
16
+ import type { CacheMetadata, CacheStorage } from './types.js';
17
17
  export interface FilesystemCacheStorageOptions {
18
18
  /** Absolute path to the cache root directory. Created on first write if missing. */
19
19
  basePath: string;
@@ -55,6 +55,7 @@ export declare class FilesystemCacheStorage implements CacheStorage {
55
55
  getInternalUploadUrl(key: string): Promise<string>;
56
56
  initMeta(key: string): Promise<void>;
57
57
  list(subPrefix: string): Promise<string[]>;
58
+ getMetadata(key: string): Promise<CacheMetadata | null>;
58
59
  copy(srcKey: string, destKey: string): Promise<void>;
59
60
  /**
60
61
  * Recursively collect data files under `dir`, returning each as a cache key
@@ -11,7 +11,7 @@
11
11
  * TTL is enforced lazily: expired items are deleted on access.
12
12
  * Pre-signed URLs are generated for getUrl() with 15-minute expiry.
13
13
  */
14
- import type { CacheStorage } from './types.js';
14
+ import type { CacheMetadata, CacheStorage } from './types.js';
15
15
  export interface S3CacheStorageOptions {
16
16
  bucket: string;
17
17
  prefix: string;
@@ -74,6 +74,7 @@ export declare class S3CacheStorage implements CacheStorage {
74
74
  getInternalUploadUrl(key: string): Promise<string>;
75
75
  initMeta(key: string): Promise<void>;
76
76
  list(subPrefix: string): Promise<string[]>;
77
+ getMetadata(key: string): Promise<CacheMetadata | null>;
77
78
  copy(srcKey: string, destKey: string): Promise<void>;
78
79
  /** Delete an S3 object. Idempotent (doesn't error if missing). */
79
80
  private deleteObject;
@@ -79,6 +79,13 @@ export interface CacheStorage {
79
79
  list(subPrefix: string): Promise<string[]>;
80
80
  /** Server-side copy from one cache key to another (used for atomic temp -> final commit). */
81
81
  copy(srcKey: string, destKey: string): Promise<void>;
82
+ /**
83
+ * Read the stored metadata (createdAt + lastAccessedAt) for a key WITHOUT
84
+ * refreshing lastAccessedAt. Returns null when the key is missing or carries
85
+ * no metadata. A pure read (no TTL-expiry side effect) — used by quota
86
+ * eviction to order candidates by access recency.
87
+ */
88
+ getMetadata(key: string): Promise<CacheMetadata | null>;
82
89
  }
83
90
  import type { SharedS3Config } from '@kici-dev/shared';
84
91
  /**
@@ -51,6 +51,10 @@ export interface GenericSourcesChangeListenerOptions {
51
51
  /** Required for universal-git source rows; null is acceptable on
52
52
  * peers that cannot serve them. */
53
53
  secretResolver: SecretResolver | null;
54
+ /** Active scaler backend on this peer. Threaded into the warm-path
55
+ * registration so a local source added at runtime on a container /
56
+ * firecracker scaler emits the same reachability warning as a cold-boot one. */
57
+ scalerBackendType?: string;
54
58
  /** Coalesce window for rapid NOTIFYs against the same row. Default 200 ms. */
55
59
  debounceMs?: number;
56
60
  /**
@@ -9,6 +9,7 @@ import { type Kysely } from 'kysely';
9
9
  import type { Database, GenericWebhookSource } from '../db/types.js';
10
10
  import type { VerificationMethod } from '../providers/generic/verification.js';
11
11
  import { type UniversalGitConfig } from '../providers/universal-git/config.js';
12
+ import { type LocalSourceConfig } from '../providers/local/local-source-config.js';
12
13
  /**
13
14
  * Input for creating a new generic webhook source.
14
15
  */
@@ -26,22 +27,31 @@ interface CreateSourceInput {
26
27
  allowedEvents?: string[];
27
28
  stripHeaders?: string[];
28
29
  rateLimitRpm?: number;
29
- /** Provider implementation: 'generic' (default) or 'internal' (E2E/staging
30
- * github-shaped payload routing through InternalWebhookNormalizer). */
31
- providerType?: 'generic' | 'internal';
30
+ /** Provider implementation: 'generic' (default) or 'local' (a git repo on
31
+ * the agent filesystem cloned via file://, routed through
32
+ * LocalWebhookNormalizer). */
33
+ providerType?: 'generic' | 'local';
32
34
  /** Universal-git configuration — when set, promotes this source from a
33
35
  * payload-only generic webhook to a full git-aware source that can register
34
36
  * workflows, fetch lock files, and be cloned by agents. Validated against
35
37
  * `UniversalGitConfigSchema`; stored as JSONB in `generic_webhook_sources.git_config`. */
36
38
  gitConfig?: UniversalGitConfig;
39
+ /** Local filesystem source config (`{ repoBasePath, cloneUrlBase? }`).
40
+ * Mutually exclusive with `gitConfig`. Stored as JSONB in the same
41
+ * `git_config` column, discriminated from universal-git by `providerType='local'`.
42
+ * Validated against `LocalSourceConfigSchema`. */
43
+ localConfig?: LocalSourceConfig;
37
44
  }
38
45
  /**
39
46
  * Input for updating an existing generic webhook source. Use `gitConfig: null`
40
47
  * to explicitly demote a universal-git source back to a plain generic webhook
41
48
  * (omitting the field leaves the existing config untouched).
42
49
  */
43
- type UpdateSourceInput = Partial<Omit<CreateSourceInput, 'orgId' | 'gitConfig'>> & {
50
+ type UpdateSourceInput = Partial<Omit<CreateSourceInput, 'orgId' | 'gitConfig' | 'localConfig'>> & {
44
51
  gitConfig?: UniversalGitConfig | null;
52
+ /** `null` clears the local config; a validated object re-serializes it to
53
+ * `git_config`. Omit to leave the existing config untouched. */
54
+ localConfig?: LocalSourceConfig | null;
45
55
  };
46
56
  /**
47
57
  * Generic webhook source manager.
@@ -77,19 +87,32 @@ export declare class GenericSourceManager {
77
87
  * List all enabled, non-deleted sources matching a provider type.
78
88
  *
79
89
  * Used at orchestrator startup to register provider bundles per routing key
80
- * (e.g. registering an InternalWebhookNormalizer bundle for every
81
- * provider_type='internal' source so the staging stg-ha smoke test and
82
- * other internal-bundle E2E harnesses can dispatch real runs through the
83
- * generic endpoint without a real GitHub signature).
90
+ * (e.g. registering a LocalWebhookNormalizer bundle for every
91
+ * provider_type='local' source so local filesystem repos can dispatch real
92
+ * runs through the generic endpoint without a real GitHub signature).
84
93
  */
85
- listByProviderType(providerType: 'generic' | 'internal'): Promise<GenericWebhookSource[]>;
94
+ listByProviderType(providerType: 'generic' | 'local'): Promise<GenericWebhookSource[]>;
86
95
  /**
87
96
  * List all enabled, non-deleted sources with a non-null `git_config`
88
97
  * (universal-git sources). Used at orchestrator startup + config reload
89
98
  * to register a universal-git `ProviderBundle` under each source's
90
99
  * routing key.
100
+ *
101
+ * Local sources ALSO carry `git_config` (they store `{ repoBasePath }` there),
102
+ * so this query MUST exclude `provider_type='local'` — otherwise a local
103
+ * source would be mis-registered as a universal-git bundle. The matching
104
+ * discrimination lives in `genericProviderTypeToSubtype` (local branch first)
105
+ * and `registerProviderBundleForSource` (local branch before the universal-git
106
+ * branch).
91
107
  */
92
108
  listUniversalGitSources(): Promise<GenericWebhookSource[]>;
109
+ /**
110
+ * List all enabled, non-deleted local filesystem sources
111
+ * (`provider_type='local'`). Used at orchestrator startup + config reload to
112
+ * register a `LocalWebhookNormalizer` bundle (built from each row's
113
+ * `git_config.repoBasePath`) under the source's routing key.
114
+ */
115
+ listLocalSources(): Promise<GenericWebhookSource[]>;
93
116
  /**
94
117
  * Update a source's configuration.
95
118
  */
@@ -151,8 +174,8 @@ export declare class GenericSourceManager {
151
174
  *
152
175
  * Returns `provider_type` alongside the routing key so callers can decide
153
176
  * whether the local orchestrator can actually serve each row. A peer that
154
- * cannot serve a provider_type (e.g. an `internal` source on a peer with no
155
- * `KICI_INTERNAL_PROVIDER_REPO_PATH`) MUST NOT advertise that routing key
177
+ * cannot serve a provider_type (e.g. a `local` source whose `repoBasePath`
178
+ * does not exist on that peer) MUST NOT advertise that routing key
156
179
  * to Platform — otherwise Platform's least-loaded relay may pick that peer
157
180
  * and the webhook is silently dropped on a pipeline that has no working
158
181
  * lock-file fetcher.
@@ -164,6 +187,9 @@ export declare function loadActiveGenericRoutingKeys(db: Kysely<Database>): Prom
164
187
  name: string;
165
188
  /** Whether the row has a non-null `git_config` (universal-git source). */
166
189
  has_git_config: boolean;
190
+ /** Raw `git_config` JSONB. Carried so the caller can check a local source's
191
+ * per-row `repoBasePath` reachability via `canServeGenericProviderType`. */
192
+ git_config: string | Record<string, unknown> | null;
167
193
  }>>;
168
194
  export {};
169
195
  //# sourceMappingURL=generic-sources.d.ts.map
@@ -24,7 +24,7 @@
24
24
  * the orchestrator's already-instantiated registry / config /
25
25
  * secret-resolver and we do the right thing.
26
26
  *
27
- * Failure semantics match the startup behaviour: internal-only-but-
27
+ * Failure semantics match the startup behaviour: local-only-but-
28
28
  * unservable peers log + skip (no throw); universal-git config errors
29
29
  * bump the per-row metric + log but never propagate. The caller can
30
30
  * assume "returns normally" means "the right thing happened or we
@@ -40,13 +40,23 @@ export interface RegisterSourceBundleDeps {
40
40
  /** Required for universal-git source rows. Null is allowed; rows that
41
41
  * carry `git_config` will be skipped with a metric bump. */
42
42
  secretResolver: SecretResolver | null;
43
+ /** Active scaler backend on this peer. When 'container' or 'firecracker', a
44
+ * local source registration emits a one-time reachability warning (the repo
45
+ * must exist inside the agent filesystem, not just on the orchestrator host). */
46
+ scalerBackendType?: string;
47
+ /** Sink for the scaler-coexistence warning. Defaults to the module logger;
48
+ * overridable in tests so the warning can be asserted without logger spying. */
49
+ onScalerWarning?: (message: string, ctx: Record<string, unknown>) => void;
43
50
  }
44
51
  /**
45
52
  * Register the per-routing-key provider bundle a source row needs.
46
53
  *
47
- * - `provider_type='internal'`: registers an `InternalWebhookNormalizer`
48
- * bundle when the peer can serve it (gated on
49
- * `internalProviderRepoPath` per `canServeGenericProviderType`).
54
+ * - `provider_type='local'`: registers a `LocalWebhookNormalizer` bundle
55
+ * built from the ROW's own `git_config.repoBasePath`, when that path
56
+ * exists as a directory on THIS peer. A peer that doesn't host the repo
57
+ * skips registration (and won't advertise the routing key — see
58
+ * `canServeGenericProviderType`). Checked BEFORE the universal-git branch
59
+ * because local sources also carry `git_config`.
50
60
  * - `git_config IS NOT NULL`: registers a universal-git bundle.
51
61
  * - everything else: no-op — the default `'generic'` bundle handles it.
52
62
  *
@@ -31,6 +31,7 @@ import type { PendingDynamicTracker } from '../cache/pending-dynamics.js';
31
31
  import type { CacheStorage } from '../storage/types.js';
32
32
  import type { AgentMetricsAggregator } from '../metrics/agent-metrics-aggregator.js';
33
33
  import type { AgentApiRegistry } from './agent-api-registry.js';
34
+ import type { FleetAgentCollector } from './fleet-agent-collector.js';
34
35
  /**
35
36
  * Run the three token-bound authorization gates against a wire-supplied
36
37
  * `agent.register` payload. The gates are identical at first register
@@ -181,6 +182,23 @@ export interface AgentWsHandlerDeps {
181
182
  dispatchCacheRefs?: DispatchCacheRefTracker;
182
183
  /** Cache storage for setting metadata after upload completion. */
183
184
  cacheStorage?: CacheStorage;
185
+ /**
186
+ * Storage used to mint presigned PUT URLs for provenance bundles. Distinct
187
+ * dep so a deployment can decide whether attestations share the cache bucket.
188
+ */
189
+ provenanceStorage?: CacheStorage;
190
+ /**
191
+ * Record a completed provenance-bundle upload (writes an attestations row).
192
+ * `runId` is resolved server-side from the job's dispatch ref, never the wire.
193
+ */
194
+ onProvenanceUpload?: (record: {
195
+ runId: string;
196
+ jobId: string;
197
+ subjectName: string;
198
+ subjectDigest: string;
199
+ storageKey: string;
200
+ mediaType: string;
201
+ }) => Promise<void>;
184
202
  /** Optional rate limiter configuration. */
185
203
  rateLimiterConfig?: RateLimiterConfig;
186
204
  /** Optional ownership tracker for validating job-related messages. */
@@ -208,6 +226,30 @@ export interface AgentWsHandlerDeps {
208
226
  action: 'proceed' | 'wait' | 'cancel';
209
227
  reason?: string;
210
228
  }>;
229
+ /**
230
+ * Optional callback when an agent blocks a `requireApproval` step. The server
231
+ * creates a step-scoped hold from the carried clauses and returns a promise
232
+ * that resolves when the hold is approved, rejected, or expired. The handler
233
+ * relays the resolution back to the originating agent as a
234
+ * `step.approval-resolved` message. The `agentId` lets the server drop the
235
+ * pending resolver when the agent disconnects.
236
+ */
237
+ onStepApproval?: (agentId: string, msg: {
238
+ runId: string;
239
+ jobId: string;
240
+ stepIndex: number;
241
+ stepName: string;
242
+ clauses: Array<{
243
+ team: string;
244
+ } | {
245
+ user: string;
246
+ }>;
247
+ reason: string;
248
+ timeoutSeconds?: number;
249
+ }) => Promise<{
250
+ outcome: 'approved' | 'rejected' | 'expired';
251
+ reason?: string;
252
+ }>;
211
253
  /**
212
254
  * Optional callback fired when an agent's WS connection closes, after the
213
255
  * dispatcher's own cleanup has run. Used by the long-poll concurrency
@@ -217,6 +259,13 @@ export interface AgentWsHandlerDeps {
217
259
  onConcurrencyAgentDisconnect?: (agentId: string) => void | Promise<void>;
218
260
  /** Agent private API registry for handling agent.api.request messages. */
219
261
  agentApiRegistry?: AgentApiRegistry;
262
+ /**
263
+ * Orchestrator-scoped collector that correlates fleet.logs.request with the
264
+ * agent's chunked fleet.bundle.chunk / fleet.bundle.error response. Routed to
265
+ * from the incoming-message switch; pending requests for a given agent are
266
+ * rejected when that agent's WS closes.
267
+ */
268
+ fleetAgentCollector?: FleetAgentCollector;
220
269
  }
221
270
  export declare function createAgentWsHandler(deps: AgentWsHandlerDeps): WSEvents;
222
271
  //# sourceMappingURL=agent-handler.d.ts.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Guarantees every forwarded dashboard request produces exactly one response
3
+ * frame keyed to its requestId. Wraps the per-type dispatch so a thrown handler
4
+ * or an unhandled message type returns a fast structured error instead of a
5
+ * silently dropped frame (which the Platform would surface as a 10s 504 at its
6
+ * forward window).
7
+ */
8
+ export interface DashboardDispatchGuardDeps {
9
+ /** Sends a raw frame back over the Platform WS. */
10
+ sendRaw: (msg: unknown) => void;
11
+ /** Runs the real per-type dispatch; resolves true if a handler owned the type. */
12
+ dispatch: (msg: {
13
+ type: string;
14
+ requestId: string;
15
+ }) => Promise<boolean>;
16
+ }
17
+ export declare function guardedDashboardDispatch(deps: DashboardDispatchGuardDeps, msg: {
18
+ type: string;
19
+ requestId: string;
20
+ }): Promise<void>;
21
+ //# sourceMappingURL=dashboard-dispatch-guard.d.ts.map
@@ -11,6 +11,8 @@
11
11
  * 3. Returns a response message with requestId and data (or error)
12
12
  */
13
13
  import { type Kysely } from 'kysely';
14
+ import type { HeldRunStore, ReleaseSignal } from '../environments/held-runs.js';
15
+ import type { TeamMembershipLookup } from '../approvals/approval-resolver.js';
14
16
  import type { DashboardPlatformToOrchMessage } from '@kici-dev/engine';
15
17
  import type { EnvironmentStore } from '../environments/environment-store.js';
16
18
  import type { VariableStore } from '../environments/variable-store.js';
@@ -44,6 +46,30 @@ export interface DashboardEnvHandlerDeps {
44
46
  accessLog?: AccessLogWriter;
45
47
  /** Routing key for access_log rows (null when not run-scoped). */
46
48
  routingKey?: string | null;
49
+ /**
50
+ * Held-run store + resume hook for the approve/reject flow. When present, the
51
+ * approve/reject handlers route through the shared `applyDecision` applier
52
+ * (eligibility check, real attribution, multi-clause accumulation, and the
53
+ * resume-after-approval re-dispatch). When absent (legacy / tests), the
54
+ * handlers fall back to the direct status flip.
55
+ */
56
+ approvals?: ApprovalHandlerDeps;
57
+ }
58
+ /** Dependencies enabling the resolver-backed approve/reject + resume path. */
59
+ export interface ApprovalHandlerDeps {
60
+ store: HeldRunStore;
61
+ /** Team name → member user ids (Plan-1 trust-policy cache). */
62
+ teamMembershipLookup: TeamMembershipLookup;
63
+ /** Re-dispatch a released job hold (consumes its pending context). */
64
+ resumeJob: (signal: ReleaseSignal) => Promise<void>;
65
+ /** Notify a waiting agent that a step hold released (step scope). */
66
+ resumeStep?: (signal: ReleaseSignal) => Promise<void>;
67
+ /** Notify a waiting agent that a step hold was rejected (step scope). */
68
+ rejectStep?: (heldRunId: string, reason?: string) => Promise<void> | void;
69
+ /** Resume a released workflow install-gate hold (workflow scope). */
70
+ resumeWorkflow?: (signal: ReleaseSignal) => Promise<void>;
71
+ /** Cancel a rejected workflow install-gate hold (workflow scope). */
72
+ rejectWorkflow?: (runId: string) => Promise<void>;
47
73
  }
48
74
  /**
49
75
  * Handler for all dashboard environment and held run WS messages.
@@ -78,6 +104,14 @@ export declare class DashboardEnvHandler {
78
104
  */
79
105
  handleMessage(msg: DashboardPlatformToOrchMessage): Promise<boolean>;
80
106
  private handleEnvList;
107
+ /**
108
+ * Map each environment id to the distinct secret key names reachable through
109
+ * its scope bindings. Joins `environment_bindings` → `scoped_secrets` on the
110
+ * scope pattern, excluding the `__empty__` sentinel. Returns key names only —
111
+ * never values. Scoped to the caller-resolved org (the request's target org,
112
+ * or the connection-level org on the legacy path).
113
+ */
114
+ private loadSecretKeysByEnv;
81
115
  private handleEnvGet;
82
116
  private handleEnvCreate;
83
117
  private handleEnvUpdate;
@@ -107,6 +141,19 @@ export declare class DashboardEnvHandler {
107
141
  private handleScopeDelete;
108
142
  private handleEnvHistory;
109
143
  private handleHeldRunsList;
144
+ /**
145
+ * Resolve the actor's Keycloak sub for approval attribution. Only `user` and
146
+ * `platform_operator` actors carry a `sub`; others fall back to a stable id.
147
+ */
148
+ private actorSub;
149
+ /** Read org_settings.allow_self_approval (default true). */
150
+ private readAllowSelfApproval;
151
+ /**
152
+ * Route an approve/reject through the shared `applyDecision` applier:
153
+ * eligibility check, real attribution, multi-clause accumulation, and the
154
+ * resume-after-approval re-dispatch. Sends the matching response message.
155
+ */
156
+ private applyApprovalDecision;
110
157
  private handleHeldRunApprove;
111
158
  private handleHeldRunReject;
112
159
  private sendError;
@@ -0,0 +1,23 @@
1
+ export interface FleetAgentCollectorOptions {
2
+ timeoutMs: number;
3
+ }
4
+ export declare class FleetAgentCollector {
5
+ private readonly opts;
6
+ private readonly waiter;
7
+ /** requestId -> owning agentId, so a disconnect rejects only that agent's requests. */
8
+ private readonly requestAgent;
9
+ constructor(opts: FleetAgentCollectorOptions);
10
+ /**
11
+ * Register the pending request `requestId` (owned by `agentId`), invoke
12
+ * `send` to dispatch the fleet.logs.request envelope, and await the
13
+ * reassembled bundle Buffer.
14
+ */
15
+ request(requestId: string, agentId: string, send: () => void): Promise<Buffer>;
16
+ onChunk(requestId: string, seq: number, dataB64: string, isLast: boolean): void;
17
+ onError(requestId: string, message: string): void;
18
+ /** Reject every pending request owned by `agentId` (called on its disconnect). */
19
+ rejectAgent(agentId: string, reason: string): void;
20
+ /** Reject every pending request regardless of owner (orchestrator shutdown). */
21
+ rejectAll(reason: string): void;
22
+ }
23
+ //# sourceMappingURL=fleet-agent-collector.d.ts.map
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Provenance ID-token relay (orchestrator side).
3
+ *
4
+ * Backs the `oidc.token.request` agent.api method: an agent asks for a
5
+ * short-lived OIDC ID token for a job it is running; the orchestrator verifies
6
+ * the agent owns that job, resolves its runId from its own dispatch state, and
7
+ * relays a mint request to the Platform's token-mint endpoint over HTTPS using
8
+ * the orchestrator's own `KICI_PLATFORM_TOKEN`. The agent never holds Platform
9
+ * credentials and never asserts its own identity claims — the Platform derives
10
+ * every identity claim from its own run/job rows.
11
+ */
12
+ import { type OidcTokenResult } from '@kici-dev/engine/protocol/messages/oidc-token-relay';
13
+ /** 404 / 409: the run/job is missing on the Platform or the job is terminal. */
14
+ export declare class MintRejectedError extends Error {
15
+ }
16
+ /** 503: provenance signing is not configured / the Platform is unavailable. */
17
+ export declare class MintUnavailableError extends Error {
18
+ }
19
+ /** Any other non-2xx from the mint endpoint. */
20
+ export declare class MintRelayError extends Error {
21
+ }
22
+ /**
23
+ * Turn the Platform WS URL (`wss://host[/base]/ws`) into its HTTP base
24
+ * (`https://host[/base]`). Maps the scheme and strips a trailing `/ws` while
25
+ * preserving the host and any basePath in front of it.
26
+ */
27
+ export declare function deriveHttpBaseFromWsUrl(wsUrl: string): string;
28
+ export interface RequestMintArgs {
29
+ httpBase: string;
30
+ token: string;
31
+ orchestratorId: string;
32
+ runId: string;
33
+ jobId: string;
34
+ audience: string;
35
+ }
36
+ /**
37
+ * Call the Platform token-mint endpoint over HTTPS using the orchestrator's
38
+ * Platform token. Maps non-2xx responses to typed errors so the agent always
39
+ * sees a clear message instead of a raw 5xx body.
40
+ */
41
+ export declare function requestMint(args: RequestMintArgs): Promise<OidcTokenResult>;
42
+ export interface OidcTokenHandlerDeps {
43
+ dispatcher: {
44
+ resolveOwnedJob(agentId: string, jobId: string): {
45
+ runId: string;
46
+ } | undefined;
47
+ };
48
+ platformToken: string;
49
+ platformHttpBase: string;
50
+ orchestratorId: string;
51
+ }
52
+ /**
53
+ * Build the agent.api handler for `OIDC_TOKEN_REQUEST_METHOD`. Validates the
54
+ * params, verifies the agent owns the named job (resolving its runId from the
55
+ * dispatcher), and relays a mint request to the Platform. A job the agent does
56
+ * not own is rejected without ever contacting the Platform.
57
+ */
58
+ export declare function createOidcTokenHandler(deps: OidcTokenHandlerDeps): (agentId: string, params: Record<string, unknown>) => Promise<OidcTokenResult>;
59
+ //# sourceMappingURL=oidc-token-relay.d.ts.map
@@ -1,4 +1,4 @@
1
- import { type OrchestratorToPlatformMessage, type WebhookRelay, type WebhookRelayResult, type TrustPolicyUpdate, type StaleCheckrunCleanup, type DashboardRunDetailRequest, type DashboardRunsListRequest, type DashboardRunsFiltersRequest, type DashboardSourcesListRequest, type DashboardStepLogsRequest, type DashboardOrchLogsRequest, type RunRerunRequest, type ManualScheduleRequest, type RunCancelRequest, type DashboardPayloadRequest, type DashboardPlatformToOrchMessage, type DashboardDiagnosticsRequest, type DashboardScalerCapacityRequest, type DashboardScalerAgentsRequest, type JoinRequest, type JoinResponse, type OrchCapabilities, type OrchRole } from '@kici-dev/engine';
1
+ import { type OrchestratorToPlatformMessage, type WebhookRelay, type WebhookRelayResult, type TrustPolicyUpdate, type StaleCheckrunCleanup, type DashboardRunDetailRequest, type DashboardRunsListRequest, type DashboardRunsFiltersRequest, type DashboardSourcesListRequest, type DashboardStepLogsRequest, type DashboardAttestationsListRequest, type DashboardOrchLogsRequest, type RunRerunRequest, type ManualScheduleRequest, type RunCancelRequest, type DashboardPayloadRequest, type DashboardPlatformToOrchMessage, type TestRelayRequest, type DashboardDiagnosticsRequest, type DashboardScalerCapacityRequest, type DashboardScalerAgentsRequest, type JoinRequest, type JoinResponse, type OrchCapabilities, type OrchRole } from '@kici-dev/engine';
2
2
  import { RelayBufferRegistry, type RelayStartMeta } from '../webhook/relay-buffer.js';
3
3
  /**
4
4
  * Verification + processing outcome returned by the chunked relay path's
@@ -87,6 +87,17 @@ export interface PlatformClientOptions {
87
87
  }) => void;
88
88
  /** Optional callback invoked after successful authentication and source registration. */
89
89
  onAuthenticated?: () => void;
90
+ /**
91
+ * Optional callback invoked when the Platform surfaces the orchestrator's
92
+ * canonical org id on `auth.success`. Used to auto-provision the
93
+ * `remote_sources` anchor (`remote:<orgId>`) so Platform-relayed
94
+ * `kici run remote` resolves the real tenant. Fires on every (re)connect;
95
+ * provisioning is idempotent.
96
+ */
97
+ onOrgIdentified?: (info: {
98
+ orgId: string;
99
+ clusterId: string | null;
100
+ }) => void;
90
101
  /** Optional callback for dashboard run detail requests from Platform. */
91
102
  onDashboardRunDetail?: (msg: DashboardRunDetailRequest) => void;
92
103
  /** Optional callback for dashboard runs.list (operator console) requests from Platform. */
@@ -97,6 +108,8 @@ export interface PlatformClientOptions {
97
108
  onDashboardSourcesList?: (msg: DashboardSourcesListRequest) => void;
98
109
  /** Optional callback for dashboard step logs requests from Platform. */
99
110
  onDashboardStepLogs?: (msg: DashboardStepLogsRequest) => void;
111
+ /** Optional callback for dashboard attestations-list requests from Platform. */
112
+ onDashboardAttestationsList?: (msg: DashboardAttestationsListRequest) => void;
100
113
  /** Optional callback for run re-run requests from Platform (dashboard action). */
101
114
  onRunRerun?: (msg: RunRerunRequest) => void;
102
115
  /** Optional callback for manual schedule trigger requests from Platform (dashboard action). */
@@ -109,6 +122,12 @@ export interface PlatformClientOptions {
109
122
  onDashboardOrchLogs?: (msg: DashboardOrchLogsRequest) => void;
110
123
  /** Optional callback for dashboard environment/held-run messages from Platform. */
111
124
  onDashboardEnvMessage?: (msg: DashboardPlatformToOrchMessage) => void;
125
+ /**
126
+ * Optional callback for Platform-first `kici run remote` control-plane relay
127
+ * requests (upload-init, trigger, status, logs, cancel). The handler performs
128
+ * the action and replies over the WS keyed by `requestId`.
129
+ */
130
+ onTestRelay?: (msg: TestRelayRequest) => void;
112
131
  /** Optional callback for dashboard diagnostics requests from Platform. */
113
132
  onDashboardDiagnostics?: (msg: DashboardDiagnosticsRequest) => void;
114
133
  /** Optional callback for dashboard scaler capacity requests from Platform. */
@@ -180,17 +199,20 @@ export declare class PlatformClient {
180
199
  private readonly onLogPullRequest?;
181
200
  private readonly onPeerDiscover?;
182
201
  private readonly onAuthenticated?;
202
+ private readonly onOrgIdentified?;
183
203
  private readonly onDashboardRunDetail?;
184
204
  private readonly onDashboardRunsList?;
185
205
  private readonly onDashboardRunsFilters?;
186
206
  private readonly onDashboardSourcesList?;
187
207
  private readonly onDashboardStepLogs?;
208
+ private readonly onDashboardAttestationsList?;
188
209
  private readonly onRunRerun?;
189
210
  private readonly onManualSchedule?;
190
211
  private readonly onRunCancel?;
191
212
  private readonly onDashboardPayload?;
192
213
  private readonly onDashboardOrchLogs?;
193
214
  private readonly onDashboardEnvMessage?;
215
+ private readonly onTestRelay?;
194
216
  private readonly onDashboardDiagnostics?;
195
217
  private readonly onDashboardScalerCapacity?;
196
218
  private readonly onDashboardScalerAgents?;
@@ -306,6 +328,13 @@ export declare class PlatformClient {
306
328
  * back to a structured warning if neither schema matches.
307
329
  */
308
330
  private handleNonStandardMessage;
331
+ /**
332
+ * If `raw` looks like a dashboard request (a `dashboard.*` type with a
333
+ * requestId) that failed schema validation, send a structured error response
334
+ * keyed to its requestId and return true. Returns false when the message is
335
+ * not a recognisable dashboard request (let the caller log a warning).
336
+ */
337
+ private respondToInvalidDashboardRequest;
309
338
  /**
310
339
  * Dispatch a parsed platform message to the appropriate per-area
311
340
  * handler. Each `case` either inlines a tiny dispatch (for one-line
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Orchestrator-side handlers for the Platform-relayed `kici run remote` control
3
+ * plane. The Platform relays five `test.relay.*` requests over the authenticated
4
+ * dashboard-proxy WS connection; each handler here performs the action by
5
+ * reusing the existing test pipeline / upload / cancel internals (no
6
+ * duplication) and returns the response payload the caller sends back keyed by
7
+ * `requestId`.
8
+ *
9
+ * The control plane carries only small JSON. The overlay tarball never reaches
10
+ * the Platform — `handleTestUploadsInit` returns an external presigned URL the
11
+ * developer PUTs to directly.
12
+ */
13
+ import type { Kysely } from 'kysely';
14
+ import type { TestRelayRequest, TestRelayUploadsInitRequest, TestRelayTriggerRequest, TestRelayRunStatusRequest, TestRelayRunLogsRequest, TestRelayCancelRequest } from '@kici-dev/engine';
15
+ import type { Database } from '../db/types.js';
16
+ import type { CacheStorage } from '../storage/types.js';
17
+ import type { LogStorage } from '../reporting/log-storage.js';
18
+ import type { AccessLogWriter } from '../audit/access-log.js';
19
+ import { type TestPipelineDeps } from '../pipeline/test-pipeline.js';
20
+ /** Dependencies shared by all five test-relay handlers. */
21
+ export interface TestRelayHandlerDeps extends TestPipelineDeps {
22
+ db: Kysely<Database>;
23
+ cacheStorage?: CacheStorage;
24
+ logStorage?: LogStorage;
25
+ accessLog?: AccessLogWriter;
26
+ /** Canonical org id this orchestrator is bound to (for access_log attribution). */
27
+ orgId?: string | null;
28
+ /** Routing key resolved for this orchestrator's bound org (for access_log attribution). */
29
+ routingKey?: string | null;
30
+ }
31
+ /** Response payload for `test.relay.uploads.init.response` (minus envelope fields). */
32
+ export interface TestUploadsInitPayload {
33
+ uploadId: string;
34
+ signedUrl: string;
35
+ publicKey: string;
36
+ expiresIn: number;
37
+ }
38
+ /**
39
+ * Mint an upload record + external presigned PUT URL. The developer's PAT
40
+ * identity (`msg.actor`) is recorded as the upload owner so the upload is
41
+ * attributable.
42
+ */
43
+ export declare function handleTestUploadsInit(msg: TestRelayUploadsInitRequest, deps: TestRelayHandlerDeps): Promise<TestUploadsInitPayload>;
44
+ /** Response payload for `test.relay.trigger.response` (minus envelope fields). */
45
+ export interface TestTriggerPayload {
46
+ runId: string;
47
+ status: 'accepted' | 'rejected';
48
+ reason?: string;
49
+ jobIds: string[];
50
+ }
51
+ /**
52
+ * Resolve the overlay tarball URL + decryption keys from the upload record, then
53
+ * run the existing `processTestTrigger` pipeline with `routingKey = remote:<orgId>`.
54
+ * The developer's PAT identity is written to `access_log` (`run.trigger`).
55
+ */
56
+ export declare function handleTestTrigger(msg: TestRelayTriggerRequest, deps: TestRelayHandlerDeps): Promise<TestTriggerPayload>;
57
+ /** Response payload for `test.relay.run.status.response` (minus envelope fields). */
58
+ export interface TestRunStatusPayload {
59
+ runId: string;
60
+ status: string;
61
+ jobs: Array<{
62
+ jobId: string;
63
+ jobName: string;
64
+ status: string;
65
+ errorMessage?: string | null;
66
+ }>;
67
+ done: boolean;
68
+ }
69
+ /** Snapshot a run's status + per-job status. `done` is true at a terminal run state. */
70
+ export declare function handleTestRunStatus(msg: TestRelayRunStatusRequest, deps: TestRelayHandlerDeps): Promise<TestRunStatusPayload | {
71
+ error: string;
72
+ }>;
73
+ /** Response payload for `test.relay.run.logs.response` (minus envelope fields). */
74
+ export interface TestRunLogsPayload {
75
+ lines: string[];
76
+ nextCursor: number;
77
+ done: boolean;
78
+ }
79
+ /**
80
+ * Return the next chunk of a run's logs from a monotonic line-offset cursor
81
+ * (spec §13a). The orchestrator concatenates every log line in a deterministic
82
+ * `(jobName ASC, stepIndex ASC, lineIndex ASC)` order; `cursor` is the count of
83
+ * lines already delivered. `done` is true only when the run is terminal AND the
84
+ * cursor has reached the end (the final tail-draining poll). A poll mid-step
85
+ * returns the lines available so far with `done: false`, so no line is ever
86
+ * dropped or skipped on a live run.
87
+ */
88
+ export declare function handleTestRunLogs(msg: TestRelayRunLogsRequest, deps: TestRelayHandlerDeps): Promise<TestRunLogsPayload | {
89
+ error: string;
90
+ }>;
91
+ /** Response payload for `test.relay.cancel.response` (minus envelope fields). */
92
+ export interface TestCancelPayload {
93
+ cancelled: boolean;
94
+ }
95
+ /**
96
+ * Cancel a running test run: signal cancellation to every dispatched agent,
97
+ * mark still-pending dispatch queue entries cancelled, and mark the
98
+ * `execution_runs` row cancelled. Returns `{ cancelled }` — true when the run
99
+ * existed, was a test run, and was not already terminal.
100
+ */
101
+ export declare function handleTestCancel(msg: TestRelayCancelRequest, deps: TestRelayHandlerDeps): Promise<TestCancelPayload | {
102
+ error: string;
103
+ }>;
104
+ /**
105
+ * Route a parsed `test.relay.*` request to its handler and build the full
106
+ * response envelope (`type` + `requestId` + payload) the caller relays back over
107
+ * the WS connection via `sendRaw`. Errors and `{ error }` results are surfaced
108
+ * on the response's `error` field; the wire stays a structured response so the
109
+ * Platform can map it to an HTTP status.
110
+ */
111
+ export declare function dispatchTestRelay(msg: TestRelayRequest, deps: TestRelayHandlerDeps): Promise<Record<string, unknown>>;
112
+ //# sourceMappingURL=test-relay-handlers.d.ts.map