@kici-dev/agent 0.1.21 → 0.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +2 -0
- package/dist/execution/job-runner.d.ts +10 -0
- package/dist/execution/reboot.d.ts +26 -0
- package/dist/execution/sandbox/ipc-protocol.d.ts +10 -1
- package/dist/execution/sandbox/step-loop.d.ts +27 -5
- package/dist/execution/sandbox/workflow-runner.d.ts +11 -0
- package/dist/index.js +3 -1
- package/dist/server.js +153 -51
- package/dist/workflow-runner.js +171 -48
- package/package.json +5 -5
- package/sbom.spdx.json +62 -62
package/dist/workflow-runner.js
CHANGED
|
@@ -8,7 +8,7 @@ import path, { dirname, isAbsolute, join, relative, resolve, sep } from "node:pa
|
|
|
8
8
|
import { $ } from "zx";
|
|
9
9
|
import { createLogger, deriveSharedSecret, initZx, normalizeLineEndings, sha256, sha256File, toErrorMessage } from "@kici-dev/shared";
|
|
10
10
|
import { CacheOutcome, CacheStepType, CheckMode, CheckStepOutcome, ExecutionJobStatus, ExecutionStepStatus, TimeoutReason } from "@kici-dev/engine";
|
|
11
|
-
import { buildKiciApi, createStepSecrets, evaluateRules, isDynamicJobFn,
|
|
11
|
+
import { buildKiciApi, buildNeedsContext, createStepSecrets, evaluateRules, isDynamicJobFn, normalizeApproval, normalizeCacheSpecs, provenanceSubjectIsPath, resolveJobOutputs, resolveStepOutputs, setJobOutputsMap, setStepOutputsMap, setStepRefMap } from "@kici-dev/sdk";
|
|
12
12
|
import { OIDC_TOKEN_REQUEST_METHOD } from "@kici-dev/engine/protocol/messages/oidc-token-relay";
|
|
13
13
|
import { sha256File as sha256File$1 } from "@kici-dev/core";
|
|
14
14
|
import { calculateJwkThumbprint, decodeJwt, exportJWK, generateKeyPair } from "jose";
|
|
@@ -1117,6 +1117,13 @@ function createRuleContext(event, changedFiles = [], env = {}) {
|
|
|
1117
1117
|
}
|
|
1118
1118
|
//#endregion
|
|
1119
1119
|
//#region src/execution/sandbox/step-loop.ts
|
|
1120
|
+
/** Result of a rejected drift gate: the run was declined by a reviewer. */
|
|
1121
|
+
var DriftGateRejectedError = class extends Error {
|
|
1122
|
+
constructor(reason) {
|
|
1123
|
+
super(reason ? `approval rejected: ${reason}` : "approval rejected");
|
|
1124
|
+
this.name = "DriftGateRejectedError";
|
|
1125
|
+
}
|
|
1126
|
+
};
|
|
1120
1127
|
/**
|
|
1121
1128
|
* Run one step honoring the run-level {@link CheckMode}, reusing the
|
|
1122
1129
|
* `runIdempotentStep` primitive for checked steps (never hand-rolled branching).
|
|
@@ -1124,10 +1131,13 @@ function createRuleContext(event, changedFiles = [], env = {}) {
|
|
|
1124
1131
|
* - Plain step (no `check`): in apply mode, runs as today; in any check mode it
|
|
1125
1132
|
* is skipped with `no_check` (a side-effecting step can't be safely previewed).
|
|
1126
1133
|
* - Checked step: adapted into an `IdempotentStep` and driven by the primitive
|
|
1127
|
-
* with `dryRun` set in check mode (so `apply`/`run` never fires)
|
|
1128
|
-
*
|
|
1134
|
+
* with `dryRun` set in check mode (so `apply`/`run` never fires). On drift the
|
|
1135
|
+
* summary is emitted as a log line. A `approval: { when: 'drift' }` step in
|
|
1136
|
+
* apply mode passes a `confirm` callback that round-trips a payload-bearing
|
|
1137
|
+
* step-approval; on reject the gate throws (fail-stop). Any other apply-mode
|
|
1138
|
+
* step uses `yes: true`.
|
|
1129
1139
|
*/
|
|
1130
|
-
async function runStepWithCheckMode(step, stepIndex, ctx, checkMode, sendFn) {
|
|
1140
|
+
async function runStepWithCheckMode(step, stepIndex, ctx, checkMode, sendFn, opts) {
|
|
1131
1141
|
if (!step.check) {
|
|
1132
1142
|
if (checkMode !== CheckMode.enum.apply) return {
|
|
1133
1143
|
checkOutcome: CheckStepOutcome.enum.no_check,
|
|
@@ -1139,15 +1149,37 @@ async function runStepWithCheckMode(step, stepIndex, ctx, checkMode, sendFn) {
|
|
|
1139
1149
|
outputs: await step.run(ctx)
|
|
1140
1150
|
};
|
|
1141
1151
|
}
|
|
1142
|
-
|
|
1152
|
+
let lastDrift = null;
|
|
1153
|
+
const adapted = {
|
|
1143
1154
|
name: step.name,
|
|
1144
|
-
check: () =>
|
|
1155
|
+
check: async () => {
|
|
1156
|
+
lastDrift = await step.check(ctx);
|
|
1157
|
+
return lastDrift;
|
|
1158
|
+
},
|
|
1145
1159
|
summarize: step.summarize,
|
|
1146
1160
|
apply: (drift) => step.run(ctx, drift),
|
|
1147
1161
|
whenInSync: step.whenInSync ? () => step.whenInSync(ctx) : void 0
|
|
1148
|
-
}
|
|
1162
|
+
};
|
|
1163
|
+
const driftGate = step.approval !== void 0 && normalizeApproval(step.approval).when === "drift" && checkMode === CheckMode.enum.apply && opts.awaitStepApprovalWithPayload !== void 0;
|
|
1164
|
+
const res = await runIdempotentStep(adapted, {
|
|
1149
1165
|
dryRun: checkMode !== CheckMode.enum.apply,
|
|
1150
|
-
|
|
1166
|
+
...driftGate ? { confirm: async () => {
|
|
1167
|
+
const norm = normalizeApproval(step.approval);
|
|
1168
|
+
const summaryMarkdown = step.summarize(lastDrift);
|
|
1169
|
+
const resolution = await opts.awaitStepApprovalWithPayload({
|
|
1170
|
+
stepIndex,
|
|
1171
|
+
stepName: step.name,
|
|
1172
|
+
clauses: norm.clauses,
|
|
1173
|
+
reason: norm.reason ?? `Approval required for drift in '${step.name}'`,
|
|
1174
|
+
...norm.timeoutSeconds !== void 0 && { timeoutSeconds: norm.timeoutSeconds },
|
|
1175
|
+
payload: {
|
|
1176
|
+
summaryMarkdown,
|
|
1177
|
+
drift: lastDrift
|
|
1178
|
+
}
|
|
1179
|
+
});
|
|
1180
|
+
if (resolution.outcome === "approved") return true;
|
|
1181
|
+
throw new DriftGateRejectedError(resolution.outcome === "expired" ? "approval expired" : resolution.reason);
|
|
1182
|
+
} } : { yes: true },
|
|
1151
1183
|
log: (line) => sendFn({
|
|
1152
1184
|
type: "log.line",
|
|
1153
1185
|
stepIndex,
|
|
@@ -1170,7 +1202,7 @@ async function runStepWithCheckMode(step, stepIndex, ctx, checkMode, sendFn) {
|
|
|
1170
1202
|
*
|
|
1171
1203
|
* Timeout pattern using Promise.race + AbortController, with IPC status reporting.
|
|
1172
1204
|
*/
|
|
1173
|
-
async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, outputsMap, getSecretsAccessLog, getSecretMountRecords, jobDeadlineSignal, checkMode
|
|
1205
|
+
async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, outputsMap, getSecretsAccessLog, getSecretMountRecords, jobDeadlineSignal, checkMode, opts) {
|
|
1174
1206
|
sendFn({
|
|
1175
1207
|
type: "step.start",
|
|
1176
1208
|
stepIndex,
|
|
@@ -1181,7 +1213,7 @@ async function executeStepInLoop(step, stepIndex, ctx, timeoutMs, sendFn, output
|
|
|
1181
1213
|
const timeoutId = setTimeout(() => abortController.abort(), timeoutMs);
|
|
1182
1214
|
try {
|
|
1183
1215
|
const phase = await Promise.race([
|
|
1184
|
-
runStepWithCheckMode(step, stepIndex, ctx, checkMode, sendFn),
|
|
1216
|
+
runStepWithCheckMode(step, stepIndex, ctx, checkMode, sendFn, opts),
|
|
1185
1217
|
new Promise((_, reject) => {
|
|
1186
1218
|
abortController.signal.addEventListener("abort", () => {
|
|
1187
1219
|
reject(/* @__PURE__ */ new Error(`Step '${step.name}' timed out after ${timeoutMs}ms`));
|
|
@@ -1311,14 +1343,17 @@ async function evaluateStepRulesAndMaybeSkip(step, stepIndex, opts) {
|
|
|
1311
1343
|
};
|
|
1312
1344
|
}
|
|
1313
1345
|
/**
|
|
1314
|
-
*
|
|
1315
|
-
* and the harness wired `awaitStepApproval`, block until the
|
|
1316
|
-
* resolves the hold.
|
|
1317
|
-
*
|
|
1346
|
+
* Pre-step manual approval gate. When the step declares `approval` with
|
|
1347
|
+
* `when: 'always'` and the harness wired `awaitStepApproval`, block until the
|
|
1348
|
+
* orchestrator resolves the hold. A `when: 'drift'` gate is NOT handled here —
|
|
1349
|
+
* it fires mid-execution inside `runStepWithCheckMode` once `check()` returns
|
|
1350
|
+
* drift. Returns a failed `StepIterationOutcome` (breaking the loop) on
|
|
1351
|
+
* reject/expired; returns null when approved or when no gate applies.
|
|
1318
1352
|
*/
|
|
1319
1353
|
async function maybeGateStepApproval(step, stepIndex, opts) {
|
|
1320
|
-
if (step.
|
|
1321
|
-
const normalized =
|
|
1354
|
+
if (step.approval === void 0 || !opts.awaitStepApproval) return null;
|
|
1355
|
+
const normalized = normalizeApproval(step.approval);
|
|
1356
|
+
if (normalized.when === "drift") return null;
|
|
1322
1357
|
opts.sendIpc({
|
|
1323
1358
|
type: "log.line",
|
|
1324
1359
|
stepIndex,
|
|
@@ -1434,7 +1469,7 @@ async function runStepIteration(step, stepIndex, opts) {
|
|
|
1434
1469
|
const timeoutMs = step.timeout ?? opts.defaultTimeoutMs;
|
|
1435
1470
|
let result;
|
|
1436
1471
|
try {
|
|
1437
|
-
result = await executeStepInLoop(step, stepIndex, ctx, timeoutMs, opts.sendIpc, opts.outputsMap, opts.getSecretsAccessLog, opts.getSecretMountRecords, opts.jobDeadlineSignal, opts.checkMode ?? CheckMode.enum.apply);
|
|
1472
|
+
result = await executeStepInLoop(step, stepIndex, ctx, timeoutMs, opts.sendIpc, opts.outputsMap, opts.getSecretsAccessLog, opts.getSecretMountRecords, opts.jobDeadlineSignal, opts.checkMode ?? CheckMode.enum.apply, opts);
|
|
1438
1473
|
} finally {
|
|
1439
1474
|
await opts.afterStepApplyEnvFiles?.();
|
|
1440
1475
|
}
|
|
@@ -3076,8 +3111,8 @@ function logSubprocessStreams(e, tokens) {
|
|
|
3076
3111
|
* no Rolldown step at runtime. `@kici-dev/sdk` and host-repo deps resolve via
|
|
3077
3112
|
* Node's normal ESM lookup against `.kici/node_modules/`.
|
|
3078
3113
|
*/
|
|
3079
|
-
const AGENT_SDK_VERSION = "0.1.
|
|
3080
|
-
const AGENT_SDK_BUNDLE_HASH = "
|
|
3114
|
+
const AGENT_SDK_VERSION = "0.1.22";
|
|
3115
|
+
const AGENT_SDK_BUNDLE_HASH = "4b26a42978f77e29db110d47523ec947d3d673c3109fc48141c1a62814bde7a1";
|
|
3081
3116
|
/**
|
|
3082
3117
|
* Register the `@kici-dev/core/ts-loader-hook` oxc-transform ESM loader hook so
|
|
3083
3118
|
* subsequent dynamic `import()` calls for `.ts` / `.tsx` files transform on the
|
|
@@ -3439,7 +3474,7 @@ async function applyOverlay(config) {
|
|
|
3439
3474
|
*/
|
|
3440
3475
|
init_download();
|
|
3441
3476
|
init_dep_restore();
|
|
3442
|
-
const AGENT_VERSION = "0.1.
|
|
3477
|
+
const AGENT_VERSION = "0.1.22";
|
|
3443
3478
|
process.on("uncaughtException", (err) => {
|
|
3444
3479
|
process.stderr.write(`[workflow-runner] UNCAUGHT EXCEPTION: ${err.message}\n`);
|
|
3445
3480
|
if (err.stack) process.stderr.write(`[workflow-runner] Stack: ${err.stack}\n`);
|
|
@@ -3832,35 +3867,49 @@ function waitForApprovalResolution(requestId) {
|
|
|
3832
3867
|
});
|
|
3833
3868
|
}
|
|
3834
3869
|
/**
|
|
3835
|
-
*
|
|
3836
|
-
* `
|
|
3837
|
-
*
|
|
3838
|
-
*
|
|
3870
|
+
* Send a step-approval `approval.request` IPC (relayed by the agent over the WS
|
|
3871
|
+
* as a `step.approval-request`), await the matching `approval.resolved`, and map
|
|
3872
|
+
* it onto a `StepApprovalResolution`. A relay error is treated as a fail-closed
|
|
3873
|
+
* reject. Shared by the `when: 'always'` and `when: 'drift'` callbacks; the
|
|
3874
|
+
* latter carries a drift `payload`.
|
|
3839
3875
|
*/
|
|
3840
|
-
function
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
|
|
3860
|
-
};
|
|
3876
|
+
async function requestStepApproval(req) {
|
|
3877
|
+
const requestId = randomUUID();
|
|
3878
|
+
sendMessage({
|
|
3879
|
+
type: "approval.request",
|
|
3880
|
+
requestId,
|
|
3881
|
+
stepIndex: req.stepIndex,
|
|
3882
|
+
stepName: req.stepName,
|
|
3883
|
+
clauses: req.clauses,
|
|
3884
|
+
reason: req.reason,
|
|
3885
|
+
...req.timeoutSeconds !== void 0 && { timeoutSeconds: req.timeoutSeconds },
|
|
3886
|
+
...req.payload !== void 0 && { payload: req.payload }
|
|
3887
|
+
});
|
|
3888
|
+
const resolution = await waitForApprovalResolution(requestId);
|
|
3889
|
+
if (resolution.error) return {
|
|
3890
|
+
outcome: "rejected",
|
|
3891
|
+
reason: resolution.error
|
|
3892
|
+
};
|
|
3893
|
+
return {
|
|
3894
|
+
outcome: resolution.outcome ?? "rejected",
|
|
3895
|
+
...resolution.reason !== void 0 && { reason: resolution.reason }
|
|
3861
3896
|
};
|
|
3862
3897
|
}
|
|
3863
3898
|
/**
|
|
3899
|
+
* Build the `awaitStepApproval` callback the step loop uses to block on an
|
|
3900
|
+
* `approval` step (`when: 'always'`).
|
|
3901
|
+
*/
|
|
3902
|
+
function buildAwaitStepApproval() {
|
|
3903
|
+
return (req) => requestStepApproval(req);
|
|
3904
|
+
}
|
|
3905
|
+
/**
|
|
3906
|
+
* Build the `awaitStepApprovalWithPayload` callback the step loop uses to block
|
|
3907
|
+
* on a `when: 'drift'` step mid-execution, carrying the computed drift payload.
|
|
3908
|
+
*/
|
|
3909
|
+
function buildAwaitStepApprovalWithPayload() {
|
|
3910
|
+
return (req) => requestStepApproval(req);
|
|
3911
|
+
}
|
|
3912
|
+
/**
|
|
3864
3913
|
* Build the {@link CacheTransport} the sandbox-side cache engine uses to reach
|
|
3865
3914
|
* the orchestrator. Each method sends a `cache.request` IPC (relayed by the
|
|
3866
3915
|
* agent over the WS as a `cache.user.*` message) and awaits the matching
|
|
@@ -4093,6 +4142,75 @@ function createIpcLogger(stepIndex, stepName, sendFn) {
|
|
|
4093
4142
|
}
|
|
4094
4143
|
};
|
|
4095
4144
|
}
|
|
4145
|
+
/** A lock needs entry maps to a base name (single/matrix/host) or a group name. */
|
|
4146
|
+
function needBaseName(need) {
|
|
4147
|
+
if (typeof need === "string") return {
|
|
4148
|
+
kind: "job",
|
|
4149
|
+
key: need
|
|
4150
|
+
};
|
|
4151
|
+
if (need && typeof need === "object") {
|
|
4152
|
+
if ("group" in need) return {
|
|
4153
|
+
kind: "group",
|
|
4154
|
+
key: need.group
|
|
4155
|
+
};
|
|
4156
|
+
if ("name" in need) return {
|
|
4157
|
+
kind: "job",
|
|
4158
|
+
key: need.name
|
|
4159
|
+
};
|
|
4160
|
+
}
|
|
4161
|
+
return null;
|
|
4162
|
+
}
|
|
4163
|
+
/**
|
|
4164
|
+
* Build `ctx.needs` for a job's steps from the dispatch envelope. Reconstructs
|
|
4165
|
+
* an {@link UpstreamSnapshot} from `upstreamJobOutputs` (flat per single job;
|
|
4166
|
+
* `byMatrix` / `byHost` envelopes per fan-out) + `upstreamJobStatuses` (keyed by
|
|
4167
|
+
* each upstream job/child name), then resolves the job's declared needs into the
|
|
4168
|
+
* `{ result, status }` / ordered-array shape via the shared SDK builder. Returns
|
|
4169
|
+
* undefined when the job declares no needs.
|
|
4170
|
+
*/
|
|
4171
|
+
function buildStepNeedsContext(declaredNeeds, upstreamJobOutputs, upstreamJobStatuses) {
|
|
4172
|
+
if (!declaredNeeds || declaredNeeds.length === 0) return void 0;
|
|
4173
|
+
const statuses = upstreamJobStatuses ?? {};
|
|
4174
|
+
const jobs = {};
|
|
4175
|
+
const groups = {};
|
|
4176
|
+
const snapStatuses = {};
|
|
4177
|
+
const resolvedNeeds = [];
|
|
4178
|
+
for (const need of declaredNeeds) {
|
|
4179
|
+
const base = needBaseName(need);
|
|
4180
|
+
if (!base) continue;
|
|
4181
|
+
const childNames = Object.keys(statuses).filter((n) => n.startsWith(`${base.key} (`));
|
|
4182
|
+
if (base.kind === "group" || childNames.length > 0) {
|
|
4183
|
+
groups[base.key] = [...childNames].sort();
|
|
4184
|
+
const envelope = upstreamJobOutputs?.[base.key];
|
|
4185
|
+
const bySuffix = envelopeChildOutputs(envelope);
|
|
4186
|
+
for (const child of childNames) {
|
|
4187
|
+
jobs[child] = bySuffix[child.slice(base.key.length + 2, -1)] ?? {};
|
|
4188
|
+
snapStatuses[child] = statuses[child];
|
|
4189
|
+
}
|
|
4190
|
+
resolvedNeeds.push({ group: base.key });
|
|
4191
|
+
} else {
|
|
4192
|
+
jobs[base.key] = upstreamJobOutputs?.[base.key] ?? {};
|
|
4193
|
+
if (statuses[base.key]) snapStatuses[base.key] = statuses[base.key];
|
|
4194
|
+
resolvedNeeds.push(base.key);
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
return buildNeedsContext({
|
|
4198
|
+
jobs,
|
|
4199
|
+
groups,
|
|
4200
|
+
statuses: snapStatuses
|
|
4201
|
+
}, resolvedNeeds);
|
|
4202
|
+
}
|
|
4203
|
+
/**
|
|
4204
|
+
* Extract per-child output records from a fan-out outputs envelope, keyed by the
|
|
4205
|
+
* combination suffix (matrix `byMatrix`) or hostname (`runsOnAll` `byHost`).
|
|
4206
|
+
* Returns an empty map for a non-envelope value.
|
|
4207
|
+
*/
|
|
4208
|
+
function envelopeChildOutputs(envelope) {
|
|
4209
|
+
if (!envelope) return {};
|
|
4210
|
+
const byMatrix = envelope.byMatrix;
|
|
4211
|
+
const byHost = envelope.byHost;
|
|
4212
|
+
return byMatrix ?? byHost ?? {};
|
|
4213
|
+
}
|
|
4096
4214
|
/**
|
|
4097
4215
|
* Build StepSecrets from the job execution request, wired with the per-step
|
|
4098
4216
|
* file-mount host (used by `ctx.secrets.mountFile` / `exposeFile`).
|
|
@@ -4288,7 +4406,11 @@ function createSandboxStepContext(workDir, stepIndex, stepName, request, maskedS
|
|
|
4288
4406
|
...request.provider && { provider: request.provider },
|
|
4289
4407
|
...request.matrixValues && { matrix: request.matrixValues },
|
|
4290
4408
|
...request.host && { host: request.host },
|
|
4291
|
-
...request.agent && { agent: request.agent }
|
|
4409
|
+
...request.agent && { agent: request.agent },
|
|
4410
|
+
...(() => {
|
|
4411
|
+
const needs = buildStepNeedsContext(request.jobNeeds, request.upstreamJobOutputs, request.upstreamJobStatuses);
|
|
4412
|
+
return needs ? { needs } : {};
|
|
4413
|
+
})()
|
|
4292
4414
|
};
|
|
4293
4415
|
}
|
|
4294
4416
|
/** Raw provider webhook body for ctx.rawPayload — nested in the envelope. */
|
|
@@ -5180,7 +5302,8 @@ async function main() {
|
|
|
5180
5302
|
},
|
|
5181
5303
|
beforeStepEnvFiles: stepEnvHooks.beforeStepEnvFiles,
|
|
5182
5304
|
afterStepApplyEnvFiles: stepEnvHooks.afterStepApplyEnvFiles,
|
|
5183
|
-
awaitStepApproval: buildAwaitStepApproval()
|
|
5305
|
+
awaitStepApproval: buildAwaitStepApproval(),
|
|
5306
|
+
awaitStepApprovalWithPayload: buildAwaitStepApprovalWithPayload()
|
|
5184
5307
|
});
|
|
5185
5308
|
jobDeadline.clear();
|
|
5186
5309
|
await maybeSaveJobCache(jobCacheSpecs, jobCacheRestore, cachePhaseDeps, !aborted && loopResult.status === ExecutionStepStatus.enum.success);
|
|
@@ -5266,6 +5389,6 @@ main().catch((error) => {
|
|
|
5266
5389
|
setTimeout(() => process.exit(1), 100);
|
|
5267
5390
|
});
|
|
5268
5391
|
//#endregion
|
|
5269
|
-
export { createSandboxStepContext, rawPayloadFromEvent };
|
|
5392
|
+
export { buildStepNeedsContext, createSandboxStepContext, rawPayloadFromEvent };
|
|
5270
5393
|
|
|
5271
5394
|
//# sourceMappingURL=workflow-runner.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kici-dev/agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
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
|
"ci",
|
|
@@ -64,10 +64,10 @@
|
|
|
64
64
|
"yaml": "^2.9.0",
|
|
65
65
|
"zod": "^4.4.3",
|
|
66
66
|
"zx": "^8.8.5",
|
|
67
|
-
"@kici-dev/core": "0.1.
|
|
68
|
-
"@kici-dev/engine": "0.1.
|
|
69
|
-
"@kici-dev/sdk": "0.1.
|
|
70
|
-
"@kici-dev/shared": "0.1.
|
|
67
|
+
"@kici-dev/core": "0.1.22",
|
|
68
|
+
"@kici-dev/engine": "0.1.22",
|
|
69
|
+
"@kici-dev/sdk": "0.1.22",
|
|
70
|
+
"@kici-dev/shared": "0.1.22"
|
|
71
71
|
},
|
|
72
72
|
"kici": {
|
|
73
73
|
"metrics": {
|