@kici-dev/agent 0.1.13 → 0.1.15
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/README.md +13 -1
- package/dist/execution/cache/cache-engine.d.ts +62 -0
- package/dist/execution/cache/cache-phase.d.ts +29 -0
- package/dist/execution/cache/index.d.ts +9 -0
- package/dist/execution/dynamic-job-serializer.d.ts +12 -1
- package/dist/execution/env-init/init-phase.d.ts +64 -0
- package/dist/execution/init-runner.d.ts +1 -1
- package/dist/execution/job-runner.d.ts +21 -0
- package/dist/execution/sandbox/env-delta.d.ts +44 -0
- package/dist/execution/sandbox/env-file.d.ts +39 -0
- package/dist/execution/sandbox/index.d.ts +1 -1
- package/dist/execution/sandbox/ipc-protocol.d.ts +68 -3
- package/dist/execution/sandbox/job-deadline.d.ts +13 -0
- package/dist/execution/sandbox/step-loop.d.ts +29 -0
- package/dist/execution/sandbox/types.d.ts +11 -1
- package/dist/execution/sandbox/workflow-runner.d.ts +9 -1
- package/dist/execution/tmp-gc.d.ts +17 -0
- package/dist/execution/workflow-loader.d.ts +2 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +345 -14
- package/dist/server.js +406 -153
- package/dist/workflow-runner.js +1885 -958
- package/dist/ws/orchestrator-client.d.ts +18 -0
- package/package.json +14 -9
- package/sbom.spdx.json +108 -48
package/dist/server.js
CHANGED
|
@@ -8,16 +8,17 @@ import { PassThrough, Readable, Transform, Writable } from "node:stream";
|
|
|
8
8
|
import { serve } from "@hono/node-server";
|
|
9
9
|
import { Hono } from "hono";
|
|
10
10
|
import winston from "winston";
|
|
11
|
-
import { RingBuffer, createHealthRoutes, createLogger, createMeter, createMetricsRoutes, deriveSharedSecret, getPrometheusExporter, getReconnectDelay, getRequestContext, guardStartup, initTelemetry, normalizeLineEndings, requestContext, setServiceName, setupGracefulShutdown, sha256, sha256File, toErrorMessage, validateRequiredTools } from "@kici-dev/shared";
|
|
11
|
+
import { RingBuffer, createHealthRoutes, createLogger, createMeter, createMetricsRoutes, deriveSharedSecret, getPrometheusExporter, getReconnectDelay, getRequestContext, guardStartup, initTelemetry, logger, normalizeLineEndings, requestContext, setServiceName, setupGracefulShutdown, sha256, sha256File, toErrorMessage, validateRequiredTools } from "@kici-dev/shared";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
import { LOGGER_ENV_VARS, defineEnv, validateUnknownKiciVars } from "@kici-dev/shared/env";
|
|
14
|
-
import { ALLOWED_SYSTEM_VARS, ExecutionJobStatus, ExecutionStepStatus, KNOWN_ROLES, PROTOCOL_VERSION, SANDBOX_DEFAULT_VARS, WS_CLOSE_AGENT_AUTH_FAILED, WS_MAX_PAYLOAD_BYTES, deriveOsArchLabels, heartbeatSchema, hostLabel, mergeAutoLabels, orchestratorToAgentMessageSchema, resolveRoleLabels, validateNoReservedLabels } from "@kici-dev/engine";
|
|
14
|
+
import { ALLOWED_SYSTEM_VARS, CacheRunEventType, CacheStepType, ExecutionJobStatus, ExecutionStepStatus, InitFailureCategory, KNOWN_ROLES, PROTOCOL_VERSION, SANDBOX_DEFAULT_VARS, WS_CLOSE_AGENT_AUTH_FAILED, WS_MAX_PAYLOAD_BYTES, deriveOsArchLabels, heartbeatSchema, hostLabel, mergeAutoLabels, orchestratorToAgentMessageSchema, resolveRoleLabels, validateNoReservedLabels } from "@kici-dev/engine";
|
|
15
15
|
import { execFile, execFileSync, execSync, fork, spawn } from "node:child_process";
|
|
16
16
|
import WebSocket from "ws";
|
|
17
17
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
18
18
|
import { format, promisify } from "node:util";
|
|
19
19
|
import { existsSync } from "node:fs";
|
|
20
20
|
import path, { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
21
|
+
import { gcStaleTmpDirs } from "@kici-dev/core/tmp-gc";
|
|
21
22
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
22
23
|
import fs, { access, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rm, unlink, writeFile } from "node:fs/promises";
|
|
23
24
|
import Docker from "dockerode";
|
|
@@ -27,7 +28,7 @@ import https from "node:https";
|
|
|
27
28
|
import http from "node:http";
|
|
28
29
|
import { pipeline } from "node:stream/promises";
|
|
29
30
|
import { createGunzip } from "node:zlib";
|
|
30
|
-
import { PackageManager, detectPackageManagerFromManifests } from "@kici-dev/shared/package-manager";
|
|
31
|
+
import { PNPM_IGNORE_BUILD_GATE_ARG, PackageManager, detectPackageManagerFromManifests } from "@kici-dev/shared/package-manager";
|
|
31
32
|
import { createInterface } from "node:readline";
|
|
32
33
|
var __defProp = Object.defineProperty;
|
|
33
34
|
var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -207,7 +208,7 @@ var LogBuffer = class extends RingBuffer {
|
|
|
207
208
|
};
|
|
208
209
|
//#endregion
|
|
209
210
|
//#region src/ws/orchestrator-client.ts
|
|
210
|
-
const logger$
|
|
211
|
+
const logger$11 = createLogger({ prefix: "orchestrator-client" });
|
|
211
212
|
/**
|
|
212
213
|
* WebSocket client that connects the agent to the customer orchestrator.
|
|
213
214
|
*
|
|
@@ -240,6 +241,8 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
240
241
|
pendingEventEmitRequests = /* @__PURE__ */ new Map();
|
|
241
242
|
/** Pending agent.api.request calls awaiting orchestrator response. */
|
|
242
243
|
pendingApiRequests = /* @__PURE__ */ new Map();
|
|
244
|
+
/** Pending user-cache restore/save requests awaiting orchestrator response. */
|
|
245
|
+
pendingUserCacheRequests = /* @__PURE__ */ new Map();
|
|
243
246
|
/** Pending concurrency report requests awaiting orchestrator ack. */
|
|
244
247
|
pendingConcurrencyRequests = /* @__PURE__ */ new Map();
|
|
245
248
|
url;
|
|
@@ -311,7 +314,7 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
311
314
|
*/
|
|
312
315
|
connect() {
|
|
313
316
|
if (this._state !== "disconnected") {
|
|
314
|
-
logger$
|
|
317
|
+
logger$11.warn("connect() called while not disconnected", { state: this._state });
|
|
315
318
|
return;
|
|
316
319
|
}
|
|
317
320
|
this.intentionalDisconnect = false;
|
|
@@ -481,6 +484,69 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
481
484
|
});
|
|
482
485
|
}
|
|
483
486
|
/**
|
|
487
|
+
* Relay a user-facing cache request from the sandbox to the orchestrator.
|
|
488
|
+
*
|
|
489
|
+
* Translates the sandbox `cache.request` IPC into the matching `cache.user.*`
|
|
490
|
+
* WS message and resolves with the orchestrator's response mapped onto the
|
|
491
|
+
* IPC response shape:
|
|
492
|
+
*
|
|
493
|
+
* - `restore` -> `cache.user.restore.request`, awaits `cache.user.restore.response`.
|
|
494
|
+
* - `beginSave` -> `cache.user.save.request`, awaits `cache.user.save.response`.
|
|
495
|
+
* - `completeSave` -> `cache.user.save.complete` (fire-and-forget; the
|
|
496
|
+
* orchestrator commits temp -> final without replying), resolves immediately.
|
|
497
|
+
*
|
|
498
|
+
* Times out after 30 seconds for the round-trip ops.
|
|
499
|
+
*/
|
|
500
|
+
async requestUserCache(jobId, request) {
|
|
501
|
+
if (request.op === "completeSave") {
|
|
502
|
+
this.sendDirect({
|
|
503
|
+
type: "cache.user.save.complete",
|
|
504
|
+
messageId: randomUUID(),
|
|
505
|
+
jobId,
|
|
506
|
+
key: request.key,
|
|
507
|
+
tarHash: request.tarHash,
|
|
508
|
+
sizeBytes: request.sizeBytes
|
|
509
|
+
});
|
|
510
|
+
return {
|
|
511
|
+
type: "cache.response",
|
|
512
|
+
requestId: request.requestId
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
const messageId = randomUUID();
|
|
516
|
+
return new Promise((resolve, reject) => {
|
|
517
|
+
const timer = setTimeout(() => {
|
|
518
|
+
this.pendingUserCacheRequests.delete(messageId);
|
|
519
|
+
reject(/* @__PURE__ */ new Error("User-cache request timed out (30s)"));
|
|
520
|
+
}, 3e4);
|
|
521
|
+
this.pendingUserCacheRequests.set(messageId, {
|
|
522
|
+
resolve: (response) => {
|
|
523
|
+
clearTimeout(timer);
|
|
524
|
+
resolve({
|
|
525
|
+
...response,
|
|
526
|
+
requestId: request.requestId
|
|
527
|
+
});
|
|
528
|
+
},
|
|
529
|
+
reject: (err) => {
|
|
530
|
+
clearTimeout(timer);
|
|
531
|
+
reject(err);
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
if (request.op === "restore") this.sendDirect({
|
|
535
|
+
type: "cache.user.restore.request",
|
|
536
|
+
messageId,
|
|
537
|
+
jobId,
|
|
538
|
+
key: request.key,
|
|
539
|
+
...request.restoreKeys && { restoreKeys: request.restoreKeys }
|
|
540
|
+
});
|
|
541
|
+
else this.sendDirect({
|
|
542
|
+
type: "cache.user.save.request",
|
|
543
|
+
messageId,
|
|
544
|
+
jobId,
|
|
545
|
+
key: request.key
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
484
550
|
* Send a job.context message to the orchestrator.
|
|
485
551
|
*
|
|
486
552
|
* Conveys execution environment details (runtime, sandbox type, env vars)
|
|
@@ -562,7 +628,7 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
562
628
|
}
|
|
563
629
|
});
|
|
564
630
|
} catch (err) {
|
|
565
|
-
logger$
|
|
631
|
+
logger$11.error("Failed to create WebSocket", { error: toErrorMessage(err) });
|
|
566
632
|
this._state = "disconnected";
|
|
567
633
|
this.scheduleReconnect();
|
|
568
634
|
return;
|
|
@@ -570,7 +636,7 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
570
636
|
this.ws.on("open", () => {
|
|
571
637
|
if (this.token) {
|
|
572
638
|
this._state = "authenticating";
|
|
573
|
-
logger$
|
|
639
|
+
logger$11.info("Connected to orchestrator, sending auth.request", {
|
|
574
640
|
url: this.url,
|
|
575
641
|
agentId: this.agentId
|
|
576
642
|
});
|
|
@@ -581,7 +647,7 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
581
647
|
}));
|
|
582
648
|
} else {
|
|
583
649
|
this._state = "registering";
|
|
584
|
-
logger$
|
|
650
|
+
logger$11.info("Connected to orchestrator, sending agent.register (no token)", {
|
|
585
651
|
url: this.url,
|
|
586
652
|
agentId: this.agentId
|
|
587
653
|
});
|
|
@@ -592,12 +658,12 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
592
658
|
this.handleMessage(data);
|
|
593
659
|
});
|
|
594
660
|
this.ws.on("close", (code, reason) => {
|
|
595
|
-
logger$
|
|
661
|
+
logger$11.info("Orchestrator connection closed", {
|
|
596
662
|
code,
|
|
597
663
|
reason: reason.toString()
|
|
598
664
|
});
|
|
599
665
|
if (code === WS_CLOSE_AGENT_AUTH_FAILED) {
|
|
600
|
-
logger$
|
|
666
|
+
logger$11.error("Orchestrator closed with auth-failed code -- token is invalid or revoked. NOT retrying.", {
|
|
601
667
|
code,
|
|
602
668
|
reason: reason.toString()
|
|
603
669
|
});
|
|
@@ -614,12 +680,14 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
614
680
|
this.pendingEventEmitRequests.clear();
|
|
615
681
|
for (const [_id, pending] of this.pendingApiRequests) pending.reject(/* @__PURE__ */ new Error("WebSocket disconnected"));
|
|
616
682
|
this.pendingApiRequests.clear();
|
|
683
|
+
for (const [_id, pending] of this.pendingUserCacheRequests) pending.reject(/* @__PURE__ */ new Error("WebSocket disconnected"));
|
|
684
|
+
this.pendingUserCacheRequests.clear();
|
|
617
685
|
for (const [_id, pending] of this.pendingConcurrencyRequests) pending.reject(/* @__PURE__ */ new Error("WebSocket disconnected"));
|
|
618
686
|
this.pendingConcurrencyRequests.clear();
|
|
619
687
|
if (!this.intentionalDisconnect) this.scheduleReconnect();
|
|
620
688
|
});
|
|
621
689
|
this.ws.on("error", (err) => {
|
|
622
|
-
logger$
|
|
690
|
+
logger$11.error(`Orchestrator WebSocket error: ${err.message}`);
|
|
623
691
|
if (this.ws && this.ws.readyState === WebSocket.OPEN) this.ws.close();
|
|
624
692
|
});
|
|
625
693
|
}
|
|
@@ -628,7 +696,7 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
628
696
|
try {
|
|
629
697
|
raw = JSON.parse(data.toString());
|
|
630
698
|
} catch {
|
|
631
|
-
logger$
|
|
699
|
+
logger$11.warn("Malformed JSON received from orchestrator");
|
|
632
700
|
return;
|
|
633
701
|
}
|
|
634
702
|
const rawMsg = raw;
|
|
@@ -662,19 +730,37 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
662
730
|
}
|
|
663
731
|
return;
|
|
664
732
|
}
|
|
733
|
+
if (rawMsg.type === "cache.user.restore.response" || rawMsg.type === "cache.user.save.response") {
|
|
734
|
+
const cacheMsg = raw;
|
|
735
|
+
const pending = this.pendingUserCacheRequests.get(cacheMsg.requestId);
|
|
736
|
+
if (pending) {
|
|
737
|
+
this.pendingUserCacheRequests.delete(cacheMsg.requestId);
|
|
738
|
+
pending.resolve({
|
|
739
|
+
type: "cache.response",
|
|
740
|
+
requestId: cacheMsg.requestId,
|
|
741
|
+
...cacheMsg.hit !== void 0 && { hit: cacheMsg.hit },
|
|
742
|
+
...cacheMsg.matchedKey && { matchedKey: cacheMsg.matchedKey },
|
|
743
|
+
...cacheMsg.downloadUrl && { downloadUrl: cacheMsg.downloadUrl },
|
|
744
|
+
...cacheMsg.tarHash && { tarHash: cacheMsg.tarHash },
|
|
745
|
+
...cacheMsg.skip !== void 0 && { skip: cacheMsg.skip },
|
|
746
|
+
...cacheMsg.uploadUrl && { uploadUrl: cacheMsg.uploadUrl }
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
665
751
|
const parsed = orchestratorToAgentMessageSchema.safeParse(raw);
|
|
666
752
|
if (parsed.success) {
|
|
667
753
|
const msg = parsed.data;
|
|
668
754
|
switch (msg.type) {
|
|
669
755
|
case "auth.success":
|
|
670
756
|
if (this._state === "authenticating") {
|
|
671
|
-
logger$
|
|
757
|
+
logger$11.info("Authentication successful, sending agent.register", { connectionId: msg.connectionId });
|
|
672
758
|
this._state = "registering";
|
|
673
759
|
this.sendAgentRegister();
|
|
674
760
|
}
|
|
675
761
|
break;
|
|
676
762
|
case "auth.failure":
|
|
677
|
-
logger$
|
|
763
|
+
logger$11.error("Authentication FAILED -- token is invalid or expired. NOT retrying.", { reason: msg.reason });
|
|
678
764
|
this.authFailed = true;
|
|
679
765
|
this.intentionalDisconnect = true;
|
|
680
766
|
if (this.ws) {
|
|
@@ -684,7 +770,7 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
684
770
|
this._state = "disconnected";
|
|
685
771
|
break;
|
|
686
772
|
case "register.ack":
|
|
687
|
-
logger$
|
|
773
|
+
logger$11.info("Registration acknowledged by orchestrator", {
|
|
688
774
|
agentId: msg.agentId,
|
|
689
775
|
labels: msg.labels,
|
|
690
776
|
scalerManaged: msg.scalerManaged,
|
|
@@ -699,14 +785,14 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
699
785
|
this.sendConfigAck(msg.agentId);
|
|
700
786
|
break;
|
|
701
787
|
case "job.dispatch":
|
|
702
|
-
logger$
|
|
788
|
+
logger$11.info("Job dispatch received", {
|
|
703
789
|
runId: msg.runId,
|
|
704
790
|
jobId: msg.jobId
|
|
705
791
|
});
|
|
706
792
|
this.onJobDispatch(msg);
|
|
707
793
|
break;
|
|
708
794
|
case "job.cancel":
|
|
709
|
-
logger$
|
|
795
|
+
logger$11.info("Job cancel received", {
|
|
710
796
|
runId: msg.runId,
|
|
711
797
|
jobId: msg.jobId,
|
|
712
798
|
reason: msg.reason
|
|
@@ -714,7 +800,7 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
714
800
|
this.onJobCancel(msg);
|
|
715
801
|
break;
|
|
716
802
|
case "job.concurrency.ack": {
|
|
717
|
-
logger$
|
|
803
|
+
logger$11.info("Concurrency ack received", {
|
|
718
804
|
requestId: msg.requestId,
|
|
719
805
|
action: msg.action
|
|
720
806
|
});
|
|
@@ -732,7 +818,7 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
732
818
|
return;
|
|
733
819
|
}
|
|
734
820
|
if (heartbeatSchema.safeParse(raw).success) return;
|
|
735
|
-
logger$
|
|
821
|
+
logger$11.warn("Invalid message from orchestrator", { errors: parsed.error.issues });
|
|
736
822
|
}
|
|
737
823
|
flushBuffer() {
|
|
738
824
|
const events = this.eventBuffer.flush();
|
|
@@ -746,11 +832,11 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
746
832
|
}
|
|
747
833
|
this.disconnectedAt = null;
|
|
748
834
|
if (events.length > 0) {
|
|
749
|
-
logger$
|
|
835
|
+
logger$11.info("Flushing event buffer", { count: events.length });
|
|
750
836
|
for (const msg of events) this.sendDirect(msg);
|
|
751
837
|
}
|
|
752
838
|
if (logLines.length > 0) {
|
|
753
|
-
logger$
|
|
839
|
+
logger$11.info("Flushing log buffer", { count: logLines.length });
|
|
754
840
|
for (let i = 0; i < logLines.length; i += OrchestratorClient.LOG_BATCH_SIZE) {
|
|
755
841
|
const batch = logLines.slice(i, i + OrchestratorClient.LOG_BATCH_SIZE);
|
|
756
842
|
this.sendAgentLogMessage(batch);
|
|
@@ -803,14 +889,14 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
803
889
|
*/
|
|
804
890
|
blockMmdsAccess() {
|
|
805
891
|
if (process.getuid?.() !== 0) {
|
|
806
|
-
logger$
|
|
892
|
+
logger$11.info("MMDS iptables block skipped (non-root) — network isolation handled by orchestrator");
|
|
807
893
|
return;
|
|
808
894
|
}
|
|
809
895
|
try {
|
|
810
896
|
execSync("iptables -A OUTPUT -d 169.254.169.254 -j DROP", { timeout: 5e3 });
|
|
811
|
-
logger$
|
|
897
|
+
logger$11.info("MMDS access blocked via iptables");
|
|
812
898
|
} catch (err) {
|
|
813
|
-
logger$
|
|
899
|
+
logger$11.warn("Failed to block MMDS access via iptables", { error: toErrorMessage(err) });
|
|
814
900
|
}
|
|
815
901
|
}
|
|
816
902
|
/**
|
|
@@ -824,7 +910,7 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
824
910
|
messageId: `config-ack-${agentId}-${Date.now()}`,
|
|
825
911
|
agentId
|
|
826
912
|
}));
|
|
827
|
-
logger$
|
|
913
|
+
logger$11.info("Config ACK sent to orchestrator", { agentId });
|
|
828
914
|
}
|
|
829
915
|
}
|
|
830
916
|
startHeartbeat() {
|
|
@@ -885,12 +971,12 @@ var OrchestratorClient = class OrchestratorClient {
|
|
|
885
971
|
scheduleReconnect() {
|
|
886
972
|
this.cancelReconnect();
|
|
887
973
|
if (this.authFailed) {
|
|
888
|
-
logger$
|
|
974
|
+
logger$11.error("Not reconnecting: authentication permanently failed");
|
|
889
975
|
return;
|
|
890
976
|
}
|
|
891
977
|
const delay = this.getReconnectDelay();
|
|
892
978
|
this.reconnectAttempts++;
|
|
893
|
-
logger$
|
|
979
|
+
logger$11.info("Scheduling reconnect", {
|
|
894
980
|
attempt: this.reconnectAttempts,
|
|
895
981
|
delayMs: Math.round(delay)
|
|
896
982
|
});
|
|
@@ -975,14 +1061,14 @@ var init_console_capture = __esmMin((() => {
|
|
|
975
1061
|
init_console_capture();
|
|
976
1062
|
function safe(name, fallback = "unknown") {
|
|
977
1063
|
switch (name) {
|
|
978
|
-
case "version": return "0.1.
|
|
979
|
-
case "buildCommit": return "
|
|
980
|
-
case "sdkVersion": return "0.1.
|
|
981
|
-
case "sdkBundleHash": return "
|
|
982
|
-
case "sharedVersion": return "0.1.
|
|
983
|
-
case "sharedBundleHash": return "
|
|
984
|
-
case "engineVersion": return "0.1.
|
|
985
|
-
case "engineBundleHash": return "
|
|
1064
|
+
case "version": return "0.1.15";
|
|
1065
|
+
case "buildCommit": return "831f6a763";
|
|
1066
|
+
case "sdkVersion": return "0.1.15";
|
|
1067
|
+
case "sdkBundleHash": return "cee24b67bbb4a1d2a770e4063dd37a9af33409faeb5ce32f936703b4431316dd";
|
|
1068
|
+
case "sharedVersion": return "0.1.15";
|
|
1069
|
+
case "sharedBundleHash": return "e82a8b68a7d72698674352158c5de4c0d10824c61bfa7166cc8f846044b8b5e4";
|
|
1070
|
+
case "engineVersion": return "0.1.15";
|
|
1071
|
+
case "engineBundleHash": return "032d6b28b3d32bcbea80ba80a52934b017ee2c86a97fcaf8fe8db0d86d72a7f7";
|
|
986
1072
|
default: return fallback;
|
|
987
1073
|
}
|
|
988
1074
|
}
|
|
@@ -1203,6 +1289,42 @@ function verifyNpmAvailable() {
|
|
|
1203
1289
|
}
|
|
1204
1290
|
var init_npm_resolver = __esmMin((() => {}));
|
|
1205
1291
|
//#endregion
|
|
1292
|
+
//#region src/execution/tmp-gc.ts
|
|
1293
|
+
init_npm_resolver();
|
|
1294
|
+
/**
|
|
1295
|
+
* Startup garbage collection for this agent's own temp-directory families.
|
|
1296
|
+
*
|
|
1297
|
+
* Job workdirs (`kici-<6 random chars>`, see job-runner.ts) and isolated
|
|
1298
|
+
* pnpm stores (`kici-pnpm-store-*`, see dep-installer.ts) clean themselves
|
|
1299
|
+
* up in `finally` blocks — but a hard process death (SIGKILL, OOM kill)
|
|
1300
|
+
* skips those, and on a long-lived bare-metal agent the leftovers then
|
|
1301
|
+
* accumulate forever. Collecting anything older than a day at startup is
|
|
1302
|
+
* safe on shared hosts: no job lives remotely that long (job timeouts are
|
|
1303
|
+
* minutes), so a concurrent agent's in-flight dirs are never eligible.
|
|
1304
|
+
*/
|
|
1305
|
+
const AGENT_TMP_GC_MAX_AGE_MS = 1440 * 60 * 1e3;
|
|
1306
|
+
/** mkdtemp's 6-char suffix on the bare `kici-` prefix — job workdirs only. */
|
|
1307
|
+
const AGENT_WORKDIR_PATTERN = /^kici-[A-Za-z0-9]{6}$/;
|
|
1308
|
+
const PNPM_STORE_PATTERN = /^kici-pnpm-store-/;
|
|
1309
|
+
/**
|
|
1310
|
+
* Collect this agent's stale temp dirs. `base` is overridable for tests;
|
|
1311
|
+
* production callers use the default temp root. Never throws.
|
|
1312
|
+
*/
|
|
1313
|
+
async function gcStaleAgentTmpDirs(base = tmpdir()) {
|
|
1314
|
+
const log = (m) => logger.info(m);
|
|
1315
|
+
return [...await gcStaleTmpDirs({
|
|
1316
|
+
base,
|
|
1317
|
+
pattern: AGENT_WORKDIR_PATTERN,
|
|
1318
|
+
maxAgeMs: AGENT_TMP_GC_MAX_AGE_MS,
|
|
1319
|
+
log
|
|
1320
|
+
}), ...await gcStaleTmpDirs({
|
|
1321
|
+
base,
|
|
1322
|
+
pattern: PNPM_STORE_PATTERN,
|
|
1323
|
+
maxAgeMs: AGENT_TMP_GC_MAX_AGE_MS,
|
|
1324
|
+
log
|
|
1325
|
+
})];
|
|
1326
|
+
}
|
|
1327
|
+
//#endregion
|
|
1206
1328
|
//#region src/metrics/prometheus.ts
|
|
1207
1329
|
var prometheus_exports = /* @__PURE__ */ __exportAll({
|
|
1208
1330
|
cloneDurationSeconds: () => cloneDurationSeconds,
|
|
@@ -1462,7 +1584,8 @@ var init_git_clone = __esmMin((() => {
|
|
|
1462
1584
|
//#region src/execution/workflow-loader.ts
|
|
1463
1585
|
/**
|
|
1464
1586
|
* Workflow module loading: transforms `.ts` workflow files on import via the
|
|
1465
|
-
*
|
|
1587
|
+
* `@kici-dev/core/ts-loader-hook` oxc-transform ESM loader hook. Customer
|
|
1588
|
+
* workflow code is imported
|
|
1466
1589
|
* directly from the cloned / extracted source tree — no intermediate bundle,
|
|
1467
1590
|
* no Rolldown step at runtime. `@kici-dev/sdk` and host-repo deps resolve via
|
|
1468
1591
|
* Node's normal ESM lookup against `.kici/node_modules/`.
|
|
@@ -1478,7 +1601,7 @@ var workflow_loader_exports = /* @__PURE__ */ __exportAll({
|
|
|
1478
1601
|
});
|
|
1479
1602
|
function ensureLoaderHookRegistered() {
|
|
1480
1603
|
if (hookRegistered) return;
|
|
1481
|
-
register("@kici-dev/
|
|
1604
|
+
register("@kici-dev/core/ts-loader-hook", import.meta.url);
|
|
1482
1605
|
hookRegistered = true;
|
|
1483
1606
|
}
|
|
1484
1607
|
/**
|
|
@@ -1629,8 +1752,8 @@ async function extractStepsFromDynamicJob(workflow, dynamicIndex, jobName, event
|
|
|
1629
1752
|
}
|
|
1630
1753
|
var AGENT_SDK_VERSION, AGENT_SDK_BUNDLE_HASH, hookRegistered;
|
|
1631
1754
|
var init_workflow_loader = __esmMin((() => {
|
|
1632
|
-
AGENT_SDK_VERSION = "0.1.
|
|
1633
|
-
AGENT_SDK_BUNDLE_HASH = "
|
|
1755
|
+
AGENT_SDK_VERSION = "0.1.15";
|
|
1756
|
+
AGENT_SDK_BUNDLE_HASH = "cee24b67bbb4a1d2a770e4063dd37a9af33409faeb5ce32f936703b4431316dd";
|
|
1634
1757
|
hookRegistered = false;
|
|
1635
1758
|
}));
|
|
1636
1759
|
//#endregion
|
|
@@ -1651,7 +1774,7 @@ var init_workflow_loader = __esmMin((() => {
|
|
|
1651
1774
|
async function packKiciSource(workDir) {
|
|
1652
1775
|
const kiciDir = join(workDir, ".kici");
|
|
1653
1776
|
if (!existsSync(kiciDir)) throw new Error(`.kici/ not found at ${kiciDir}`);
|
|
1654
|
-
logger$
|
|
1777
|
+
logger$10.info("Packing .kici/ source tarball", { dir: workDir });
|
|
1655
1778
|
const startTime = Date.now();
|
|
1656
1779
|
const stream = c({
|
|
1657
1780
|
gzip: true,
|
|
@@ -1665,7 +1788,7 @@ async function packKiciSource(workDir) {
|
|
|
1665
1788
|
const hash = sha256(tarball);
|
|
1666
1789
|
const sizeKB = (tarball.length / 1024).toFixed(2);
|
|
1667
1790
|
const durationMs = Date.now() - startTime;
|
|
1668
|
-
logger$
|
|
1791
|
+
logger$10.info(".kici/ source packed", {
|
|
1669
1792
|
sizeKB,
|
|
1670
1793
|
hash: hash.slice(0, 12),
|
|
1671
1794
|
durationMs
|
|
@@ -1675,9 +1798,9 @@ async function packKiciSource(workDir) {
|
|
|
1675
1798
|
hash
|
|
1676
1799
|
};
|
|
1677
1800
|
}
|
|
1678
|
-
var logger$
|
|
1801
|
+
var logger$10;
|
|
1679
1802
|
var init_source_packer = __esmMin((() => {
|
|
1680
|
-
logger$
|
|
1803
|
+
logger$10 = createLogger({ prefix: "source-packer" });
|
|
1681
1804
|
}));
|
|
1682
1805
|
//#endregion
|
|
1683
1806
|
//#region src/execution/dep-restore.ts
|
|
@@ -1818,7 +1941,7 @@ async function cleanupScratch(scratchDir) {
|
|
|
1818
1941
|
force: true
|
|
1819
1942
|
});
|
|
1820
1943
|
} catch (cleanupErr) {
|
|
1821
|
-
logger$
|
|
1944
|
+
logger$9.warn("Scratch dir cleanup failed (orphan left behind)", {
|
|
1822
1945
|
scratchDir,
|
|
1823
1946
|
error: cleanupErr instanceof Error ? cleanupErr.message : String(cleanupErr)
|
|
1824
1947
|
});
|
|
@@ -1843,7 +1966,7 @@ async function cleanupScratch(scratchDir) {
|
|
|
1843
1966
|
*/
|
|
1844
1967
|
async function restoreDeps(workDir, depsUrl, depsHash) {
|
|
1845
1968
|
depsUrl = resolveOrchestratorUrl(depsUrl);
|
|
1846
|
-
logger$
|
|
1969
|
+
logger$9.info("Downloading dependency tarball", { url: depsUrl });
|
|
1847
1970
|
const kiciDir = join(workDir, ".kici");
|
|
1848
1971
|
if (depsUrl.startsWith("file://")) {
|
|
1849
1972
|
const localPath = fileURLToPath(depsUrl);
|
|
@@ -1857,7 +1980,7 @@ async function restoreDeps(workDir, depsUrl, depsHash) {
|
|
|
1857
1980
|
await moveScratchIntoRepo(scratchDir, workDir);
|
|
1858
1981
|
await cleanupScratch(scratchDir);
|
|
1859
1982
|
const sizeMB = (data.length / (1024 * 1024)).toFixed(2);
|
|
1860
|
-
logger$
|
|
1983
|
+
logger$9.info("Dependencies restored from cache (file)", {
|
|
1861
1984
|
sizeMB,
|
|
1862
1985
|
targetDir: workDir
|
|
1863
1986
|
});
|
|
@@ -1866,7 +1989,7 @@ async function restoreDeps(workDir, depsUrl, depsHash) {
|
|
|
1866
1989
|
if (!depsUrl.startsWith("http://") && !depsUrl.startsWith("https://")) throw new Error(`Unsupported deps URL scheme: ${depsUrl}`);
|
|
1867
1990
|
let lastError;
|
|
1868
1991
|
for (let attempt = 0; attempt <= 2; attempt++) {
|
|
1869
|
-
if (attempt > 0) logger$
|
|
1992
|
+
if (attempt > 0) logger$9.warn("Retrying dep tarball download", {
|
|
1870
1993
|
attempt,
|
|
1871
1994
|
url: depsUrl
|
|
1872
1995
|
});
|
|
@@ -1875,11 +1998,11 @@ async function restoreDeps(workDir, depsUrl, depsHash) {
|
|
|
1875
1998
|
if (depsHash && hash !== depsHash) throw new Error(`Dep tarball hash mismatch: expected ${depsHash}, got ${hash}`);
|
|
1876
1999
|
await moveScratchIntoRepo(scratchDir, workDir);
|
|
1877
2000
|
await cleanupScratch(scratchDir);
|
|
1878
|
-
logger$
|
|
2001
|
+
logger$9.info("Dependencies restored from cache (stream)", { targetDir: workDir });
|
|
1879
2002
|
return;
|
|
1880
2003
|
} catch (err) {
|
|
1881
2004
|
lastError = err instanceof Error ? err : new Error(String(err));
|
|
1882
|
-
logger$
|
|
2005
|
+
logger$9.warn("Dep tarball download failed", {
|
|
1883
2006
|
attempt,
|
|
1884
2007
|
error: lastError.message
|
|
1885
2008
|
});
|
|
@@ -1887,9 +2010,9 @@ async function restoreDeps(workDir, depsUrl, depsHash) {
|
|
|
1887
2010
|
}
|
|
1888
2011
|
throw new Error(`Dep tarball download failed after 3 attempts: ${lastError?.message}`);
|
|
1889
2012
|
}
|
|
1890
|
-
var logger$
|
|
2013
|
+
var logger$9, DOWNLOAD_TIMEOUT_MS$1, SCRATCH_DIR_BASENAME_PREFIX;
|
|
1891
2014
|
var init_dep_restore = __esmMin((() => {
|
|
1892
|
-
logger$
|
|
2015
|
+
logger$9 = createLogger({ prefix: "dep-restore" });
|
|
1893
2016
|
DOWNLOAD_TIMEOUT_MS$1 = 300 * 1e3;
|
|
1894
2017
|
SCRATCH_DIR_BASENAME_PREFIX = ".dep-restore-scratch-";
|
|
1895
2018
|
`${SCRATCH_DIR_BASENAME_PREFIX}`;
|
|
@@ -2001,7 +2124,7 @@ async function extractSourceTarball(data, targetDir) {
|
|
|
2001
2124
|
}
|
|
2002
2125
|
async function restoreSource(workDir, sourceTarUrl) {
|
|
2003
2126
|
sourceTarUrl = resolveOrchestratorUrl(sourceTarUrl);
|
|
2004
|
-
logger$
|
|
2127
|
+
logger$8.info("Restoring .kici/ source from tarball", { sourceTarUrl });
|
|
2005
2128
|
const startTime = Date.now();
|
|
2006
2129
|
let data;
|
|
2007
2130
|
if (sourceTarUrl.startsWith("file://")) {
|
|
@@ -2011,16 +2134,16 @@ async function restoreSource(workDir, sourceTarUrl) {
|
|
|
2011
2134
|
else throw new Error(`Unsupported source tarball URL scheme: ${sourceTarUrl}`);
|
|
2012
2135
|
await extractSourceTarball(data, workDir);
|
|
2013
2136
|
const durationMs = Date.now() - startTime;
|
|
2014
|
-
logger$
|
|
2137
|
+
logger$8.info(".kici/ source restored", {
|
|
2015
2138
|
sizeKB: (data.length / 1024).toFixed(2),
|
|
2016
2139
|
durationMs
|
|
2017
2140
|
});
|
|
2018
2141
|
}
|
|
2019
|
-
var logger$
|
|
2142
|
+
var logger$8;
|
|
2020
2143
|
var init_source_restore = __esmMin((() => {
|
|
2021
2144
|
init_download();
|
|
2022
2145
|
init_dep_restore();
|
|
2023
|
-
logger$
|
|
2146
|
+
logger$8 = createLogger({ prefix: "source-restore" });
|
|
2024
2147
|
}));
|
|
2025
2148
|
//#endregion
|
|
2026
2149
|
//#region src/execution/timeout-util.ts
|
|
@@ -2068,7 +2191,7 @@ function findJobByName(workflow, jobName) {
|
|
|
2068
2191
|
*
|
|
2069
2192
|
* @param workflow - The extracted Workflow object
|
|
2070
2193
|
* @param jobName - Name of the job whose dynamic fields to evaluate
|
|
2071
|
-
* @param event - Normalized
|
|
2194
|
+
* @param event - Normalized event envelope — same shape every dynamic-function call site receives.
|
|
2072
2195
|
* @param flags - Which fields are dynamic and need evaluation
|
|
2073
2196
|
* @param timeoutMs - Timeout per dynamic function call (default 60_000ms)
|
|
2074
2197
|
*/
|
|
@@ -2257,7 +2380,12 @@ async function serializeMatrix(matrix, jobName, runsOn, ctx) {
|
|
|
2257
2380
|
log: ctx.log,
|
|
2258
2381
|
env: ctx.env
|
|
2259
2382
|
};
|
|
2260
|
-
|
|
2383
|
+
let values;
|
|
2384
|
+
try {
|
|
2385
|
+
values = await withTimeout(() => matrix(matrixCtx), DYNAMIC_FIELD_TIMEOUT_MS, `dynamic matrix for generated job '${jobName}'`);
|
|
2386
|
+
} catch (err) {
|
|
2387
|
+
throw new MatrixExpansionError(jobName, `Matrix expansion failed for job '${jobName}': ${err.message}`);
|
|
2388
|
+
}
|
|
2261
2389
|
if (Array.isArray(values)) return {
|
|
2262
2390
|
_type: "static",
|
|
2263
2391
|
values
|
|
@@ -2266,11 +2394,19 @@ async function serializeMatrix(matrix, jobName, runsOn, ctx) {
|
|
|
2266
2394
|
_type: "static",
|
|
2267
2395
|
values
|
|
2268
2396
|
};
|
|
2269
|
-
throw new
|
|
2397
|
+
throw new MatrixExpansionError(jobName, `Job '${jobName}': dynamic matrix function returned an unsupported value (expected array or object, got ${typeof values})`);
|
|
2270
2398
|
}
|
|
2271
|
-
var DYNAMIC_FIELD_TIMEOUT_MS;
|
|
2399
|
+
var MatrixExpansionError, DYNAMIC_FIELD_TIMEOUT_MS;
|
|
2272
2400
|
var init_dynamic_job_serializer = __esmMin((() => {
|
|
2273
2401
|
init_timeout_util();
|
|
2402
|
+
MatrixExpansionError = class MatrixExpansionError extends Error {
|
|
2403
|
+
name = "MatrixExpansionError";
|
|
2404
|
+
constructor(jobName, message) {
|
|
2405
|
+
super(message);
|
|
2406
|
+
this.jobName = jobName;
|
|
2407
|
+
Object.setPrototypeOf(this, MatrixExpansionError.prototype);
|
|
2408
|
+
}
|
|
2409
|
+
};
|
|
2274
2410
|
DYNAMIC_FIELD_TIMEOUT_MS = 6e4;
|
|
2275
2411
|
})), DEFAULT_MAX_LOG_SIZE_BYTES, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_FLUSH_LINE_THRESHOLD, PAUSE_SAFETY_TIMEOUT_MS, LogStreamer;
|
|
2276
2412
|
var init_log_streamer = __esmMin((() => {
|
|
@@ -2539,7 +2675,7 @@ async function applyOverlay(config) {
|
|
|
2539
2675
|
const { tarballUrl, cliPublicKey, orchestratorPrivateKey, repoDir } = config;
|
|
2540
2676
|
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "kici-overlay-"));
|
|
2541
2677
|
try {
|
|
2542
|
-
logger$
|
|
2678
|
+
logger$7.info("Downloading overlay tarball", { url: tarballUrl.replace(/\?.*$/, "?[redacted]") });
|
|
2543
2679
|
let encryptedData;
|
|
2544
2680
|
try {
|
|
2545
2681
|
encryptedData = await downloadUrl(tarballUrl);
|
|
@@ -2549,7 +2685,7 @@ async function applyOverlay(config) {
|
|
|
2549
2685
|
const cliPubKeyBuf = Buffer.from(cliPublicKey, "base64");
|
|
2550
2686
|
const aesKey = deriveSharedSecret(Buffer.from(orchestratorPrivateKey, "base64"), cliPubKeyBuf);
|
|
2551
2687
|
const decryptedData = decryptBuffer(encryptedData, aesKey);
|
|
2552
|
-
logger$
|
|
2688
|
+
logger$7.info("Extracting overlay tarball", { size: decryptedData.length });
|
|
2553
2689
|
const extractDir = path.join(tmpDir, "extracted");
|
|
2554
2690
|
await fs.mkdir(extractDir, { recursive: true });
|
|
2555
2691
|
try {
|
|
@@ -2598,10 +2734,10 @@ async function applyOverlay(config) {
|
|
|
2598
2734
|
await fs.unlink(targetPath);
|
|
2599
2735
|
filesDeleted++;
|
|
2600
2736
|
} catch {
|
|
2601
|
-
logger$
|
|
2737
|
+
logger$7.debug("Deletion target not found, skipping", { file });
|
|
2602
2738
|
}
|
|
2603
2739
|
}
|
|
2604
|
-
logger$
|
|
2740
|
+
logger$7.info("Overlay applied successfully", {
|
|
2605
2741
|
filesApplied,
|
|
2606
2742
|
filesDeleted
|
|
2607
2743
|
});
|
|
@@ -2617,10 +2753,10 @@ async function applyOverlay(config) {
|
|
|
2617
2753
|
}).catch(() => {});
|
|
2618
2754
|
}
|
|
2619
2755
|
}
|
|
2620
|
-
var logger$
|
|
2756
|
+
var logger$7, IV_LENGTH$1, AUTH_TAG_LENGTH;
|
|
2621
2757
|
var init_overlay_applier = __esmMin((() => {
|
|
2622
2758
|
init_download();
|
|
2623
|
-
logger$
|
|
2759
|
+
logger$7 = createLogger({ prefix: "overlay-applier" });
|
|
2624
2760
|
IV_LENGTH$1 = 12;
|
|
2625
2761
|
AUTH_TAG_LENGTH = 16;
|
|
2626
2762
|
}));
|
|
@@ -2934,7 +3070,7 @@ async function detectKiciPackageManager(repoRoot, kiciDir) {
|
|
|
2934
3070
|
async function installDeps(kiciDir, opts = {}) {
|
|
2935
3071
|
const repoRoot = opts.repoRoot ?? dirname(kiciDir);
|
|
2936
3072
|
const packageManager = await detectKiciPackageManager(repoRoot, kiciDir);
|
|
2937
|
-
logger$
|
|
3073
|
+
logger$6.info("Installing deps inline", {
|
|
2938
3074
|
packageManager,
|
|
2939
3075
|
dir: kiciDir
|
|
2940
3076
|
});
|
|
@@ -2975,7 +3111,7 @@ async function installDeps(kiciDir, opts = {}) {
|
|
|
2975
3111
|
if (packageManager === PackageManager.Pnpm && await kiciHasLocalProtocolDeps(kiciDir)) await buildWorkspaceClosure(repoRoot);
|
|
2976
3112
|
const durationMs = Date.now() - startTime;
|
|
2977
3113
|
process.stderr.write(`[dep-installer:trace] install complete: ${durationMs}ms\n`);
|
|
2978
|
-
logger$
|
|
3114
|
+
logger$6.info("Deps installed inline", {
|
|
2979
3115
|
packageManager,
|
|
2980
3116
|
durationMs
|
|
2981
3117
|
});
|
|
@@ -3041,7 +3177,8 @@ async function runPnpmInstall(args) {
|
|
|
3041
3177
|
`--config.store-dir=${storeDir}`,
|
|
3042
3178
|
"--config.package-import-method=copy",
|
|
3043
3179
|
"--config.confirm-modules-purge=false",
|
|
3044
|
-
"--config.side-effects-cache=false"
|
|
3180
|
+
"--config.side-effects-cache=false",
|
|
3181
|
+
PNPM_IGNORE_BUILD_GATE_ARG
|
|
3045
3182
|
];
|
|
3046
3183
|
if (args.hasPrivateRegistry) argv.push("--ignore-scripts");
|
|
3047
3184
|
try {
|
|
@@ -3112,12 +3249,11 @@ function logSubprocessStreams(e, tokens) {
|
|
|
3112
3249
|
if (e && typeof e === "object" && "stdout" in e) process.stderr.write(`[dep-installer:trace] stdout: ${redactNpmOutput(String(e.stdout), tokens).slice(0, 500)}\n`);
|
|
3113
3250
|
if (e && typeof e === "object" && "stderr" in e) process.stderr.write(`[dep-installer:trace] stderr: ${redactNpmOutput(String(e.stderr), tokens).slice(0, 500)}\n`);
|
|
3114
3251
|
}
|
|
3115
|
-
var logger$
|
|
3252
|
+
var logger$6, execFileAsync, INSTALL_TIMEOUT_MS, INSTALL_MAX_BUFFER;
|
|
3116
3253
|
var init_dep_installer = __esmMin((() => {
|
|
3117
|
-
init_npm_resolver();
|
|
3118
3254
|
init_npm_registry_config();
|
|
3119
3255
|
init_validate_kici_deps();
|
|
3120
|
-
logger$
|
|
3256
|
+
logger$6 = createLogger({ prefix: "dep-installer" });
|
|
3121
3257
|
execFileAsync = promisify(execFile);
|
|
3122
3258
|
INSTALL_TIMEOUT_MS = 6e5;
|
|
3123
3259
|
INSTALL_MAX_BUFFER = 128 * 1024 * 1024;
|
|
@@ -3156,7 +3292,7 @@ async function packNodeModules(kiciDir) {
|
|
|
3156
3292
|
const workDir = dirname(kiciDir);
|
|
3157
3293
|
const packageManager = await detectPackageManagerFromManifests(workDir) ?? await detectPackageManagerFromManifests(kiciDir) ?? PackageManager.Npm;
|
|
3158
3294
|
const entries = await closureEntries(workDir, kiciDir, packageManager);
|
|
3159
|
-
logger$
|
|
3295
|
+
logger$5.info("Packing dependency closure into tarball", {
|
|
3160
3296
|
dir: workDir,
|
|
3161
3297
|
packageManager,
|
|
3162
3298
|
entries
|
|
@@ -3172,7 +3308,7 @@ async function packNodeModules(kiciDir) {
|
|
|
3172
3308
|
const tarball = Buffer.concat(chunks);
|
|
3173
3309
|
const hash = sha256(tarball);
|
|
3174
3310
|
const sizeMB = (tarball.length / (1024 * 1024)).toFixed(2);
|
|
3175
|
-
logger$
|
|
3311
|
+
logger$5.info("Dependency closure packed", {
|
|
3176
3312
|
sizeMB,
|
|
3177
3313
|
hash: hash.slice(0, 12),
|
|
3178
3314
|
durationMs: Date.now() - startTime
|
|
@@ -3260,9 +3396,9 @@ function isInside(root, target) {
|
|
|
3260
3396
|
function isAbsoluteRel(rel) {
|
|
3261
3397
|
return rel.length > 1 && rel[1] === ":";
|
|
3262
3398
|
}
|
|
3263
|
-
var logger$
|
|
3399
|
+
var logger$5;
|
|
3264
3400
|
var init_dep_packer = __esmMin((() => {
|
|
3265
|
-
logger$
|
|
3401
|
+
logger$5 = createLogger({ prefix: "dep-packer" });
|
|
3266
3402
|
}));
|
|
3267
3403
|
//#endregion
|
|
3268
3404
|
//#region src/execution/sandbox/env-sanitizer.ts
|
|
@@ -3425,6 +3561,7 @@ function buildRequest(dispatch, workDir) {
|
|
|
3425
3561
|
contentHash: jobConfig.contentHash,
|
|
3426
3562
|
resolvedHashFiles: jobConfig.resolvedHashFiles,
|
|
3427
3563
|
maxLogSizeBytes: dispatch.maxLogSizeBytes,
|
|
3564
|
+
jobTimeoutMs: jobConfig.timeout,
|
|
3428
3565
|
container: jobConfig.container,
|
|
3429
3566
|
event: jobConfig.event,
|
|
3430
3567
|
provider: jobConfig.provider,
|
|
@@ -3666,6 +3803,24 @@ function relayAgentApiRequest(msg, ctx) {
|
|
|
3666
3803
|
error: toErrorMessage(err)
|
|
3667
3804
|
}));
|
|
3668
3805
|
}
|
|
3806
|
+
/** Relay `cache.request` and pipe the orchestrator response (or an error
|
|
3807
|
+
* response, or a "not configured" response when the agent didn't provide
|
|
3808
|
+
* the callback) back into the sandbox runner. */
|
|
3809
|
+
function relayCacheRequest$1(msg, ctx) {
|
|
3810
|
+
if (!ctx.execOptions.onCacheRequest) {
|
|
3811
|
+
safeSendToChild(ctx.child, {
|
|
3812
|
+
type: "cache.response",
|
|
3813
|
+
requestId: msg.requestId,
|
|
3814
|
+
error: "Cache not available in this agent configuration"
|
|
3815
|
+
});
|
|
3816
|
+
return;
|
|
3817
|
+
}
|
|
3818
|
+
ctx.execOptions.onCacheRequest(msg).then((response) => safeSendToChild(ctx.child, response), (err) => safeSendToChild(ctx.child, {
|
|
3819
|
+
type: "cache.response",
|
|
3820
|
+
requestId: msg.requestId,
|
|
3821
|
+
error: toErrorMessage(err)
|
|
3822
|
+
}));
|
|
3823
|
+
}
|
|
3669
3824
|
/** Resolve the result promise for `job.complete` IPC messages. Encrypts
|
|
3670
3825
|
* secret outputs (when a runPublicKey is available) and overrides status to
|
|
3671
3826
|
* `cancelled` if a cancel was already in flight. */
|
|
@@ -3712,7 +3867,9 @@ function relayChildIpcMessage(msg, dispatch, ctx) {
|
|
|
3712
3867
|
ctx.execOptions.onStepStatus(msg.stepIndex, ctx.stepNames.get(msg.stepIndex) ?? "", msg.status, {
|
|
3713
3868
|
durationMs: msg.durationMs,
|
|
3714
3869
|
...msg.error && { error: msg.error },
|
|
3715
|
-
...msg.secretsAccessed && { secretsAccessed: msg.secretsAccessed }
|
|
3870
|
+
...msg.secretsAccessed && { secretsAccessed: msg.secretsAccessed },
|
|
3871
|
+
...msg.step_type && { step_type: msg.step_type },
|
|
3872
|
+
...msg.data && msg.data
|
|
3716
3873
|
});
|
|
3717
3874
|
return;
|
|
3718
3875
|
case "step.secret_mount":
|
|
@@ -3733,6 +3890,9 @@ function relayChildIpcMessage(msg, dispatch, ctx) {
|
|
|
3733
3890
|
case "agent.api.request":
|
|
3734
3891
|
relayAgentApiRequest(msg, ctx);
|
|
3735
3892
|
return;
|
|
3893
|
+
case "cache.request":
|
|
3894
|
+
relayCacheRequest$1(msg, ctx);
|
|
3895
|
+
return;
|
|
3736
3896
|
case "job.complete":
|
|
3737
3897
|
handleJobComplete(msg, dispatch, ctx);
|
|
3738
3898
|
return;
|
|
@@ -3804,6 +3964,7 @@ function buildCancelFn(child, ctx, defaultGracePeriodMs, agentMaxGracePeriodMs)
|
|
|
3804
3964
|
*/
|
|
3805
3965
|
function createForkRunner(options, execOptions) {
|
|
3806
3966
|
const sanitizedEnv = buildSanitizedEnv(options.env);
|
|
3967
|
+
if (options.useBwrap) sanitizedEnv.TMPDIR = "/tmp";
|
|
3807
3968
|
const effectiveWorkDir = options.workDir ?? "/workspace";
|
|
3808
3969
|
const dispatch = execOptions.dispatch;
|
|
3809
3970
|
const { child, pidAssigned } = spawnRunnerChild(options, sanitizedEnv, effectiveWorkDir);
|
|
@@ -3902,10 +4063,10 @@ var init_fork_runner = __esmMin((() => {
|
|
|
3902
4063
|
* network access. This mode provides credential isolation only and should
|
|
3903
4064
|
* be used in trusted environments.
|
|
3904
4065
|
*/
|
|
3905
|
-
var logger$
|
|
4066
|
+
var logger$4, BareMetalSandbox;
|
|
3906
4067
|
var init_bare_metal_sandbox = __esmMin((() => {
|
|
3907
4068
|
init_fork_runner();
|
|
3908
|
-
logger$
|
|
4069
|
+
logger$4 = createLogger({ prefix: "bare-metal-sandbox" });
|
|
3909
4070
|
BareMetalSandbox = class {
|
|
3910
4071
|
runnerPath;
|
|
3911
4072
|
useBwrap;
|
|
@@ -3932,12 +4093,12 @@ var init_bare_metal_sandbox = __esmMin((() => {
|
|
|
3932
4093
|
if (this.useBwrap) try {
|
|
3933
4094
|
const { execSync } = await import("node:child_process");
|
|
3934
4095
|
execSync("which bwrap", { stdio: "ignore" });
|
|
3935
|
-
if (this.sandboxNetwork === "isolated") logger$
|
|
3936
|
-
else logger$
|
|
4096
|
+
if (this.sandboxNetwork === "isolated") logger$4.info("Bubblewrap (bwrap) sandbox enabled with network isolation (--unshare-net)");
|
|
4097
|
+
else logger$4.info("Bubblewrap (bwrap) sandbox enabled with host network (KICI_SANDBOX_NETWORK=host)");
|
|
3937
4098
|
} catch {
|
|
3938
4099
|
throw new Error("Bubblewrap (bwrap) not found. Install bubblewrap or set sandbox=false. On Debian/Ubuntu: apt install bubblewrap");
|
|
3939
4100
|
}
|
|
3940
|
-
else logger$
|
|
4101
|
+
else logger$4.warn("Bare-metal without sandbox provides limited isolation. Only environment sanitization is active. Enable sandbox=true with bubblewrap for PID/IPC/filesystem namespace isolation.");
|
|
3941
4102
|
}
|
|
3942
4103
|
/**
|
|
3943
4104
|
* Execute a job by forking the workflow runner with sanitized environment.
|
|
@@ -4127,6 +4288,32 @@ function relayApiRequest(stream, options, apiMsg) {
|
|
|
4127
4288
|
} catch {}
|
|
4128
4289
|
}
|
|
4129
4290
|
/**
|
|
4291
|
+
* Relay cache.request from the container runner to the orchestrator via
|
|
4292
|
+
* options.onCacheRequest, then write the response back through `stream`. If
|
|
4293
|
+
* the agent doesn't expose a cache relay, write a structured error response
|
|
4294
|
+
* so the runner doesn't hang.
|
|
4295
|
+
*/
|
|
4296
|
+
function relayCacheRequest(stream, options, cacheMsg) {
|
|
4297
|
+
const writeResponse = (response) => {
|
|
4298
|
+
try {
|
|
4299
|
+
stream.write(JSON.stringify(response) + "\n");
|
|
4300
|
+
} catch {}
|
|
4301
|
+
};
|
|
4302
|
+
if (!options.onCacheRequest) {
|
|
4303
|
+
writeResponse({
|
|
4304
|
+
type: "cache.response",
|
|
4305
|
+
requestId: cacheMsg.requestId,
|
|
4306
|
+
error: "Cache not available in this agent configuration"
|
|
4307
|
+
});
|
|
4308
|
+
return;
|
|
4309
|
+
}
|
|
4310
|
+
options.onCacheRequest(cacheMsg).then((response) => writeResponse(response), (err) => writeResponse({
|
|
4311
|
+
type: "cache.response",
|
|
4312
|
+
requestId: cacheMsg.requestId,
|
|
4313
|
+
error: toErrorMessage(err)
|
|
4314
|
+
}));
|
|
4315
|
+
}
|
|
4316
|
+
/**
|
|
4130
4317
|
* Apply a job.complete message to the mutable runner state: capture status,
|
|
4131
4318
|
* merge any bulk-reported step results, propagate plain outputs, and encrypt
|
|
4132
4319
|
* secret outputs if a run public key is available.
|
|
@@ -4141,14 +4328,14 @@ function applyJobComplete(msg, stepResults, state, options) {
|
|
|
4141
4328
|
if (msg.secretOutputs && options.dispatch.runPublicKey) try {
|
|
4142
4329
|
state.encryptedSecretOutputs = encryptSecretOutputs(msg.secretOutputs, options.dispatch.runPublicKey);
|
|
4143
4330
|
} catch (err) {
|
|
4144
|
-
logger$
|
|
4331
|
+
logger$3.warn("Failed to encrypt secret outputs", { error: toErrorMessage(err) });
|
|
4145
4332
|
}
|
|
4146
4333
|
}
|
|
4147
|
-
var logger$
|
|
4334
|
+
var logger$3, MAX_STDERR_LINES, ABORT_GRACE_MS, CONTAINER_STOP_TIMEOUT, ContainerSandbox;
|
|
4148
4335
|
var init_container_sandbox = __esmMin((() => {
|
|
4149
4336
|
init_fork_runner();
|
|
4150
4337
|
init_secret_encryption();
|
|
4151
|
-
logger$
|
|
4338
|
+
logger$3 = createLogger({ prefix: "container-sandbox" });
|
|
4152
4339
|
MAX_STDERR_LINES = 20;
|
|
4153
4340
|
ABORT_GRACE_MS = 1e4;
|
|
4154
4341
|
CONTAINER_STOP_TIMEOUT = 10;
|
|
@@ -4180,7 +4367,7 @@ var init_container_sandbox = __esmMin((() => {
|
|
|
4180
4367
|
async setup(options) {
|
|
4181
4368
|
this.containerName = `kici-sandbox-${this.jobId}-${Date.now()}`;
|
|
4182
4369
|
const envArray = Object.entries(this.env).map(([k, v]) => `${k}=${v}`);
|
|
4183
|
-
logger$
|
|
4370
|
+
logger$3.info("Creating sandbox container", {
|
|
4184
4371
|
name: this.containerName,
|
|
4185
4372
|
image: this.image,
|
|
4186
4373
|
workDir: options.workDir
|
|
@@ -4198,7 +4385,7 @@ var init_container_sandbox = __esmMin((() => {
|
|
|
4198
4385
|
HostConfig: { Binds: [`${options.workDir}:/workspace`, `${this.runnerPath}:${this.runnerMountPath}:ro`] }
|
|
4199
4386
|
});
|
|
4200
4387
|
await this.container.start();
|
|
4201
|
-
logger$
|
|
4388
|
+
logger$3.info("Sandbox container started", {
|
|
4202
4389
|
name: this.containerName,
|
|
4203
4390
|
containerId: this.container.id.slice(0, 12)
|
|
4204
4391
|
});
|
|
@@ -4211,7 +4398,7 @@ var init_container_sandbox = __esmMin((() => {
|
|
|
4211
4398
|
try {
|
|
4212
4399
|
outcome = await this.awaitJobCompletion(streamCtx, options);
|
|
4213
4400
|
} catch (err) {
|
|
4214
|
-
logger$
|
|
4401
|
+
logger$3.error("Job execution error", {
|
|
4215
4402
|
error: toErrorMessage(err),
|
|
4216
4403
|
stderrTail: streamCtx.stderrLines.slice(-5).join("\n")
|
|
4217
4404
|
});
|
|
@@ -4261,7 +4448,7 @@ var init_container_sandbox = __esmMin((() => {
|
|
|
4261
4448
|
});
|
|
4262
4449
|
const abortHandler = () => {
|
|
4263
4450
|
this.handleAbort().catch((err) => {
|
|
4264
|
-
logger$
|
|
4451
|
+
logger$3.warn("Error during abort", { error: toErrorMessage(err) });
|
|
4265
4452
|
});
|
|
4266
4453
|
};
|
|
4267
4454
|
options.signal.addEventListener("abort", abortHandler, { once: true });
|
|
@@ -4298,7 +4485,7 @@ var init_container_sandbox = __esmMin((() => {
|
|
|
4298
4485
|
try {
|
|
4299
4486
|
msg = JSON.parse(line);
|
|
4300
4487
|
} catch {
|
|
4301
|
-
logger$
|
|
4488
|
+
logger$3.warn("Non-JSON output from runner", { line: line.slice(0, 200) });
|
|
4302
4489
|
return;
|
|
4303
4490
|
}
|
|
4304
4491
|
if (this.dispatchRunnerMessage(msg, stream, options, stepNames, stepResults, state)) resolve({
|
|
@@ -4346,7 +4533,9 @@ var init_container_sandbox = __esmMin((() => {
|
|
|
4346
4533
|
options.onStepStatus(msg.stepIndex, name, msg.status, {
|
|
4347
4534
|
durationMs: msg.durationMs,
|
|
4348
4535
|
...msg.error && { error: msg.error },
|
|
4349
|
-
...msg.secretsAccessed && { secretsAccessed: msg.secretsAccessed }
|
|
4536
|
+
...msg.secretsAccessed && { secretsAccessed: msg.secretsAccessed },
|
|
4537
|
+
...msg.step_type && { step_type: msg.step_type },
|
|
4538
|
+
...msg.data && msg.data
|
|
4350
4539
|
});
|
|
4351
4540
|
stepResults.push({
|
|
4352
4541
|
name,
|
|
@@ -4378,11 +4567,14 @@ var init_container_sandbox = __esmMin((() => {
|
|
|
4378
4567
|
case "agent.api.request":
|
|
4379
4568
|
relayApiRequest(stream, options, msg);
|
|
4380
4569
|
return false;
|
|
4570
|
+
case "cache.request":
|
|
4571
|
+
relayCacheRequest(stream, options, msg);
|
|
4572
|
+
return false;
|
|
4381
4573
|
case "job.complete":
|
|
4382
4574
|
applyJobComplete(msg, stepResults, state, options);
|
|
4383
4575
|
return true;
|
|
4384
4576
|
default:
|
|
4385
|
-
logger$
|
|
4577
|
+
logger$3.warn("Unrecognized IPC message from container runner", { type: msg.type });
|
|
4386
4578
|
return false;
|
|
4387
4579
|
}
|
|
4388
4580
|
}
|
|
@@ -4409,14 +4601,14 @@ var init_container_sandbox = __esmMin((() => {
|
|
|
4409
4601
|
async teardown() {
|
|
4410
4602
|
if (!this.container) return;
|
|
4411
4603
|
if (this.keepFailed && this.jobFailed) {
|
|
4412
|
-
logger$
|
|
4604
|
+
logger$3.info("Keeping failed container for debugging", {
|
|
4413
4605
|
name: this.containerName,
|
|
4414
4606
|
containerId: this.container.id.slice(0, 12)
|
|
4415
4607
|
});
|
|
4416
4608
|
this.container = null;
|
|
4417
4609
|
return;
|
|
4418
4610
|
}
|
|
4419
|
-
logger$
|
|
4611
|
+
logger$3.info("Tearing down sandbox container", { name: this.containerName });
|
|
4420
4612
|
try {
|
|
4421
4613
|
await this.container.stop({ t: CONTAINER_STOP_TIMEOUT });
|
|
4422
4614
|
} catch {}
|
|
@@ -4447,7 +4639,7 @@ var init_container_sandbox = __esmMin((() => {
|
|
|
4447
4639
|
*/
|
|
4448
4640
|
async handleAbort() {
|
|
4449
4641
|
if (!this.execStream && !this.container) return;
|
|
4450
|
-
logger$
|
|
4642
|
+
logger$3.info("Aborting sandbox execution", { name: this.containerName });
|
|
4451
4643
|
if (this.execStream) try {
|
|
4452
4644
|
this.execStream.write(JSON.stringify({ type: "abort" }) + "\n");
|
|
4453
4645
|
} catch {}
|
|
@@ -4516,7 +4708,7 @@ function determineExecutionMode(jobConfig, agentConfig) {
|
|
|
4516
4708
|
if (agentConfig.scalerManaged) return "firecracker";
|
|
4517
4709
|
return "bare-metal";
|
|
4518
4710
|
}
|
|
4519
|
-
var logger$
|
|
4711
|
+
var logger$2, JobRunner$1;
|
|
4520
4712
|
var init_job_runner = __esmMin((() => {
|
|
4521
4713
|
init_git_clone();
|
|
4522
4714
|
init_workflow_loader();
|
|
@@ -4533,7 +4725,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
4533
4725
|
init_download();
|
|
4534
4726
|
init_sandbox();
|
|
4535
4727
|
init_prometheus();
|
|
4536
|
-
logger$
|
|
4728
|
+
logger$2 = createLogger({ prefix: "job-runner" });
|
|
4537
4729
|
JobRunner$1 = class {
|
|
4538
4730
|
send;
|
|
4539
4731
|
sendDirect;
|
|
@@ -4547,6 +4739,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
4547
4739
|
_sendRunEvent;
|
|
4548
4740
|
_sendConcurrencyReport;
|
|
4549
4741
|
_sendApiRequest;
|
|
4742
|
+
_requestUserCache;
|
|
4550
4743
|
/** Tracks running jobs for concurrency and cancellation */
|
|
4551
4744
|
activeJobs = /* @__PURE__ */ new Map();
|
|
4552
4745
|
/** Active sandbox for the current job (used for abort). */
|
|
@@ -4564,6 +4757,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
4564
4757
|
this._sendRunEvent = deps.sendRunEvent;
|
|
4565
4758
|
this._sendConcurrencyReport = deps.sendConcurrencyReport;
|
|
4566
4759
|
this._sendApiRequest = deps.sendApiRequest;
|
|
4760
|
+
this._requestUserCache = deps.requestUserCache;
|
|
4567
4761
|
}
|
|
4568
4762
|
/**
|
|
4569
4763
|
* Execute a dispatched job through its full lifecycle.
|
|
@@ -4631,7 +4825,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
4631
4825
|
}
|
|
4632
4826
|
if (jobConfig.buildOnly === true) {
|
|
4633
4827
|
if (jobConfig.fullRepo) {
|
|
4634
|
-
logger$
|
|
4828
|
+
logger$2.warn("Build job received for fullRepo run -- skipping (should not happen)", {
|
|
4635
4829
|
jobId,
|
|
4636
4830
|
runId
|
|
4637
4831
|
});
|
|
@@ -4653,7 +4847,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
4653
4847
|
async executeStandardJob(dispatch, workDir, abortController) {
|
|
4654
4848
|
const { runId, jobId } = dispatch;
|
|
4655
4849
|
const ctx = getRequestContext();
|
|
4656
|
-
logger$
|
|
4850
|
+
logger$2.info(`Run: ${ctx.runId ?? runId} | Trace: ${ctx.requestId ?? "N/A"}`);
|
|
4657
4851
|
this.sendJobStatus(dispatch, ExecutionJobStatus.enum.running);
|
|
4658
4852
|
const heartbeatTimer = setInterval(() => {
|
|
4659
4853
|
this.send({
|
|
@@ -4678,7 +4872,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
4678
4872
|
if (sandbox) {
|
|
4679
4873
|
this.emitRunEvent(runId, "agent.teardown", { jobId });
|
|
4680
4874
|
await sandbox.teardown().catch((err) => {
|
|
4681
|
-
logger$
|
|
4875
|
+
logger$2.warn("Sandbox teardown error", { error: toErrorMessage(err) });
|
|
4682
4876
|
});
|
|
4683
4877
|
}
|
|
4684
4878
|
}
|
|
@@ -4706,7 +4900,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
4706
4900
|
environmentVars: typedConfig.environmentVars ?? void 0,
|
|
4707
4901
|
jobEnv: typedConfig.jobEnv ?? void 0
|
|
4708
4902
|
});
|
|
4709
|
-
logger$
|
|
4903
|
+
logger$2.info("Creating execution sandbox", {
|
|
4710
4904
|
executionMode,
|
|
4711
4905
|
jobId,
|
|
4712
4906
|
runnerPath
|
|
@@ -4777,6 +4971,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
4777
4971
|
onStepStatus: (stepIndex, stepName, state, data) => {
|
|
4778
4972
|
let logBytesStreamed;
|
|
4779
4973
|
if (state === ExecutionStepStatus.enum.success || state === ExecutionStepStatus.enum.failed || state === ExecutionStepStatus.enum.skipped) logBytesStreamed = logStreamers.get(stepIndex)?.getTotalBytes() ?? 0;
|
|
4974
|
+
this.maybeEmitCacheRunEvent(runId, jobId, stepIndex, state, data);
|
|
4780
4975
|
this.sendStepStatus(dispatch, stepIndex, stepName, state, data, logBytesStreamed);
|
|
4781
4976
|
},
|
|
4782
4977
|
onLogLine: (stepIndex, line) => {
|
|
@@ -4801,6 +4996,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
4801
4996
|
};
|
|
4802
4997
|
},
|
|
4803
4998
|
onApiRequest: this._sendApiRequest ? async (method, params) => this._sendApiRequest(method, params) : void 0,
|
|
4999
|
+
onCacheRequest: this._requestUserCache ? async (request) => this._requestUserCache(jobId, request) : void 0,
|
|
4804
5000
|
onSecretMount: (event) => {
|
|
4805
5001
|
this.emitRunEvent(runId, "step.secret_mount", {
|
|
4806
5002
|
jobId,
|
|
@@ -4834,7 +5030,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
4834
5030
|
stepsTotal.add(1, { status: stepResult.status });
|
|
4835
5031
|
if (stepResult.durationMs > 0) stepDurationSeconds.record(stepResult.durationMs / 1e3);
|
|
4836
5032
|
}
|
|
4837
|
-
if (result.status === ExecutionJobStatus.enum.failed) logger$
|
|
5033
|
+
if (result.status === ExecutionJobStatus.enum.failed) logger$2.error("Sandbox returned failed result", {
|
|
4838
5034
|
durationMs: result.durationMs,
|
|
4839
5035
|
stepCount: result.stepResults.length,
|
|
4840
5036
|
steps: result.stepResults.map((r) => `${r.name}:${r.status}`).join(","),
|
|
@@ -4863,7 +5059,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
4863
5059
|
async handleBuildJob(dispatch, workDir, abortController) {
|
|
4864
5060
|
const { runId, jobId, jobConfig } = dispatch;
|
|
4865
5061
|
const buildCtx = getRequestContext();
|
|
4866
|
-
logger$
|
|
5062
|
+
logger$2.info(`Run: ${buildCtx.runId ?? runId} | Trace: ${buildCtx.requestId ?? "N/A"}`);
|
|
4867
5063
|
this.sendJobStatus(dispatch, ExecutionJobStatus.enum.running);
|
|
4868
5064
|
const buildStreamer = this.createStepStreamer(dispatch, 0);
|
|
4869
5065
|
const buildLog = (msg) => buildStreamer.addLine(msg);
|
|
@@ -4952,14 +5148,14 @@ var init_job_runner = __esmMin((() => {
|
|
|
4952
5148
|
const cliPublicKey = jobConfig.cliPublicKey;
|
|
4953
5149
|
const orchestratorPrivateKey = jobConfig.orchestratorPrivateKey;
|
|
4954
5150
|
if (tarballUrl && cliPublicKey && orchestratorPrivateKey) {
|
|
4955
|
-
logger$
|
|
5151
|
+
logger$2.info("Applying overlay tarball for test run", { jobId });
|
|
4956
5152
|
const overlayResult = await applyOverlay({
|
|
4957
5153
|
tarballUrl,
|
|
4958
5154
|
cliPublicKey,
|
|
4959
5155
|
orchestratorPrivateKey,
|
|
4960
5156
|
repoDir: workDir
|
|
4961
5157
|
});
|
|
4962
|
-
logger$
|
|
5158
|
+
logger$2.info("Overlay applied", {
|
|
4963
5159
|
filesApplied: overlayResult.filesApplied,
|
|
4964
5160
|
filesDeleted: overlayResult.filesDeleted
|
|
4965
5161
|
});
|
|
@@ -4987,9 +5183,9 @@ var init_job_runner = __esmMin((() => {
|
|
|
4987
5183
|
platform: os.platform(),
|
|
4988
5184
|
arch: os.arch()
|
|
4989
5185
|
};
|
|
4990
|
-
logger$
|
|
5186
|
+
logger$2.info("Requesting dep upload URL from orchestrator", { lockfileHash: buildConfig.lockfileHash });
|
|
4991
5187
|
const depUploadUrl = await this.requestUploadUrl(dispatch.jobId, "deps", depKey);
|
|
4992
|
-
logger$
|
|
5188
|
+
logger$2.info("Uploading dep tarball to S3", {
|
|
4993
5189
|
size: tarball.length,
|
|
4994
5190
|
hash: hash.slice(0, 12)
|
|
4995
5191
|
});
|
|
@@ -4998,7 +5194,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
4998
5194
|
...depKey,
|
|
4999
5195
|
depsHash: hash
|
|
5000
5196
|
});
|
|
5001
|
-
logger$
|
|
5197
|
+
logger$2.info("Dep tarball upload complete", { lockfileHash: buildConfig.lockfileHash });
|
|
5002
5198
|
buildLog(`Deps tarball uploaded (${tarball.length} bytes)`);
|
|
5003
5199
|
this.sendJobStatus(dispatch, ExecutionJobStatus.enum.running, {
|
|
5004
5200
|
buildEvent: "deps_packed",
|
|
@@ -5024,15 +5220,15 @@ var init_job_runner = __esmMin((() => {
|
|
|
5024
5220
|
platform: os.platform(),
|
|
5025
5221
|
arch: os.arch()
|
|
5026
5222
|
};
|
|
5027
|
-
logger$
|
|
5223
|
+
logger$2.info("Requesting source tarball upload URL from orchestrator", { contentHash: buildConfig.contentHash });
|
|
5028
5224
|
const sourceUploadUrl = await this.requestUploadUrl(dispatch.jobId, "source", sourceKey);
|
|
5029
|
-
logger$
|
|
5225
|
+
logger$2.info("Uploading source tarball to S3", {
|
|
5030
5226
|
size: tarball.length,
|
|
5031
5227
|
contentHash: buildConfig.contentHash
|
|
5032
5228
|
});
|
|
5033
5229
|
await uploadToPresignedUrl(sourceUploadUrl, tarball);
|
|
5034
5230
|
this.sendUploadComplete(dispatch.jobId, "source", sourceKey);
|
|
5035
|
-
logger$
|
|
5231
|
+
logger$2.info("Source tarball upload complete", { contentHash: buildConfig.contentHash });
|
|
5036
5232
|
buildLog(`Source tarball packed and uploaded (${tarball.length} bytes, hash: ${buildConfig.contentHash.slice(0, 12)})`);
|
|
5037
5233
|
this.sendJobStatus(dispatch, ExecutionJobStatus.enum.running, {
|
|
5038
5234
|
buildEvent: "source_packed",
|
|
@@ -5055,7 +5251,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
5055
5251
|
async handleInitJob(dispatch, workDir, abortController) {
|
|
5056
5252
|
const { runId, jobId, jobConfig } = dispatch;
|
|
5057
5253
|
const config = jobConfig;
|
|
5058
|
-
logger$
|
|
5254
|
+
logger$2.info("Starting init job", {
|
|
5059
5255
|
jobId,
|
|
5060
5256
|
targetJobName: config.targetJobName,
|
|
5061
5257
|
workflowName: config.workflowName
|
|
@@ -5103,7 +5299,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
5103
5299
|
}
|
|
5104
5300
|
const kiciDir = join(workDir, ".kici");
|
|
5105
5301
|
const hasPackage = await fileExists(join(kiciDir, "package.json"));
|
|
5106
|
-
logger$
|
|
5302
|
+
logger$2.info("Init job: checking deps", {
|
|
5107
5303
|
kiciDir,
|
|
5108
5304
|
hasPackageJson: hasPackage,
|
|
5109
5305
|
source: config.source
|
|
@@ -5126,7 +5322,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
5126
5322
|
dynamicConcurrencyGroup: config.dynamicConcurrencyGroup
|
|
5127
5323
|
}, config.timeoutMs);
|
|
5128
5324
|
});
|
|
5129
|
-
logger$
|
|
5325
|
+
logger$2.info("Init job completed successfully", {
|
|
5130
5326
|
jobId,
|
|
5131
5327
|
hasEnvironment: initResult.environmentName !== void 0,
|
|
5132
5328
|
hasEnv: initResult.env !== void 0,
|
|
@@ -5142,7 +5338,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
5142
5338
|
});
|
|
5143
5339
|
} catch (err) {
|
|
5144
5340
|
const errorMsg = toErrorMessage(err);
|
|
5145
|
-
logger$
|
|
5341
|
+
logger$2.error("Init job failed", {
|
|
5146
5342
|
jobId,
|
|
5147
5343
|
error: errorMsg
|
|
5148
5344
|
});
|
|
@@ -5169,7 +5365,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
5169
5365
|
const { runId, jobId, jobConfig } = dispatch;
|
|
5170
5366
|
const config = jobConfig;
|
|
5171
5367
|
const timeoutMs = config.timeoutMs ?? 12e4;
|
|
5172
|
-
logger$
|
|
5368
|
+
logger$2.info("Starting DynamicJobFn evaluation", {
|
|
5173
5369
|
jobId,
|
|
5174
5370
|
workflowName: config.workflowName,
|
|
5175
5371
|
sourceIndex: config.source.index
|
|
@@ -5270,7 +5466,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
5270
5466
|
workflowName: config.workflowName
|
|
5271
5467
|
});
|
|
5272
5468
|
});
|
|
5273
|
-
logger$
|
|
5469
|
+
logger$2.info("DynamicJobFn evaluation completed", {
|
|
5274
5470
|
jobId,
|
|
5275
5471
|
generatedJobCount: lockJobs.length,
|
|
5276
5472
|
jobNames: lockJobs.map((j) => j.name)
|
|
@@ -5285,7 +5481,7 @@ var init_job_runner = __esmMin((() => {
|
|
|
5285
5481
|
});
|
|
5286
5482
|
} catch (err) {
|
|
5287
5483
|
const errorMsg = toErrorMessage(err);
|
|
5288
|
-
logger$
|
|
5484
|
+
logger$2.error("DynamicJobFn evaluation failed", {
|
|
5289
5485
|
jobId,
|
|
5290
5486
|
error: errorMsg
|
|
5291
5487
|
});
|
|
@@ -5293,10 +5489,17 @@ var init_job_runner = __esmMin((() => {
|
|
|
5293
5489
|
await evalStreamer.flush();
|
|
5294
5490
|
evalStreamer.destroy();
|
|
5295
5491
|
this.sendStepStatus(dispatch, 0, "evaluate", ExecutionStepStatus.enum.failed, { error: errorMsg }, evalStreamer.getTotalBytes());
|
|
5296
|
-
|
|
5492
|
+
const dynamicData = {
|
|
5297
5493
|
error: errorMsg,
|
|
5298
5494
|
dynamicFailed: true
|
|
5299
|
-
}
|
|
5495
|
+
};
|
|
5496
|
+
if (err instanceof MatrixExpansionError) dynamicData.initFailure = {
|
|
5497
|
+
scope: "job",
|
|
5498
|
+
category: InitFailureCategory.enum.matrix_expansion,
|
|
5499
|
+
message: errorMsg,
|
|
5500
|
+
jobName: err.jobName
|
|
5501
|
+
};
|
|
5502
|
+
this.sendJobStatus(dispatch, ExecutionJobStatus.enum.failed, dynamicData);
|
|
5300
5503
|
} finally {
|
|
5301
5504
|
clearInterval(heartbeatTimer);
|
|
5302
5505
|
}
|
|
@@ -5358,6 +5561,31 @@ var init_job_runner = __esmMin((() => {
|
|
|
5358
5561
|
this._sendRunEvent(runId, eventType, opts);
|
|
5359
5562
|
}
|
|
5360
5563
|
/**
|
|
5564
|
+
* Emit a `cache.restore` / `cache.save` run event for a cache pseudo-step.
|
|
5565
|
+
*
|
|
5566
|
+
* The cache phase tags its `step.complete` IPC with a {@link CacheStepType}
|
|
5567
|
+
* `step_type` and a `data.cacheOutcome` ({@link CacheOutcome}); when one of
|
|
5568
|
+
* those terminal pseudo-step statuses arrives here, mirror it onto the run
|
|
5569
|
+
* timeline as a `run.event` so hit/miss/saved/skipped/error is recorded for
|
|
5570
|
+
* the dashboard. A no-op for regular steps and hooks.
|
|
5571
|
+
*/
|
|
5572
|
+
maybeEmitCacheRunEvent(runId, jobId, stepIndex, state, data) {
|
|
5573
|
+
if (state === ExecutionStepStatus.enum.running) return;
|
|
5574
|
+
const stepType = data?.step_type;
|
|
5575
|
+
const eventType = stepType === CacheStepType.enum["cache:restore"] ? CacheRunEventType.enum["cache.restore"] : stepType === CacheStepType.enum["cache:save"] ? CacheRunEventType.enum["cache.save"] : void 0;
|
|
5576
|
+
if (!eventType) return;
|
|
5577
|
+
this.emitRunEvent(runId, eventType, {
|
|
5578
|
+
jobId,
|
|
5579
|
+
metadata: {
|
|
5580
|
+
stepIndex,
|
|
5581
|
+
...data?.cacheOutcome !== void 0 && { outcome: data.cacheOutcome },
|
|
5582
|
+
...data?.key !== void 0 && { key: data.key },
|
|
5583
|
+
...data?.matchedKey !== void 0 && { matchedKey: data.matchedKey },
|
|
5584
|
+
...data?.bytes !== void 0 && { bytes: data.bytes }
|
|
5585
|
+
}
|
|
5586
|
+
});
|
|
5587
|
+
}
|
|
5588
|
+
/**
|
|
5361
5589
|
* Create a LogStreamer for a synthetic step (build, evaluate, etc.).
|
|
5362
5590
|
*/
|
|
5363
5591
|
createStepStreamer(dispatch, stepIndex) {
|
|
@@ -5441,14 +5669,14 @@ var init_job_runner = __esmMin((() => {
|
|
|
5441
5669
|
*/
|
|
5442
5670
|
init_console_capture();
|
|
5443
5671
|
init_npm_resolver();
|
|
5444
|
-
const AGENT_VERSION = "0.1.
|
|
5445
|
-
const BUILD_COMMIT = "
|
|
5446
|
-
const SDK_VERSION = "0.1.
|
|
5447
|
-
const SDK_BUNDLE_HASH = "
|
|
5448
|
-
const SHARED_VERSION = "0.1.
|
|
5449
|
-
const SHARED_BUNDLE_HASH = "
|
|
5450
|
-
const ENGINE_VERSION = "0.1.
|
|
5451
|
-
const ENGINE_BUNDLE_HASH = "
|
|
5672
|
+
const AGENT_VERSION = "0.1.15";
|
|
5673
|
+
const BUILD_COMMIT = "831f6a763";
|
|
5674
|
+
const SDK_VERSION = "0.1.15";
|
|
5675
|
+
const SDK_BUNDLE_HASH = "cee24b67bbb4a1d2a770e4063dd37a9af33409faeb5ce32f936703b4431316dd";
|
|
5676
|
+
const SHARED_VERSION = "0.1.15";
|
|
5677
|
+
const SHARED_BUNDLE_HASH = "e82a8b68a7d72698674352158c5de4c0d10824c61bfa7166cc8f846044b8b5e4";
|
|
5678
|
+
const ENGINE_VERSION = "0.1.15";
|
|
5679
|
+
const ENGINE_BUNDLE_HASH = "032d6b28b3d32bcbea80ba80a52934b017ee2c86a97fcaf8fe8db0d86d72a7f7";
|
|
5452
5680
|
initTelemetry({
|
|
5453
5681
|
serviceName: "kici-agent",
|
|
5454
5682
|
otlpEndpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT
|
|
@@ -5456,18 +5684,19 @@ initTelemetry({
|
|
|
5456
5684
|
const { connectionStatus, jobsActive, jobsTotal } = await Promise.resolve().then(() => (init_prometheus(), prometheus_exports));
|
|
5457
5685
|
const { JobRunner } = await Promise.resolve().then(() => (init_job_runner(), job_runner_exports));
|
|
5458
5686
|
setServiceName("agent");
|
|
5459
|
-
const logger = createLogger({ prefix: "agent" });
|
|
5687
|
+
const logger$1 = createLogger({ prefix: "agent" });
|
|
5460
5688
|
installConsoleCapture();
|
|
5461
|
-
await guardStartup(logger, async () => {
|
|
5689
|
+
await guardStartup(logger$1, async () => {
|
|
5462
5690
|
const config = loadConfig();
|
|
5463
|
-
logger.info("Agent starting", {
|
|
5691
|
+
logger$1.info("Agent starting", {
|
|
5464
5692
|
agentId: config.agentId,
|
|
5465
5693
|
orchestratorUrl: config.orchestratorUrl,
|
|
5466
5694
|
labels: config.labels,
|
|
5467
5695
|
roles: config.roles,
|
|
5468
5696
|
port: config.port
|
|
5469
5697
|
});
|
|
5470
|
-
|
|
5698
|
+
gcStaleAgentTmpDirs();
|
|
5699
|
+
logger$1.info("agent.build.info", {
|
|
5471
5700
|
agentVersion: AGENT_VERSION,
|
|
5472
5701
|
buildCommit: BUILD_COMMIT,
|
|
5473
5702
|
sdkVersion: SDK_VERSION,
|
|
@@ -5489,7 +5718,7 @@ await guardStartup(logger, async () => {
|
|
|
5489
5718
|
if (toolErrors.length > 0) throw new Error("Agent required-tools validation failed:\n" + toolErrors.map((e) => ` - ${e}`).join("\n"));
|
|
5490
5719
|
if (config.roles === void 0 || config.roles.includes("builder")) {
|
|
5491
5720
|
const npmVersion = verifyNpmAvailable();
|
|
5492
|
-
logger.info("Builder role: npm verified", { npmVersion });
|
|
5721
|
+
logger$1.info("Builder role: npm verified", { npmVersion });
|
|
5493
5722
|
}
|
|
5494
5723
|
let isDraining = false;
|
|
5495
5724
|
let idleShutdownTimer;
|
|
@@ -5506,7 +5735,8 @@ await guardStartup(logger, async () => {
|
|
|
5506
5735
|
sendJobContext: (runId, jobId, context) => client.sendJobContext(runId, jobId, context),
|
|
5507
5736
|
sendRunEvent: (runId, eventType, opts) => client.sendRunEvent(runId, eventType, opts),
|
|
5508
5737
|
sendConcurrencyReport: (runId, jobId, group) => client.sendConcurrencyReport(runId, jobId, group),
|
|
5509
|
-
sendApiRequest: (method, params) => client.sendApiRequest(method, params ?? {})
|
|
5738
|
+
sendApiRequest: (method, params) => client.sendApiRequest(method, params ?? {}),
|
|
5739
|
+
requestUserCache: (jobId, request) => client.requestUserCache(jobId, request)
|
|
5510
5740
|
});
|
|
5511
5741
|
/** Build and send an agent.status message with dynamic OS metadata. */
|
|
5512
5742
|
function sendAgentStatus() {
|
|
@@ -5530,15 +5760,31 @@ await guardStartup(logger, async () => {
|
|
|
5530
5760
|
jobId: dispatch.jobId
|
|
5531
5761
|
}, () => {
|
|
5532
5762
|
if (isDraining) {
|
|
5533
|
-
logger.info("Draining: rejecting job dispatch", { jobId: dispatch.jobId });
|
|
5763
|
+
logger$1.info("Draining: rejecting job dispatch", { jobId: dispatch.jobId });
|
|
5764
|
+
client.sendDirect({
|
|
5765
|
+
type: "job.reject",
|
|
5766
|
+
messageId: randomUUID(),
|
|
5767
|
+
runId: dispatch.runId,
|
|
5768
|
+
jobId: dispatch.jobId,
|
|
5769
|
+
reason: "draining",
|
|
5770
|
+
timestamp: Date.now()
|
|
5771
|
+
});
|
|
5534
5772
|
sendAgentStatus();
|
|
5535
5773
|
return;
|
|
5536
5774
|
}
|
|
5537
5775
|
if (jobRunner.activeJobs.size > 0) {
|
|
5538
|
-
logger.warn("Already running a job,
|
|
5776
|
+
logger$1.warn("Already running a job, rejecting dispatch", {
|
|
5539
5777
|
jobId: dispatch.jobId,
|
|
5540
5778
|
activeJobs: jobRunner.activeJobs.size
|
|
5541
5779
|
});
|
|
5780
|
+
client.sendDirect({
|
|
5781
|
+
type: "job.reject",
|
|
5782
|
+
messageId: randomUUID(),
|
|
5783
|
+
runId: dispatch.runId,
|
|
5784
|
+
jobId: dispatch.jobId,
|
|
5785
|
+
reason: "busy",
|
|
5786
|
+
timestamp: Date.now()
|
|
5787
|
+
});
|
|
5542
5788
|
sendAgentStatus();
|
|
5543
5789
|
return;
|
|
5544
5790
|
}
|
|
@@ -5546,7 +5792,14 @@ await guardStartup(logger, async () => {
|
|
|
5546
5792
|
clearTimeout(idleShutdownTimer);
|
|
5547
5793
|
idleShutdownTimer = void 0;
|
|
5548
5794
|
}
|
|
5549
|
-
|
|
5795
|
+
client.sendDirect({
|
|
5796
|
+
type: "job.ack",
|
|
5797
|
+
messageId: randomUUID(),
|
|
5798
|
+
runId: dispatch.runId,
|
|
5799
|
+
jobId: dispatch.jobId,
|
|
5800
|
+
timestamp: Date.now()
|
|
5801
|
+
});
|
|
5802
|
+
logger$1.info("Accepting job dispatch", {
|
|
5550
5803
|
jobId: dispatch.jobId,
|
|
5551
5804
|
runId: dispatch.runId,
|
|
5552
5805
|
activeJobs: jobRunner.activeJobs.size + 1
|
|
@@ -5555,7 +5808,7 @@ await guardStartup(logger, async () => {
|
|
|
5555
5808
|
jobRunner.execute(dispatch).then(() => {
|
|
5556
5809
|
jobsTotal.add(1, { status: "success" });
|
|
5557
5810
|
}).catch((err) => {
|
|
5558
|
-
logger.error("Job execution error", {
|
|
5811
|
+
logger$1.error("Job execution error", {
|
|
5559
5812
|
jobId: dispatch.jobId,
|
|
5560
5813
|
error: toErrorMessage(err)
|
|
5561
5814
|
});
|
|
@@ -5566,7 +5819,7 @@ await guardStartup(logger, async () => {
|
|
|
5566
5819
|
sendAgentStatus();
|
|
5567
5820
|
if (config.scalerManaged && jobRunner.activeJobs.size === 0) {
|
|
5568
5821
|
if (client.state !== "registered") {
|
|
5569
|
-
logger.info("Scaler-managed agent idle but disconnected, deferring shutdown until reconnected");
|
|
5822
|
+
logger$1.info("Scaler-managed agent idle but disconnected, deferring shutdown until reconnected");
|
|
5570
5823
|
return;
|
|
5571
5824
|
}
|
|
5572
5825
|
startIdleShutdownTimer();
|
|
@@ -5575,7 +5828,7 @@ await guardStartup(logger, async () => {
|
|
|
5575
5828
|
});
|
|
5576
5829
|
},
|
|
5577
5830
|
onJobCancel: (cancel) => {
|
|
5578
|
-
logger.info("Job cancel received", {
|
|
5831
|
+
logger$1.info("Job cancel received", {
|
|
5579
5832
|
jobId: cancel.jobId,
|
|
5580
5833
|
reason: cancel.reason
|
|
5581
5834
|
});
|
|
@@ -5593,13 +5846,13 @@ await guardStartup(logger, async () => {
|
|
|
5593
5846
|
const idleMs = config.scalerIdleTimeoutMs;
|
|
5594
5847
|
if (idleShutdownTimer) clearTimeout(idleShutdownTimer);
|
|
5595
5848
|
if (idleMs <= 0) {
|
|
5596
|
-
logger.info("Scaler-managed agent idle after job completion, shutting down");
|
|
5849
|
+
logger$1.info("Scaler-managed agent idle after job completion, shutting down");
|
|
5597
5850
|
gracefulShutdown("scaler-idle");
|
|
5598
5851
|
} else {
|
|
5599
|
-
logger.info(`Scaler-managed agent idle, waiting ${idleMs}ms for follow-up jobs`);
|
|
5852
|
+
logger$1.info(`Scaler-managed agent idle, waiting ${idleMs}ms for follow-up jobs`);
|
|
5600
5853
|
idleShutdownTimer = setTimeout(() => {
|
|
5601
5854
|
if (jobRunner.activeJobs.size === 0) {
|
|
5602
|
-
logger.info("Scaler-managed agent still idle after timeout, shutting down");
|
|
5855
|
+
logger$1.info("Scaler-managed agent still idle after timeout, shutting down");
|
|
5603
5856
|
gracefulShutdown("scaler-idle");
|
|
5604
5857
|
}
|
|
5605
5858
|
}, idleMs);
|
|
@@ -5609,17 +5862,17 @@ await guardStartup(logger, async () => {
|
|
|
5609
5862
|
if (!config.scalerManaged || jobRunner.activeJobs.size > 0) return;
|
|
5610
5863
|
if (pendingDispatch) {
|
|
5611
5864
|
const safetyMs = config.scalerPendingDispatchTimeoutMs;
|
|
5612
|
-
logger.info(`Scaler-managed agent registered with pending bound dispatch, deferring idle shutdown for ${safetyMs}ms`);
|
|
5865
|
+
logger$1.info(`Scaler-managed agent registered with pending bound dispatch, deferring idle shutdown for ${safetyMs}ms`);
|
|
5613
5866
|
if (idleShutdownTimer) clearTimeout(idleShutdownTimer);
|
|
5614
5867
|
idleShutdownTimer = setTimeout(() => {
|
|
5615
5868
|
if (jobRunner.activeJobs.size === 0) {
|
|
5616
|
-
logger.warn("Scaler-managed agent pending-dispatch safety timeout exceeded, shutting down");
|
|
5869
|
+
logger$1.warn("Scaler-managed agent pending-dispatch safety timeout exceeded, shutting down");
|
|
5617
5870
|
gracefulShutdown("scaler-pending-dispatch-timeout");
|
|
5618
5871
|
}
|
|
5619
5872
|
}, safetyMs);
|
|
5620
5873
|
return;
|
|
5621
5874
|
}
|
|
5622
|
-
logger.info("Scaler-managed agent reconnected and idle, starting idle shutdown timer");
|
|
5875
|
+
logger$1.info("Scaler-managed agent reconnected and idle, starting idle shutdown timer");
|
|
5623
5876
|
startIdleShutdownTimer();
|
|
5624
5877
|
};
|
|
5625
5878
|
const wsTransport = new winston.transports.Stream({
|
|
@@ -5629,14 +5882,14 @@ await guardStartup(logger, async () => {
|
|
|
5629
5882
|
} }),
|
|
5630
5883
|
format: winston.format.combine(winston.format.timestamp(), winston.format.json())
|
|
5631
5884
|
});
|
|
5632
|
-
logger.add(wsTransport);
|
|
5885
|
+
logger$1.add(wsTransport);
|
|
5633
5886
|
const envProbes = {};
|
|
5634
5887
|
for (const [k, v] of Object.entries(process.env)) {
|
|
5635
5888
|
if (v === void 0) continue;
|
|
5636
5889
|
if (!/_ENV_PROBE$|_ENV_PROBE_/.test(k)) continue;
|
|
5637
5890
|
envProbes[k] = v.length <= 64 ? v : `${v.slice(0, 61)}...`;
|
|
5638
5891
|
}
|
|
5639
|
-
if (Object.keys(envProbes).length > 0) logger.info("Agent startup env probes (diagnostic)", envProbes);
|
|
5892
|
+
if (Object.keys(envProbes).length > 0) logger$1.info("Agent startup env probes (diagnostic)", envProbes);
|
|
5640
5893
|
client.connect();
|
|
5641
5894
|
connectionStatus.add(1);
|
|
5642
5895
|
const metricsReporter = new MetricsReporter({
|
|
@@ -5676,13 +5929,13 @@ await guardStartup(logger, async () => {
|
|
|
5676
5929
|
fetch: app.fetch,
|
|
5677
5930
|
port: config.port
|
|
5678
5931
|
}, (info) => {
|
|
5679
|
-
logger.info(`Agent started on port ${info.port}`, {
|
|
5932
|
+
logger$1.info(`Agent started on port ${info.port}`, {
|
|
5680
5933
|
port: info.port,
|
|
5681
5934
|
agentId: config.agentId
|
|
5682
5935
|
});
|
|
5683
5936
|
});
|
|
5684
5937
|
const { shutdown: gracefulShutdown } = setupGracefulShutdown({
|
|
5685
|
-
logger,
|
|
5938
|
+
logger: logger$1,
|
|
5686
5939
|
timeoutMs: 1e4,
|
|
5687
5940
|
onForceExit: () => {
|
|
5688
5941
|
for (const job of jobRunner.activeJobs.values()) job.abortController.abort();
|
|
@@ -5700,7 +5953,7 @@ await guardStartup(logger, async () => {
|
|
|
5700
5953
|
name: "Waiting for active jobs to complete",
|
|
5701
5954
|
fn: async () => {
|
|
5702
5955
|
if (jobRunner.activeJobs.size > 0) {
|
|
5703
|
-
logger.info("Active jobs remaining", { activeJobs: jobRunner.activeJobs.size });
|
|
5956
|
+
logger$1.info("Active jobs remaining", { activeJobs: jobRunner.activeJobs.size });
|
|
5704
5957
|
await Promise.allSettled([...jobRunner.activeJobs.values()].map((j) => j.completionPromise));
|
|
5705
5958
|
}
|
|
5706
5959
|
}
|
|
@@ -5734,17 +5987,17 @@ await guardStartup(logger, async () => {
|
|
|
5734
5987
|
]
|
|
5735
5988
|
});
|
|
5736
5989
|
process.on("SIGUSR1", () => {
|
|
5737
|
-
logger.info("Received SIGUSR1, entering drain mode");
|
|
5990
|
+
logger$1.info("Received SIGUSR1, entering drain mode");
|
|
5738
5991
|
isDraining = true;
|
|
5739
5992
|
if (jobRunner.activeJobs.size === 0) {
|
|
5740
|
-
logger.info("No active jobs, shutting down immediately");
|
|
5993
|
+
logger$1.info("No active jobs, shutting down immediately");
|
|
5741
5994
|
gracefulShutdown("SIGUSR1-drain");
|
|
5742
5995
|
return;
|
|
5743
5996
|
}
|
|
5744
5997
|
const checkDrained = setInterval(() => {
|
|
5745
5998
|
if (jobRunner.activeJobs.size === 0) {
|
|
5746
5999
|
clearInterval(checkDrained);
|
|
5747
|
-
logger.info("All jobs drained, shutting down");
|
|
6000
|
+
logger$1.info("All jobs drained, shutting down");
|
|
5748
6001
|
gracefulShutdown("SIGUSR1-drain");
|
|
5749
6002
|
}
|
|
5750
6003
|
}, 1e3);
|