@kici-dev/orchestrator 0.1.19 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/host-roster.d.ts +36 -1
- package/dist/agent/registry.d.ts +22 -0
- package/dist/app.d.ts +5 -0
- package/dist/cli/commands/source-manifest.d.ts +34 -0
- package/dist/cli/loopback-callback.d.ts +24 -0
- package/dist/cli/open-browser.d.ts +12 -0
- package/dist/cli.js +847 -205
- package/dist/cluster/coordinator.d.ts +5 -3
- package/dist/cluster/index.d.ts +2 -0
- package/dist/cluster/join-token.d.ts +27 -5
- package/dist/cluster/peer-auth-coordinator.d.ts +36 -0
- package/dist/cluster/peer-client.d.ts +34 -14
- package/dist/cluster/peer-credentials.d.ts +14 -2
- package/dist/cluster/peer-handler.d.ts +2 -2
- package/dist/cluster/rerouted-job-guard.d.ts +39 -0
- package/dist/db/migrations/043_rerouted_to_peer.d.ts +4 -0
- package/dist/db/migrations/044_check_mode.d.ts +4 -0
- package/dist/db/migrations/045_host_properties.d.ts +16 -0
- package/dist/db/migrations/046_join_token_consumed_by_instance.d.ts +17 -0
- package/dist/db/types.d.ts +33 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +1168 -33
- package/dist/lockfile-validate.d.ts +24 -0
- package/dist/pipeline/dispatch-matched-workflow.d.ts +53 -1
- package/dist/pipeline/test-pipeline.d.ts +7 -0
- package/dist/providers/github/manifest-form.d.ts +15 -0
- package/dist/providers/github/manifest.d.ts +78 -0
- package/dist/reporting/execution-tracker.d.ts +10 -1
- package/dist/routes/admin-sources.d.ts +11 -0
- package/dist/routes/admin.d.ts +8 -0
- package/dist/secrets/pg-secret-store.d.ts +9 -0
- package/dist/server.js +3052 -2004
- package/dist/stale-detector/stale-run-detector.d.ts +8 -0
- package/dist/standalone.js +2837 -1817
- package/dist/worker/in-memory-job-queue.d.ts +17 -0
- package/dist/worker/peer-outbox.d.ts +36 -0
- package/dist/worker/worker-outbox-relay.d.ts +13 -0
- package/dist/ws/inventory-api.d.ts +17 -0
- package/installer-image-digests.json +3 -3
- package/package.json +4 -4
- package/sbom.spdx.json +77 -128
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - "Peers report step-by-step progress back to coordinator"
|
|
11
11
|
* - "Cancel mode: graceful -- finish current step, cancel remaining"
|
|
12
12
|
*/
|
|
13
|
-
import type { JobReroute, JobProgress, PeerScalerEvent, PeerToPeerMessage, ResourceRequest, LabelMatcher } from '@kici-dev/engine';
|
|
13
|
+
import type { JobReroute, JobProgress, JobProgressAck, PeerScalerEvent, PeerToPeerMessage, ResourceRequest, LabelMatcher } from '@kici-dev/engine';
|
|
14
14
|
import type { PeerRegistry } from './peer-registry.js';
|
|
15
15
|
import type { PeerClient } from './peer-client.js';
|
|
16
16
|
import type { Dispatcher } from '../agent/dispatcher.js';
|
|
@@ -142,7 +142,7 @@ export declare class RunCoordinator {
|
|
|
142
142
|
* this split, every job-level event was silently funnelled into
|
|
143
143
|
* `onStepStatus` and the run stayed in `running` forever.
|
|
144
144
|
*/
|
|
145
|
-
onPeerJobProgress(msg: JobProgress): void;
|
|
145
|
+
onPeerJobProgress(msg: JobProgress, reply?: (m: JobProgressAck) => void): void;
|
|
146
146
|
/**
|
|
147
147
|
* Handle a scaler provisioning event forwarded by a worker peer.
|
|
148
148
|
*
|
|
@@ -211,7 +211,9 @@ export declare class RunCoordinator {
|
|
|
211
211
|
*/
|
|
212
212
|
private computeAvailableCapacity;
|
|
213
213
|
/**
|
|
214
|
-
* Track a rerouted job for cancel propagation
|
|
214
|
+
* Track a rerouted job for cancel propagation, and durably tag the projected
|
|
215
|
+
* `execution_jobs` row with the owning worker peer so run-recovery sweepers
|
|
216
|
+
* do not force-fail the job while its worker is connected.
|
|
215
217
|
*/
|
|
216
218
|
private trackReroutedJob;
|
|
217
219
|
}
|
package/dist/cluster/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export { PeerRegistry } from './peer-registry.js';
|
|
|
8
8
|
export type { PeerInfo, PeerAgentInfo, PeerRegistryOptions } from './peer-registry.js';
|
|
9
9
|
export { PeerClient } from './peer-client.js';
|
|
10
10
|
export type { PeerClientOptions } from './peer-client.js';
|
|
11
|
+
export { PeerAuthCoordinator } from './peer-auth-coordinator.js';
|
|
12
|
+
export type { AuthDecision, RejectionAction } from './peer-auth-coordinator.js';
|
|
11
13
|
export { createPeerHandler } from './peer-handler.js';
|
|
12
14
|
export type { PeerHandlerDeps } from './peer-handler.js';
|
|
13
15
|
export { PeerCredentialStore, readCredentialFile, writeCredentialFile, } from './peer-credentials.js';
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* Config bundle encryption: AES-256-GCM with random 12-byte IV.
|
|
13
13
|
* Wire format: <12-byte IV><16-byte auth tag><ciphertext>
|
|
14
14
|
*/
|
|
15
|
-
import
|
|
15
|
+
import { Kysely } from 'kysely';
|
|
16
16
|
/**
|
|
17
17
|
* Silence the module-level logger (e.g. when a CLI is emitting JSON on stdout
|
|
18
18
|
* and a stray log line would break the contract).
|
|
@@ -57,15 +57,37 @@ export declare class JoinTokenManager {
|
|
|
57
57
|
* `peer_credentials_active_uniq` partial unique index).
|
|
58
58
|
*
|
|
59
59
|
* On a 0-row claim, a follow-up SELECT disambiguates not-found / expired
|
|
60
|
-
* / already-used so callers can branch on the
|
|
61
|
-
* recovery path in peer-handler.ts keys on the
|
|
62
|
-
* specifically. The follow-up only fires on the
|
|
60
|
+
* / already-used / reusable-by-same-instance so callers can branch on the
|
|
61
|
+
* specific reason — the recovery path in peer-handler.ts keys on the
|
|
62
|
+
* "already used" string specifically. The follow-up only fires on the
|
|
63
|
+
* unhappy path.
|
|
64
|
+
*
|
|
65
|
+
* Self-healing reuse: a join token is re-consumable by the same joining
|
|
66
|
+
* peer (`peerInstanceId`) until its `expires_at`. A peer that lost its
|
|
67
|
+
* credential (transient outage / deleted credential file) re-presents the
|
|
68
|
+
* still-valid join token already in its env; the coordinator accepts the
|
|
69
|
+
* reuse and issues a fresh credential — no operator action, no cluster
|
|
70
|
+
* redeploy. Reuse is bounded by BOTH `expires_at` AND the consuming
|
|
71
|
+
* instanceId, so it never widens a leaked token's usefulness beyond the
|
|
72
|
+
* instance that first consumed it.
|
|
63
73
|
*/
|
|
64
|
-
validateAndConsumeToken(token: string, consumedBy: string): Promise<{
|
|
74
|
+
validateAndConsumeToken(token: string, consumedBy: string, peerInstanceId: string): Promise<{
|
|
65
75
|
routing: TokenRouting;
|
|
66
76
|
keys: DerivedKeys;
|
|
67
77
|
}>;
|
|
68
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Build a JoinTokenManager backed by its own connection pool to the given
|
|
81
|
+
* orchestrator database URL. Mirrors `createPeerCredentialStoreFromUrl`;
|
|
82
|
+
* consumed by E2E tests that need to exercise token validation/reuse against
|
|
83
|
+
* the real cluster DB.
|
|
84
|
+
*/
|
|
85
|
+
export declare function createJoinTokenManagerFromUrl(databaseUrl: string, opts?: {
|
|
86
|
+
maxConnections?: number;
|
|
87
|
+
}): {
|
|
88
|
+
manager: JoinTokenManager;
|
|
89
|
+
dispose: () => Promise<void>;
|
|
90
|
+
};
|
|
69
91
|
/**
|
|
70
92
|
* Narrow detector for the "already been used" error thrown by
|
|
71
93
|
* validateAndConsumeToken(). Used by peer-handler.ts to branch into the
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type CredentialFileData } from './peer-credentials.js';
|
|
2
|
+
export type AuthDecision = {
|
|
3
|
+
mode: 'credential';
|
|
4
|
+
credential: CredentialFileData;
|
|
5
|
+
} | {
|
|
6
|
+
mode: 'token-join';
|
|
7
|
+
token: string;
|
|
8
|
+
complete: (issued: CredentialFileData | null) => void;
|
|
9
|
+
} | {
|
|
10
|
+
mode: 'no-auth';
|
|
11
|
+
};
|
|
12
|
+
export type RejectionAction = 'retry-credential' | 'rejoin';
|
|
13
|
+
export declare class PeerAuthCoordinator {
|
|
14
|
+
private readonly credentialFile;
|
|
15
|
+
private readonly instanceId;
|
|
16
|
+
private readonly joinToken?;
|
|
17
|
+
private readonly joinWaitTimeoutMs;
|
|
18
|
+
/** Promise-chain mutex tail; every file op awaits the prior one. */
|
|
19
|
+
private lock;
|
|
20
|
+
/** Set while one peer-client is mid token-join; siblings await it. */
|
|
21
|
+
private inFlightJoin;
|
|
22
|
+
constructor(opts: {
|
|
23
|
+
credentialFile: string;
|
|
24
|
+
instanceId: string;
|
|
25
|
+
joinToken?: string;
|
|
26
|
+
joinWaitTimeoutMs?: number;
|
|
27
|
+
});
|
|
28
|
+
/** Run `fn` exclusively against the credential file. */
|
|
29
|
+
private withLock;
|
|
30
|
+
private readValidCredential;
|
|
31
|
+
decideAuth(): Promise<AuthDecision>;
|
|
32
|
+
private makeComplete;
|
|
33
|
+
private awaitInFlightJoin;
|
|
34
|
+
reportRejection(provedCredential: string | null, reason: string): Promise<RejectionAction>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=peer-auth-coordinator.d.ts.map
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
* Authentication uses ECDH key exchange followed by join token (first connect)
|
|
11
11
|
* or HMAC credential proof (reconnection).
|
|
12
12
|
*/
|
|
13
|
-
import { type PeerHeartbeat, type PeerToPeerMessage, type JobReroute, type JobProgress, type PeerJobCancel, type PeerLogChunk, type PeerCacheUploadRequest, type PeerCacheUploadResponse, type PeerConfigReload, type PeerConfigReloadResponse, type PeerLogsCollectRequest, type PeerLeaving, type PeerAgentTokenRevoke, type RaftVoteRequest, type RaftVoteResponse, type RaftAppendEntries } from '@kici-dev/engine';
|
|
13
|
+
import { type PeerHeartbeat, type PeerToPeerMessage, type JobReroute, type JobProgress, type JobProgressAck, type PeerJobCancel, type PeerLogChunk, type PeerCacheUploadRequest, type PeerCacheUploadResponse, type PeerConfigReload, type PeerConfigReloadResponse, type PeerLogsCollectRequest, type PeerLeaving, type PeerAgentTokenRevoke, type RaftVoteRequest, type RaftVoteResponse, type RaftAppendEntries } from '@kici-dev/engine';
|
|
14
14
|
import type { PeerRegistry } from './peer-registry.js';
|
|
15
|
+
import type { PeerAuthCoordinator } from './peer-auth-coordinator.js';
|
|
15
16
|
type PeerConnectionState = 'disconnected' | 'connecting' | 'handshaking' | 'authenticating' | 'connected';
|
|
16
17
|
export interface PeerClientOptions {
|
|
17
18
|
/** WebSocket URL of the remote peer orchestrator. */
|
|
@@ -20,6 +21,8 @@ export interface PeerClientOptions {
|
|
|
20
21
|
joinToken?: string;
|
|
21
22
|
/** Path to the credential file for reconnection. */
|
|
22
23
|
credentialFile: string;
|
|
24
|
+
/** Shared per-orchestrator coordinator that owns the credential file. */
|
|
25
|
+
authCoordinator: PeerAuthCoordinator;
|
|
23
26
|
/** This orchestrator's instance ID. */
|
|
24
27
|
instanceId: string;
|
|
25
28
|
/** Peer registry to update on heartbeats from remote peer. */
|
|
@@ -33,7 +36,14 @@ export interface PeerClientOptions {
|
|
|
33
36
|
/** Callback when a job reroute request is received from peer. */
|
|
34
37
|
onJobReroute: (msg: JobReroute) => Promise<void>;
|
|
35
38
|
/** Callback when a job progress update is received from peer. */
|
|
36
|
-
onJobProgress: (msg: JobProgress) => void;
|
|
39
|
+
onJobProgress: (msg: JobProgress, reply: (m: JobProgressAck) => void) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Callback invoked once this client reaches the `connected` state (initial
|
|
42
|
+
* connect AND every reconnect). Carries this client's peer URL.
|
|
43
|
+
*/
|
|
44
|
+
onConnected?: (url: string) => void;
|
|
45
|
+
/** Callback when a coordinator ACKs a terminal job.progress this worker sent. */
|
|
46
|
+
onJobProgressAck?: (msg: JobProgressAck) => void;
|
|
37
47
|
/** This orchestrator's role. Default: 'coordinator'. */
|
|
38
48
|
role?: 'coordinator' | 'worker';
|
|
39
49
|
/** Callback when a job cancel request is received from peer. */
|
|
@@ -107,8 +117,13 @@ export declare class PeerClient {
|
|
|
107
117
|
/** Correlates peer.logs.collect.request with the peer's chunked subtree response. */
|
|
108
118
|
private readonly logsCollectWaiters;
|
|
109
119
|
private readonly url;
|
|
110
|
-
private readonly
|
|
111
|
-
|
|
120
|
+
private readonly authCoordinator;
|
|
121
|
+
/** Set true between deciding token-join and receiving the auth response. */
|
|
122
|
+
private isJoiner;
|
|
123
|
+
/** complete() callback for the in-flight token-join, if this client is joiner. */
|
|
124
|
+
private joinComplete;
|
|
125
|
+
/** The credential string this client last built an HMAC proof with. */
|
|
126
|
+
private lastProvedCredential;
|
|
112
127
|
private readonly instanceId;
|
|
113
128
|
private readonly role;
|
|
114
129
|
private readonly peerRegistry;
|
|
@@ -117,6 +132,8 @@ export declare class PeerClient {
|
|
|
117
132
|
private readonly maxReconnectDelayMs;
|
|
118
133
|
private readonly onJobReroute;
|
|
119
134
|
private readonly onJobProgress;
|
|
135
|
+
private readonly onConnected?;
|
|
136
|
+
private readonly onJobProgressAck?;
|
|
120
137
|
private readonly onJobCancel;
|
|
121
138
|
private readonly onPeerLogChunk?;
|
|
122
139
|
private readonly onPeerCacheUploadRequest?;
|
|
@@ -185,6 +202,15 @@ export declare class PeerClient {
|
|
|
185
202
|
getReconnectDelay(): number;
|
|
186
203
|
private doConnect;
|
|
187
204
|
private handleMessage;
|
|
205
|
+
/**
|
|
206
|
+
* Handle a rejected peer.auth.response. For the three credential-divergence
|
|
207
|
+
* reasons, delegate to the coordinator (which deletes the shared file only if
|
|
208
|
+
* no sibling has refreshed it, preventing a revocation cascade). A failed
|
|
209
|
+
* token-join releases the in-flight join so siblings can retry. Config-error
|
|
210
|
+
* reasons (role mismatch / missing auth method / protocol version) skip the
|
|
211
|
+
* coordinator entirely — deletion would not help.
|
|
212
|
+
*/
|
|
213
|
+
private handleAuthRejected;
|
|
188
214
|
/**
|
|
189
215
|
* Determine auth method and send encrypted auth request.
|
|
190
216
|
*
|
|
@@ -196,16 +222,10 @@ export declare class PeerClient {
|
|
|
196
222
|
* connecting to any peer can use the same credential, as long as the
|
|
197
223
|
* `instanceId` on disk matches ours.
|
|
198
224
|
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* reject it (URL mismatch), fall through to the join token, and get
|
|
204
|
-
* permanently rejected because the token had already been consumed by the
|
|
205
|
-
* first peer-client. The Option B fix (commit 79f93da4) keyed the match on
|
|
206
|
-
* `instanceId`; this follow-up (Plan 04 Task 1 cleanup) drops the
|
|
207
|
-
* `coordinatorUrl` field from the credential file schema entirely because
|
|
208
|
-
* it was dead weight once the match switched to identity scope.
|
|
225
|
+
* The auth-method decision is delegated to the shared `PeerAuthCoordinator`,
|
|
226
|
+
* which serializes sibling peer-clients so exactly one token-joins per
|
|
227
|
+
* reconnect storm (the rest reuse the freshly-written credential). This
|
|
228
|
+
* client never reads or writes the credential file directly.
|
|
209
229
|
*/
|
|
210
230
|
private sendAuthRequest;
|
|
211
231
|
private routeMessage;
|
|
@@ -36,7 +36,17 @@ export declare class PeerCredentialStore {
|
|
|
36
36
|
private readonly db;
|
|
37
37
|
constructor(db: Kysely<any>);
|
|
38
38
|
/**
|
|
39
|
-
* Save a new peer credential
|
|
39
|
+
* Save a new peer credential, revoking any prior active credential for the
|
|
40
|
+
* same instanceId (the `peer_credentials_active_uniq` partial unique index
|
|
41
|
+
* permits exactly one active credential per instanceId).
|
|
42
|
+
*
|
|
43
|
+
* Returns `{ revokedCount }` — how many previously-active credentials this
|
|
44
|
+
* save revoked. Because a coordinator's peer-clients share one
|
|
45
|
+
* identity-scoped credential, a `revokedCount > 0` here means every sibling
|
|
46
|
+
* peer-client of `instanceId` that was authenticating with the old
|
|
47
|
+
* credential is now invalidated and will be rejected on its next proof. The
|
|
48
|
+
* caller logs this so a revoke that cascades sibling rejections is visible in
|
|
49
|
+
* the orchestrator logs.
|
|
40
50
|
*/
|
|
41
51
|
save(opts: {
|
|
42
52
|
instanceId: string;
|
|
@@ -45,7 +55,9 @@ export declare class PeerCredentialStore {
|
|
|
45
55
|
routingKeys: string[];
|
|
46
56
|
sourceTokenHash?: string;
|
|
47
57
|
expiryDays?: number;
|
|
48
|
-
}): Promise<
|
|
58
|
+
}): Promise<{
|
|
59
|
+
revokedCount: number;
|
|
60
|
+
}>;
|
|
49
61
|
/**
|
|
50
62
|
* Find a non-revoked, non-expired credential by its hash.
|
|
51
63
|
*/
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* proof (reconnection), registers the peer in PeerRegistry, and routes
|
|
7
7
|
* messages bidirectionally. Sends periodic heartbeats to the connecting peer.
|
|
8
8
|
*/
|
|
9
|
-
import { type PeerHeartbeat, type PeerToPeerMessage, type JobReroute, type JobProgress, type PeerScalerEvent, type PeerJobCancel, type PeerLogChunk, type PeerCacheUploadRequest, type PeerCacheUploadResponse, type PeerConfigReload, type PeerConfigReloadResponse, type PeerLogsCollectRequest, type PeerLeaving, type PeerAgentTokenRevoke, type RaftVoteRequest, type RaftVoteResponse, type RaftAppendEntries } from '@kici-dev/engine';
|
|
9
|
+
import { type PeerHeartbeat, type PeerToPeerMessage, type JobReroute, type JobProgress, type JobProgressAck, type PeerScalerEvent, type PeerJobCancel, type PeerLogChunk, type PeerCacheUploadRequest, type PeerCacheUploadResponse, type PeerConfigReload, type PeerConfigReloadResponse, type PeerLogsCollectRequest, type PeerLeaving, type PeerAgentTokenRevoke, type RaftVoteRequest, type RaftVoteResponse, type RaftAppendEntries } from '@kici-dev/engine';
|
|
10
10
|
import type { PeerRegistry } from './peer-registry.js';
|
|
11
11
|
import type { PeerCredentialStore } from './peer-credentials.js';
|
|
12
12
|
import { type JoinTokenManager } from './join-token.js';
|
|
@@ -40,7 +40,7 @@ export interface PeerHandlerDeps {
|
|
|
40
40
|
/** Callback when a job reroute request is received from peer. */
|
|
41
41
|
onJobReroute: (msg: JobReroute) => Promise<void>;
|
|
42
42
|
/** Callback when a job progress update is received from peer. */
|
|
43
|
-
onJobProgress: (msg: JobProgress) => void;
|
|
43
|
+
onJobProgress: (msg: JobProgress, reply: (m: JobProgressAck) => void) => void;
|
|
44
44
|
/** Callback when a scaler provisioning event is forwarded by a worker peer. */
|
|
45
45
|
onPeerScalerEvent?: (msg: PeerScalerEvent) => void;
|
|
46
46
|
/** Callback when a job cancel request is received from peer. */
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { PeerRegistry } from './peer-registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* Grace window (ms) during which a rerouted job stays deferred from the
|
|
4
|
+
* recovery sweepers even though its worker peer is momentarily disconnected.
|
|
5
|
+
*
|
|
6
|
+
* A worker that completes a job durably buffers the terminal status in its
|
|
7
|
+
* PeerOutbox and replays it on the next reconnect. A coordinator restart or a
|
|
8
|
+
* leadership transition makes the worker's peer-WS flap (an abnormal 1006
|
|
9
|
+
* close) for a few seconds before it reconnects and replays — so sampling
|
|
10
|
+
* `peer.connected` at the exact instant a sweeper runs can catch the peer
|
|
11
|
+
* mid-flap and wrongly force-fail a run whose job already succeeded.
|
|
12
|
+
*
|
|
13
|
+
* The grace is two peer-stale-timeout windows (peerStaleTimeoutMs default
|
|
14
|
+
* 60_000): a peer that flaps and reconnects inside one stale window is always
|
|
15
|
+
* deferred, while a peer absent for two full windows is treated as dead and
|
|
16
|
+
* its job is failed — so a genuinely dead worker can never hang a run forever.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_REROUTE_FLAP_GRACE_MS = 120000;
|
|
19
|
+
/**
|
|
20
|
+
* A non-terminal job that was rerouted to a remote worker peer must NOT be
|
|
21
|
+
* force-failed by the run-recovery sweepers (OrphanRecovery, StaleRunDetector)
|
|
22
|
+
* while that worker peer can still replay the job's terminal status from its
|
|
23
|
+
* durable outbox.
|
|
24
|
+
*
|
|
25
|
+
* Returns `true` (defer the sweeper's force-fail) when the job carries a
|
|
26
|
+
* `rerouted_to_peer` marker AND its peer is either currently connected OR was
|
|
27
|
+
* last seen within the flap-grace window (a transient peer-WS reconnect during
|
|
28
|
+
* a coordinator restart / leadership transition). A rerouted job whose peer has
|
|
29
|
+
* been gone longer than the grace window — or is no longer tracked at all — is
|
|
30
|
+
* NOT deferred, so a dead worker's job is still failed and cannot hang forever.
|
|
31
|
+
* Local (non-rerouted) jobs — `rerouted_to_peer === null` — are never deferred.
|
|
32
|
+
*/
|
|
33
|
+
export declare function shouldDeferReroutedJob(job: {
|
|
34
|
+
rerouted_to_peer: string | null;
|
|
35
|
+
}, peerRegistry: Pick<PeerRegistry, 'getPeer'>, opts?: {
|
|
36
|
+
nowMs?: number;
|
|
37
|
+
flapGraceMs?: number;
|
|
38
|
+
}): boolean;
|
|
39
|
+
//# sourceMappingURL=rerouted-job-guard.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Add the typed host-vars dimension to the host roster:
|
|
4
|
+
*
|
|
5
|
+
* - `host_roster.host_properties jsonb NOT NULL DEFAULT '{}'` — the typed
|
|
6
|
+
* property bag (`string | number | boolean` values) reported by the agent at
|
|
7
|
+
* registration and/or declared by the operator (`kici-admin host declare
|
|
8
|
+
* --prop`). Labels stay the flat-string grouping dimension; properties are
|
|
9
|
+
* the separate queryable host-vars dimension.
|
|
10
|
+
*
|
|
11
|
+
* Idempotent (`ADD COLUMN IF NOT EXISTS`): re-running on a DB that already has
|
|
12
|
+
* the column is a no-op. Staging data is preserved (additive column).
|
|
13
|
+
*/
|
|
14
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
15
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=045_host_properties.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Record the joining peer's instanceId at first consumption of a join token:
|
|
4
|
+
*
|
|
5
|
+
* - `join_tokens.consumed_by_instance text` — the `instanceId` of the peer that
|
|
6
|
+
* consumed the token. This is the *joining peer*, distinct from `consumed_by`
|
|
7
|
+
* (the coordinator that processed the claim). It lets a token be reused by the
|
|
8
|
+
* same peer instance until `expires_at`, so a peer that lost its credential
|
|
9
|
+
* (transient outage / deleted credential file) self-heals by re-presenting the
|
|
10
|
+
* join token already in its env — no operator action, no cluster redeploy.
|
|
11
|
+
*
|
|
12
|
+
* Idempotent (`ADD COLUMN IF NOT EXISTS`): re-running on a DB that already has
|
|
13
|
+
* the column is a no-op. Staging data is preserved (additive nullable column).
|
|
14
|
+
*/
|
|
15
|
+
export declare function up(db: Kysely<unknown>): Promise<void>;
|
|
16
|
+
export declare function down(db: Kysely<unknown>): Promise<void>;
|
|
17
|
+
//# sourceMappingURL=046_join_token_consumed_by_instance.d.ts.map
|
package/dist/db/types.d.ts
CHANGED
|
@@ -86,8 +86,14 @@ export interface JoinTokenTable {
|
|
|
86
86
|
expires_at: Date;
|
|
87
87
|
/** When this token was consumed (null = unused) */
|
|
88
88
|
consumed_at: Date | null;
|
|
89
|
-
/** Who consumed this token (null = unused) */
|
|
89
|
+
/** Who consumed this token (null = unused) — the coordinator that processed the claim */
|
|
90
90
|
consumed_by: string | null;
|
|
91
|
+
/**
|
|
92
|
+
* The instanceId of the joining peer that consumed this token (null = unused).
|
|
93
|
+
* Distinct from `consumed_by` (the coordinator). Lets the same peer instance
|
|
94
|
+
* reuse the token until `expires_at` to self-heal after a transient outage.
|
|
95
|
+
*/
|
|
96
|
+
consumed_by_instance: string | null;
|
|
91
97
|
}
|
|
92
98
|
export type JoinToken = Selectable<JoinTokenTable>;
|
|
93
99
|
export type NewJoinToken = Insertable<JoinTokenTable>;
|
|
@@ -300,6 +306,12 @@ export interface ExecutionRunTable {
|
|
|
300
306
|
workflow_timeout_ms: ColumnType<number | null, number | null | undefined, number | null>;
|
|
301
307
|
/** When this record was created */
|
|
302
308
|
created_at: Generated<Date>;
|
|
309
|
+
/**
|
|
310
|
+
* Run mode for idempotent steps (`apply` | `check` | `check-fail-on-drift`,
|
|
311
|
+
* the `CheckMode` enum). NULL means a legacy/apply run. A non-apply value
|
|
312
|
+
* labels the run a check-mode preview in the dashboard.
|
|
313
|
+
*/
|
|
314
|
+
check_mode: string | null;
|
|
303
315
|
/**
|
|
304
316
|
* Set inside the cold-store archive transaction before the row is
|
|
305
317
|
* DELETEd. Survivors carry NULL. Exists so a future
|
|
@@ -382,6 +394,8 @@ export interface ExecutionJobTable {
|
|
|
382
394
|
wave_max_parallel: number | null;
|
|
383
395
|
/** The fan-out base's `failFast` policy, stamped on every child. NULL = no bounded wave. */
|
|
384
396
|
wave_fail_fast: boolean | null;
|
|
397
|
+
/** Instance id of the worker peer this job was rerouted to, or null if local. */
|
|
398
|
+
rerouted_to_peer: string | null;
|
|
385
399
|
/** When this record was created */
|
|
386
400
|
created_at: Generated<Date>;
|
|
387
401
|
/**
|
|
@@ -429,6 +443,16 @@ export interface ExecutionStepTable {
|
|
|
429
443
|
step_type: Generated<string>;
|
|
430
444
|
/** JSON array of secret context names accessed by this step. NULL = tracking not available (old runs). */
|
|
431
445
|
secrets_accessed: string | null;
|
|
446
|
+
/**
|
|
447
|
+
* Idempotent per-step outcome (`CheckStepOutcome`: skipped | applied |
|
|
448
|
+
* declined | dry-run | no_check). NULL when the step ran without a check
|
|
449
|
+
* mode. Orthogonal to `status`.
|
|
450
|
+
*/
|
|
451
|
+
check_outcome: string | null;
|
|
452
|
+
/** Human-readable drift summary (`summarize(drift)`). NULL when no drift. */
|
|
453
|
+
drift_summary: string | null;
|
|
454
|
+
/** Structured drift value returned by `check()` (JSONB). NULL when no drift. */
|
|
455
|
+
drift: ColumnType<unknown | null, unknown, unknown>;
|
|
432
456
|
/** When this record was created */
|
|
433
457
|
created_at: Generated<Date>;
|
|
434
458
|
/**
|
|
@@ -1583,6 +1607,14 @@ export interface HostRosterTable {
|
|
|
1583
1607
|
arch: string | null;
|
|
1584
1608
|
/** Which orchestrator instance holds the live WS; null = disconnected. */
|
|
1585
1609
|
connected_instance_id: string | null;
|
|
1610
|
+
/**
|
|
1611
|
+
* Typed host-vars dimension (jsonb). A `{ [key]: string | number | boolean }`
|
|
1612
|
+
* bag reported by the agent at registration and/or declared by the operator
|
|
1613
|
+
* (`kici-admin host declare --prop`), shallow-merged on upsert. NOT NULL,
|
|
1614
|
+
* defaults to `{}`. pg returns the parsed object on select; accept a
|
|
1615
|
+
* JSON-stringified value on insert/update.
|
|
1616
|
+
*/
|
|
1617
|
+
host_properties: ColumnType<Record<string, string | number | boolean>, Record<string, string | number | boolean> | string | undefined, Record<string, string | number | boolean> | string>;
|
|
1586
1618
|
last_seen: ColumnType<Date, Date | string | undefined, Date | string>;
|
|
1587
1619
|
created_at: Generated<Date>;
|
|
1588
1620
|
updated_at: ColumnType<Date, Date | string | undefined, Date | string>;
|
package/dist/index.d.ts
CHANGED
|
@@ -4,5 +4,9 @@ export { createCacheStorage } from './storage/index.js';
|
|
|
4
4
|
export { SourceCache } from './cache/source-cache.js';
|
|
5
5
|
export { DepCache } from './cache/dep-cache.js';
|
|
6
6
|
export { UserCache, DEFAULT_USER_CACHE_QUOTA_BYTES, DEFAULT_USER_CACHE_TTL_MS, type UserCacheRef, type UserCacheRestoreResult, type UserCacheBeginSaveResult, } from './cache/user-cache.js';
|
|
7
|
-
export { PeerCredentialStore, createPeerCredentialStoreFromUrl, type PeerCredential, } from './cluster/peer-credentials.js';
|
|
7
|
+
export { PeerCredentialStore, createPeerCredentialStoreFromUrl, type PeerCredential, type CredentialFileData, } from './cluster/peer-credentials.js';
|
|
8
|
+
export { PeerAuthCoordinator, type AuthDecision, type RejectionAction, } from './cluster/peer-auth-coordinator.js';
|
|
9
|
+
export { JoinTokenManager, createJoinTokenManagerFromUrl } from './cluster/join-token.js';
|
|
10
|
+
export { AdminApiClient } from './cli/api-client.js';
|
|
11
|
+
export { runGithubManifestSetup, type ManifestSetupOptions, type ManifestSetupDeps, } from './cli/commands/source-manifest.js';
|
|
8
12
|
//# sourceMappingURL=index.d.ts.map
|