@kici-dev/agent 0.0.0 → 0.1.2

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 (50) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +1 -6
  3. package/dist/checkout/git-clone.d.ts +59 -0
  4. package/dist/checkout/ssh-auth.d.ts +34 -0
  5. package/dist/config.d.ts +109 -0
  6. package/dist/execution/console-capture.d.ts +35 -0
  7. package/dist/execution/dep-installer.d.ts +44 -0
  8. package/dist/execution/dep-packer.d.ts +25 -0
  9. package/dist/execution/dep-restore.d.ts +85 -0
  10. package/dist/execution/download.d.ts +29 -0
  11. package/dist/execution/dynamic-job-serializer.d.ts +51 -0
  12. package/dist/execution/hook-executor.d.ts +46 -0
  13. package/dist/execution/init-runner.d.ts +33 -0
  14. package/dist/execution/job-runner.d.ts +266 -0
  15. package/dist/execution/log-streamer.d.ts +126 -0
  16. package/dist/execution/npm-registry-config.d.ts +63 -0
  17. package/dist/execution/npm-resolver.d.ts +40 -0
  18. package/dist/execution/overlay-applier.d.ts +51 -0
  19. package/dist/execution/rule-evaluator.d.ts +11 -0
  20. package/dist/execution/sandbox/bare-metal-sandbox.d.ts +69 -0
  21. package/dist/execution/sandbox/container-sandbox.d.ts +100 -0
  22. package/dist/execution/sandbox/env-sanitizer.d.ts +43 -0
  23. package/dist/execution/sandbox/firecracker-sandbox.d.ts +65 -0
  24. package/dist/execution/sandbox/fork-runner.d.ts +94 -0
  25. package/dist/execution/sandbox/index.d.ts +14 -0
  26. package/dist/execution/sandbox/ipc-protocol.d.ts +311 -0
  27. package/dist/execution/sandbox/log-masker.d.ts +45 -0
  28. package/dist/execution/sandbox/secret-encryption.d.ts +37 -0
  29. package/dist/execution/sandbox/secret-merge.d.ts +18 -0
  30. package/dist/execution/sandbox/step-loop.d.ts +77 -0
  31. package/dist/execution/sandbox/types.d.ts +142 -0
  32. package/dist/execution/sandbox/workflow-runner.d.ts +17 -0
  33. package/dist/execution/source-packer.d.ts +18 -0
  34. package/dist/execution/source-restore.d.ts +23 -0
  35. package/dist/execution/timeout-util.d.ts +11 -0
  36. package/dist/execution/workflow-loader.d.ts +70 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.js +128 -0
  39. package/dist/metrics/metrics-reporter.d.ts +32 -0
  40. package/dist/metrics/prometheus.d.ts +95 -0
  41. package/dist/routes/health.d.ts +27 -0
  42. package/dist/server.d.ts +20 -0
  43. package/dist/server.js +5347 -0
  44. package/dist/workflow-runner.js +2978 -0
  45. package/dist/ws/event-buffer.d.ts +16 -0
  46. package/dist/ws/log-buffer.d.ts +15 -0
  47. package/dist/ws/orchestrator-client.d.ts +269 -0
  48. package/package.json +59 -7
  49. package/sbom.spdx.json +10125 -0
  50. package/index.js +0 -3
@@ -0,0 +1,16 @@
1
+ import type { AgentToOrchestratorMessage } from '@kici-dev/engine';
2
+ import { RingBuffer } from '@kici-dev/shared';
3
+ /**
4
+ * In-memory buffer for agent-to-orchestrator messages during disconnection.
5
+ *
6
+ * Wraps the generic RingBuffer from @kici-dev/shared with the agent's
7
+ * message type and a default maxSize of 5,000. When the agent loses its
8
+ * WebSocket connection to the orchestrator, outgoing messages are buffered
9
+ * here and flushed in order on reconnection.
10
+ */
11
+ export declare class EventBuffer extends RingBuffer<AgentToOrchestratorMessage> {
12
+ constructor(options?: {
13
+ maxSize?: number;
14
+ });
15
+ }
16
+ //# sourceMappingURL=event-buffer.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { RingBuffer } from '@kici-dev/shared';
2
+ /**
3
+ * Ring buffer for agent log lines during WS disconnection.
4
+ *
5
+ * Wraps the generic RingBuffer from @kici-dev/shared with string type
6
+ * and a default capacity of 10,000 lines. When the agent loses its
7
+ * WebSocket connection, operational log lines are buffered here and
8
+ * replayed on reconnection.
9
+ */
10
+ export declare class LogBuffer extends RingBuffer<string> {
11
+ constructor(options?: {
12
+ maxLines?: number;
13
+ });
14
+ }
15
+ //# sourceMappingURL=log-buffer.d.ts.map
@@ -0,0 +1,269 @@
1
+ import { type AgentToOrchestratorMessage, type JobDispatch, type JobCancel } from '@kici-dev/engine';
2
+ export type ConnectionState = 'disconnected' | 'connecting' | 'authenticating' | 'registering' | 'registered';
3
+ interface OrchestratorClientOptions {
4
+ /** WebSocket URL of the orchestrator. */
5
+ url: string;
6
+ /** Agent's unique identifier. */
7
+ agentId: string;
8
+ /** Agent's label set for job routing. */
9
+ labels: string[];
10
+ /** Callback invoked when a job.dispatch message is received. */
11
+ onJobDispatch: (dispatch: JobDispatch) => void;
12
+ /** Callback invoked when a job.cancel message is received. */
13
+ onJobCancel: (cancel: JobCancel) => void;
14
+ /** Agent authentication token (kat_ prefixed). When provided, sends auth.request before agent.register. */
15
+ token?: string;
16
+ /** Heartbeat interval in ms. Default: 30000 (30s). */
17
+ heartbeatIntervalMs?: number;
18
+ /** Maximum reconnect delay in ms. Default: 60000 (60s). */
19
+ maxReconnectDelayMs?: number;
20
+ /** Maximum event buffer size. Default: 5000. */
21
+ maxBufferSize?: number;
22
+ /** Maximum log buffer lines for agent.log during disconnection. Default: 10000. */
23
+ maxLogBufferLines?: number;
24
+ /** Callback to retrieve in-flight jobs for reconnection reporting. */
25
+ getInFlightJobs?: () => Array<{
26
+ jobId: string;
27
+ runId: string;
28
+ }>;
29
+ /** Agent roles. undefined = all, [] = execution only. Used to derive kici:role:* auto-labels. */
30
+ roles?: string[];
31
+ /**
32
+ * Whether the agent was spawned by the orchestrator's auto-scaler.
33
+ * Mirrors KICI_SCALER_MANAGED=1 from config; passed in so the client
34
+ * doesn't have to read process.env directly.
35
+ */
36
+ scalerManaged?: boolean;
37
+ }
38
+ /**
39
+ * WebSocket client that connects the agent to the customer orchestrator.
40
+ *
41
+ * Handles:
42
+ * - Registration handshake (sends agent.register on connect)
43
+ * - Periodic heartbeat messages to keep the connection alive
44
+ * - Auto-reconnect with exponential backoff (1s initial, 1.5x, jitter, 60s max)
45
+ * - Job dispatch and cancel message routing to callbacks
46
+ * - Event buffering during disconnection with flush on reconnect
47
+ *
48
+ * Mirrors the proven PlatformClient pattern from packages/orchestrator but adapted
49
+ * for the agent-to-orchestrator protocol direction.
50
+ */
51
+ export declare class OrchestratorClient {
52
+ private ws;
53
+ private _state;
54
+ private readonly eventBuffer;
55
+ private readonly logBuffer;
56
+ private heartbeatTimer;
57
+ private reconnectTimer;
58
+ private reconnectAttempts;
59
+ private intentionalDisconnect;
60
+ private pendingLogBatch;
61
+ private logFlushTimer;
62
+ private static readonly LOG_BATCH_SIZE;
63
+ private static readonly LOG_FLUSH_INTERVAL_MS;
64
+ /** Pending upload URL requests awaiting orchestrator response. */
65
+ private readonly pendingUploadRequests;
66
+ /** Pending event.emit requests awaiting orchestrator response. */
67
+ private readonly pendingEventEmitRequests;
68
+ /** Pending agent.api.request calls awaiting orchestrator response. */
69
+ private readonly pendingApiRequests;
70
+ /** Pending concurrency report requests awaiting orchestrator ack. */
71
+ private readonly pendingConcurrencyRequests;
72
+ private readonly url;
73
+ private readonly agentId;
74
+ private readonly labels;
75
+ private readonly onJobDispatch;
76
+ private readonly onJobCancel;
77
+ private readonly token?;
78
+ private readonly heartbeatIntervalMs;
79
+ private readonly maxReconnectDelayMs;
80
+ private readonly getInFlightJobs?;
81
+ private readonly roles;
82
+ private readonly scalerManaged;
83
+ /** Timestamp when the connection was lost, used for gap marker outage duration. */
84
+ private disconnectedAt;
85
+ /** Set to true when auth.failure is received. Prevents retrying with a bad token. */
86
+ private authFailed;
87
+ /**
88
+ * Callback invoked when the client transitions to the 'registered' state.
89
+ * Fires on both initial registration and re-registration after reconnection.
90
+ * Used by server.ts to re-evaluate idle shutdown after reconnection.
91
+ *
92
+ * `pendingDispatch` is set by the orchestrator when a queued job has been
93
+ * pre-bound to this agent and the dispatch.job message is in flight. The
94
+ * agent must defer arming the short scaler-idle timer in this case.
95
+ */
96
+ onRegistered: ((info: {
97
+ pendingDispatch: boolean;
98
+ }) => void) | null;
99
+ constructor(options: OrchestratorClientOptions);
100
+ /** Current connection state. */
101
+ get state(): ConnectionState;
102
+ /** Number of messages currently buffered. */
103
+ getBufferedCount(): number;
104
+ /**
105
+ * Get the underlying WebSocket's bufferedAmount (bytes pending in the send queue).
106
+ * Used by LogStreamer for backpressure detection.
107
+ */
108
+ getBufferedAmount(): number;
109
+ /**
110
+ * Register a one-time callback for the WebSocket 'drain' event.
111
+ * Fires when the send buffer has been flushed to the kernel.
112
+ * Used by LogStreamer to resume sending after backpressure.
113
+ */
114
+ onDrain(callback: () => void): void;
115
+ /**
116
+ * Initiate connection to the orchestrator.
117
+ * Starts the connect -> register -> registered lifecycle.
118
+ */
119
+ connect(): void;
120
+ /**
121
+ * Gracefully disconnect from the orchestrator. Does not trigger reconnection.
122
+ */
123
+ disconnect(): void;
124
+ /**
125
+ * Send a message to the orchestrator. If registered, sends immediately.
126
+ * If not registered, buffers the message for later delivery.
127
+ */
128
+ send(message: AgentToOrchestratorMessage): void;
129
+ /**
130
+ * Send a message directly on the WebSocket without buffering.
131
+ * Used for heartbeat and other protocol messages that must not be buffered.
132
+ */
133
+ sendDirect(message: AgentToOrchestratorMessage): void;
134
+ /**
135
+ * Stream a log line to the orchestrator via agent.log messages.
136
+ *
137
+ * If connected and registered, lines are batched (up to 50 lines or 100ms)
138
+ * and sent as agent.log messages. If disconnected, lines are buffered in the
139
+ * LogBuffer for replay on reconnection.
140
+ */
141
+ streamLog(line: string): void;
142
+ /**
143
+ * Request a pre-signed S3 upload URL from the orchestrator.
144
+ *
145
+ * Sends a cache.upload.request WS message and waits for a cache.upload.response.
146
+ * Times out after 30 seconds.
147
+ */
148
+ requestUploadUrl(jobId: string, cacheType: 'source' | 'deps', key: {
149
+ contentHash?: string;
150
+ lockfileHash?: string;
151
+ platform: string;
152
+ arch: string;
153
+ }): Promise<string>;
154
+ /**
155
+ * Notify orchestrator that an S3 upload completed successfully.
156
+ *
157
+ * The orchestrator uses this to initialize metadata on the S3 object.
158
+ */
159
+ sendUploadComplete(jobId: string, cacheType: 'source' | 'deps', key: {
160
+ contentHash?: string;
161
+ lockfileHash?: string;
162
+ platform: string;
163
+ arch: string;
164
+ depsHash?: string;
165
+ }): void;
166
+ /**
167
+ * Send an event.emit WS message to the orchestrator and await the response.
168
+ *
169
+ * Used by the job runner to relay custom event emissions from the sandbox
170
+ * (ctx.emit()) to the orchestrator for persistence and routing.
171
+ * Times out after 5 seconds (matching the sandbox-side timeout).
172
+ */
173
+ sendEventEmit(jobId: string, requestId: string, eventName: string, payload: Record<string, unknown>, target?: {
174
+ repos?: string[];
175
+ }): Promise<{
176
+ requestId: string;
177
+ deliveryId?: string;
178
+ error?: string;
179
+ }>;
180
+ /**
181
+ * Send a typed API request to the orchestrator and await the response.
182
+ *
183
+ * This is the transport layer for the agent private API. The SDK's typed
184
+ * KiciApi interface calls this with dot-namespaced method names.
185
+ */
186
+ sendApiRequest(method: string, params?: Record<string, unknown>): Promise<unknown>;
187
+ /**
188
+ * Send a job.context message to the orchestrator.
189
+ *
190
+ * Conveys execution environment details (runtime, sandbox type, env vars)
191
+ * for the Summary tab. The orchestrator enriches with orgId before forwarding to Platform.
192
+ */
193
+ sendJobContext(runId: string, jobId: string, context: {
194
+ envVars?: Array<{
195
+ name: string;
196
+ value: string;
197
+ category: 'system' | 'user' | 'inherited' | 'secret';
198
+ }>;
199
+ runtime?: {
200
+ nodeVersion?: string;
201
+ os?: string;
202
+ arch?: string;
203
+ };
204
+ sandboxType?: string;
205
+ labels?: string[];
206
+ workingDirectory?: string;
207
+ gitRef?: string;
208
+ }): void;
209
+ /**
210
+ * Send a run.event message to the orchestrator.
211
+ *
212
+ * Emits infrastructure lifecycle events (clone, execution, teardown).
213
+ * The orchestrator enriches with orgId before forwarding to Platform.
214
+ */
215
+ sendRunEvent(runId: string, eventType: string, opts?: {
216
+ jobId?: string;
217
+ metadata?: Record<string, unknown>;
218
+ durationMs?: number;
219
+ }): void;
220
+ /**
221
+ * Send a job.concurrency.report WS message and wait for job.concurrency.ack.
222
+ *
223
+ * The orchestrator evaluates the concurrency group and responds with an action:
224
+ * - proceed: continue with execution
225
+ * - wait: release the agent slot, orchestrator will re-dispatch later
226
+ * - cancel: cancel the job (superseded by newer run)
227
+ *
228
+ * Times out after 30 seconds.
229
+ */
230
+ sendConcurrencyReport(runId: string, jobId: string, group: string): Promise<{
231
+ action: 'proceed' | 'wait' | 'cancel';
232
+ reason?: string;
233
+ }>;
234
+ getReconnectDelay(): number;
235
+ private doConnect;
236
+ private handleMessage;
237
+ private flushBuffer;
238
+ /** Send the current pending log batch as an agent.log message. */
239
+ private sendLogBatch;
240
+ /** Send an agent.log message directly on the WebSocket. */
241
+ private sendAgentLogMessage;
242
+ /**
243
+ * Drain pending log batch into the LogBuffer.
244
+ * Called on disconnect to preserve pending lines for replay on reconnect.
245
+ */
246
+ private drainPendingLogBatch;
247
+ /**
248
+ * Block MMDS access via iptables.
249
+ * Called in Firecracker/scaler-managed mode after receiving config via register.ack.
250
+ * This prevents the agent (and any user code) from accessing MMDS metadata.
251
+ */
252
+ private blockMmdsAccess;
253
+ /**
254
+ * Send config.ack to orchestrator to confirm receipt of registration config.
255
+ * This signals the orchestrator to clear MMDS data for Firecracker agents.
256
+ */
257
+ private sendConfigAck;
258
+ private startHeartbeat;
259
+ private stopHeartbeat;
260
+ /**
261
+ * Send agent.register message on the WebSocket.
262
+ * Extracted to avoid duplication between authenticated and unauthenticated flows.
263
+ */
264
+ private sendAgentRegister;
265
+ private scheduleReconnect;
266
+ private cancelReconnect;
267
+ }
268
+ export {};
269
+ //# sourceMappingURL=orchestrator-client.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kici-dev/agent",
3
- "version": "0.0.0",
3
+ "version": "0.1.2",
4
4
  "description": "Customer-deployable agent for the KiCI CI/CD stack. Connects to an orchestrator, clones the workflow repo, executes steps, and streams logs back.",
5
5
  "keywords": [
6
6
  "kici",
@@ -9,17 +9,69 @@
9
9
  "ci-cd",
10
10
  "typescript",
11
11
  "workflows",
12
- "devops"
12
+ "devops",
13
+ "agent",
14
+ "runner"
13
15
  ],
14
16
  "homepage": "https://kici.dev",
15
17
  "author": {
16
18
  "name": "KiCI",
17
19
  "email": "hello@kici.dev"
18
20
  },
19
- "license": "Apache-2.0",
20
- "main": "index.js",
21
+ "license": "AGPL-3.0-only",
22
+ "engines": {
23
+ "node": ">=24"
24
+ },
25
+ "type": "module",
26
+ "files": [
27
+ "dist",
28
+ "!dist/**/*.map",
29
+ "sbom.spdx.json"
30
+ ],
21
31
  "publishConfig": {
22
- "access": "public",
23
- "registry": "https://registry.npmjs.org/"
32
+ "registry": "https://registry.npmjs.org/",
33
+ "access": "public"
34
+ },
35
+ "main": "dist/index.js",
36
+ "types": "dist/index.d.ts",
37
+ "exports": {
38
+ ".": {
39
+ "import": "./dist/index.js",
40
+ "types": "./dist/index.d.ts"
41
+ }
42
+ },
43
+ "build": {
44
+ "entries": {
45
+ "server.js": "src/server.ts",
46
+ "workflow-runner.js": "src/execution/sandbox/workflow-runner.ts",
47
+ "index.js": "src/index.ts"
48
+ }
49
+ },
50
+ "dependencies": {
51
+ "@hono/node-server": "^2.0.0",
52
+ "dockerode": "^5.0.0",
53
+ "hono": "^4.12.18",
54
+ "tar": "^7.5.13",
55
+ "winston": "^3.19.0",
56
+ "ws": "^8.20.0",
57
+ "zod": "^4.3.6",
58
+ "zx": "^8.8.5",
59
+ "@kici-dev/engine": "0.1.2",
60
+ "@kici-dev/sdk": "0.1.2",
61
+ "@kici-dev/shared": "0.1.2"
62
+ },
63
+ "kici": {
64
+ "metrics": {
65
+ "mode": "push",
66
+ "jobName": "kici-agent"
67
+ }
68
+ },
69
+ "devDependencies": {
70
+ "@types/dockerode": "^4.0.1"
71
+ },
72
+ "scripts": {
73
+ "build": "node ../../scripts/build-service.mjs && tsc --emitDeclarationOnly",
74
+ "typecheck": "tsc --noEmit",
75
+ "test": "vitest run"
24
76
  }
25
- }
77
+ }