@kici-dev/compiler 0.1.20 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +14 -0
- package/dist/cli.js +52 -5
- package/dist/commands/check-mode.d.ts +19 -0
- package/dist/commands/check-mode.js +21 -0
- package/dist/commands/compile.js +1 -1
- package/dist/commands/run.js +4 -2
- package/dist/commands/test.d.ts +7 -0
- package/dist/llm-context/llms-full.txt +305 -11
- package/dist/llm-context/llms.txt +1 -0
- package/dist/local-executor/index.js +15 -2
- package/dist/local-executor/job-runner.d.ts +3 -0
- package/dist/local-executor/job-runner.js +52 -4
- package/dist/local-executor/output-streamer.js +3 -1
- package/dist/local-executor/types.d.ts +7 -0
- package/dist/lockfile/generator.js +2 -0
- package/dist/remote/platform-client.d.ts +7 -0
- package/dist/templates/package-json.js +1 -1
- package/dist/test-runner/job-executor.d.ts +6 -1
- package/dist/test-runner/step-context.js +4 -0
- package/dist/types.d.ts +10 -1
- package/package.json +4 -4
- package/sbom.spdx.json +35 -35
package/dist/cli.d.ts
CHANGED
|
@@ -8,4 +8,18 @@ import { Command } from 'commander';
|
|
|
8
8
|
export declare function buildProgram(): Command;
|
|
9
9
|
/** Build the program and parse argv — the bin-shim entry point. */
|
|
10
10
|
export declare function runCli(argv?: string[]): void;
|
|
11
|
+
/**
|
|
12
|
+
* Decide whether this module is the process entry point, tolerating a
|
|
13
|
+
* symlinked `argv[1]`. A `node_modules/.bin/kici` entry is a symlink, and when
|
|
14
|
+
* it points at this compiled `cli.js` (the compiler package declares a `kici`
|
|
15
|
+
* bin), `process.argv[1]` is the symlink path while `import.meta.url` is the
|
|
16
|
+
* real file. A plain `resolve()` comparison sees two different paths and never
|
|
17
|
+
* matches, silently skipping `runCli()` — so `kici compile` (and every other
|
|
18
|
+
* subcommand) becomes a no-op when invoked through the bin symlink.
|
|
19
|
+
* Dereference both sides with `realpathSync` so a symlinked invocation is
|
|
20
|
+
* correctly recognised as the entry point. Falls back to a plain `resolve()`
|
|
21
|
+
* comparison when `argv[1]` doesn't resolve to a real file (e.g. a virtual
|
|
22
|
+
* entry point), preserving the previous behaviour for that edge case.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isMainEntryPoint(argv1: string | undefined, importMetaUrl: string): boolean;
|
|
11
25
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.js
CHANGED
|
@@ -3,10 +3,11 @@ import "./chunk-BTugEXQM.js";
|
|
|
3
3
|
import { shouldSuppressBanner } from "./cli-banner.js";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
|
+
import { realpathSync } from "node:fs";
|
|
6
7
|
import { Argument, Command, Option } from "commander";
|
|
7
8
|
import pc from "picocolors";
|
|
8
9
|
//#region src/cli.ts
|
|
9
|
-
const version = "0.1.
|
|
10
|
+
const version = "0.1.21";
|
|
10
11
|
/**
|
|
11
12
|
* Build the kici Commander program with every command registered. Exported so
|
|
12
13
|
* the surface registry can walk the real command tree without parsing argv (no
|
|
@@ -43,7 +44,7 @@ function buildProgram() {
|
|
|
43
44
|
await fixtureCommand(event, options);
|
|
44
45
|
});
|
|
45
46
|
const runCommand = program.command("run").description("Execute workflows locally or remotely");
|
|
46
|
-
runCommand.command("local").argument("[event]", "Event type (e.g., push, pr:open, schedule) — optional with --pick").description("Execute workflows locally without orchestrator infrastructure").option("-p, --pick", "Interactively pick a workflow and trigger to simulate", false).option("--workflow <name>", "Run only the specified workflow").option("--job <name>", "Run only the specified job (and its dependencies)").option("--branch <name>", "Override detected git branch").option("--sha <hash>", "Override detected git SHA").option("--payload <path>", "Path to explicit event payload JSON file").option("--concurrency <n>", "Max parallel jobs (default: CPU cores)", parseInt).option("--keep-going", "Continue after job failure", false).option("--container", "Use Podman container isolation", false).option("--env <KEY=VALUE>", "Environment variable override (repeatable)", (val, prev) => [...prev, val], []).option("--quiet", "Suppress streaming output", false).option("--json", "Output structured JSON result", false).option("--junit <path>", "Output JUnit XML result").option("--files <path>", "Override changed file paths (repeatable, default: git diff)", (val, prev) => [...prev, val], []).option("--debug", "Verbose internals", false).option("--kici-dir <path>", "Path to .kici directory", ".kici").option("--in-place", "Run against the real working directory instead of an isolated tmp checkout", false).option("--keep", "Always retain the isolated tmp checkout (default: keep only on failure)", false).action(async (event, options) => {
|
|
47
|
+
runCommand.command("local").argument("[event]", "Event type (e.g., push, pr:open, schedule) — optional with --pick").description("Execute workflows locally without orchestrator infrastructure").option("-p, --pick", "Interactively pick a workflow and trigger to simulate", false).option("--workflow <name>", "Run only the specified workflow").option("--job <name>", "Run only the specified job (and its dependencies)").option("--branch <name>", "Override detected git branch").option("--sha <hash>", "Override detected git SHA").option("--payload <path>", "Path to explicit event payload JSON file").option("--concurrency <n>", "Max parallel jobs (default: CPU cores)", parseInt).option("--keep-going", "Continue after job failure", false).option("--container", "Use Podman container isolation", false).option("--env <KEY=VALUE>", "Environment variable override (repeatable)", (val, prev) => [...prev, val], []).option("--quiet", "Suppress streaming output", false).option("--json", "Output structured JSON result", false).option("--junit <path>", "Output JUnit XML result").option("--files <path>", "Override changed file paths (repeatable, default: git diff)", (val, prev) => [...prev, val], []).option("--debug", "Verbose internals", false).option("--kici-dir <path>", "Path to .kici directory", ".kici").option("--in-place", "Run against the real working directory instead of an isolated tmp checkout", false).option("--keep", "Always retain the isolated tmp checkout (default: keep only on failure)", false).option("--check", "Run in check mode: report drift, change nothing", false).option("--fail-on-drift", "In check mode, exit non-zero if any step reports drift", false).action(async (event, options) => {
|
|
47
48
|
if (options.pick && options.workflow) {
|
|
48
49
|
console.error("Error: --pick is mutually exclusive with --workflow.");
|
|
49
50
|
process.exit(2);
|
|
@@ -53,8 +54,20 @@ function buildProgram() {
|
|
|
53
54
|
process.exit(2);
|
|
54
55
|
}
|
|
55
56
|
const { runLocalCommand } = await import("./commands/index.js");
|
|
57
|
+
const { resolveCheckMode } = await import("./commands/check-mode.js");
|
|
58
|
+
let checkMode;
|
|
59
|
+
try {
|
|
60
|
+
checkMode = resolveCheckMode({
|
|
61
|
+
check: options.check,
|
|
62
|
+
failOnDrift: options.failOnDrift
|
|
63
|
+
});
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
66
|
+
process.exit(2);
|
|
67
|
+
}
|
|
56
68
|
const success = await runLocalCommand({
|
|
57
69
|
event,
|
|
70
|
+
checkMode,
|
|
58
71
|
pick: options.pick,
|
|
59
72
|
workflow: options.workflow,
|
|
60
73
|
job: options.job,
|
|
@@ -76,10 +89,22 @@ function buildProgram() {
|
|
|
76
89
|
});
|
|
77
90
|
process.exit(success ? 0 : 1);
|
|
78
91
|
});
|
|
79
|
-
runCommand.command("remote").argument("[fixture]", "Fixture name or glob pattern (omit to list available)").description("Execute fixtures remotely via orchestrator").option("--workflow <name>", "Run a specific workflow directly (bypass triggers)").option("--all", "Run all available fixtures", false).option("--parallel", "Run matching fixtures concurrently", false).option("--no-wait", "Fire and forget (print runIds, don't stream)").option("--quiet", "Suppress output except final result", false).option("--json", "Output structured JSON result", false).option("--junit <path>", "Output JUnit XML result").option("--history", "Show recent run history", false).option("--routing-key <key>", "Override routing key for this run").option("--org <id>", "Target organization (overrides the active org)").option("--orchestrator <name>", "Target orchestrator cluster (overrides the per-org default)").option("--debug", "Verbose internals", false).option("--kici-dir <path>", "Path to .kici directory", ".kici").option("--context <ctx.key=value>", "Inject a namespaced context secret, uploaded encrypted to the orchestrator (repeatable)", (val, prev) => [...prev, val], []).option("--env <KEY=VALUE>", "Provide a per-run secret (repeatable); uploaded encrypted to the orchestrator", (val, prev) => [...prev, val], []).action(async (fixture, options) => {
|
|
92
|
+
runCommand.command("remote").argument("[fixture]", "Fixture name or glob pattern (omit to list available)").description("Execute fixtures remotely via orchestrator").option("--workflow <name>", "Run a specific workflow directly (bypass triggers)").option("--all", "Run all available fixtures", false).option("--parallel", "Run matching fixtures concurrently", false).option("--no-wait", "Fire and forget (print runIds, don't stream)").option("--quiet", "Suppress output except final result", false).option("--json", "Output structured JSON result", false).option("--junit <path>", "Output JUnit XML result").option("--history", "Show recent run history", false).option("--routing-key <key>", "Override routing key for this run").option("--org <id>", "Target organization (overrides the active org)").option("--orchestrator <name>", "Target orchestrator cluster (overrides the per-org default)").option("--debug", "Verbose internals", false).option("--kici-dir <path>", "Path to .kici directory", ".kici").option("--context <ctx.key=value>", "Inject a namespaced context secret, uploaded encrypted to the orchestrator (repeatable)", (val, prev) => [...prev, val], []).option("--env <KEY=VALUE>", "Provide a per-run secret (repeatable); uploaded encrypted to the orchestrator", (val, prev) => [...prev, val], []).option("--check", "Run in check mode: report drift, change nothing", false).option("--fail-on-drift", "In check mode, exit non-zero if any step reports drift", false).action(async (fixture, options) => {
|
|
80
93
|
const { runRemoteCommand } = await import("./commands/index.js");
|
|
94
|
+
const { resolveCheckMode } = await import("./commands/check-mode.js");
|
|
95
|
+
let checkMode;
|
|
96
|
+
try {
|
|
97
|
+
checkMode = resolveCheckMode({
|
|
98
|
+
check: options.check,
|
|
99
|
+
failOnDrift: options.failOnDrift
|
|
100
|
+
});
|
|
101
|
+
} catch (err) {
|
|
102
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
103
|
+
process.exit(2);
|
|
104
|
+
}
|
|
81
105
|
const success = await runRemoteCommand(fixture, {
|
|
82
106
|
...options,
|
|
107
|
+
checkMode,
|
|
83
108
|
envFlags: options.env
|
|
84
109
|
});
|
|
85
110
|
process.exit(success ? 0 : 1);
|
|
@@ -263,8 +288,30 @@ Environment variables:
|
|
|
263
288
|
function runCli(argv = process.argv) {
|
|
264
289
|
buildProgram().parse(argv);
|
|
265
290
|
}
|
|
266
|
-
|
|
291
|
+
/**
|
|
292
|
+
* Decide whether this module is the process entry point, tolerating a
|
|
293
|
+
* symlinked `argv[1]`. A `node_modules/.bin/kici` entry is a symlink, and when
|
|
294
|
+
* it points at this compiled `cli.js` (the compiler package declares a `kici`
|
|
295
|
+
* bin), `process.argv[1]` is the symlink path while `import.meta.url` is the
|
|
296
|
+
* real file. A plain `resolve()` comparison sees two different paths and never
|
|
297
|
+
* matches, silently skipping `runCli()` — so `kici compile` (and every other
|
|
298
|
+
* subcommand) becomes a no-op when invoked through the bin symlink.
|
|
299
|
+
* Dereference both sides with `realpathSync` so a symlinked invocation is
|
|
300
|
+
* correctly recognised as the entry point. Falls back to a plain `resolve()`
|
|
301
|
+
* comparison when `argv[1]` doesn't resolve to a real file (e.g. a virtual
|
|
302
|
+
* entry point), preserving the previous behaviour for that edge case.
|
|
303
|
+
*/
|
|
304
|
+
function isMainEntryPoint(argv1, importMetaUrl) {
|
|
305
|
+
if (!argv1) return false;
|
|
306
|
+
const modulePath = fileURLToPath(importMetaUrl);
|
|
307
|
+
try {
|
|
308
|
+
return realpathSync(argv1) === realpathSync(modulePath);
|
|
309
|
+
} catch {
|
|
310
|
+
return resolve(argv1) === resolve(modulePath);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (isMainEntryPoint(process.argv[1], import.meta.url)) runCli();
|
|
267
314
|
//#endregion
|
|
268
|
-
export { buildProgram, runCli };
|
|
315
|
+
export { buildProgram, isMainEntryPoint, runCli };
|
|
269
316
|
|
|
270
317
|
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CheckMode } from '@kici-dev/engine';
|
|
2
|
+
/** The two run-mode flags shared by `kici run local` and `kici run remote`. */
|
|
3
|
+
export interface CheckModeFlags {
|
|
4
|
+
/** --check: report drift, change nothing. */
|
|
5
|
+
check?: boolean;
|
|
6
|
+
/** --fail-on-drift: in check mode, exit non-zero if any step reports drift. */
|
|
7
|
+
failOnDrift?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the run {@link CheckMode} from the `--check` / `--fail-on-drift` flags.
|
|
11
|
+
*
|
|
12
|
+
* - no flags -> `apply` (the unchanged default: converge).
|
|
13
|
+
* - `--check` -> `check` (report-only, changes nothing).
|
|
14
|
+
* - `--check --fail-on-drift` -> `check-fail-on-drift` (fails the run on drift).
|
|
15
|
+
*
|
|
16
|
+
* `--fail-on-drift` without `--check` is an error — it only modifies check mode.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveCheckMode(flags: CheckModeFlags): CheckMode;
|
|
19
|
+
//# sourceMappingURL=check-mode.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "../chunk-BTugEXQM.js";
|
|
2
|
+
import { CheckMode } from "@kici-dev/engine";
|
|
3
|
+
//#region src/commands/check-mode.ts
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the run {@link CheckMode} from the `--check` / `--fail-on-drift` flags.
|
|
6
|
+
*
|
|
7
|
+
* - no flags -> `apply` (the unchanged default: converge).
|
|
8
|
+
* - `--check` -> `check` (report-only, changes nothing).
|
|
9
|
+
* - `--check --fail-on-drift` -> `check-fail-on-drift` (fails the run on drift).
|
|
10
|
+
*
|
|
11
|
+
* `--fail-on-drift` without `--check` is an error — it only modifies check mode.
|
|
12
|
+
*/
|
|
13
|
+
function resolveCheckMode(flags) {
|
|
14
|
+
if (flags.failOnDrift && !flags.check) throw new Error("--fail-on-drift requires --check");
|
|
15
|
+
if (flags.check) return flags.failOnDrift ? CheckMode.enum["check-fail-on-drift"] : CheckMode.enum.check;
|
|
16
|
+
return CheckMode.enum.apply;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { resolveCheckMode };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=check-mode.js.map
|
package/dist/commands/compile.js
CHANGED
|
@@ -8,8 +8,8 @@ import "../validation/index.js";
|
|
|
8
8
|
import { computeLockfileHash, detectGitRoot, generateLockFile, serializeLockFile } from "../lockfile/generator.js";
|
|
9
9
|
import "../lockfile/index.js";
|
|
10
10
|
import path from "node:path";
|
|
11
|
-
import pc from "picocolors";
|
|
12
11
|
import { existsSync } from "node:fs";
|
|
12
|
+
import pc from "picocolors";
|
|
13
13
|
import fs from "node:fs/promises";
|
|
14
14
|
import { logger, toErrorMessage } from "@kici-dev/core";
|
|
15
15
|
import { PackageManager, detectPackageManagerSync } from "@kici-dev/core/package-manager";
|
package/dist/commands/run.js
CHANGED
|
@@ -294,7 +294,8 @@ async function runSingleFixture(fixture, ctx, options, config, history) {
|
|
|
294
294
|
},
|
|
295
295
|
workflowName: opts.workflowName,
|
|
296
296
|
inlineLockFile: overlay.inlineLockFile,
|
|
297
|
-
fullRepo: true
|
|
297
|
+
fullRepo: true,
|
|
298
|
+
...options.checkMode && { checkMode: options.checkMode }
|
|
298
299
|
});
|
|
299
300
|
if (triggerResult.status === "rejected") {
|
|
300
301
|
if (!options.quiet) logger.info(pc.red(`Rejected: ${triggerResult.reason ?? "unknown reason"}`));
|
|
@@ -481,7 +482,8 @@ async function runDirectWorkflow(workflowName, options) {
|
|
|
481
482
|
},
|
|
482
483
|
workflowName,
|
|
483
484
|
inlineLockFile: overlay.inlineLockFile,
|
|
484
|
-
fullRepo: true
|
|
485
|
+
fullRepo: true,
|
|
486
|
+
...options.checkMode && { checkMode: options.checkMode }
|
|
485
487
|
});
|
|
486
488
|
if (!options.quiet) logger.info(pc.green(`Run started: ${triggerResult.runId}`));
|
|
487
489
|
if (options.wait === false) return triggerResult.status === "accepted";
|
package/dist/commands/test.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type PayloadOptions } from '../test-runner/payload-builder.js';
|
|
2
|
+
import { type CheckMode } from '@kici-dev/engine';
|
|
2
3
|
/** Options for the kici test command (dry-run trigger preview) */
|
|
3
4
|
export interface TestOptions extends PayloadOptions {
|
|
4
5
|
/** Filter to specific workflow */
|
|
@@ -38,6 +39,12 @@ export interface RemoteRunOptions extends TestOptions {
|
|
|
38
39
|
org?: string;
|
|
39
40
|
/** Target orchestrator cluster name (overrides the per-org default). */
|
|
40
41
|
orchestrator?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Run mode resolved from --check / --fail-on-drift, threaded onto the dispatch
|
|
44
|
+
* payload so the orchestrator runs the agent step loop in the requested mode.
|
|
45
|
+
* Defaults to `apply`.
|
|
46
|
+
*/
|
|
47
|
+
checkMode?: CheckMode;
|
|
41
48
|
}
|
|
42
49
|
/** Result of a single remote fixture run */
|
|
43
50
|
export interface RemoteRunResult {
|
|
@@ -2108,6 +2108,28 @@ const build = step('build', {
|
|
|
2108
2108
|
|
|
2109
2109
|
**StepRunFn type:** `(ctx: StepContext) => Promise<void>`
|
|
2110
2110
|
|
|
2111
|
+
**With a check facet (idempotent step):**
|
|
2112
|
+
|
|
2113
|
+
Add a `check` function to describe _desired state_ instead of a fixed action. When
|
|
2114
|
+
`check` is present, `run` becomes the _apply_ function and receives the drift value
|
|
2115
|
+
`check` returned; `summarize` (required) renders that drift for logs and the
|
|
2116
|
+
dashboard; `whenInSync` optionally produces the step's outputs when already in sync.
|
|
2117
|
+
|
|
2118
|
+
```typescript
|
|
2119
|
+
const configureNginx = step('configure-nginx', {
|
|
2120
|
+
check: async (ctx) => ((await inSync(ctx)) ? null : { want: DESIRED }),
|
|
2121
|
+
summarize: (drift) => `would rewrite nginx.conf (${drift.want.length} bytes)`,
|
|
2122
|
+
run: async (ctx, drift) => {
|
|
2123
|
+
await writeConfig(drift.want);
|
|
2124
|
+
return { reloaded: true };
|
|
2125
|
+
},
|
|
2126
|
+
whenInSync: async () => ({ reloaded: false }),
|
|
2127
|
+
});
|
|
2128
|
+
```
|
|
2129
|
+
|
|
2130
|
+
A checked step can run in apply mode (converge) or `--check` preview mode (report
|
|
2131
|
+
drift, change nothing). See [Idempotent steps and check mode](../idempotent-steps.md).
|
|
2132
|
+
|
|
2111
2133
|
### Per-job resources
|
|
2112
2134
|
|
|
2113
2135
|
`options.resources` declares the CPU and memory the job needs. The orchestrator's auto-scaler uses these numbers to:
|
|
@@ -4021,6 +4043,70 @@ const publish = job('publish', {
|
|
|
4021
4043
|
- The step never holds platform credentials — the request is relayed through the orchestrator, which mints the token on the step's behalf.
|
|
4022
4044
|
- Only available inside a running job step; calling it outside one (for example, during local execution) rejects with a clear error.
|
|
4023
4045
|
|
|
4046
|
+
### ctx.kici.inventory.query(selector?) / .get(agentId)
|
|
4047
|
+
|
|
4048
|
+
Query the **host inventory** — the roster of agents in the caller's orchestrator cluster — from inside a workflow. Each host is a `HostInventoryEntry`:
|
|
4049
|
+
|
|
4050
|
+
```typescript
|
|
4051
|
+
interface HostInventoryEntry {
|
|
4052
|
+
agentId: string;
|
|
4053
|
+
labels: string[]; // flat-string grouping/tags dimension
|
|
4054
|
+
properties: Record<string, string | number | boolean>; // typed host-vars dimension
|
|
4055
|
+
hostname: string | null;
|
|
4056
|
+
platform: string | null;
|
|
4057
|
+
arch: string | null;
|
|
4058
|
+
lifecycleClass: 'static' | 'ephemeral';
|
|
4059
|
+
status: 'ready' | 'unreachable' | 'stale';
|
|
4060
|
+
lastSeen: string; // ISO timestamp
|
|
4061
|
+
}
|
|
4062
|
+
```
|
|
4063
|
+
|
|
4064
|
+
Two dimensions describe a host. **Labels** are flat strings used for grouping and targeting (the same labels `runsOn` / `runsOnAll` match). **Properties** are typed host-vars (`string | number | boolean`) — the place for facts like `region`, `cores`, or `gpu`. A host reports its own properties via the agent's `KICI_PROPERTIES` config, and an operator can pre-declare them with `kici-admin host declare --prop key=value`; the two are shallow-merged (agent-reported keys win).
|
|
4065
|
+
|
|
4066
|
+
```typescript
|
|
4067
|
+
// All hosts:
|
|
4068
|
+
const all = await ctx.kici.inventory.query();
|
|
4069
|
+
|
|
4070
|
+
// Server-side label filter (OR-of-AND include groups, plus exclude):
|
|
4071
|
+
const dbHosts = await ctx.kici.inventory.query({
|
|
4072
|
+
include: [[{ kind: 'exact', value: 'role:db' }]],
|
|
4073
|
+
});
|
|
4074
|
+
|
|
4075
|
+
// Property filtering is client-side — plain JS in the workflow:
|
|
4076
|
+
const euDbHosts = dbHosts.filter((h) => h.properties.region === 'eu');
|
|
4077
|
+
|
|
4078
|
+
// One host by id:
|
|
4079
|
+
const host = await ctx.kici.inventory.get('box-1'); // HostInventoryEntry | null
|
|
4080
|
+
```
|
|
4081
|
+
|
|
4082
|
+
**The label selector is applied server-side** (reusing the same glob/regex matchers as `runsOnAll`). **Property filtering is client-side** — you filter the returned array in plain JavaScript, so there is no query DSL to learn.
|
|
4083
|
+
|
|
4084
|
+
**Headline use — dynamic-job fan-out.** A dynamic-job generator can query the inventory and return one job per matching host, fanning a workflow out across a fleet:
|
|
4085
|
+
|
|
4086
|
+
```typescript
|
|
4087
|
+
const migrate = job('migrate', async (ctx) => {
|
|
4088
|
+
const hosts = await ctx.kici.inventory.query({
|
|
4089
|
+
include: [[{ kind: 'exact', value: 'role:db' }]],
|
|
4090
|
+
});
|
|
4091
|
+
return hosts
|
|
4092
|
+
.filter((h) => h.properties.region === 'eu')
|
|
4093
|
+
.map((h) =>
|
|
4094
|
+
job(`migrate-${h.agentId}`, {
|
|
4095
|
+
runsOn: [h.agentId],
|
|
4096
|
+
run: async (c) => {
|
|
4097
|
+
await c.$`./migrate.sh`;
|
|
4098
|
+
},
|
|
4099
|
+
}),
|
|
4100
|
+
);
|
|
4101
|
+
});
|
|
4102
|
+
```
|
|
4103
|
+
|
|
4104
|
+
A `runsOn` of a single host's `agentId` (as in `runsOn: [h.agentId]` above) **pins the job to that host**: the orchestrator routes it to that agent only, and queues it with the pin if the host is momentarily offline — the same host-pin path `runsOnAll` uses. A `runsOn` with multiple labels or a glob/regex pattern stays ordinary label routing.
|
|
4105
|
+
|
|
4106
|
+
`ctx.kici.inventory` is available to **both** steps and dynamic-job generators (unlike `ctx.kici.oidc.token`, which is job-bound — the inventory is cluster-scoped, not job-bound).
|
|
4107
|
+
|
|
4108
|
+
**Determinism caveat.** The inventory is **live**: it can change between when a dynamic-job generator first runs (at dispatch) and when it re-evaluates (at agent time). Generating jobs from `inventory.query()` therefore inherits the same non-determinism contract as `infrastructure.list()` — KiCI warns when the re-evaluated job set drifts (a sibling job name changed) and hard-errors when a targeted job vanishes. Prefer stable inputs where you can, and treat a fanned-out job set as a snapshot of the roster at generation time.
|
|
4109
|
+
|
|
4024
4110
|
### ctx.attestProvenance({ subject })
|
|
4025
4111
|
|
|
4026
4112
|
Build, sign, and persist a build-provenance attestation for an artifact your step produced. KiCI assembles an in-toto SLSA v1.0 provenance statement whose build identity (`repository`, `ref`, `sha`, run/job ids) comes from the platform — not from the step — so it cannot be spoofed, signs it, and stores a verifiable bundle that the dashboard surfaces and the `kici verify-attestation` CLI checks.
|
|
@@ -5582,7 +5668,7 @@ kici run remote push-main --org xyz789ghi012
|
|
|
5582
5668
|
kici run remote push-main --orchestrator us-east
|
|
5583
5669
|
|
|
5584
5670
|
# Run all push-related fixtures
|
|
5585
|
-
kici run remote push-*
|
|
5671
|
+
kici run remote 'push-*'
|
|
5586
5672
|
|
|
5587
5673
|
# Run everything
|
|
5588
5674
|
kici run remote --all
|
|
@@ -6813,7 +6899,7 @@ The lock file (`kici.lock.json`) is a JSON file with the following top-level fie
|
|
|
6813
6899
|
|
|
6814
6900
|
| Field | Description |
|
|
6815
6901
|
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
6816
|
-
| `schemaVersion` | Lock file schema version (currently
|
|
6902
|
+
| `schemaVersion` | Lock file schema version (currently 20). Incremented on breaking format changes. |
|
|
6817
6903
|
| `source` | Reference to the source file and export (e.g., `{ file: “.kici/workflows/ci.ts”, export: “#default” }`). |
|
|
6818
6904
|
| `contentHash` | SHA-256 of the serialized lock file content (excluding itself). Changes when any workflow, trigger, or job changes. |
|
|
6819
6905
|
| `lockfileHash` | SHA-256 of the detected package manager's lockfile, used as the dependency cache key. The lockfile is `.kici/package-lock.json` for npm, or the repo-root `pnpm-lock.yaml` / `yarn.lock` for a pnpm/yarn workspace; the hash input is prefixed with the manager name so a manager change is a guaranteed cache miss. Omitted when no lockfile exists. |
|
|
@@ -6988,12 +7074,14 @@ kici run remote
|
|
|
6988
7074
|
kici run remote push-main
|
|
6989
7075
|
|
|
6990
7076
|
# Run all fixtures matching a glob
|
|
6991
|
-
kici run remote push-*
|
|
7077
|
+
kici run remote 'push-*'
|
|
6992
7078
|
|
|
6993
7079
|
# Run everything
|
|
6994
7080
|
kici run remote --all
|
|
6995
7081
|
```
|
|
6996
7082
|
|
|
7083
|
+
The single quotes keep your shell from expanding `push-*` against local files, so the pattern reaches KiCI intact for its own fixture-glob matching.
|
|
7084
|
+
|
|
6997
7085
|
## Fixture reference
|
|
6998
7086
|
|
|
6999
7087
|
### Event types
|
|
@@ -7089,7 +7177,7 @@ kici run remote
|
|
|
7089
7177
|
kici run remote push-main
|
|
7090
7178
|
|
|
7091
7179
|
# Glob matching -- run all push-related fixtures
|
|
7092
|
-
kici run remote push-*
|
|
7180
|
+
kici run remote 'push-*'
|
|
7093
7181
|
|
|
7094
7182
|
# Run all fixtures sequentially
|
|
7095
7183
|
kici run remote --all
|
|
@@ -8833,6 +8921,135 @@ Non-push triggers work too — `pr()`, `tag()`, `comment()`, `release()`, `workf
|
|
|
8833
8921
|
|
|
8834
8922
|
---
|
|
8835
8923
|
|
|
8924
|
+
## Idempotent steps and check mode
|
|
8925
|
+
|
|
8926
|
+
Source: https://docs.kici.dev/user/idempotent-steps/
|
|
8927
|
+
|
|
8928
|
+
An **idempotent step** describes _desired state_ rather than a fixed sequence of
|
|
8929
|
+
commands. You give the step a `check` function that inspects the world and a
|
|
8930
|
+
`run` function that converges it. KiCI then executes the workflow in one of two
|
|
8931
|
+
modes:
|
|
8932
|
+
|
|
8933
|
+
- **Apply mode** (the default): for each step, `check()` runs first; on drift the
|
|
8934
|
+
step applies the change; when already in sync the step is skipped.
|
|
8935
|
+
- **Check mode** (`--check`): for each step, `check()` runs and KiCI reports what
|
|
8936
|
+
_would_ change — **without changing anything**. This is the same model as a
|
|
8937
|
+
dry-run plan: you see the drift before any side effect happens.
|
|
8938
|
+
|
|
8939
|
+
This turns a workflow into convergent configuration management: re-running an
|
|
8940
|
+
apply is safe (in-sync steps do nothing), and a check-mode run is a read-only
|
|
8941
|
+
preview you can gate a build on.
|
|
8942
|
+
|
|
8943
|
+
## Authoring a checked step
|
|
8944
|
+
|
|
8945
|
+
Add a `check` facet to the existing `step()` factory. When `check` is present,
|
|
8946
|
+
`run` becomes the _apply_ function and receives the drift value `check`
|
|
8947
|
+
returned:
|
|
8948
|
+
|
|
8949
|
+
```typescript
|
|
8950
|
+
import { step, z } from '@kici-dev/sdk';
|
|
8951
|
+
|
|
8952
|
+
const configureNginx = step('configure-nginx', {
|
|
8953
|
+
// optional schema for the drift value — gives the dashboard a typed shape
|
|
8954
|
+
drift: z.object({ want: z.string() }),
|
|
8955
|
+
|
|
8956
|
+
// read-only inspection; return null when already in the desired state
|
|
8957
|
+
check: async (ctx) => {
|
|
8958
|
+
const current = await ctx.$`nginx -T`;
|
|
8959
|
+
return current.stdout.includes(DESIRED) ? null : { want: DESIRED };
|
|
8960
|
+
},
|
|
8961
|
+
|
|
8962
|
+
// human-readable preview line — REQUIRED when check is set. It is the drift's
|
|
8963
|
+
// serializable face: it streams to the logs and persists for the dashboard.
|
|
8964
|
+
summarize: (drift) => `would rewrite nginx.conf (${drift.want.length} bytes)`,
|
|
8965
|
+
|
|
8966
|
+
// apply — runs only when check returned drift (apply mode); receives that drift
|
|
8967
|
+
run: async (ctx, drift) => {
|
|
8968
|
+
await writeConfig(drift.want);
|
|
8969
|
+
return { reloaded: true };
|
|
8970
|
+
},
|
|
8971
|
+
|
|
8972
|
+
// optional — runs when check returned null, to produce the step's outputs
|
|
8973
|
+
whenInSync: async () => ({ reloaded: false }),
|
|
8974
|
+
});
|
|
8975
|
+
```
|
|
8976
|
+
|
|
8977
|
+
### The facet fields
|
|
8978
|
+
|
|
8979
|
+
| Field | Required | Purpose |
|
|
8980
|
+
| ------------ | ---------------- | ---------------------------------------------------------------------- |
|
|
8981
|
+
| `check` | to opt in | Read-only inspection. Return a drift value, or `null` when in sync. |
|
|
8982
|
+
| `summarize` | when `check` set | Human-readable, serializable preview of the drift. Streams + persists. |
|
|
8983
|
+
| `run` | always | Apply function. With `check`, it receives the drift as its second arg. |
|
|
8984
|
+
| `whenInSync` | optional | Produces the step's outputs when `check` returned `null`. |
|
|
8985
|
+
| `drift` | optional | Schema that validates / shapes the drift value. |
|
|
8986
|
+
|
|
8987
|
+
`summarize` is **required** whenever `check` is declared. `run` and `whenInSync`
|
|
8988
|
+
both produce the same output type — one output shape per step, whichever path
|
|
8989
|
+
runs. Every other step facet (`cache`, `rules`, `continueOnError`, `timeout`,
|
|
8990
|
+
`requireApproval`, `onCancel`, `cleanup`, `outputs`) composes unchanged.
|
|
8991
|
+
|
|
8992
|
+
A plain `step()` without `check` keeps its exact current behavior — the check
|
|
8993
|
+
facet is fully optional.
|
|
8994
|
+
|
|
8995
|
+
## Run modes
|
|
8996
|
+
|
|
8997
|
+
A run carries one of three modes:
|
|
8998
|
+
|
|
8999
|
+
| Mode | CLI flags | Behavior |
|
|
9000
|
+
| --------------------- | ------------------------- | ------------------------------------------------------------------------------------------ |
|
|
9001
|
+
| `apply` | (default, no flags) | Converge: drift ⇒ apply ⇒ **applied**; null ⇒ **in sync** (skipped). |
|
|
9002
|
+
| `check` | `--check` | Preview only: drift ⇒ **would change**; null ⇒ **in sync**. Never applies. Always exits 0. |
|
|
9003
|
+
| `check-fail-on-drift` | `--check --fail-on-drift` | Same as check, but the run **fails** if any step reports drift. |
|
|
9004
|
+
|
|
9005
|
+
Per-step outcomes:
|
|
9006
|
+
|
|
9007
|
+
- **applied** — drift was found and the step applied the change (apply mode).
|
|
9008
|
+
- **in sync** — `check` returned `null`; nothing to do.
|
|
9009
|
+
- **would change** — drift was found in check mode; the change was previewed, not applied.
|
|
9010
|
+
- **no check** — a plain step (no `check`) reached under check mode. A
|
|
9011
|
+
side-effecting step can't be safely previewed, so it is skipped.
|
|
9012
|
+
|
|
9013
|
+
In check mode KiCI never invokes a checked step's `run` (apply) — the preview is
|
|
9014
|
+
guaranteed side-effect-free.
|
|
9015
|
+
|
|
9016
|
+
## Running in check mode
|
|
9017
|
+
|
|
9018
|
+
`--check` and `--fail-on-drift` work on both local and remote runs:
|
|
9019
|
+
|
|
9020
|
+
```bash
|
|
9021
|
+
# Apply (default): converge the workflow.
|
|
9022
|
+
kici run local push
|
|
9023
|
+
kici run remote my-fixture
|
|
9024
|
+
|
|
9025
|
+
# Check: report drift, change nothing. Always exits 0.
|
|
9026
|
+
kici run local push --check
|
|
9027
|
+
kici run remote my-fixture --check
|
|
9028
|
+
|
|
9029
|
+
# Check + fail on drift: exit non-zero (2) locally, or fail the run remotely,
|
|
9030
|
+
# when any step reports drift. Use this as a CI gate ("fail the build if prod
|
|
9031
|
+
# has drifted").
|
|
9032
|
+
kici run local push --check --fail-on-drift
|
|
9033
|
+
```
|
|
9034
|
+
|
|
9035
|
+
`--fail-on-drift` only modifies check mode — passing it without `--check` is an
|
|
9036
|
+
error.
|
|
9037
|
+
|
|
9038
|
+
## Where outcomes show up
|
|
9039
|
+
|
|
9040
|
+
A check-mode run is labeled in the dashboard with a **CHECK MODE — preview**
|
|
9041
|
+
badge on the run header. Each step shows its outcome chip — applied / in sync /
|
|
9042
|
+
would change / no check — and, when drift was detected, the `summarize` line
|
|
9043
|
+
describing what would change. The rendering is read-only.
|
|
9044
|
+
|
|
9045
|
+
## See also
|
|
9046
|
+
|
|
9047
|
+
- [Idempotent SDK helpers](./sdk/idempotent.md) — the `idempotent()` / `idempotentStep()` convenience wrappers, which always apply on drift inside a single step (no run-level check mode).
|
|
9048
|
+
- [Core SDK reference](./sdk/core.md) — the `step()`, `job()`, and `workflow()` factories the check facet extends.
|
|
9049
|
+
- [Lock file and drift](./lock-file-and-drift.md) — how the lock file carries step capability flags.
|
|
9050
|
+
|
|
9051
|
+
---
|
|
9052
|
+
|
|
8836
9053
|
## Private npm registries
|
|
8837
9054
|
|
|
8838
9055
|
Source: https://docs.kici.dev/user/private-registries/
|
|
@@ -9618,7 +9835,82 @@ forge side looks like:
|
|
|
9618
9835
|
Use the App when you can; the `github-repo` preset is a fallback for
|
|
9619
9836
|
repos where you can't install an App.
|
|
9620
9837
|
|
|
9621
|
-
##
|
|
9838
|
+
## One-click setup (recommended)
|
|
9839
|
+
|
|
9840
|
+
`kici-admin source add github --manifest` creates **and** configures the
|
|
9841
|
+
GitHub App for you via GitHub's App Manifest flow. KiCI builds a manifest
|
|
9842
|
+
with the exact permissions, events, webhook URL, and webhook secret baked
|
|
9843
|
+
in, so you never pick permissions, paste a URL, generate a secret, or
|
|
9844
|
+
download a `.pem` by hand — the App is correct by construction.
|
|
9845
|
+
|
|
9846
|
+
```bash
|
|
9847
|
+
kici-admin --url http://<orchestrator-host>:4000 --token $KICI_BOOTSTRAP_ADMIN_TOKEN \
|
|
9848
|
+
source add github --manifest --name my-org --github-org my-org
|
|
9849
|
+
```
|
|
9850
|
+
|
|
9851
|
+
`--github-org <slug>` creates the App under a GitHub **organization** (the
|
|
9852
|
+
`<slug>` is the org's `github.com/<slug>` URL slug, not its display name) — the
|
|
9853
|
+
recommended default, since org-owned Apps can be installed across the org. Drop
|
|
9854
|
+
the flag only when you deliberately want a personal-account App, which can be
|
|
9855
|
+
installed solely on repos you own. You need permission to create Apps in that
|
|
9856
|
+
org (be an org owner, or have the org allow member App creation).
|
|
9857
|
+
|
|
9858
|
+
What happens:
|
|
9859
|
+
|
|
9860
|
+
1. The CLI resolves your org's webhook URL and opens GitHub with a
|
|
9861
|
+
pre-filled App manifest. You click **"Create GitHub App"** once — the
|
|
9862
|
+
only manual step.
|
|
9863
|
+
2. GitHub redirects back to a localhost callback; the CLI exchanges the
|
|
9864
|
+
returned setup code for the App's id, private key, and webhook secret.
|
|
9865
|
+
**The private key is exchanged and stored only on your orchestrator
|
|
9866
|
+
host — it never transits the KiCI Platform.**
|
|
9867
|
+
3. The CLI stores the credentials encrypted under `KICI_SECRET_KEY` and
|
|
9868
|
+
registers the routing key `github:<appId>`, reusing the same storage
|
|
9869
|
+
path as the manual flow.
|
|
9870
|
+
4. It opens the App's install page so you can pick repos, then verifies
|
|
9871
|
+
end-to-end: it waits for the installation, mints an installation token,
|
|
9872
|
+
and confirms repo access before declaring success.
|
|
9873
|
+
|
|
9874
|
+
```
|
|
9875
|
+
$ kici-admin source add github --manifest --name my-org --github-org my-org
|
|
9876
|
+
→ Opening GitHub to create your App…
|
|
9877
|
+
→ ✓ App created (id 12345), credentials captured
|
|
9878
|
+
→ ✓ Stored on orchestrator (encrypted), registered as github:12345
|
|
9879
|
+
→ Install the App on your repos: https://github.com/apps/my-org/installations/new
|
|
9880
|
+
→ ✓ Installation detected (account my-org)
|
|
9881
|
+
→ ✓ Credentials verified (3 repositories reachable)
|
|
9882
|
+
|
|
9883
|
+
GitHub App "my-org" is live.
|
|
9884
|
+
Webhook: https://<platform-host>/webhook/<orgId>/github
|
|
9885
|
+
```
|
|
9886
|
+
|
|
9887
|
+
**Flags:**
|
|
9888
|
+
|
|
9889
|
+
| Flag | Effect |
|
|
9890
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
9891
|
+
| `--name <name>` | The App name on GitHub (required). |
|
|
9892
|
+
| `--github-org <slug>` | Create the App under a GitHub organization instead of your personal account. |
|
|
9893
|
+
| `--no-browser` | Headless mode: the CLI prints a `kici.dev` URL to open, then reads the setup code you paste back. The page is pure client-side — it only displays the short-lived code, which is useless once the CLI exchanges it. |
|
|
9894
|
+
|
|
9895
|
+
The manifest flow always creates a **new** App on GitHub. If a source for
|
|
9896
|
+
that App id already exists on the orchestrator, the command refuses — use
|
|
9897
|
+
`source update` to rotate an existing App's credentials.
|
|
9898
|
+
|
|
9899
|
+
If any step after App creation fails (e.g. storage), the CLI prints the
|
|
9900
|
+
captured App id and writes the private key to a `0600` file, then tells
|
|
9901
|
+
you how to finish with the manual `source add github` command — so a
|
|
9902
|
+
created App is never orphaned.
|
|
9903
|
+
|
|
9904
|
+
Independent-mode orchestrators have no GitHub-App ingress (it is
|
|
9905
|
+
Platform-relayed), so the manifest flow is unavailable there; use a
|
|
9906
|
+
generic webhook source instead.
|
|
9907
|
+
|
|
9908
|
+
## Manual setup (fallback)
|
|
9909
|
+
|
|
9910
|
+
When you'd rather create the App by hand — or your environment can't run
|
|
9911
|
+
the manifest flow — follow these steps.
|
|
9912
|
+
|
|
9913
|
+
### Create the GitHub App on GitHub's side
|
|
9622
9914
|
|
|
9623
9915
|
1. **Decide the App scope.** User-owned Apps can only be installed on
|
|
9624
9916
|
repos you own; organization-owned Apps can be installed anywhere in
|
|
@@ -9694,7 +9986,7 @@ repos where you can't install an App.
|
|
|
9694
9986
|
KiCI runs. Re-install to add repos later — this is live and
|
|
9695
9987
|
revocable without redeploying the App.
|
|
9696
9988
|
|
|
9697
|
-
|
|
9989
|
+
### Register the App with the orchestrator
|
|
9698
9990
|
|
|
9699
9991
|
With the App ID, private key `.pem`, and webhook secret in hand:
|
|
9700
9992
|
|
|
@@ -10273,7 +10565,7 @@ Source: https://docs.kici.dev/architecture/data-flows/
|
|
|
10273
10565
|
|
|
10274
10566
|
This document describes the key data flows through the KiCI architecture: webhook delivery, job execution, developer-initiated remote runs, dependency caching, re-run and cancel, trace ID propagation, internal event routing, and generic webhook ingestion.
|
|
10275
10567
|
|
|
10276
|
-
> **Lock file schema version:** The lock file uses schema version
|
|
10568
|
+
> **Lock file schema version:** The lock file uses schema version 21, which adds the `CheckMode` / `CheckStepOutcome` enums for check-mode step execution on top of v20's `LabelMatcher` (exact/regex) selectors for `runsOn`/`runsOnAll`/`excludeLabels`, v19's `maxParallel`/`failFast` fan-out concurrency, v18's `runsOnAll` host fan-out predicate and `onUnreachable` policy, v17's typed init presets (`mise` / `{ mise }`) and `auto` detection, v16's normalized approval config, v15's per-job init config, v14's declarative cache specs, v11's `LockInlineValue` for pure function inline evaluation, v10's simplified negative patterns (! prefix in repos/paths arrays), v9's global workflow repos matching, and v8's runsOn polymorphic type support.
|
|
10277
10569
|
|
|
10278
10570
|
## Webhook delivery flow
|
|
10279
10571
|
|
|
@@ -10479,7 +10771,7 @@ Dep cache misses alone do **not** trigger a build job. Deps are platform-specifi
|
|
|
10479
10771
|
|
|
10480
10772
|
### Cross-source / no-contentHash workflows
|
|
10481
10773
|
|
|
10482
|
-
- **Lock files without `contentHash`** (schema v1) skip the source cache entirely; agents compile from source. Regenerate lock files with `kici compile` to enable caching. The current lock file schema version is
|
|
10774
|
+
- **Lock files without `contentHash`** (schema v1) skip the source cache entirely; agents compile from source. Regenerate lock files with `kici compile` to enable caching. The current lock file schema version is 21.
|
|
10483
10775
|
- **Cross-source / global-workflow dispatch** (a workflow registered against source A fired by a webhook on source B) bypasses both caches. The registration's lock file entry still carries `contentHash`, but the cross-source path always clone-and-installs — the eval temp dir doesn't ship `@kici-dev/sdk`. The execution agent still verifies `contentHash` against the cloned source for drift detection.
|
|
10484
10776
|
|
|
10485
10777
|
### Build deduplication
|
|
@@ -10518,7 +10810,7 @@ Both source and dep caches use `S3CacheStorage` as the sole backend. The `CacheS
|
|
|
10518
10810
|
|
|
10519
10811
|
Cache keys reflect that source tarballs and deps have different platform characteristics:
|
|
10520
10812
|
|
|
10521
|
-
- **Source:** `source/{contentHash}.tar.gz` — platform-agnostic. Raw TypeScript source is identical regardless of CPU architecture, so one entry is shared across all platforms. `contentHash` is the per-workflow hash from the lock file (`SHA-256(
|
|
10813
|
+
- **Source:** `source/{contentHash}.tar.gz` — platform-agnostic. Raw TypeScript source is identical regardless of CPU architecture, so one entry is shared across all platforms. `contentHash` is the per-workflow hash from the lock file (`SHA-256(COMPILE_SCHEMA_VERSION + ":" + rawSource [+ "\0" + assetDigest])`, where `COMPILE_SCHEMA_VERSION = 5` and line endings are normalized to LF so the hash agrees across platforms).
|
|
10522
10814
|
- **Deps:** `deps/{platform}-{arch}/{lockfileHash}.tar.gz` (e.g., `deps/linux-arm64/def456.tar.gz`) — platform-specific. Native dependencies in `node_modules` differ across architectures, so each platform/arch combination gets its own cache entry.
|
|
10523
10815
|
|
|
10524
10816
|
The orchestrator derives the target platform/arch for dep cache lookups by probing `AgentRegistry.findAvailable()` with the workflow's first job's `runsOn` labels to find a representative matching agent, then using that agent's platform and arch. Falls back to `linux/x64` if no matching agents are registered.
|
|
@@ -11163,7 +11455,9 @@ This model also enables fully self-hosted deployment: all three tiers can run on
|
|
|
11163
11455
|
|
|
11164
11456
|
### Platform
|
|
11165
11457
|
|
|
11166
|
-
The Platform
|
|
11458
|
+
The Platform is KiCI's hosted, multi-tenant control plane. It provides the hosted dashboard (run listing, run detail, live log streaming, settings), identity and authentication (OIDC, personal access tokens, API keys, JWTs), multi-tenant organization / team / role-based access management, billing, and webhook ingestion -- verifying inbound signatures (HMAC-SHA256, timing-safe) and relaying payloads to the correct orchestrator over WebSocket. It aggregates execution telemetry and status forwarded by orchestrators, registers sources, and matchmakes peers for clustering.
|
|
11459
|
+
|
|
11460
|
+
The Platform never processes, stores, or executes customer code, and never sees customer secrets. It routes webhook payloads and aggregates execution status; the code itself only ever lives on the customer's orchestrator and agent tiers. In the execution path the Platform is deliberately thin -- it does not run jobs -- but functionally it is a full platform, not merely a relay. The hosted Platform is EU-sovereign.
|
|
11167
11461
|
|
|
11168
11462
|
### Orchestrator (`@kici-dev/orchestrator`)
|
|
11169
11463
|
|
|
@@ -11198,7 +11492,7 @@ The agent is the execution worker. It runs on customer infrastructure and has fu
|
|
|
11198
11492
|
|
|
11199
11493
|
Shared business logic used by all three tiers. Single source of truth for cross-tier concerns. Has no internal `@kici-dev/*` dependencies -- only a handful of third-party libraries.
|
|
11200
11494
|
|
|
11201
|
-
- Protocol message schemas (Zod-based, direction-specific unions including dashboard REST-over-WS, browser live streaming, test
|
|
11495
|
+
- Protocol message schemas (Zod-based, direction-specific unions including dashboard REST-over-WS, browser live streaming, the test-relay control plane, log pull, run events, peer-to-peer, cluster join, and source registration)
|
|
11202
11496
|
- Provider interfaces (WebhookNormalizer, LockFileFetcher, ChangedFilesFetcher, CloneTokenProvider, RepoUrlBuilder, ContributorResolver, CheckStatusPoster)
|
|
11203
11497
|
- Trigger matching engine (branch, path, event evaluation)
|
|
11204
11498
|
- Execution state machine (11 states, 16 events, pure functions)
|
|
@@ -52,6 +52,7 @@ The full markdown bundle of every page indexed here is available at https://docs
|
|
|
52
52
|
- [Environments](https://docs.kici.dev/user/environments/): Configure deployment environments with variables, secrets, and protection rules
|
|
53
53
|
- [Event system](https://docs.kici.dev/user/events/): How KiCI's event model works -- event types, the registration model, event matching, and circuit breaker protection
|
|
54
54
|
- [Global workflows](https://docs.kici.dev/user/global-workflows/): Cross-repo workflows that run on events from any repo in the same org
|
|
55
|
+
- [Idempotent steps and check mode](https://docs.kici.dev/user/idempotent-steps/): Declare desired state with a step check facet, then run in apply or --check preview mode
|
|
55
56
|
- [Private npm registries](https://docs.kici.dev/user/private-registries/): Authenticate `npm install` against private registries (CodeArtifact, GitHub Packages, Verdaccio, …) from a workflow's `.kici/package.json`
|
|
56
57
|
- [Build provenance and attestations](https://docs.kici.dev/user/provenance/): Generate and verify signed SLSA provenance for the artifacts your workflows build
|
|
57
58
|
- [Secrets](https://docs.kici.dev/user/secrets/): How to access secrets in KiCI workflow steps
|
|
@@ -15,7 +15,7 @@ import path from "node:path";
|
|
|
15
15
|
import pc from "picocolors";
|
|
16
16
|
import { writeFile } from "node:fs/promises";
|
|
17
17
|
import { logger } from "@kici-dev/core";
|
|
18
|
-
import { matchAllWorkflows } from "@kici-dev/engine";
|
|
18
|
+
import { CheckMode, CheckStepOutcome, matchAllWorkflows } from "@kici-dev/engine";
|
|
19
19
|
import os from "node:os";
|
|
20
20
|
//#region src/local-executor/index.ts
|
|
21
21
|
/**
|
|
@@ -169,7 +169,8 @@ async function runWorkflowBody(workflow, ctx, options, secrets, kiciDir, concurr
|
|
|
169
169
|
kiciDir,
|
|
170
170
|
execDir: ctx.execDir,
|
|
171
171
|
jobOutputsMap,
|
|
172
|
-
signal
|
|
172
|
+
signal,
|
|
173
|
+
checkMode: options.checkMode
|
|
173
174
|
});
|
|
174
175
|
},
|
|
175
176
|
isSuccess: (result) => result.status === "success" || result.status === "skipped"
|
|
@@ -305,6 +306,11 @@ async function executeLocal(options) {
|
|
|
305
306
|
if (!isQuiet) logger.info(pc.green(`JUnit XML written to ${options.junit}`));
|
|
306
307
|
}
|
|
307
308
|
if (!isQuiet) displayLocalSummary(workflowResults);
|
|
309
|
+
if (options.checkMode === CheckMode.enum["check-fail-on-drift"] && hasDrift(workflowResults)) {
|
|
310
|
+
if (!isQuiet) logger.info(pc.yellow("Drift detected in check mode (--fail-on-drift): exiting with code 2"));
|
|
311
|
+
if (materialized && !options.keep) await materialized.cleanup();
|
|
312
|
+
process.exit(2);
|
|
313
|
+
}
|
|
308
314
|
return allSucceeded;
|
|
309
315
|
} finally {
|
|
310
316
|
if (materialized) if (allSucceeded && !options.keep) await materialized.cleanup();
|
|
@@ -312,6 +318,13 @@ async function executeLocal(options) {
|
|
|
312
318
|
}
|
|
313
319
|
}
|
|
314
320
|
/**
|
|
321
|
+
* True when any step across all workflows reported drift (`dry-run` outcome) —
|
|
322
|
+
* the signal `check-fail-on-drift` uses to exit non-zero.
|
|
323
|
+
*/
|
|
324
|
+
function hasDrift(results) {
|
|
325
|
+
return results.some((wf) => wf.jobs.some((job) => (job.steps ?? []).some((s) => s.checkOutcome === CheckStepOutcome.enum["dry-run"])));
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
315
328
|
* Get default concurrency based on available CPU parallelism.
|
|
316
329
|
*/
|
|
317
330
|
function getDefaultConcurrency() {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* - Single-job step-by-step execution with rules, abort, and output chaining
|
|
9
9
|
*/
|
|
10
10
|
import type { Workflow, OutputsMap } from '@kici-dev/sdk';
|
|
11
|
+
import { CheckMode } from '@kici-dev/engine';
|
|
11
12
|
import type { SimulatedEvent } from '@kici-dev/engine';
|
|
12
13
|
import type { ParsedSecrets } from '../test-runner/secrets-file.js';
|
|
13
14
|
import type { ResolvedJob, LocalJobResult } from './types.js';
|
|
@@ -24,6 +25,8 @@ export interface JobExecutionContext {
|
|
|
24
25
|
execDir: string;
|
|
25
26
|
jobOutputsMap: OutputsMap;
|
|
26
27
|
signal: AbortSignal;
|
|
28
|
+
/** Run mode for idempotent steps. Defaults to `apply` when unset. */
|
|
29
|
+
checkMode?: CheckMode;
|
|
27
30
|
}
|
|
28
31
|
/**
|
|
29
32
|
* Resolve all jobs in a workflow, expanding matrix jobs and evaluating dynamic jobs.
|
|
@@ -8,7 +8,8 @@ import { toEventPayload } from "./to-event-payload.js";
|
|
|
8
8
|
import { pathToFileURL } from "node:url";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { applyIncludeExclude, expandMatrix, isDynamicJobFn, setJobOutputsMap, setStepOutputsMap, setStepRefMap } from "@kici-dev/sdk";
|
|
11
|
-
import { formatMatrixSuffix } from "@kici-dev/engine";
|
|
11
|
+
import { CheckMode, CheckStepOutcome, formatMatrixSuffix } from "@kici-dev/engine";
|
|
12
|
+
import { runIdempotentStep } from "@kici-dev/core/idempotency";
|
|
12
13
|
//#region src/local-executor/job-runner.ts
|
|
13
14
|
/**
|
|
14
15
|
* Job resolution (matrix/dynamic) and single-job execution with step context.
|
|
@@ -51,6 +52,45 @@ function resolveNeedName(need) {
|
|
|
51
52
|
return need.name;
|
|
52
53
|
}
|
|
53
54
|
/**
|
|
55
|
+
* Run one local step honoring the run-level {@link CheckMode}, reusing the
|
|
56
|
+
* `runIdempotentStep` primitive for checked steps (never hand-rolled branching).
|
|
57
|
+
* Mirrors the agent step loop's check phase so `kici run local --check` and a
|
|
58
|
+
* remote check run produce identical per-step outcomes.
|
|
59
|
+
*/
|
|
60
|
+
async function runLocalStepWithCheckMode(step, ctx, checkMode) {
|
|
61
|
+
if (!step.check) {
|
|
62
|
+
if (checkMode !== CheckMode.enum.apply) return {
|
|
63
|
+
checkOutcome: CheckStepOutcome.enum.no_check,
|
|
64
|
+
status: "skipped",
|
|
65
|
+
outputs: void 0
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
status: "success",
|
|
69
|
+
outputs: await step.run(ctx)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const res = await runIdempotentStep({
|
|
73
|
+
name: step.name,
|
|
74
|
+
check: () => step.check(ctx),
|
|
75
|
+
summarize: step.summarize,
|
|
76
|
+
apply: (drift) => step.run(ctx, drift),
|
|
77
|
+
whenInSync: step.whenInSync ? () => step.whenInSync(ctx) : void 0
|
|
78
|
+
}, {
|
|
79
|
+
dryRun: checkMode !== CheckMode.enum.apply,
|
|
80
|
+
yes: true,
|
|
81
|
+
log: (line) => ctx.log.info(line)
|
|
82
|
+
});
|
|
83
|
+
const driftSummary = res.drift != null ? step.summarize(res.drift) : void 0;
|
|
84
|
+
const status = res.outcome === CheckStepOutcome.enum.applied ? "success" : "skipped";
|
|
85
|
+
const mappedStatus = res.outcome === CheckStepOutcome.enum["dry-run"] ? "success" : status;
|
|
86
|
+
return {
|
|
87
|
+
checkOutcome: res.outcome,
|
|
88
|
+
status: mappedStatus,
|
|
89
|
+
outputs: res.result,
|
|
90
|
+
...driftSummary !== void 0 && { driftSummary }
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
54
94
|
* Maintain the base-name `{ byMatrix, merged }` envelope as each matrix child
|
|
55
95
|
* completes. `merged` is rebuilt last-write-wins in suffix order so the result
|
|
56
96
|
* is deterministic regardless of child completion order — matching the remote
|
|
@@ -101,6 +141,10 @@ async function resolveJobs(workflow, event) {
|
|
|
101
141
|
scalers: [],
|
|
102
142
|
agents: []
|
|
103
143
|
}) },
|
|
144
|
+
inventory: {
|
|
145
|
+
query: () => Promise.resolve([]),
|
|
146
|
+
get: () => Promise.resolve(null)
|
|
147
|
+
},
|
|
104
148
|
oidc: { token: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.oidc.token() is not available during local execution")) }
|
|
105
149
|
}
|
|
106
150
|
});
|
|
@@ -267,14 +311,18 @@ async function executeResolvedJobInner(resolvedJob, context, startTime) {
|
|
|
267
311
|
formatter.logStepStart(expandedName, normalizedStep.name);
|
|
268
312
|
const stepStart = Date.now();
|
|
269
313
|
try {
|
|
270
|
-
const
|
|
314
|
+
const checkMode = context.checkMode ?? CheckMode.enum.apply;
|
|
315
|
+
const phase = await runLocalStepWithCheckMode(normalizedStep, stepCtx, checkMode);
|
|
316
|
+
const outputs = phase.outputs;
|
|
271
317
|
const stepDuration = Date.now() - stepStart;
|
|
272
318
|
formatter.logStepComplete(expandedName, normalizedStep.name, stepDuration);
|
|
273
319
|
const stepResult = {
|
|
274
320
|
name: normalizedStep.name,
|
|
275
|
-
status:
|
|
321
|
+
status: phase.status,
|
|
276
322
|
durationMs: stepDuration,
|
|
277
|
-
outputs
|
|
323
|
+
outputs,
|
|
324
|
+
...phase.checkOutcome !== void 0 && { checkOutcome: phase.checkOutcome },
|
|
325
|
+
...phase.driftSummary !== void 0 && { driftSummary: phase.driftSummary }
|
|
278
326
|
};
|
|
279
327
|
if (outputs != null) outputsMap.set(normalizedStep.name, outputs);
|
|
280
328
|
stepResults.push(stepResult);
|
|
@@ -95,7 +95,9 @@ function formatLocalJsonResult(results) {
|
|
|
95
95
|
name: step.name,
|
|
96
96
|
status: step.status,
|
|
97
97
|
durationMs: step.durationMs,
|
|
98
|
-
...step.error && { error: step.error.message }
|
|
98
|
+
...step.error && { error: step.error.message },
|
|
99
|
+
...step.checkOutcome && { checkOutcome: step.checkOutcome },
|
|
100
|
+
...step.driftSummary && { driftSummary: step.driftSummary }
|
|
99
101
|
})),
|
|
100
102
|
...job.ruleResults && job.ruleResults.length > 0 && { ruleResults: job.ruleResults.map((r) => ({
|
|
101
103
|
label: r.label,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { MatrixValues, Job } from '@kici-dev/sdk';
|
|
2
|
+
import type { CheckMode } from '@kici-dev/engine';
|
|
2
3
|
import type { JobResult } from '../test-runner/job-executor.js';
|
|
3
4
|
/**
|
|
4
5
|
* All CLI flags for `kici run local`.
|
|
@@ -42,6 +43,12 @@ export interface RunLocalOptions {
|
|
|
42
43
|
inPlace?: boolean;
|
|
43
44
|
/** --keep: always retain the isolated tmp checkout (default: keep only on failure) */
|
|
44
45
|
keep?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Run mode resolved from --check / --fail-on-drift. Threads to the agent step
|
|
48
|
+
* loop: `apply` (default) converges, `check` previews drift, `check-fail-on-drift`
|
|
49
|
+
* previews drift and fails the run if any step reports drift. Defaults to `apply`.
|
|
50
|
+
*/
|
|
51
|
+
checkMode?: CheckMode;
|
|
45
52
|
}
|
|
46
53
|
/**
|
|
47
54
|
* A job after matrix expansion with resolved values.
|
|
@@ -650,6 +650,8 @@ function transformSteps(steps, gitRoot) {
|
|
|
650
650
|
},
|
|
651
651
|
...step.onCancel !== void 0 && { hasOnCancel: true },
|
|
652
652
|
...step.cleanup !== void 0 && { hasCleanup: true },
|
|
653
|
+
...step.check !== void 0 && { hasCheck: true },
|
|
654
|
+
...step.whenInSync !== void 0 && { hasWhenInSync: true },
|
|
653
655
|
...step.requireApproval !== void 0 && { approval: toLockApproval(step.requireApproval) }
|
|
654
656
|
};
|
|
655
657
|
});
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* The overlay tarball does NOT flow through here: `initUpload` returns an
|
|
11
11
|
* external presigned URL the CLI PUTs to directly (data plane).
|
|
12
12
|
*/
|
|
13
|
+
import type { CheckMode } from '@kici-dev/engine';
|
|
13
14
|
export declare class AuthenticationError extends Error {
|
|
14
15
|
constructor(message?: string);
|
|
15
16
|
}
|
|
@@ -70,6 +71,12 @@ export interface PlatformTriggerInput {
|
|
|
70
71
|
secrets?: Record<string, string>;
|
|
71
72
|
encryptedSecrets?: string;
|
|
72
73
|
encryptedSecretsKey?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Run mode for the dispatched run (`apply` | `check` | `check-fail-on-drift`).
|
|
76
|
+
* The Platform relays it onto the dispatch event so the orchestrator runs the
|
|
77
|
+
* agent step loop in the requested mode. Omitted means `apply`.
|
|
78
|
+
*/
|
|
79
|
+
checkMode?: CheckMode;
|
|
73
80
|
}
|
|
74
81
|
export interface PlatformTriggerResponse {
|
|
75
82
|
runId: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Job, Workflow } from '@kici-dev/sdk';
|
|
2
2
|
import type { OutputsMap, StepRefMap } from '@kici-dev/sdk';
|
|
3
|
+
import type { CheckStepOutcome } from '@kici-dev/engine';
|
|
3
4
|
import type { SimulatedEvent } from '@kici-dev/engine';
|
|
4
5
|
import type { RuleResult } from '@kici-dev/sdk';
|
|
5
6
|
import type { ParsedSecrets } from './secrets-file.js';
|
|
@@ -15,10 +16,14 @@ interface SdkOutputSetters {
|
|
|
15
16
|
}
|
|
16
17
|
export interface StepResult {
|
|
17
18
|
name: string;
|
|
18
|
-
status: 'success' | 'failure';
|
|
19
|
+
status: 'success' | 'failure' | 'skipped';
|
|
19
20
|
durationMs: number;
|
|
20
21
|
error?: Error;
|
|
21
22
|
outputs?: Record<string, unknown>;
|
|
23
|
+
/** Idempotent per-step outcome under a non-default check mode. */
|
|
24
|
+
checkOutcome?: CheckStepOutcome;
|
|
25
|
+
/** Human-readable drift summary, present when drift was detected. */
|
|
26
|
+
driftSummary?: string;
|
|
22
27
|
}
|
|
23
28
|
export interface JobResult {
|
|
24
29
|
name: string;
|
|
@@ -133,6 +133,10 @@ function createStepContext(workflowInfo, jobInfo, repoRoot, inputs = {}, matrix,
|
|
|
133
133
|
scalers: [],
|
|
134
134
|
agents: []
|
|
135
135
|
}) },
|
|
136
|
+
inventory: {
|
|
137
|
+
query: () => Promise.resolve([]),
|
|
138
|
+
get: () => Promise.resolve(null)
|
|
139
|
+
},
|
|
136
140
|
oidc: { token: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.oidc.token() is not available in the local test runner")) }
|
|
137
141
|
},
|
|
138
142
|
cache: {
|
package/dist/types.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export interface LockApproval {
|
|
|
23
23
|
readonly timeoutSeconds?: number;
|
|
24
24
|
}
|
|
25
25
|
/** Schema version - re-exported from engine as single source of truth */
|
|
26
|
-
export declare const SCHEMA_VERSION:
|
|
26
|
+
export declare const SCHEMA_VERSION: 21;
|
|
27
27
|
/**
|
|
28
28
|
* Source file reference with meaningful path.
|
|
29
29
|
* Format: file is relative path from git root, export uses hash syntax.
|
|
@@ -334,6 +334,15 @@ export interface LockStep {
|
|
|
334
334
|
readonly hasOnCancel?: boolean;
|
|
335
335
|
/** Whether this step has a cleanup hook. */
|
|
336
336
|
readonly hasCleanup?: boolean;
|
|
337
|
+
/**
|
|
338
|
+
* Whether this step declares an idempotent `check` facet. When true the
|
|
339
|
+
* orchestrator knows the step is check-capable and a run can be dispatched in
|
|
340
|
+
* check mode. The check/apply closures themselves are never serialized — the
|
|
341
|
+
* agent re-evaluates the real workflow TypeScript.
|
|
342
|
+
*/
|
|
343
|
+
readonly hasCheck?: boolean;
|
|
344
|
+
/** Whether this step declares a `whenInSync` facet (produces outputs when in sync). */
|
|
345
|
+
readonly hasWhenInSync?: boolean;
|
|
337
346
|
/** Normalized approval gate; when set the step pauses for a human approval. */
|
|
338
347
|
readonly approval?: LockApproval;
|
|
339
348
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kici-dev/compiler",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "Compiler and CLI for KiCI workflows. Compiles `.kici/workflows/*.ts` to a `kici.lock.json` file consumed by the orchestrator and agents, and runs workflows locally or against a remote orchestrator.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ci",
|
|
@@ -63,11 +63,11 @@
|
|
|
63
63
|
"yaml": "^2.9.0",
|
|
64
64
|
"zod": "^4.4.3",
|
|
65
65
|
"zx": "^8.8.5",
|
|
66
|
-
"@kici-dev/core": "0.1.
|
|
67
|
-
"@kici-dev/engine": "0.1.
|
|
66
|
+
"@kici-dev/core": "0.1.21",
|
|
67
|
+
"@kici-dev/engine": "0.1.21"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
|
-
"@kici-dev/sdk": "0.1.
|
|
70
|
+
"@kici-dev/sdk": "0.1.21"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"@types/proper-lockfile": "^4.1.4"
|
package/sbom.spdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"spdxVersion": "SPDX-2.3",
|
|
3
3
|
"dataLicense": "CC0-1.0",
|
|
4
4
|
"SPDXID": "SPDXRef-DOCUMENT",
|
|
5
|
-
"name": "@kici-dev/compiler@0.1.
|
|
6
|
-
"documentNamespace": "https://kici.dev/sbom/%40kici-dev%2Fcompiler/0.1.
|
|
5
|
+
"name": "@kici-dev/compiler@0.1.21",
|
|
6
|
+
"documentNamespace": "https://kici.dev/sbom/%40kici-dev%2Fcompiler/0.1.21/4162fc88-26bd-41e3-8085-1dc26bb0eb1f",
|
|
7
7
|
"creationInfo": {
|
|
8
|
-
"created": "2026-06-
|
|
8
|
+
"created": "2026-06-23T05:10:19Z",
|
|
9
9
|
"creators": [
|
|
10
10
|
"Tool: kici-sbom-generator"
|
|
11
11
|
]
|
|
@@ -484,7 +484,7 @@
|
|
|
484
484
|
{
|
|
485
485
|
"SPDXID": "SPDXRef-RootPackage",
|
|
486
486
|
"name": "@kici-dev/compiler",
|
|
487
|
-
"versionInfo": "0.1.
|
|
487
|
+
"versionInfo": "0.1.21",
|
|
488
488
|
"downloadLocation": "NOASSERTION",
|
|
489
489
|
"filesAnalyzed": false,
|
|
490
490
|
"licenseConcluded": "NOASSERTION",
|
|
@@ -495,16 +495,16 @@
|
|
|
495
495
|
{
|
|
496
496
|
"referenceCategory": "PACKAGE-MANAGER",
|
|
497
497
|
"referenceType": "purl",
|
|
498
|
-
"referenceLocator": "pkg:npm/%40kici-dev/compiler@0.1.
|
|
498
|
+
"referenceLocator": "pkg:npm/%40kici-dev/compiler@0.1.21"
|
|
499
499
|
}
|
|
500
500
|
],
|
|
501
501
|
"description": "Compiler and CLI for KiCI workflows. Compiles `.kici/workflows/*.ts` to a `kici.lock.json` file consumed by the orchestrator and agents, and runs workflows locally or against a remote orchestrator.",
|
|
502
502
|
"homepage": "https://kici.dev"
|
|
503
503
|
},
|
|
504
504
|
{
|
|
505
|
-
"SPDXID": "SPDXRef-Package--kici-dev-core-0.1.
|
|
505
|
+
"SPDXID": "SPDXRef-Package--kici-dev-core-0.1.21",
|
|
506
506
|
"name": "@kici-dev/core",
|
|
507
|
-
"versionInfo": "0.1.
|
|
507
|
+
"versionInfo": "0.1.21",
|
|
508
508
|
"downloadLocation": "NOASSERTION",
|
|
509
509
|
"filesAnalyzed": false,
|
|
510
510
|
"licenseConcluded": "NOASSERTION",
|
|
@@ -515,16 +515,16 @@
|
|
|
515
515
|
{
|
|
516
516
|
"referenceCategory": "PACKAGE-MANAGER",
|
|
517
517
|
"referenceType": "purl",
|
|
518
|
-
"referenceLocator": "pkg:npm/%40kici-dev/core@0.1.
|
|
518
|
+
"referenceLocator": "pkg:npm/%40kici-dev/core@0.1.21"
|
|
519
519
|
}
|
|
520
520
|
],
|
|
521
521
|
"description": "Light shared utilities for the KiCI stack (logging, errors, formatting, crypto, zx init, the TypeScript ESM loader hook). No server-side dependencies.",
|
|
522
522
|
"homepage": "https://kici.dev"
|
|
523
523
|
},
|
|
524
524
|
{
|
|
525
|
-
"SPDXID": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
525
|
+
"SPDXID": "SPDXRef-Package--kici-dev-engine-0.1.21",
|
|
526
526
|
"name": "@kici-dev/engine",
|
|
527
|
-
"versionInfo": "0.1.
|
|
527
|
+
"versionInfo": "0.1.21",
|
|
528
528
|
"downloadLocation": "NOASSERTION",
|
|
529
529
|
"filesAnalyzed": false,
|
|
530
530
|
"licenseConcluded": "NOASSERTION",
|
|
@@ -535,16 +535,16 @@
|
|
|
535
535
|
{
|
|
536
536
|
"referenceCategory": "PACKAGE-MANAGER",
|
|
537
537
|
"referenceType": "purl",
|
|
538
|
-
"referenceLocator": "pkg:npm/%40kici-dev/engine@0.1.
|
|
538
|
+
"referenceLocator": "pkg:npm/%40kici-dev/engine@0.1.21"
|
|
539
539
|
}
|
|
540
540
|
],
|
|
541
541
|
"description": "Shared business logic for the KiCI CI/CD stack: protocol, triggers, state machine, and provider interfaces used by the Platform relay, orchestrator, and compiler.",
|
|
542
542
|
"homepage": "https://kici.dev"
|
|
543
543
|
},
|
|
544
544
|
{
|
|
545
|
-
"SPDXID": "SPDXRef-Package--kici-dev-sdk-0.1.
|
|
545
|
+
"SPDXID": "SPDXRef-Package--kici-dev-sdk-0.1.21",
|
|
546
546
|
"name": "@kici-dev/sdk",
|
|
547
|
-
"versionInfo": "0.1.
|
|
547
|
+
"versionInfo": "0.1.21",
|
|
548
548
|
"downloadLocation": "NOASSERTION",
|
|
549
549
|
"filesAnalyzed": false,
|
|
550
550
|
"licenseConcluded": "NOASSERTION",
|
|
@@ -555,7 +555,7 @@
|
|
|
555
555
|
{
|
|
556
556
|
"referenceCategory": "PACKAGE-MANAGER",
|
|
557
557
|
"referenceType": "purl",
|
|
558
|
-
"referenceLocator": "pkg:npm/%40kici-dev/sdk@0.1.
|
|
558
|
+
"referenceLocator": "pkg:npm/%40kici-dev/sdk@0.1.21"
|
|
559
559
|
}
|
|
560
560
|
],
|
|
561
561
|
"description": "TypeScript SDK for defining KiCI workflows. Import into `.kici/workflows/*.ts` to declare workflows, jobs, steps, triggers, rules, and matrix configurations.",
|
|
@@ -3222,17 +3222,17 @@
|
|
|
3222
3222
|
},
|
|
3223
3223
|
{
|
|
3224
3224
|
"spdxElementId": "SPDXRef-RootPackage",
|
|
3225
|
-
"relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.
|
|
3225
|
+
"relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.21",
|
|
3226
3226
|
"relationshipType": "DEPENDS_ON"
|
|
3227
3227
|
},
|
|
3228
3228
|
{
|
|
3229
3229
|
"spdxElementId": "SPDXRef-RootPackage",
|
|
3230
|
-
"relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
3230
|
+
"relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.21",
|
|
3231
3231
|
"relationshipType": "DEPENDS_ON"
|
|
3232
3232
|
},
|
|
3233
3233
|
{
|
|
3234
3234
|
"spdxElementId": "SPDXRef-RootPackage",
|
|
3235
|
-
"relatedSpdxElement": "SPDXRef-Package--kici-dev-sdk-0.1.
|
|
3235
|
+
"relatedSpdxElement": "SPDXRef-Package--kici-dev-sdk-0.1.21",
|
|
3236
3236
|
"relationshipType": "DEPENDS_ON"
|
|
3237
3237
|
},
|
|
3238
3238
|
{
|
|
@@ -3301,82 +3301,82 @@
|
|
|
3301
3301
|
"relationshipType": "DEPENDS_ON"
|
|
3302
3302
|
},
|
|
3303
3303
|
{
|
|
3304
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.
|
|
3304
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.21",
|
|
3305
3305
|
"relatedSpdxElement": "SPDXRef-Package-oxc-transform-0.135.0",
|
|
3306
3306
|
"relationshipType": "DEPENDS_ON"
|
|
3307
3307
|
},
|
|
3308
3308
|
{
|
|
3309
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.
|
|
3309
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.21",
|
|
3310
3310
|
"relatedSpdxElement": "SPDXRef-Package-picocolors-1.1.1",
|
|
3311
3311
|
"relationshipType": "DEPENDS_ON"
|
|
3312
3312
|
},
|
|
3313
3313
|
{
|
|
3314
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.
|
|
3314
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.21",
|
|
3315
3315
|
"relatedSpdxElement": "SPDXRef-Package-winston-daily-rotate-file-5.0.0",
|
|
3316
3316
|
"relationshipType": "DEPENDS_ON"
|
|
3317
3317
|
},
|
|
3318
3318
|
{
|
|
3319
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.
|
|
3319
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.21",
|
|
3320
3320
|
"relatedSpdxElement": "SPDXRef-Package-winston-3.19.0",
|
|
3321
3321
|
"relationshipType": "DEPENDS_ON"
|
|
3322
3322
|
},
|
|
3323
3323
|
{
|
|
3324
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.
|
|
3324
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.21",
|
|
3325
3325
|
"relatedSpdxElement": "SPDXRef-Package-zod-4.4.3",
|
|
3326
3326
|
"relationshipType": "DEPENDS_ON"
|
|
3327
3327
|
},
|
|
3328
3328
|
{
|
|
3329
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.
|
|
3329
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.21",
|
|
3330
3330
|
"relatedSpdxElement": "SPDXRef-Package-zx-8.8.5",
|
|
3331
3331
|
"relationshipType": "DEPENDS_ON"
|
|
3332
3332
|
},
|
|
3333
3333
|
{
|
|
3334
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
3334
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.21",
|
|
3335
3335
|
"relatedSpdxElement": "SPDXRef-Package-jose-6.2.3",
|
|
3336
3336
|
"relationshipType": "DEPENDS_ON"
|
|
3337
3337
|
},
|
|
3338
3338
|
{
|
|
3339
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
3339
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.21",
|
|
3340
3340
|
"relatedSpdxElement": "SPDXRef-Package-jsonpath-plus-10.4.0",
|
|
3341
3341
|
"relationshipType": "DEPENDS_ON"
|
|
3342
3342
|
},
|
|
3343
3343
|
{
|
|
3344
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
3344
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.21",
|
|
3345
3345
|
"relatedSpdxElement": "SPDXRef-Package-picomatch-4.0.4",
|
|
3346
3346
|
"relationshipType": "DEPENDS_ON"
|
|
3347
3347
|
},
|
|
3348
3348
|
{
|
|
3349
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
3349
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.21",
|
|
3350
3350
|
"relatedSpdxElement": "SPDXRef-Package-safe-regex-2.1.1",
|
|
3351
3351
|
"relationshipType": "DEPENDS_ON"
|
|
3352
3352
|
},
|
|
3353
3353
|
{
|
|
3354
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
3354
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.21",
|
|
3355
3355
|
"relatedSpdxElement": "SPDXRef-Package-zod-4.4.3",
|
|
3356
3356
|
"relationshipType": "DEPENDS_ON"
|
|
3357
3357
|
},
|
|
3358
3358
|
{
|
|
3359
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.
|
|
3360
|
-
"relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.
|
|
3359
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.21",
|
|
3360
|
+
"relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.21",
|
|
3361
3361
|
"relationshipType": "DEPENDS_ON"
|
|
3362
3362
|
},
|
|
3363
3363
|
{
|
|
3364
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.
|
|
3365
|
-
"relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.
|
|
3364
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.21",
|
|
3365
|
+
"relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.21",
|
|
3366
3366
|
"relationshipType": "DEPENDS_ON"
|
|
3367
3367
|
},
|
|
3368
3368
|
{
|
|
3369
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.
|
|
3369
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.21",
|
|
3370
3370
|
"relatedSpdxElement": "SPDXRef-Package-micromatch-4.0.8",
|
|
3371
3371
|
"relationshipType": "DEPENDS_ON"
|
|
3372
3372
|
},
|
|
3373
3373
|
{
|
|
3374
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.
|
|
3374
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.21",
|
|
3375
3375
|
"relatedSpdxElement": "SPDXRef-Package-zod-4.4.3",
|
|
3376
3376
|
"relationshipType": "DEPENDS_ON"
|
|
3377
3377
|
},
|
|
3378
3378
|
{
|
|
3379
|
-
"spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.
|
|
3379
|
+
"spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.21",
|
|
3380
3380
|
"relatedSpdxElement": "SPDXRef-Package-zx-8.8.5",
|
|
3381
3381
|
"relationshipType": "DEPENDS_ON"
|
|
3382
3382
|
},
|