@kici-dev/compiler 0.6.0 → 0.7.0
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.js +10 -2
- package/dist/commands/compile.js +5 -1
- package/dist/commands/doctor.js +8 -2
- package/dist/commands/feedback.d.ts +53 -0
- package/dist/commands/feedback.js +142 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +2 -1
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.js +77 -12
- package/dist/commands/preview.js +1 -1
- package/dist/commands/report/identity.d.ts +11 -0
- package/dist/commands/report/identity.js +7 -2
- package/dist/commands/run-routed.js +1 -0
- package/dist/commands/types.d.ts +6 -1
- package/dist/commands/types.js +2 -1
- package/dist/execution/executor.js +7 -1
- package/dist/llm-context/llms-architecture.txt +72 -86
- package/dist/llm-context/llms-cli-remote.txt +2380 -0
- package/dist/llm-context/llms-cli.txt +348 -2615
- package/dist/llm-context/llms-features-execution.txt +80 -23
- package/dist/llm-context/llms-features.txt +137 -6
- package/dist/llm-context/llms-full.txt +2582 -2025
- package/dist/llm-context/llms-getting-started.txt +152 -5
- package/dist/llm-context/llms-patterns.txt +81 -5
- package/dist/llm-context/llms-providers.txt +6 -2
- package/dist/llm-context/llms-sdk-runtime.txt +22 -18
- package/dist/llm-context/llms-sdk.txt +47 -7
- package/dist/llm-context/llms.txt +20 -13
- package/dist/local-plane/orchestrator-process.d.ts +0 -8
- package/dist/local-plane/orchestrator-process.js +3 -14
- package/dist/local-plane/plane-manager.js +2 -2
- package/dist/lockfile/generator.js +25 -9
- package/dist/lockfile/hasher.d.ts +5 -13
- package/dist/lockfile/hasher.js +1 -15
- package/dist/lockfile/workspace-siblings.d.ts +46 -0
- package/dist/lockfile/workspace-siblings.js +197 -0
- package/dist/templates/package-json.js +1 -1
- package/dist/test-runner/job-executor.js +1 -1
- package/dist/test-runner/rule-evaluator.js +1 -1
- package/dist/types.d.ts +6 -1
- package/package.json +7 -9
- package/sbom.spdx.json +123 -123
- package/dist/postinstall.d.ts +0 -9
- package/dist/postinstall.js +0 -62
- package/hack/postinstall.mjs +0 -105
|
@@ -4,19 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
The full markdown bundle of every page indexed here is available at https://docs.kici.dev/llms-full.txt.
|
|
6
6
|
|
|
7
|
+
Found a mismatch between what these docs advertise and what KiCI does? Read https://docs.kici.dev/user/reporting-discrepancies/ (or run `kici feedback`) before reporting it: a report needs a reproduction, and you draft it but a human decides to file it.
|
|
8
|
+
|
|
7
9
|
## Bundles
|
|
8
10
|
|
|
9
11
|
Each bundle below is a self-contained markdown file for one authoring task. Fetch only the one your task needs instead of the full bundle:
|
|
10
12
|
|
|
11
|
-
- [getting-started](https://docs.kici.dev/llms-getting-started.txt) (
|
|
12
|
-
- [patterns](https://docs.kici.dev/llms-patterns.txt) (
|
|
13
|
-
- [sdk](https://docs.kici.dev/llms-sdk.txt) (
|
|
14
|
-
- [sdk-runtime](https://docs.kici.dev/llms-sdk-runtime.txt) (
|
|
15
|
-
- [cli](https://docs.kici.dev/llms-cli.txt) (
|
|
16
|
-
- [
|
|
17
|
-
- [features
|
|
13
|
+
- [getting-started](https://docs.kici.dev/llms-getting-started.txt) (65 KB) — Adopt KiCI: why it exists, how workflows execute, migrating from GitHub Actions, installing the SDK, and writing/compiling/testing your first workflow
|
|
14
|
+
- [patterns](https://docs.kici.dev/llms-patterns.txt) (61 KB) — Copy-paste workflow recipes: triggers, conditionals, matrix, scheduling, integrations
|
|
15
|
+
- [sdk](https://docs.kici.dev/llms-sdk.txt) (131 KB) — Core authoring API: workflow/job/step factories, triggers, rules, matrix, dynamic jobs, cross-job outputs
|
|
16
|
+
- [sdk-runtime](https://docs.kici.dev/llms-sdk-runtime.txt) (115 KB) — Runtime and advanced authoring: step runtime context, event payloads, host fan-out, idempotent steps, caching, artifacts, waiting
|
|
17
|
+
- [cli](https://docs.kici.dev/llms-cli.txt) (97 KB) — Running the CLI locally: compile, test, run local, hooks, lock-file drift, common failures
|
|
18
|
+
- [cli-remote](https://docs.kici.dev/llms-cli-remote.txt) (109 KB) — Auth, org and orchestrator selection, runs, approvals, notifications, diagnostics, and the MCP server a coding agent connects to
|
|
19
|
+
- [features](https://docs.kici.dev/llms-features.txt) (97 KB) — Deployment contexts, scoped secrets, private registries, approvals, provenance, dashboard and account access
|
|
20
|
+
- [features-execution](https://docs.kici.dev/llms-features-execution.txt) (124 KB) — Concurrency, dynamic values, events, container jobs, environment variables, global workflows, idempotent steps
|
|
18
21
|
- [providers](https://docs.kici.dev/llms-providers.txt) (35 KB) — Connecting sources: GitHub App, universal-git (Forgejo/Gitea/GitLab), local file://
|
|
19
|
-
- [architecture](https://docs.kici.dev/llms-architecture.txt) (
|
|
22
|
+
- [architecture](https://docs.kici.dev/llms-architecture.txt) (100 KB) — How the runtime works: three-tier relay model, data flows, configuration
|
|
20
23
|
|
|
21
24
|
## Getting started
|
|
22
25
|
|
|
@@ -26,6 +29,7 @@ Each bundle below is a self-contained markdown file for one authoring task. Fetc
|
|
|
26
29
|
- [Getting started with workflows](https://docs.kici.dev/user/getting-started/): Install the SDK, write your first workflow, compile and test locally
|
|
27
30
|
- [Migrating from GitHub Actions](https://docs.kici.dev/user/migrating-from-github-actions/): Map GitHub Actions concepts to KiCI, translate a real workflow side-by-side, and see what has no equivalent yet
|
|
28
31
|
- [5-minute quickstart](https://docs.kici.dev/user/quickstart/): Stand up a KiCI orchestrator + agent and run your first workflow
|
|
32
|
+
- [Reporting a discrepancy](https://docs.kici.dev/user/reporting-discrepancies/): For coding agents — how to report a mismatch between what KiCI's docs advertise and what the tooling does, with the reproduction and the approval a report needs.
|
|
29
33
|
- [Why KiCI](https://docs.kici.dev/user/why-kici/): The case for running CI on infrastructure you control, with typed TypeScript workflows you test locally
|
|
30
34
|
|
|
31
35
|
## Workflow patterns
|
|
@@ -58,18 +62,21 @@ Each bundle below is a self-contained markdown file for one authoring task. Fetc
|
|
|
58
62
|
- [SDK reference: temp directories](https://docs.kici.dev/user/sdk/temp-directories/): Allocate job-scoped scratch dirs and files with ctx.mktemp() / ctx.mktempFile()
|
|
59
63
|
- [SDK reference: waitFor](https://docs.kici.dev/user/sdk/wait-for/): Polling helpers for steps that pause until an external condition becomes true
|
|
60
64
|
|
|
61
|
-
## CLI
|
|
65
|
+
## CLI: authoring on your own machine
|
|
62
66
|
|
|
63
|
-
- [
|
|
64
|
-
- [CLI authentication](https://docs.kici.dev/user/cli-auth/): Authenticate the KiCI CLI with browser OAuth, device flow, or API key paste
|
|
65
|
-
- [CLI reference](https://docs.kici.dev/user/cli-reference/): All CLI commands: compile, preview, local, fixture, types, workflows, hook, docs, run, runs, approve, reject, login, logout, init, org, pat, secrets, admin, orchestrators, endpoints, notifications, verify-attestation, diagnostics, doctor, report
|
|
67
|
+
- [CLI reference](https://docs.kici.dev/user/cli-reference/): All CLI commands: compile, preview, local, fixture, types, workflows, hook, docs, run, runs, approve, reject, login, logout, init, org, pat, secrets, admin, orchestrators, endpoints, notifications, verify-attestation, diagnostics, doctor, report, feedback
|
|
66
68
|
- [Common failures](https://docs.kici.dev/user/common-failures/): Symptom-to-fix reference for the failures workflow authors hit most — no jobs dispatched, lock-file drift, missing webhooks, and agents that won't connect
|
|
67
69
|
- [Lifecycle hooks](https://docs.kici.dev/user/hooks/): SDK hook API for cancel, cleanup, success, failure, and step-level callbacks
|
|
68
70
|
- [Lock file and workflow drift](https://docs.kici.dev/user/lock-file-and-drift/): Keep the lock file in sync with your workflow source and avoid drift
|
|
69
71
|
- [Testing guide](https://docs.kici.dev/user/testing-guide/): Running remote test fixtures with kici run remote
|
|
70
72
|
- [Workflow patterns](https://docs.kici.dev/user/workflow-patterns/): Common patterns: monorepo builds, conditional jobs, dynamic matrices, generic webhooks, scheduling
|
|
71
|
-
- [kici: account & org](https://docs.kici.dev/user/cli/account-and-org/): Login, org selection, personal access tokens, secrets, and admin
|
|
72
73
|
- [kici: authoring & local dev](https://docs.kici.dev/user/cli/authoring-and-local/): Compile, preview, local execution, fixtures, types, workflows, hooks, and docs
|
|
74
|
+
|
|
75
|
+
## CLI: driving a deployed orchestrator
|
|
76
|
+
|
|
77
|
+
- [Drive KiCI from your coding agent](https://docs.kici.dev/user/ai-agents/): Point a coding agent at KiCI's MCP server and let it trigger, read, and re-run your CI under your own identity.
|
|
78
|
+
- [CLI authentication](https://docs.kici.dev/user/cli-auth/): Authenticate the KiCI CLI with browser OAuth, device flow, or API key paste
|
|
79
|
+
- [kici: account & org](https://docs.kici.dev/user/cli/account-and-org/): Login, org selection, personal access tokens, secrets, and admin
|
|
73
80
|
- [kici: notifications & diagnostics](https://docs.kici.dev/user/cli/notifications-and-diagnostics/): Notification channels, attestation verification, and diagnostics
|
|
74
81
|
- [kici: runs & approvals](https://docs.kici.dev/user/cli/runs-and-approvals/): Run push and inspection plus approval / rejection of held runs
|
|
75
82
|
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resolve a host address the plane's co-located bare-metal agent can reach for
|
|
3
|
-
* cache-blob fetches. The orchestrator refuses to hand a scaler a loopback
|
|
4
|
-
* storage URL (it assumes remote agents), so the plane advertises its first
|
|
5
|
-
* non-internal IPv4 (still this machine — the agent is co-located). Falls back
|
|
6
|
-
* to the hostname when no external interface is present.
|
|
7
|
-
*/
|
|
8
|
-
export declare function resolveAgentReachableHost(): string;
|
|
9
1
|
/**
|
|
10
2
|
* Resolve the built `@kici-dev/orchestrator/standalone` entry the local plane
|
|
11
3
|
* launches in independent (offline) mode.
|
|
@@ -3,20 +3,8 @@ import { planePaths, planePorts } from "./paths.js";
|
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
-
import os from "node:os";
|
|
7
6
|
//#region src/local-plane/orchestrator-process.ts
|
|
8
7
|
/**
|
|
9
|
-
* Resolve a host address the plane's co-located bare-metal agent can reach for
|
|
10
|
-
* cache-blob fetches. The orchestrator refuses to hand a scaler a loopback
|
|
11
|
-
* storage URL (it assumes remote agents), so the plane advertises its first
|
|
12
|
-
* non-internal IPv4 (still this machine — the agent is co-located). Falls back
|
|
13
|
-
* to the hostname when no external interface is present.
|
|
14
|
-
*/
|
|
15
|
-
function resolveAgentReachableHost() {
|
|
16
|
-
for (const addrs of Object.values(os.networkInterfaces())) for (const a of addrs ?? []) if (a.family === "IPv4" && !a.internal) return a.address;
|
|
17
|
-
return os.hostname();
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
8
|
* Resolve the built `@kici-dev/orchestrator/standalone` entry the local plane
|
|
21
9
|
* launches in independent (offline) mode.
|
|
22
10
|
*/
|
|
@@ -68,6 +56,7 @@ function spawnOrchestratorProcess(databaseUrl, opts) {
|
|
|
68
56
|
KICI_PORT: String(port),
|
|
69
57
|
KICI_DATABASE_URL: databaseUrl,
|
|
70
58
|
KICI_AGENT_AUTH: "none",
|
|
59
|
+
KICI_HOST: "127.0.0.1",
|
|
71
60
|
KICI_AUTO_MIGRATE: "true",
|
|
72
61
|
KICI_DATA_DIR: root,
|
|
73
62
|
KICI_BOOTSTRAP_ADMIN_TOKEN: opts.adminToken,
|
|
@@ -76,7 +65,7 @@ function spawnOrchestratorProcess(databaseUrl, opts) {
|
|
|
76
65
|
KICI_WEBHOOK_PUBLIC_URL: `http://127.0.0.1:${port}`,
|
|
77
66
|
KICI_STORAGE_TYPE: "filesystem",
|
|
78
67
|
KICI_STORAGE_FS_PATH: cacheDir,
|
|
79
|
-
KICI_STORAGE_FS_BASE_URL: `http
|
|
68
|
+
KICI_STORAGE_FS_BASE_URL: `http://127.0.0.1:${port}`
|
|
80
69
|
};
|
|
81
70
|
let entry;
|
|
82
71
|
let modeEnv;
|
|
@@ -124,6 +113,6 @@ async function awaitOrchestratorReady(port, attempts = 120, intervalMs = 500) {
|
|
|
124
113
|
throw new Error("local orchestrator did not become ready");
|
|
125
114
|
}
|
|
126
115
|
//#endregion
|
|
127
|
-
export { awaitOrchestratorReady, orchestratorReady,
|
|
116
|
+
export { awaitOrchestratorReady, orchestratorReady, resolveServerEntry, resolveStandaloneEntry, spawnOrchestratorProcess };
|
|
128
117
|
|
|
129
118
|
//# sourceMappingURL=orchestrator-process.js.map
|
|
@@ -2,18 +2,20 @@ import "../rolldown-runtime-ClRpJifh.js";
|
|
|
2
2
|
import { compilerError } from "../errors/formatter.js";
|
|
3
3
|
import { locationForJob, locationForWorkflow } from "../errors/source-location.js";
|
|
4
4
|
import "../errors/index.js";
|
|
5
|
+
import { computeSiblingsDigest } from "./workspace-siblings.js";
|
|
5
6
|
import { BREAKING_FLOOR as BREAKING_FLOOR$1, SCHEMA_VERSION as SCHEMA_VERSION$1 } from "../types.js";
|
|
6
|
-
import { computeContentHash } from "./hasher.js";
|
|
7
|
+
import { COMPILE_SCHEMA_VERSION, computeContentHash } from "./hasher.js";
|
|
7
8
|
import { resolveHashFiles } from "./hash-files.js";
|
|
8
9
|
import path from "node:path";
|
|
9
10
|
import { readFileSync } from "node:fs";
|
|
10
|
-
import { getDynamicJobGroup, getDynamicJobNeeds, isDynamicFunction, isDynamicGroupRef, isDynamicJobFn, isParallelGroup, isStaticArray, isStaticObject
|
|
11
|
+
import { getDynamicJobGitCredentials, getDynamicJobGroup, getDynamicJobNeeds, isDynamicFunction, isDynamicGroupRef, isDynamicJobFn, isParallelGroup, isStaticArray, isStaticObject } from "@kici-dev/sdk";
|
|
11
12
|
import { sha256 } from "@kici-dev/core";
|
|
12
13
|
import { PackageManager, detectPackageManagerSync, detectYarnFlavorSync } from "@kici-dev/core/package-manager";
|
|
13
|
-
import { assertScheduleInputsSatisfiable, extractInputsDescriptorMap, resolveContentFormat, resolveWhenToRunOn, validateResourceRequest } from "@kici-dev/engine";
|
|
14
|
+
import { assertScheduleInputsSatisfiable, extractInputsDescriptorMap, resolveContentFormat, resolveWhenToRunOn, stripStatefulRegexFlags, validateResourceRequest } from "@kici-dev/engine";
|
|
14
15
|
import { assertSafeRegex } from "@kici-dev/engine/safe-regex";
|
|
15
|
-
import { normalizeRunsOnAllToMatchers, normalizeRunsOnToMatchers, runsOnPickFromInput } from "@kici-dev/engine/labels/compile";
|
|
16
16
|
import { execSync } from "node:child_process";
|
|
17
|
+
import { normalizeRunsOnAllToMatchers, normalizeRunsOnToMatchers, runsOnPickFromInput } from "@kici-dev/engine/labels/compile";
|
|
18
|
+
import { normalizeApproval, normalizeCacheSpecs } from "@kici-dev/sdk/internal";
|
|
17
19
|
//#region src/lockfile/generator.ts
|
|
18
20
|
/**
|
|
19
21
|
* Courtesy compatibility warning for `kici compile`.
|
|
@@ -124,6 +126,7 @@ function formatExportRef(source) {
|
|
|
124
126
|
function generateLockFile(workflowsWithSource) {
|
|
125
127
|
const gitRoot = detectGitRoot();
|
|
126
128
|
const lockfileHash = computeLockfileHash(gitRoot);
|
|
129
|
+
const siblingsDigest = computeSiblingsDigest(gitRoot);
|
|
127
130
|
const firstSource = workflowsWithSource[0]?.source;
|
|
128
131
|
const topLevelSource = firstSource ? {
|
|
129
132
|
file: path.relative(gitRoot, firstSource.file).replaceAll("\\", "/"),
|
|
@@ -147,6 +150,7 @@ function generateLockFile(workflowsWithSource) {
|
|
|
147
150
|
source: topLevelSource,
|
|
148
151
|
contentHash,
|
|
149
152
|
...lockfileHash && { lockfileHash },
|
|
153
|
+
...siblingsDigest && { siblingsDigest },
|
|
150
154
|
workflows
|
|
151
155
|
};
|
|
152
156
|
}
|
|
@@ -169,8 +173,8 @@ function transformWorkflow(workflow, sourceFile, exportRef, bundleSource, gitRoo
|
|
|
169
173
|
resolvedHashFiles = resolved.resolvedPaths;
|
|
170
174
|
}
|
|
171
175
|
}
|
|
172
|
-
const contentHash = bundleSource !== void 0 ? computeContentHash(bundleSource,
|
|
173
|
-
const compileSchemaVersion = bundleSource !== void 0 ?
|
|
176
|
+
const contentHash = bundleSource !== void 0 ? computeContentHash(bundleSource, COMPILE_SCHEMA_VERSION, assetDigest) : "";
|
|
177
|
+
const compileSchemaVersion = bundleSource !== void 0 ? COMPILE_SCHEMA_VERSION : 0;
|
|
174
178
|
return {
|
|
175
179
|
name: workflow.name,
|
|
176
180
|
source: {
|
|
@@ -220,15 +224,25 @@ function toArray(v) {
|
|
|
220
224
|
return Array.isArray(v) ? v : [v];
|
|
221
225
|
}
|
|
222
226
|
/**
|
|
227
|
+
* The `/pattern/flags` shape the lock file stores.
|
|
228
|
+
*
|
|
229
|
+
* The flag class must name every ECMAScript flag, not just the ones that
|
|
230
|
+
* predate ES2022. When it read `[gimsuy]`, a `d`- or `v`-flagged regex failed
|
|
231
|
+
* to unwrap, so `pattern` became the whole `/release-\d+/v` string — which
|
|
232
|
+
* `new RegExp` happily accepts as a pattern matching literal slashes. The lock
|
|
233
|
+
* then stored `/\/release-\d+\/v/`, which compiles green and can never match.
|
|
234
|
+
*/
|
|
235
|
+
const REGEX_ENTRY = /^\/(.+)\/([dgimsuvy]*)$/;
|
|
236
|
+
/**
|
|
223
237
|
* Normalize one regex entry to the lock's `/pattern/flags` form, rejecting an
|
|
224
238
|
* invalid or ReDoS-prone pattern at compile time so a catastrophic pattern fails
|
|
225
239
|
* `kici compile` with author feedback instead of reaching the orchestrator.
|
|
226
240
|
*/
|
|
227
241
|
function serializeRegexEntry(entry, ctx) {
|
|
228
242
|
const source = entry instanceof RegExp ? `/${entry.source}/${entry.flags}` : entry;
|
|
229
|
-
const wrapped =
|
|
243
|
+
const wrapped = REGEX_ENTRY.exec(source);
|
|
230
244
|
const pattern = wrapped ? wrapped[1] : source;
|
|
231
|
-
const flags = wrapped ? wrapped[2] : "";
|
|
245
|
+
const flags = stripStatefulRegexFlags(wrapped ? wrapped[2] : "");
|
|
232
246
|
let re;
|
|
233
247
|
try {
|
|
234
248
|
re = new RegExp(pattern, flags);
|
|
@@ -605,6 +619,7 @@ function transformJobs(jobs, configPath, gitRoot) {
|
|
|
605
619
|
if (isDynamicJobFn(jobOrFactory)) {
|
|
606
620
|
const groupName = getDynamicJobGroup(jobOrFactory);
|
|
607
621
|
const declaredNeeds = getDynamicJobNeeds(jobOrFactory);
|
|
622
|
+
const declaredGitCredentials = getDynamicJobGitCredentials(jobOrFactory);
|
|
608
623
|
const resolvedNeeds = declaredNeeds ? resolveNeedsForLock(declaredNeeds, uuidToName) : void 0;
|
|
609
624
|
return {
|
|
610
625
|
_type: "dynamic",
|
|
@@ -616,7 +631,8 @@ function transformJobs(jobs, configPath, gitRoot) {
|
|
|
616
631
|
...resolvedNeeds && {
|
|
617
632
|
needs: resolvedNeeds.needs,
|
|
618
633
|
resultAware: true
|
|
619
|
-
}
|
|
634
|
+
},
|
|
635
|
+
...declaredGitCredentials && { gitCredentials: declaredGitCredentials }
|
|
620
636
|
};
|
|
621
637
|
}
|
|
622
638
|
const j = jobOrFactory;
|
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Compile schema version
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Bumped 3 → 4: the agent artifact switched from a Rolldown-bundled
|
|
7
|
-
* `.compiled.mjs` to a raw `.kici/` source tarball that the agent extracts
|
|
8
|
-
* and imports via the shared oxc-transform ESM loader hook.
|
|
9
|
-
*
|
|
10
|
-
* Bumped 4 → 5: the hash input is now line-ending-normalized (CRLF → LF) so a
|
|
11
|
-
* lockfile produced on Linux (LF) matches the agent's hash on Windows, where
|
|
12
|
-
* Git's `core.autocrlf=true` system default rewrites checked-out text files to
|
|
13
|
-
* CRLF. Old lockfiles must be regenerated via `kici compile`.
|
|
2
|
+
* Compile schema version, re-exported from its single definition in
|
|
3
|
+
* `@kici-dev/core/kici-source-digest` — where it sits beside the digest it
|
|
4
|
+
* qualifies, so the compiler that writes a `contentHash` and the agent that
|
|
5
|
+
* re-verifies it cannot drift onto different numbers.
|
|
14
6
|
*/
|
|
15
|
-
export
|
|
7
|
+
export { COMPILE_SCHEMA_VERSION } from '@kici-dev/core/kici-source-digest';
|
|
16
8
|
/**
|
|
17
9
|
* Compute content hash for a compiled workflow bundle, optionally including asset file contents.
|
|
18
10
|
* Hash = SHA-256(schemaVersion + ":" + bundleSource + "\0" + assetDigest) when assetDigest is provided,
|
package/dist/lockfile/hasher.js
CHANGED
|
@@ -1,22 +1,8 @@
|
|
|
1
1
|
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import { COMPILE_SCHEMA_VERSION } from "@kici-dev/core/kici-source-digest";
|
|
2
3
|
import { normalizeLineEndings, sha256 } from "@kici-dev/core";
|
|
3
4
|
//#region src/lockfile/hasher.ts
|
|
4
5
|
/**
|
|
5
|
-
* Compile schema version -- bump when compilation approach changes
|
|
6
|
-
* (bundler change, bundling config, output format, etc.).
|
|
7
|
-
* This is NOT the lock file schema version.
|
|
8
|
-
*
|
|
9
|
-
* Bumped 3 → 4: the agent artifact switched from a Rolldown-bundled
|
|
10
|
-
* `.compiled.mjs` to a raw `.kici/` source tarball that the agent extracts
|
|
11
|
-
* and imports via the shared oxc-transform ESM loader hook.
|
|
12
|
-
*
|
|
13
|
-
* Bumped 4 → 5: the hash input is now line-ending-normalized (CRLF → LF) so a
|
|
14
|
-
* lockfile produced on Linux (LF) matches the agent's hash on Windows, where
|
|
15
|
-
* Git's `core.autocrlf=true` system default rewrites checked-out text files to
|
|
16
|
-
* CRLF. Old lockfiles must be regenerated via `kici compile`.
|
|
17
|
-
*/
|
|
18
|
-
const COMPILE_SCHEMA_VERSION = 5;
|
|
19
|
-
/**
|
|
20
6
|
* Compute content hash for a compiled workflow bundle, optionally including asset file contents.
|
|
21
7
|
* Hash = SHA-256(schemaVersion + ":" + bundleSource + "\0" + assetDigest) when assetDigest is provided,
|
|
22
8
|
* otherwise SHA-256(schemaVersion + ":" + bundleSource) for backward compatibility.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static resolution of the in-repo `workspace:` sibling closure `.kici` depends
|
|
3
|
+
* on, and a digest over its source.
|
|
4
|
+
*
|
|
5
|
+
* The deps tarball carries those sibling package directories **with their built
|
|
6
|
+
* output** (`packKiciDeps`), while the pointer that names it is keyed on
|
|
7
|
+
* `lockfileHash` — a hash of the package-manager lock file. Editing a sibling's
|
|
8
|
+
* source moves no package-manager lock file, so a repo whose `.kici` depends on
|
|
9
|
+
* an in-repo `workspace:` package restored that sibling's STALE build over the
|
|
10
|
+
* fresh clone on every warm-cache run. `siblingsDigest` closes that: it enters
|
|
11
|
+
* the dep pointer key, so a sibling edit is a pointer miss and the deps are
|
|
12
|
+
* rebuilt.
|
|
13
|
+
*
|
|
14
|
+
* Resolution is **static** — manifests and `git ls-files`, never an installed
|
|
15
|
+
* `node_modules`. The agent's own `collectInRepoSiblings` walks an installed
|
|
16
|
+
* tree, which cannot be used here: `kici compile` would then emit a different
|
|
17
|
+
* lock file before and after an install, and a lock file whose contents depend
|
|
18
|
+
* on whether you have installed is not hermetic.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* The workspace globs declared at the repo root — `pnpm-workspace.yaml`'s
|
|
22
|
+
* `packages:` first, then `package.json#workspaces` (npm / yarn / bun). Empty
|
|
23
|
+
* when the repo declares no workspace, in which case a `workspace:` specifier
|
|
24
|
+
* resolves to nothing and is skipped rather than guessed at.
|
|
25
|
+
*/
|
|
26
|
+
export declare function readWorkspaceGlobs(gitRoot: string): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Every in-repo sibling directory `.kici` depends on, transitively, as sorted
|
|
29
|
+
* repo-relative POSIX paths.
|
|
30
|
+
*
|
|
31
|
+
* `.kici` itself is excluded: its own source is already covered by
|
|
32
|
+
* `contentHash`, and including it here would make a `.kici` edit invalidate the
|
|
33
|
+
* dep cache for no reason.
|
|
34
|
+
*/
|
|
35
|
+
export declare function collectInRepoSiblings(gitRoot: string, kiciDir?: string): string[];
|
|
36
|
+
/**
|
|
37
|
+
* SHA-256 over the git-tracked source of every resolved sibling directory, or
|
|
38
|
+
* null when `.kici` depends on none.
|
|
39
|
+
*
|
|
40
|
+
* `git ls-files` is install-independent and is already the compiler's world (it
|
|
41
|
+
* resolves `gitRoot` to generate the lock at all). Content is read as text and
|
|
42
|
+
* `\0`-delimited against its path, exactly as the `.kici/` tree digest is, so a
|
|
43
|
+
* rename cannot be disguised as a content edit.
|
|
44
|
+
*/
|
|
45
|
+
export declare function computeSiblingsDigest(gitRoot: string, kiciDir?: string): string | null;
|
|
46
|
+
//# sourceMappingURL=workspace-siblings.d.ts.map
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { sha256 } from "@kici-dev/core";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
|
+
import { parse } from "yaml";
|
|
7
|
+
//#region src/lockfile/workspace-siblings.ts
|
|
8
|
+
/**
|
|
9
|
+
* Static resolution of the in-repo `workspace:` sibling closure `.kici` depends
|
|
10
|
+
* on, and a digest over its source.
|
|
11
|
+
*
|
|
12
|
+
* The deps tarball carries those sibling package directories **with their built
|
|
13
|
+
* output** (`packKiciDeps`), while the pointer that names it is keyed on
|
|
14
|
+
* `lockfileHash` — a hash of the package-manager lock file. Editing a sibling's
|
|
15
|
+
* source moves no package-manager lock file, so a repo whose `.kici` depends on
|
|
16
|
+
* an in-repo `workspace:` package restored that sibling's STALE build over the
|
|
17
|
+
* fresh clone on every warm-cache run. `siblingsDigest` closes that: it enters
|
|
18
|
+
* the dep pointer key, so a sibling edit is a pointer miss and the deps are
|
|
19
|
+
* rebuilt.
|
|
20
|
+
*
|
|
21
|
+
* Resolution is **static** — manifests and `git ls-files`, never an installed
|
|
22
|
+
* `node_modules`. The agent's own `collectInRepoSiblings` walks an installed
|
|
23
|
+
* tree, which cannot be used here: `kici compile` would then emit a different
|
|
24
|
+
* lock file before and after an install, and a lock file whose contents depend
|
|
25
|
+
* on whether you have installed is not hermetic.
|
|
26
|
+
*/
|
|
27
|
+
/** Dependency protocols that name a directory inside this repository. */
|
|
28
|
+
const IN_REPO_PROTOCOLS = [
|
|
29
|
+
"workspace:",
|
|
30
|
+
"file:",
|
|
31
|
+
"link:",
|
|
32
|
+
"portal:"
|
|
33
|
+
];
|
|
34
|
+
function readManifest(dir) {
|
|
35
|
+
const file = path.join(dir, "package.json");
|
|
36
|
+
if (!existsSync(file)) return null;
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(readFileSync(file, "utf-8"));
|
|
39
|
+
} catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Every dependency specifier in a manifest, across all four dependency maps. */
|
|
44
|
+
function allSpecifiers(m) {
|
|
45
|
+
return Object.entries({
|
|
46
|
+
...m.dependencies,
|
|
47
|
+
...m.devDependencies,
|
|
48
|
+
...m.optionalDependencies,
|
|
49
|
+
...m.peerDependencies
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The workspace globs declared at the repo root — `pnpm-workspace.yaml`'s
|
|
54
|
+
* `packages:` first, then `package.json#workspaces` (npm / yarn / bun). Empty
|
|
55
|
+
* when the repo declares no workspace, in which case a `workspace:` specifier
|
|
56
|
+
* resolves to nothing and is skipped rather than guessed at.
|
|
57
|
+
*/
|
|
58
|
+
function readWorkspaceGlobs(gitRoot) {
|
|
59
|
+
const pnpmFile = path.join(gitRoot, "pnpm-workspace.yaml");
|
|
60
|
+
if (existsSync(pnpmFile)) try {
|
|
61
|
+
const doc = parse(readFileSync(pnpmFile, "utf-8"));
|
|
62
|
+
if (Array.isArray(doc?.packages)) return doc.packages.filter((p) => typeof p === "string");
|
|
63
|
+
} catch {}
|
|
64
|
+
const ws = readManifest(gitRoot)?.workspaces;
|
|
65
|
+
if (Array.isArray(ws)) return ws;
|
|
66
|
+
if (ws && Array.isArray(ws.packages)) return ws.packages;
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Directories the workspace globs match, each carrying a manifest. Resolved with
|
|
71
|
+
* `git ls-files` so the set is exactly what is tracked — the same authority the
|
|
72
|
+
* digest itself uses, and one that needs no install.
|
|
73
|
+
*/
|
|
74
|
+
function workspacePackageDirs(gitRoot, globs) {
|
|
75
|
+
const byName = /* @__PURE__ */ new Map();
|
|
76
|
+
if (globs.length === 0) return byName;
|
|
77
|
+
let manifests;
|
|
78
|
+
try {
|
|
79
|
+
manifests = execFileSync("git", [
|
|
80
|
+
"ls-files",
|
|
81
|
+
"--",
|
|
82
|
+
"*package.json",
|
|
83
|
+
"package.json"
|
|
84
|
+
], {
|
|
85
|
+
cwd: gitRoot,
|
|
86
|
+
encoding: "utf-8",
|
|
87
|
+
maxBuffer: 33554432
|
|
88
|
+
}).split("\n").filter(Boolean);
|
|
89
|
+
} catch {
|
|
90
|
+
return byName;
|
|
91
|
+
}
|
|
92
|
+
for (const rel of manifests) {
|
|
93
|
+
const dir = path.dirname(rel);
|
|
94
|
+
if (dir.split("/").includes("node_modules")) continue;
|
|
95
|
+
const m = readManifest(path.join(gitRoot, dir));
|
|
96
|
+
if (m?.name) byName.set(m.name, dir);
|
|
97
|
+
}
|
|
98
|
+
return byName;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Resolve one specifier to a repo-relative directory, or null when it does not
|
|
102
|
+
* name one. `file:` / `link:` / `portal:` carry the path; `workspace:` carries a
|
|
103
|
+
* version range, so the package NAME is resolved through the workspace globs.
|
|
104
|
+
*/
|
|
105
|
+
function resolveSpecifier(pkgName, spec, fromDir, gitRoot, workspacePkgs) {
|
|
106
|
+
if (spec.startsWith("workspace:")) return workspacePkgs.get(pkgName) ?? null;
|
|
107
|
+
for (const proto of [
|
|
108
|
+
"file:",
|
|
109
|
+
"link:",
|
|
110
|
+
"portal:"
|
|
111
|
+
]) {
|
|
112
|
+
if (!spec.startsWith(proto)) continue;
|
|
113
|
+
const target = spec.slice(proto.length);
|
|
114
|
+
const abs = path.resolve(gitRoot, fromDir, target);
|
|
115
|
+
const rel = path.relative(gitRoot, abs).replaceAll("\\", "/");
|
|
116
|
+
if (rel.startsWith("..") || path.isAbsolute(rel)) return null;
|
|
117
|
+
return rel;
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Every in-repo sibling directory `.kici` depends on, transitively, as sorted
|
|
123
|
+
* repo-relative POSIX paths.
|
|
124
|
+
*
|
|
125
|
+
* `.kici` itself is excluded: its own source is already covered by
|
|
126
|
+
* `contentHash`, and including it here would make a `.kici` edit invalidate the
|
|
127
|
+
* dep cache for no reason.
|
|
128
|
+
*/
|
|
129
|
+
function collectInRepoSiblings(gitRoot, kiciDir = ".kici") {
|
|
130
|
+
const kiciManifest = readManifest(path.join(gitRoot, kiciDir));
|
|
131
|
+
if (!kiciManifest) return [];
|
|
132
|
+
const workspacePkgs = workspacePackageDirs(gitRoot, readWorkspaceGlobs(gitRoot));
|
|
133
|
+
const found = /* @__PURE__ */ new Set();
|
|
134
|
+
const queue = [{
|
|
135
|
+
dir: kiciDir,
|
|
136
|
+
manifest: kiciManifest
|
|
137
|
+
}];
|
|
138
|
+
const visited = /* @__PURE__ */ new Set([kiciDir]);
|
|
139
|
+
while (queue.length > 0) {
|
|
140
|
+
const { dir, manifest } = queue.shift();
|
|
141
|
+
for (const [name, spec] of allSpecifiers(manifest)) {
|
|
142
|
+
if (!IN_REPO_PROTOCOLS.some((p) => spec.startsWith(p))) continue;
|
|
143
|
+
const target = resolveSpecifier(name, spec, dir, gitRoot, workspacePkgs);
|
|
144
|
+
if (target === null || visited.has(target)) continue;
|
|
145
|
+
visited.add(target);
|
|
146
|
+
found.add(target);
|
|
147
|
+
const m = readManifest(path.join(gitRoot, target));
|
|
148
|
+
if (m) queue.push({
|
|
149
|
+
dir: target,
|
|
150
|
+
manifest: m
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return [...found].sort();
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* SHA-256 over the git-tracked source of every resolved sibling directory, or
|
|
158
|
+
* null when `.kici` depends on none.
|
|
159
|
+
*
|
|
160
|
+
* `git ls-files` is install-independent and is already the compiler's world (it
|
|
161
|
+
* resolves `gitRoot` to generate the lock at all). Content is read as text and
|
|
162
|
+
* `\0`-delimited against its path, exactly as the `.kici/` tree digest is, so a
|
|
163
|
+
* rename cannot be disguised as a content edit.
|
|
164
|
+
*/
|
|
165
|
+
function computeSiblingsDigest(gitRoot, kiciDir = ".kici") {
|
|
166
|
+
const siblings = collectInRepoSiblings(gitRoot, kiciDir);
|
|
167
|
+
if (siblings.length === 0) return null;
|
|
168
|
+
const parts = [];
|
|
169
|
+
for (const dir of siblings) {
|
|
170
|
+
let files;
|
|
171
|
+
try {
|
|
172
|
+
files = execFileSync("git", [
|
|
173
|
+
"ls-files",
|
|
174
|
+
"--",
|
|
175
|
+
dir
|
|
176
|
+
], {
|
|
177
|
+
cwd: gitRoot,
|
|
178
|
+
encoding: "utf-8",
|
|
179
|
+
maxBuffer: 67108864
|
|
180
|
+
}).split("\n").filter(Boolean).sort();
|
|
181
|
+
} catch {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
for (const rel of files) {
|
|
185
|
+
let content = "";
|
|
186
|
+
try {
|
|
187
|
+
content = readFileSync(path.join(gitRoot, rel), "utf-8");
|
|
188
|
+
} catch {}
|
|
189
|
+
parts.push(`${rel}\0${content.replaceAll("\r\n", "\n")}\0`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return parts.length > 0 ? sha256(parts.join("")) : null;
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
195
|
+
export { collectInRepoSiblings, computeSiblingsDigest, readWorkspaceGlobs };
|
|
196
|
+
|
|
197
|
+
//# sourceMappingURL=workspace-siblings.js.map
|
|
@@ -6,8 +6,8 @@ import { localRunsOnString } from "./runs-on-display.js";
|
|
|
6
6
|
import { pathToFileURL } from "node:url";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import pc from "picocolors";
|
|
9
|
-
import { setJobOutputsMap, setStepOutputsMap, setStepRefMap } from "@kici-dev/sdk";
|
|
10
9
|
import { logger } from "@kici-dev/core";
|
|
10
|
+
import { setJobOutputsMap, setStepOutputsMap, setStepRefMap } from "@kici-dev/sdk/internal";
|
|
11
11
|
//#region src/test-runner/job-executor.ts
|
|
12
12
|
/**
|
|
13
13
|
* Resolve SDK output setter functions from the workflow's module instance.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
2
|
import { formatter } from "./output-formatter.js";
|
|
3
|
-
import { createRuleContext as createRuleContext$1, evaluateRules } from "@kici-dev/sdk";
|
|
4
3
|
import { initZx } from "@kici-dev/core";
|
|
4
|
+
import { createRuleContext as createRuleContext$1, evaluateRules } from "@kici-dev/sdk/internal";
|
|
5
5
|
//#region src/test-runner/rule-evaluator.ts
|
|
6
6
|
initZx();
|
|
7
7
|
/**
|
package/dist/types.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export interface LockApproval {
|
|
|
28
28
|
readonly when: 'always' | 'drift';
|
|
29
29
|
}
|
|
30
30
|
/** Schema version - re-exported from engine as single source of truth */
|
|
31
|
-
export declare const SCHEMA_VERSION:
|
|
31
|
+
export declare const SCHEMA_VERSION: 41;
|
|
32
32
|
/** Lock compatibility-window floor - re-exported from engine as single source of truth */
|
|
33
33
|
export declare const BREAKING_FLOOR: 30;
|
|
34
34
|
/**
|
|
@@ -570,6 +570,11 @@ export interface LockDynamicJobFn {
|
|
|
570
570
|
readonly needs?: readonly (string | LockNeedsEntry | LockNeedsGroupEntry)[];
|
|
571
571
|
/** True when this dynamic entry was authored as dynamicJob(group, { needs, generate }). */
|
|
572
572
|
readonly resultAware?: boolean;
|
|
573
|
+
/**
|
|
574
|
+
* Named git credentials the generator declares, inherited by every job it
|
|
575
|
+
* generates. Mirrors the engine `LockDynamicJobFn.gitCredentials`.
|
|
576
|
+
*/
|
|
577
|
+
readonly gitCredentials?: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
573
578
|
}
|
|
574
579
|
/** Job or dynamic job generator */
|
|
575
580
|
export type LockJobOrFactory = LockJob | LockDynamicJobFn;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kici-dev/compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
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",
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
"files": [
|
|
34
34
|
"dist",
|
|
35
35
|
"!dist/**/*.map",
|
|
36
|
-
"hack/postinstall.mjs",
|
|
37
36
|
"sbom.spdx.json"
|
|
38
37
|
],
|
|
39
38
|
"main": "dist/index.js",
|
|
@@ -62,24 +61,23 @@
|
|
|
62
61
|
"yaml": "^2.9.0",
|
|
63
62
|
"zod": "^4.4.3",
|
|
64
63
|
"zx": "^8.8.5",
|
|
65
|
-
"@kici-dev/agent": "0.
|
|
66
|
-
"@kici-dev/
|
|
67
|
-
"@kici-dev/
|
|
68
|
-
"@kici-dev/
|
|
64
|
+
"@kici-dev/agent": "0.7.0",
|
|
65
|
+
"@kici-dev/orchestrator": "0.7.0",
|
|
66
|
+
"@kici-dev/core": "0.7.0",
|
|
67
|
+
"@kici-dev/engine": "0.7.0"
|
|
69
68
|
},
|
|
70
69
|
"devDependencies": {
|
|
71
70
|
"@types/archiver": "^8.0.0",
|
|
72
71
|
"jszip": "^3.10.1"
|
|
73
72
|
},
|
|
74
73
|
"peerDependencies": {
|
|
75
|
-
"@kici-dev/sdk": "0.
|
|
74
|
+
"@kici-dev/sdk": "0.7.0"
|
|
76
75
|
},
|
|
77
76
|
"scripts": {
|
|
78
77
|
"build": "node ../../scripts/build-ts.mjs && tsgo --emitDeclarationOnly",
|
|
79
78
|
"postbuild": "zx hack/postbuild.mjs",
|
|
80
79
|
"typecheck": "tsgo --noEmit",
|
|
81
80
|
"test": "vitest run",
|
|
82
|
-
"test:watch": "vitest"
|
|
83
|
-
"postinstall": "node hack/postinstall.mjs"
|
|
81
|
+
"test:watch": "vitest"
|
|
84
82
|
}
|
|
85
83
|
}
|