@kici-dev/compiler 0.1.16 → 0.1.18
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/auth/headless-detect.js +1 -1
- package/dist/cli-banner.js +1 -1
- package/dist/cli.js +56 -19
- package/dist/commands/approve.js +1 -1
- package/dist/commands/compile.js +19 -17
- package/dist/commands/diagnostics.d.ts +7 -0
- package/dist/commands/diagnostics.js +58 -0
- package/dist/commands/docs.js +1 -1
- package/dist/commands/drain-worker.js +1 -1
- package/dist/commands/endpoints.js +1 -1
- package/dist/commands/fixture.js +1 -1
- package/dist/commands/held-run-client.js +1 -1
- package/dist/commands/held-run-resolve.js +1 -1
- package/dist/commands/hook.js +1 -1
- package/dist/commands/index.d.ts +16 -4
- package/dist/commands/index.js +10 -4
- package/dist/commands/init.js +2 -2
- package/dist/commands/login.d.ts +0 -2
- package/dist/commands/login.js +1 -3
- package/dist/commands/logout.js +1 -1
- package/dist/commands/orchestrators.d.ts +25 -0
- package/dist/commands/orchestrators.js +115 -0
- package/dist/commands/org.js +1 -1
- package/dist/commands/reject.js +1 -1
- package/dist/commands/run.d.ts +2 -9
- package/dist/commands/run.js +256 -315
- package/dist/commands/runs/cancel.d.ts +6 -0
- package/dist/commands/runs/cancel.js +32 -0
- package/dist/commands/runs/list.d.ts +6 -0
- package/dist/commands/runs/list.js +49 -0
- package/dist/commands/runs/logs.d.ts +7 -0
- package/dist/commands/runs/logs.js +67 -0
- package/dist/commands/runs/rerun.d.ts +5 -0
- package/dist/commands/runs/rerun.js +21 -0
- package/dist/commands/runs/show.d.ts +5 -0
- package/dist/commands/runs/show.js +65 -0
- package/dist/commands/secrets-list.d.ts +7 -9
- package/dist/commands/secrets-list.js +40 -64
- package/dist/commands/test.d.ts +4 -0
- package/dist/commands/test.js +4 -6
- package/dist/commands/types.d.ts +3 -5
- package/dist/commands/types.js +13 -37
- package/dist/commands/verify-attestation.d.ts +12 -0
- package/dist/commands/verify-attestation.js +69 -0
- package/dist/commands/watch.js +1 -1
- package/dist/commands/workflows.js +14 -34
- package/dist/errors/capability-gap.js +1 -1
- package/dist/errors/formatter.js +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/execution/executor.js +1 -1
- package/dist/execution/index.js +1 -1
- package/dist/execution/sdk-alias.js +1 -1
- package/dist/execution/ts-loader.js +1 -1
- package/dist/fixtures/compiler.js +1 -1
- package/dist/fixtures/defaults/index.js +1 -1
- package/dist/format.js +1 -1
- package/dist/generators/secrets-dts.js +2 -2
- package/dist/hooks/detector.js +1 -1
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/installer.js +1 -1
- package/dist/hooks/templates.js +1 -1
- package/dist/index.js +1 -1
- package/dist/llm-context/llms-full.txt +1549 -1282
- package/dist/llm-context/llms.txt +6 -2
- package/dist/local-executor/dag-scheduler.js +1 -1
- package/dist/local-executor/index.js +1 -1
- package/dist/local-executor/job-runner.js +40 -18
- package/dist/local-executor/materializer.js +1 -1
- package/dist/local-executor/output-streamer.js +1 -1
- package/dist/local-executor/payload-generator.js +1 -1
- package/dist/local-executor/picker.js +1 -1
- package/dist/local-executor/runs-on-display.d.ts +9 -0
- package/dist/local-executor/runs-on-display.js +23 -0
- package/dist/local-executor/secret-loader.js +1 -1
- package/dist/local-executor/to-event-payload.js +1 -1
- package/dist/local-executor/types.js +1 -1
- package/dist/local-executor/workflow-lock.js +0 -0
- package/dist/lockfile/generator.d.ts +9 -5
- package/dist/lockfile/generator.js +58 -38
- package/dist/lockfile/hash-files.js +1 -1
- package/dist/lockfile/hasher.js +1 -1
- package/dist/lockfile/index.js +1 -1
- package/dist/lockfile/purity-analyzer.js +1 -1
- package/dist/postinstall.js +1 -1
- package/dist/provenance-trust-root.d.ts +14 -0
- package/dist/provenance-trust-root.js +49 -0
- package/dist/remote/config.d.ts +6 -2
- package/dist/remote/config.js +7 -4
- package/dist/remote/dashboard-client.d.ts +92 -0
- package/dist/remote/dashboard-client.js +168 -0
- package/dist/remote/encryption.js +3 -4
- package/dist/remote/history.js +1 -1
- package/dist/remote/oauth.js +1 -1
- package/dist/remote/oidc-discovery.js +1 -1
- package/dist/remote/output/json.js +1 -1
- package/dist/remote/output/junit.js +1 -1
- package/dist/remote/output/streaming.js +1 -1
- package/dist/remote/output/summary.js +1 -1
- package/dist/remote/platform-client.d.ts +142 -0
- package/dist/remote/platform-client.js +187 -0
- package/dist/remote/prod-defaults.js +1 -1
- package/dist/remote/render.d.ts +11 -0
- package/dist/remote/render.js +48 -0
- package/dist/remote/secret-upload.js +1 -1
- package/dist/remote/uploader.d.ts +9 -0
- package/dist/remote/uploader.js +51 -4
- package/dist/templates/agents-md.d.ts +1 -1
- package/dist/templates/agents-md.js +8 -3
- package/dist/templates/index.js +1 -1
- package/dist/templates/package-json.js +4 -5
- package/dist/templates/tsconfig-json.js +1 -1
- package/dist/templates/workflows/hello-world.js +2 -2
- package/dist/templates/workflows/hello-world.ts +1 -1
- package/dist/templates/workflows/pr-checks.js +3 -3
- package/dist/templates/workflows/pr-checks.ts +2 -2
- package/dist/test-runner/dry-run.js +1 -1
- package/dist/test-runner/event-types.js +1 -1
- package/dist/test-runner/git-detector.js +1 -1
- package/dist/test-runner/index.js +1 -1
- package/dist/test-runner/job-executor.js +3 -2
- package/dist/test-runner/output-formatter.js +1 -1
- package/dist/test-runner/payload-builder.js +1 -1
- package/dist/test-runner/rule-evaluator.js +1 -1
- package/dist/test-runner/secrets-file.js +1 -1
- package/dist/test-runner/step-context.js +10 -6
- package/dist/types.d.ts +29 -8
- package/dist/types.js +2 -1
- package/dist/validation/index.js +1 -1
- package/dist/validation/validator.js +1 -1
- package/dist/workflows/hello-world.ts +1 -1
- package/dist/workflows/pr-checks.ts +2 -2
- package/package.json +10 -9
- package/sbom.spdx.json +465 -410
- package/dist/commands/cancel.js +0 -124
- package/dist/commands/status.js +0 -212
- package/dist/remote/client.js +0 -203
- package/dist/remote/observer.js +0 -174
- /package/dist/{chunk-gOLHoazu.js → chunk-BTugEXQM.js} +0 -0
|
@@ -25,6 +25,7 @@ The full markdown bundle of every page indexed here is available at https://docs
|
|
|
25
25
|
- [Event payload reference](https://docs.kici.dev/user/sdk/event-payloads/): Generated schema of the normalized event envelope passed to rules and dynamic functions.
|
|
26
26
|
- [SDK reference: idempotent](https://docs.kici.dev/user/sdk/idempotent/): Idempotent helpers for declarative check / apply patterns inside workflow steps
|
|
27
27
|
- [SDK reference: rules, matrix, dynamic jobs](https://docs.kici.dev/user/sdk/rules-matrix-dynamic/): rule(), skip(), matrix builds (static + dynamic), and dynamicJob / dynamicGroup
|
|
28
|
+
- [SDK reference: runsOnAll host fan-out](https://docs.kici.dev/user/sdk/runs-on-all/): Fan one job out to every matching connected host, one pinned execution per host
|
|
28
29
|
- [SDK reference: runtime](https://docs.kici.dev/user/sdk/runtime/): Types index, StepContext, secrets, fixtures
|
|
29
30
|
- [SDK reference: triggers](https://docs.kici.dev/user/sdk/triggers/): GitHub event triggers, kiciEvent, workflowComplete, jobComplete, genericWebhook, schedule, lifecycle
|
|
30
31
|
- [SDK reference: validation & events](https://docs.kici.dev/user/sdk/validation-events/): validateDag(), event definitions, and event emission
|
|
@@ -34,7 +35,7 @@ The full markdown bundle of every page indexed here is available at https://docs
|
|
|
34
35
|
## CLI and authoring
|
|
35
36
|
|
|
36
37
|
- [CLI authentication](https://docs.kici.dev/user/cli-auth/): Authenticate the KiCI CLI with browser OAuth, device flow, or API key paste
|
|
37
|
-
- [CLI reference](https://docs.kici.dev/user/cli-reference/): All CLI commands: compile, run (local/remote), test, login, logout, org,
|
|
38
|
+
- [CLI reference](https://docs.kici.dev/user/cli-reference/): All CLI commands: compile, run (local/remote), orchestrators, test, login, logout, org, diagnostics, runs (list/show/logs/rerun/cancel), secrets, types, fixture, init, hook, endpoints, workflows, docs, admin
|
|
38
39
|
- [Lifecycle hooks](https://docs.kici.dev/user/hooks/): SDK hook API for cancel, cleanup, success, failure, and step-level callbacks
|
|
39
40
|
- [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
|
|
40
41
|
- [Testing guide](https://docs.kici.dev/user/testing-guide/): Running remote test fixtures with kici run remote
|
|
@@ -43,19 +44,22 @@ The full markdown bundle of every page indexed here is available at https://docs
|
|
|
43
44
|
## Workflow features
|
|
44
45
|
|
|
45
46
|
- [Account and sign-in](https://docs.kici.dev/user/account-and-login/): How your KiCI account relates to sign-in methods, and how to change the way you sign in.
|
|
47
|
+
- [Approval gates](https://docs.kici.dev/user/approvals/): Pause a workflow for human sign-off at step, job, or workflow granularity with requireApproval
|
|
46
48
|
- [Concurrency groups](https://docs.kici.dev/user/concurrency/): Control parallel execution with auto-cancel and queue modes
|
|
47
|
-
- [Dashboard](https://docs.kici.dev/user/dashboard/)
|
|
49
|
+
- [Dashboard](https://docs.kici.dev/user/dashboard/): Web UI for monitoring workflow runs, managing sources, secrets, and organization settings.
|
|
48
50
|
- [Dynamic values](https://docs.kici.dev/user/dynamic-values/)
|
|
49
51
|
- [Environment variables](https://docs.kici.dev/user/env-vars/): KICI_* environment variable reference for the CLI
|
|
50
52
|
- [Environments](https://docs.kici.dev/user/environments/): Configure deployment environments with variables, secrets, and protection rules
|
|
51
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
|
|
52
54
|
- [Global workflows](https://docs.kici.dev/user/global-workflows/): Cross-repo workflows that run on events from any repo in the same org
|
|
53
55
|
- [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
|
+
- [Build provenance and attestations](https://docs.kici.dev/user/provenance/): Generate and verify signed SLSA provenance for the artifacts your workflows build
|
|
54
57
|
- [Secrets](https://docs.kici.dev/user/secrets/): How to access secrets in KiCI workflow steps
|
|
55
58
|
|
|
56
59
|
## Providers
|
|
57
60
|
|
|
58
61
|
- [GitHub App provider](https://docs.kici.dev/user/providers/github/): Connect KiCI to GitHub via a GitHub App — full Checks API, installation-token clones, and cross-org dispatch
|
|
62
|
+
- [Local filesystem (file://) source](https://docs.kici.dev/user/providers/local-file/): Register a git repo present on the agent filesystem as a file:// source, triggered by the kici-admin CLI or a push hook
|
|
59
63
|
- [Universal-git provider](https://docs.kici.dev/user/providers/universal-git/): Connect Forgejo, Gitea, Gogs, GitLab, or plain GitHub via webhook + PAT or SSH deploy key
|
|
60
64
|
|
|
61
65
|
## Architecture overview
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import "../chunk-
|
|
1
|
+
import "../chunk-BTugEXQM.js";
|
|
2
2
|
import { ensureTsLoaderHook } from "../execution/ts-loader.js";
|
|
3
3
|
import { formatter } from "../test-runner/output-formatter.js";
|
|
4
4
|
import { createStepContext } from "../test-runner/step-context.js";
|
|
5
5
|
import { createRuleContext, evaluateRules as evaluateRulesWithFormatting } from "../test-runner/rule-evaluator.js";
|
|
6
|
+
import { localRunsOnString } from "./runs-on-display.js";
|
|
6
7
|
import { toEventPayload } from "./to-event-payload.js";
|
|
7
8
|
import { pathToFileURL } from "node:url";
|
|
8
9
|
import path from "node:path";
|
|
9
10
|
import { applyIncludeExclude, expandMatrix, isDynamicJobFn, setJobOutputsMap, setStepOutputsMap, setStepRefMap } from "@kici-dev/sdk";
|
|
11
|
+
import { formatMatrixSuffix } from "@kici-dev/engine";
|
|
10
12
|
//#region src/local-executor/job-runner.ts
|
|
11
13
|
/**
|
|
12
14
|
* Job resolution (matrix/dynamic) and single-job execution with step context.
|
|
@@ -39,15 +41,6 @@ async function resolveSdkSetters(kiciDir) {
|
|
|
39
41
|
};
|
|
40
42
|
}
|
|
41
43
|
/**
|
|
42
|
-
* Format matrix values into a display string for the job name.
|
|
43
|
-
* Single-dimension: "node-18"
|
|
44
|
-
* Multi-dimensional: "linux, 18"
|
|
45
|
-
*/
|
|
46
|
-
function formatMatrixSuffix(matrixValues) {
|
|
47
|
-
if ("value" in matrixValues && matrixValues.value !== void 0) return matrixValues.value;
|
|
48
|
-
return Object.values(matrixValues).filter((v) => v !== void 0).join(", ");
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
44
|
* Resolve a needs entry to a string name.
|
|
52
45
|
* Handles Job objects, strings, DynamicGroupRef, and object forms.
|
|
53
46
|
*/
|
|
@@ -58,6 +51,27 @@ function resolveNeedName(need) {
|
|
|
58
51
|
return need.name;
|
|
59
52
|
}
|
|
60
53
|
/**
|
|
54
|
+
* Maintain the base-name `{ byMatrix, merged }` envelope as each matrix child
|
|
55
|
+
* completes. `merged` is rebuilt last-write-wins in suffix order so the result
|
|
56
|
+
* is deterministic regardless of child completion order — matching the remote
|
|
57
|
+
* path's name-ordered merge.
|
|
58
|
+
*/
|
|
59
|
+
function updateBaseMatrixEnvelope(jobOutputsMap, baseName, matrixValues, childOutputs) {
|
|
60
|
+
const existing = jobOutputsMap.get(baseName);
|
|
61
|
+
const byMatrix = existing && "byMatrix" in existing ? { ...existing.byMatrix } : {};
|
|
62
|
+
const suffix = formatMatrixSuffix(matrixValues);
|
|
63
|
+
byMatrix[suffix] = childOutputs;
|
|
64
|
+
let merged = {};
|
|
65
|
+
for (const key of Object.keys(byMatrix).sort()) merged = {
|
|
66
|
+
...merged,
|
|
67
|
+
...byMatrix[key]
|
|
68
|
+
};
|
|
69
|
+
jobOutputsMap.set(baseName, {
|
|
70
|
+
byMatrix,
|
|
71
|
+
merged
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
61
75
|
* Resolve all jobs in a workflow, expanding matrix jobs and evaluating dynamic jobs.
|
|
62
76
|
*
|
|
63
77
|
* - Static jobs: create ResolvedJob with name, needs, job reference
|
|
@@ -82,10 +96,13 @@ async function resolveJobs(workflow, event) {
|
|
|
82
96
|
debug: () => {}
|
|
83
97
|
},
|
|
84
98
|
env: { ...process.env },
|
|
85
|
-
kici: {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
99
|
+
kici: {
|
|
100
|
+
infrastructure: { list: () => Promise.resolve({
|
|
101
|
+
scalers: [],
|
|
102
|
+
agents: []
|
|
103
|
+
}) },
|
|
104
|
+
oidc: { token: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.oidc.token() is not available during local execution")) }
|
|
105
|
+
}
|
|
89
106
|
});
|
|
90
107
|
for (const genJob of generatedJobs) {
|
|
91
108
|
const genResolved = await resolveStaticJob(genJob, expansionMap);
|
|
@@ -119,7 +136,7 @@ async function resolveStaticJob(job, expansionMap) {
|
|
|
119
136
|
workflow: { name: "" },
|
|
120
137
|
job: {
|
|
121
138
|
name: job.name,
|
|
122
|
-
runsOn: job.runsOn
|
|
139
|
+
runsOn: localRunsOnString(job.runsOn)
|
|
123
140
|
}
|
|
124
141
|
},
|
|
125
142
|
log: {
|
|
@@ -219,7 +236,7 @@ async function executeResolvedJobInner(resolvedJob, context, startTime) {
|
|
|
219
236
|
const repoRoot = context.execDir;
|
|
220
237
|
const stepCtx = createStepContext({ name: context.workflowName }, {
|
|
221
238
|
name: expandedName,
|
|
222
|
-
runsOn:
|
|
239
|
+
runsOn: localRunsOnString(job.runsOn)
|
|
223
240
|
}, repoRoot, void 0, hasMatrix ? matrixValues : void 0, context.secrets, void 0, context.event.payload, context.event.provider);
|
|
224
241
|
const stepResults = [];
|
|
225
242
|
let stepCounter = 0;
|
|
@@ -285,11 +302,16 @@ async function executeResolvedJobInner(resolvedJob, context, startTime) {
|
|
|
285
302
|
}
|
|
286
303
|
}
|
|
287
304
|
const stepsWithOutputs = stepResults.filter((s) => s.outputs != null);
|
|
288
|
-
|
|
305
|
+
let jobOutputs;
|
|
306
|
+
if (stepsWithOutputs.length === 1 && stepResults.length === 1) jobOutputs = stepsWithOutputs[0].outputs;
|
|
289
307
|
else if (stepsWithOutputs.length > 0) {
|
|
290
308
|
const aggregated = {};
|
|
291
309
|
for (const stepResult of stepsWithOutputs) aggregated[stepResult.name] = stepResult.outputs;
|
|
292
|
-
|
|
310
|
+
jobOutputs = aggregated;
|
|
311
|
+
}
|
|
312
|
+
if (jobOutputs) {
|
|
313
|
+
context.jobOutputsMap.set(expandedName, jobOutputs);
|
|
314
|
+
if (expandedName !== job.name) updateBaseMatrixEnvelope(context.jobOutputsMap, job.name, matrixValues, jobOutputs);
|
|
293
315
|
}
|
|
294
316
|
const durationMs = Date.now() - startTime;
|
|
295
317
|
formatter.logJobComplete(expandedName, durationMs);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RunsOn } from '@kici-dev/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Render a job's `runsOn` to the comma-joined label string the local/test
|
|
4
|
+
* runners surface as `ctx.job.runsOn`. A `runsOnAll` job has no `runsOn` —
|
|
5
|
+
* locally it runs once on this machine, so we surface the marker `'runsOnAll'`.
|
|
6
|
+
* RegExp selectors render via their `/source/flags` literal.
|
|
7
|
+
*/
|
|
8
|
+
export declare function localRunsOnString(runsOn: RunsOn | undefined): string;
|
|
9
|
+
//# sourceMappingURL=runs-on-display.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import "../chunk-BTugEXQM.js";
|
|
2
|
+
//#region src/local-executor/runs-on-display.ts
|
|
3
|
+
/** Render one selector element (string label or RegExp) for the local display string. */
|
|
4
|
+
function runsOnElementString(el) {
|
|
5
|
+
return el instanceof RegExp ? el.toString() : el;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Render a job's `runsOn` to the comma-joined label string the local/test
|
|
9
|
+
* runners surface as `ctx.job.runsOn`. A `runsOnAll` job has no `runsOn` —
|
|
10
|
+
* locally it runs once on this machine, so we surface the marker `'runsOnAll'`.
|
|
11
|
+
* RegExp selectors render via their `/source/flags` literal.
|
|
12
|
+
*/
|
|
13
|
+
function localRunsOnString(runsOn) {
|
|
14
|
+
if (runsOn === void 0) return "runsOnAll";
|
|
15
|
+
if (typeof runsOn === "string") return runsOn;
|
|
16
|
+
if (runsOn instanceof RegExp) return runsOn.toString();
|
|
17
|
+
if (Array.isArray(runsOn)) return runsOn.map(runsOnElementString).join(",");
|
|
18
|
+
return (Array.isArray(runsOn.labels) ? runsOn.labels : [runsOn.labels]).map(runsOnElementString).join(",");
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { localRunsOnString };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=runs-on-display.js.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import "../chunk-
|
|
1
|
+
import "../chunk-BTugEXQM.js";
|
|
2
2
|
export {};
|
|
Binary file
|
|
@@ -13,11 +13,15 @@ export declare function detectGitRoot(): string;
|
|
|
13
13
|
*
|
|
14
14
|
* The authoritative lockfile differs by manager: `.kici/package-lock.json` for
|
|
15
15
|
* npm, the repo-root `pnpm-lock.yaml` for a pnpm workspace, the repo-root
|
|
16
|
-
* `yarn.lock` for yarn. A pnpm/yarn `.kici/` member resolves against
|
|
17
|
-
* lockfile, so keying on `.kici/package-lock.json` alone would miss the
|
|
18
|
-
* authoritative graph.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
16
|
+
* `yarn.lock` for a yarn workspace. A pnpm/yarn `.kici/` member resolves against
|
|
17
|
+
* the root lockfile, so keying on `.kici/package-lock.json` alone would miss the
|
|
18
|
+
* authoritative graph. A standalone `.kici` yarn project (its own
|
|
19
|
+
* `packageManager` field + `.kici/yarn.lock`, no root signal) is detected from
|
|
20
|
+
* `.kici/` and keyed on `.kici/yarn.lock`. The hash input is prefixed with the
|
|
21
|
+
* manager name so two managers can never collide on identical lockfile bytes
|
|
22
|
+
* (and a restored tree can never be the wrong layout for the agent's manager).
|
|
23
|
+
* For yarn the prefix also carries the flavor (`yarn-classic` / `yarn-berry`),
|
|
24
|
+
* so a classic-layout dep-cache tarball is never restored into a berry install.
|
|
21
25
|
*
|
|
22
26
|
* @param gitRoot - Absolute path to git repository root
|
|
23
27
|
* @returns Hex SHA-256 hash string, or null if no lockfile is found
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import "../chunk-
|
|
1
|
+
import "../chunk-BTugEXQM.js";
|
|
2
2
|
import { SCHEMA_VERSION as SCHEMA_VERSION$1 } from "../types.js";
|
|
3
3
|
import { computeContentHash } from "./hasher.js";
|
|
4
4
|
import { resolveHashFiles } from "./hash-files.js";
|
|
5
5
|
import { analyzePurity } from "./purity-analyzer.js";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { readFileSync } from "node:fs";
|
|
8
|
-
import { getDynamicJobGroup, isDynamicFunction, isDynamicGroupRef, isDynamicJobFn, isStaticArray, isStaticObject, normalizeCacheSpecs, normalizeRequireApproval } from "@kici-dev/sdk";
|
|
8
|
+
import { getDynamicJobGroup, getDynamicJobNeeds, isDynamicFunction, isDynamicGroupRef, isDynamicJobFn, isStaticArray, isStaticObject, normalizeCacheSpecs, normalizeRequireApproval } from "@kici-dev/sdk";
|
|
9
9
|
import { sha256 } from "@kici-dev/core";
|
|
10
|
-
import { PackageManager, detectPackageManagerSync } from "@kici-dev/core/package-manager";
|
|
10
|
+
import { PackageManager, detectPackageManagerSync, detectYarnFlavorSync } from "@kici-dev/core/package-manager";
|
|
11
11
|
import { validateResourceRequest } from "@kici-dev/engine";
|
|
12
|
+
import { normalizeRunsOnAllToMatchers, normalizeRunsOnToMatchers } from "@kici-dev/engine/labels/compile";
|
|
12
13
|
import { execSync } from "node:child_process";
|
|
13
14
|
//#region src/lockfile/generator.ts
|
|
14
15
|
/**
|
|
@@ -37,23 +38,27 @@ function detectGitRoot() {
|
|
|
37
38
|
*
|
|
38
39
|
* The authoritative lockfile differs by manager: `.kici/package-lock.json` for
|
|
39
40
|
* npm, the repo-root `pnpm-lock.yaml` for a pnpm workspace, the repo-root
|
|
40
|
-
* `yarn.lock` for yarn. A pnpm/yarn `.kici/` member resolves against
|
|
41
|
-
* lockfile, so keying on `.kici/package-lock.json` alone would miss the
|
|
42
|
-
* authoritative graph.
|
|
43
|
-
*
|
|
44
|
-
*
|
|
41
|
+
* `yarn.lock` for a yarn workspace. A pnpm/yarn `.kici/` member resolves against
|
|
42
|
+
* the root lockfile, so keying on `.kici/package-lock.json` alone would miss the
|
|
43
|
+
* authoritative graph. A standalone `.kici` yarn project (its own
|
|
44
|
+
* `packageManager` field + `.kici/yarn.lock`, no root signal) is detected from
|
|
45
|
+
* `.kici/` and keyed on `.kici/yarn.lock`. The hash input is prefixed with the
|
|
46
|
+
* manager name so two managers can never collide on identical lockfile bytes
|
|
47
|
+
* (and a restored tree can never be the wrong layout for the agent's manager).
|
|
48
|
+
* For yarn the prefix also carries the flavor (`yarn-classic` / `yarn-berry`),
|
|
49
|
+
* so a classic-layout dep-cache tarball is never restored into a berry install.
|
|
45
50
|
*
|
|
46
51
|
* @param gitRoot - Absolute path to git repository root
|
|
47
52
|
* @returns Hex SHA-256 hash string, or null if no lockfile is found
|
|
48
53
|
*/
|
|
49
54
|
function computeLockfileHash(gitRoot) {
|
|
50
|
-
const pm = detectPackageManagerSync(gitRoot);
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
const pm = detectPackageManagerSync(gitRoot) === PackageManager.Npm ? detectPackageManagerSync(path.join(gitRoot, ".kici")) : detectPackageManagerSync(gitRoot);
|
|
56
|
+
const candidates = pm === PackageManager.Pnpm ? [path.join(gitRoot, "pnpm-lock.yaml"), path.join(gitRoot, ".kici", "pnpm-lock.yaml")] : pm === PackageManager.Yarn ? [path.join(gitRoot, "yarn.lock"), path.join(gitRoot, ".kici", "yarn.lock")] : [path.join(gitRoot, ".kici", "package-lock.json")];
|
|
57
|
+
const prefix = pm === PackageManager.Yarn ? `${pm}-${detectYarnFlavorSync(detectPackageManagerSync(gitRoot) === PackageManager.Yarn ? gitRoot : path.join(gitRoot, ".kici"))}` : pm;
|
|
58
|
+
for (const lockfilePath of candidates) try {
|
|
59
|
+
return sha256(`${prefix}\n${readFileSync(lockfilePath, "utf-8")}`);
|
|
60
|
+
} catch {}
|
|
61
|
+
return null;
|
|
57
62
|
}
|
|
58
63
|
/**
|
|
59
64
|
* Format export reference with hash syntax.
|
|
@@ -421,13 +426,19 @@ function transformJobs(jobs, configPath, gitRoot) {
|
|
|
421
426
|
return jobs.map((jobOrFactory, index) => {
|
|
422
427
|
if (isDynamicJobFn(jobOrFactory)) {
|
|
423
428
|
const groupName = getDynamicJobGroup(jobOrFactory);
|
|
429
|
+
const declaredNeeds = getDynamicJobNeeds(jobOrFactory);
|
|
430
|
+
const resolvedNeeds = declaredNeeds ? resolveNeedsForLock(declaredNeeds, uuidToName) : void 0;
|
|
424
431
|
return {
|
|
425
432
|
_type: "dynamic",
|
|
426
433
|
source: {
|
|
427
434
|
file: configPath,
|
|
428
435
|
index
|
|
429
436
|
},
|
|
430
|
-
...groupName && { group: groupName }
|
|
437
|
+
...groupName && { group: groupName },
|
|
438
|
+
...resolvedNeeds && {
|
|
439
|
+
needs: resolvedNeeds.needs,
|
|
440
|
+
resultAware: true
|
|
441
|
+
}
|
|
431
442
|
};
|
|
432
443
|
}
|
|
433
444
|
const j = jobOrFactory;
|
|
@@ -439,40 +450,45 @@ function transformJobs(jobs, configPath, gitRoot) {
|
|
|
439
450
|
});
|
|
440
451
|
}
|
|
441
452
|
/**
|
|
442
|
-
* Normalize SDK runsOn into lock file
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
* - Object form: `{ labels: ['linux'], exclude: ['gpu'] }` -> `{ runsOn: ['linux'], excludeLabels: ['gpu'] }`
|
|
453
|
+
* Normalize SDK runsOn into lock file matchers.
|
|
454
|
+
* Plain strings stay exact; globs convert to regex; RegExp literals become regex.
|
|
455
|
+
* Throws (via the engine ReDoS gate) on a ReDoS-prone author regex.
|
|
446
456
|
*/
|
|
447
|
-
function normalizeRunsOnForLock(runsOn) {
|
|
448
|
-
|
|
449
|
-
if (Array.isArray(runsOn)) return { runsOn };
|
|
450
|
-
const labels = Array.isArray(runsOn.labels) ? runsOn.labels : [runsOn.labels];
|
|
451
|
-
const exclude = runsOn.exclude ? Array.isArray(runsOn.exclude) ? runsOn.exclude : [runsOn.exclude] : void 0;
|
|
457
|
+
function normalizeRunsOnForLock(runsOn, jobName) {
|
|
458
|
+
const { include, exclude } = normalizeRunsOnToMatchers(runsOn, `job '${jobName}' runsOn`);
|
|
452
459
|
return {
|
|
453
|
-
runsOn:
|
|
454
|
-
...exclude
|
|
460
|
+
runsOn: include,
|
|
461
|
+
...exclude.length > 0 ? { excludeLabels: exclude } : {}
|
|
455
462
|
};
|
|
456
463
|
}
|
|
457
464
|
/**
|
|
458
|
-
* Validate runsOn labels:
|
|
459
|
-
*
|
|
460
|
-
*
|
|
465
|
+
* Validate runsOn labels: reject overlap between required and excluded labels.
|
|
466
|
+
*
|
|
467
|
+
* `kici:` labels ARE valid runsOn selectors. runsOn expresses a requirement on
|
|
468
|
+
* candidate agents — it only narrows the set, it never grants authority — so
|
|
469
|
+
* targeting a `kici:os:linux` / `kici:role:builder` label is benign. The forgery
|
|
470
|
+
* boundary is label *setting* (scaler labelSet validation + the agent
|
|
471
|
+
* register-time scope gate), which is enforced elsewhere and unaffected here.
|
|
472
|
+
*
|
|
473
|
+
* The overlap check compares exact matchers only — a glob/regex include and an
|
|
474
|
+
* exact exclude (or vice versa) cannot be statically known to overlap.
|
|
461
475
|
*/
|
|
462
476
|
function validateRunsOn(runsOn, jobName) {
|
|
463
|
-
const
|
|
464
|
-
const
|
|
465
|
-
|
|
466
|
-
if (typeof runsOn === "string" || Array.isArray(runsOn)) return;
|
|
467
|
-
const exclude = runsOn.exclude ? Array.isArray(runsOn.exclude) ? runsOn.exclude : [runsOn.exclude] : [];
|
|
468
|
-
const overlap = labels.filter((l) => exclude.includes(l));
|
|
477
|
+
const { include, exclude } = normalizeRunsOnToMatchers(runsOn, `job '${jobName}' runsOn`);
|
|
478
|
+
const includeExact = new Set(include.filter((m) => m.kind === "exact").map((m) => m.value));
|
|
479
|
+
const overlap = exclude.filter((m) => m.kind === "exact").map((m) => m.value).filter((v) => includeExact.has(v));
|
|
469
480
|
if (overlap.length > 0) throw new Error(`Job "${jobName}": labels and exclude overlap on [${overlap.join(", ")}]. A label cannot be both required and excluded.`);
|
|
470
481
|
}
|
|
471
482
|
/**
|
|
472
483
|
* Transform a static job to lock file format.
|
|
473
484
|
*/
|
|
474
485
|
function transformJob(job, configPath, index, gitRoot, uuidToName) {
|
|
475
|
-
|
|
486
|
+
if (job.runsOn !== void 0 && job.runsOnAll !== void 0) throw new Error(`job '${job.name}': runsOn and runsOnAll are mutually exclusive`);
|
|
487
|
+
if (job.runsOn === void 0 && job.runsOnAll === void 0) throw new Error(`job '${job.name}': one of runsOn or runsOnAll is required`);
|
|
488
|
+
if (job.onUnreachable !== void 0 && job.runsOnAll === void 0) console.warn(`[kici] job '${job.name}': onUnreachable is ignored without runsOnAll`);
|
|
489
|
+
if (job.maxParallel !== void 0 && job.maxParallel < 1) throw new Error(`job '${job.name}': maxParallel must be >= 1`);
|
|
490
|
+
if (!(job.matrix !== void 0 || job.runsOnAll !== void 0) && (job.maxParallel !== void 0 || job.failFast !== void 0)) console.warn(`[kici] job '${job.name}': maxParallel/failFast are ignored without matrix or runsOnAll (no fan-out to bound)`);
|
|
491
|
+
if (job.runsOn !== void 0) validateRunsOn(job.runsOn, job.name);
|
|
476
492
|
const environmentFields = {};
|
|
477
493
|
if (job.environment !== void 0) {
|
|
478
494
|
if (typeof job.environment === "function") {
|
|
@@ -521,7 +537,11 @@ function transformJob(job, configPath, index, gitRoot, uuidToName) {
|
|
|
521
537
|
return {
|
|
522
538
|
_type: "static",
|
|
523
539
|
name: job.name,
|
|
524
|
-
...normalizeRunsOnForLock(job.runsOn),
|
|
540
|
+
...job.runsOn !== void 0 ? normalizeRunsOnForLock(job.runsOn, job.name) : {},
|
|
541
|
+
...job.runsOnAll !== void 0 && { runsOnAll: normalizeRunsOnAllToMatchers(job.runsOnAll, `job '${job.name}' runsOnAll`) },
|
|
542
|
+
...job.onUnreachable !== void 0 && { onUnreachable: job.onUnreachable },
|
|
543
|
+
...job.maxParallel !== void 0 && { maxParallel: job.maxParallel },
|
|
544
|
+
...job.failFast !== void 0 && { failFast: job.failFast },
|
|
525
545
|
...resolveNeedsForLock(job.needs, uuidToName),
|
|
526
546
|
steps: transformSteps(job.steps, gitRoot),
|
|
527
547
|
matrix: job.matrix ? transformMatrix(job.matrix, job.name, configPath) : void 0,
|
package/dist/lockfile/hasher.js
CHANGED
package/dist/lockfile/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "../chunk-
|
|
1
|
+
import "../chunk-BTugEXQM.js";
|
|
2
2
|
import { COMPILE_SCHEMA_VERSION, computeContentHash } from "./hasher.js";
|
|
3
3
|
import { computeLockfileHash, detectGitRoot, generateLockFile, serializeLockFile } from "./generator.js";
|
|
4
4
|
export { COMPILE_SCHEMA_VERSION, computeContentHash, computeLockfileHash, detectGitRoot, generateLockFile, serializeLockFile };
|
package/dist/postinstall.js
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Structural JWKS shape — the engine verifier owns the real `jose` type. */
|
|
2
|
+
export interface JsonWebKeySet {
|
|
3
|
+
keys: Record<string, unknown>[];
|
|
4
|
+
}
|
|
5
|
+
export interface TrustRoot {
|
|
6
|
+
issuer: string;
|
|
7
|
+
jwks: JsonWebKeySet;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolve `--trust-root`: an HTTPS issuer URL (discovery → `jwks_uri` → JWKS) or
|
|
11
|
+
* a self-contained offline file `{ issuer, jwks }`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveTrustRoot(trustRoot: string): Promise<TrustRoot>;
|
|
14
|
+
//# sourceMappingURL=provenance-trust-root.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import "./chunk-BTugEXQM.js";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
//#region src/provenance-trust-root.ts
|
|
4
|
+
/**
|
|
5
|
+
* Trust-root resolution for `kici verify-attestation`.
|
|
6
|
+
*
|
|
7
|
+
* The verifier never blindly trusts a provenance token's own `iss`: the trusted
|
|
8
|
+
* issuer + its JWKS are supplied out-of-band via `--trust-root`. This resolver
|
|
9
|
+
* turns that flag into a concrete `{ issuer, jwks }`:
|
|
10
|
+
*
|
|
11
|
+
* - an **HTTPS issuer URL** (online): fetch `<url>/.well-known/openid-configuration`,
|
|
12
|
+
* read its `issuer` + `jwks_uri`, then fetch the JWKS;
|
|
13
|
+
* - a **local file path** (offline / air-gapped): a self-contained
|
|
14
|
+
* `{ "issuer": "...", "jwks": { "keys": [...] } }` JSON document, no network.
|
|
15
|
+
*
|
|
16
|
+
* Node-specific (`fetch` + `fs`) so the engine verify core stays pure /
|
|
17
|
+
* browser-safe; the JWKS is passed straight through to the engine, which owns
|
|
18
|
+
* the crypto, so this module defines only a structural `JsonWebKeySet` type
|
|
19
|
+
* rather than depending on `jose`.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Resolve `--trust-root`: an HTTPS issuer URL (discovery → `jwks_uri` → JWKS) or
|
|
23
|
+
* a self-contained offline file `{ issuer, jwks }`.
|
|
24
|
+
*/
|
|
25
|
+
async function resolveTrustRoot(trustRoot) {
|
|
26
|
+
if (/^https?:\/\//.test(trustRoot)) {
|
|
27
|
+
const base = trustRoot.replace(/\/+$/, "");
|
|
28
|
+
const disco = await fetchJson(`${base}/.well-known/openid-configuration`);
|
|
29
|
+
return {
|
|
30
|
+
issuer: typeof disco.issuer === "string" ? disco.issuer : base,
|
|
31
|
+
jwks: await fetchJson(typeof disco.jwks_uri === "string" ? disco.jwks_uri : `${base}/.well-known/jwks.json`)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const file = JSON.parse(await readFile(trustRoot, "utf-8"));
|
|
35
|
+
if (typeof file.issuer !== "string" || !file.jwks || !Array.isArray(file.jwks.keys)) throw new Error("trust-root file must contain { issuer, jwks: { keys: [...] } }");
|
|
36
|
+
return {
|
|
37
|
+
issuer: file.issuer,
|
|
38
|
+
jwks: file.jwks
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async function fetchJson(url) {
|
|
42
|
+
const res = await fetch(url);
|
|
43
|
+
if (!res.ok) throw new Error(`failed to fetch ${url}: ${res.status}`);
|
|
44
|
+
return await res.json();
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { resolveTrustRoot };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=provenance-trust-root.js.map
|
package/dist/remote/config.d.ts
CHANGED
|
@@ -9,8 +9,6 @@ export interface GlobalConfig {
|
|
|
9
9
|
endpoint?: string;
|
|
10
10
|
/** Platform relay URL */
|
|
11
11
|
platformEndpoint?: string;
|
|
12
|
-
/** Connection mode: 'platform' or 'direct' */
|
|
13
|
-
connectionMode?: 'platform' | 'direct';
|
|
14
12
|
/** Routing key for webhook source identification (e.g., 'github:42') */
|
|
15
13
|
routingKey?: string;
|
|
16
14
|
/** Personal access token (from OAuth login) */
|
|
@@ -23,6 +21,12 @@ export interface GlobalConfig {
|
|
|
23
21
|
activeOrgId?: string;
|
|
24
22
|
/** User email from OIDC token */
|
|
25
23
|
userEmail?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Per-org default orchestrator cluster for `kici run remote`, keyed by org id.
|
|
26
|
+
* Set by `kici orchestrators use <cluster>`; consulted when `--orchestrator`
|
|
27
|
+
* is omitted so a developer with one preferred cluster types no flag.
|
|
28
|
+
*/
|
|
29
|
+
defaultClusters?: Record<string, string>;
|
|
26
30
|
}
|
|
27
31
|
/**
|
|
28
32
|
* Returns the global KiCI config directory path.
|
package/dist/remote/config.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import "../chunk-
|
|
1
|
+
import "../chunk-BTugEXQM.js";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
//#region src/remote/config.ts
|
|
6
|
-
const VALID_CONNECTION_MODES = ["platform", "direct"];
|
|
7
6
|
/**
|
|
8
7
|
* Strips unknown keys and validates known fields from a raw config object.
|
|
9
8
|
* Returns a clean GlobalConfig with only recognized, valid fields.
|
|
@@ -15,13 +14,17 @@ function sanitizeConfig(raw) {
|
|
|
15
14
|
if (typeof obj.token === "string") config.token = obj.token;
|
|
16
15
|
if (typeof obj.endpoint === "string") config.endpoint = obj.endpoint;
|
|
17
16
|
if (typeof obj.platformEndpoint === "string") config.platformEndpoint = obj.platformEndpoint;
|
|
18
|
-
if (typeof obj.connectionMode === "string" && VALID_CONNECTION_MODES.includes(obj.connectionMode)) config.connectionMode = obj.connectionMode;
|
|
19
17
|
if (typeof obj.routingKey === "string") config.routingKey = obj.routingKey;
|
|
20
18
|
if (typeof obj.pat === "string") config.pat = obj.pat;
|
|
21
19
|
if (typeof obj.patId === "string") config.patId = obj.patId;
|
|
22
20
|
if (typeof obj.patExpiresAt === "string") config.patExpiresAt = obj.patExpiresAt;
|
|
23
21
|
if (typeof obj.activeOrgId === "string") config.activeOrgId = obj.activeOrgId;
|
|
24
22
|
if (typeof obj.userEmail === "string") config.userEmail = obj.userEmail;
|
|
23
|
+
if (typeof obj.defaultClusters === "object" && obj.defaultClusters !== null && !Array.isArray(obj.defaultClusters)) {
|
|
24
|
+
const clusters = {};
|
|
25
|
+
for (const [orgId, clusterName] of Object.entries(obj.defaultClusters)) if (typeof clusterName === "string") clusters[orgId] = clusterName;
|
|
26
|
+
if (Object.keys(clusters).length > 0) config.defaultClusters = clusters;
|
|
27
|
+
}
|
|
25
28
|
return config;
|
|
26
29
|
}
|
|
27
30
|
/**
|
|
@@ -76,13 +79,13 @@ async function mergeGlobalConfig(partial) {
|
|
|
76
79
|
if (partial.token !== void 0) merged.token = partial.token;
|
|
77
80
|
if (partial.endpoint !== void 0) merged.endpoint = partial.endpoint;
|
|
78
81
|
if (partial.platformEndpoint !== void 0) merged.platformEndpoint = partial.platformEndpoint;
|
|
79
|
-
if (partial.connectionMode !== void 0) merged.connectionMode = partial.connectionMode;
|
|
80
82
|
if (partial.routingKey !== void 0) merged.routingKey = partial.routingKey;
|
|
81
83
|
if (partial.pat !== void 0) merged.pat = partial.pat;
|
|
82
84
|
if (partial.patId !== void 0) merged.patId = partial.patId;
|
|
83
85
|
if (partial.patExpiresAt !== void 0) merged.patExpiresAt = partial.patExpiresAt;
|
|
84
86
|
if (partial.activeOrgId !== void 0) merged.activeOrgId = partial.activeOrgId;
|
|
85
87
|
if (partial.userEmail !== void 0) merged.userEmail = partial.userEmail;
|
|
88
|
+
if (partial.defaultClusters !== void 0) merged.defaultClusters = partial.defaultClusters;
|
|
86
89
|
await saveGlobalConfig(merged);
|
|
87
90
|
return merged;
|
|
88
91
|
}
|