@kici-dev/compiler 0.6.1 → 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 +1 -1
- package/dist/commands/compile.js +5 -1
- package/dist/commands/doctor.js +8 -2
- 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 +40 -7
- package/dist/llm-context/llms-cli.txt +67 -34
- package/dist/llm-context/llms-features-execution.txt +52 -6
- package/dist/llm-context/llms-features.txt +137 -6
- package/dist/llm-context/llms-full.txt +527 -174
- package/dist/llm-context/llms-getting-started.txt +3 -3
- 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 +8 -8
- 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
|
@@ -126,6 +126,12 @@ If you belong to a single organization, the org is resolved automatically. If
|
|
|
126
126
|
you belong to several, pass an `orgId` argument to any tool (use `list_orgs` to
|
|
127
127
|
find it).
|
|
128
128
|
|
|
129
|
+
Only an **active** membership counts: an organization you have been suspended
|
|
130
|
+
in, one that has been disabled, and one that has been deleted are all skipped.
|
|
131
|
+
So a single active membership alongside a disabled one still resolves
|
|
132
|
+
automatically, and a tool call against an organization you are suspended in is
|
|
133
|
+
refused with the same message the dashboard gives.
|
|
134
|
+
|
|
129
135
|
### Limits and pagination
|
|
130
136
|
|
|
131
137
|
The MCP server applies a few bounds so an agent loop can't overwhelm the shared
|
|
@@ -215,7 +221,7 @@ you hold, so an agent cannot escalate beyond its creator.
|
|
|
215
221
|
|
|
216
222
|
**Repository scope comes along too.** If your role is restricted to a set of
|
|
217
223
|
repositories, an agent token you mint is restricted to the same set. Runs
|
|
218
|
-
outside it are
|
|
224
|
+
outside it are not there: they are filtered out of `list_runs`,
|
|
219
225
|
`cancel_runs_by_branch` skips them, and naming one directly answers "not found"
|
|
220
226
|
— the same answer a run id that does not exist gets, so an agent cannot use the
|
|
221
227
|
tools to discover which repositories it is missing. Your organization's audit
|
|
@@ -458,14 +464,18 @@ Tokens authenticate; RBAC authorizes. Every org-scoped route runs `orgContextMid
|
|
|
458
464
|
|
|
459
465
|
### Configurable surfaces
|
|
460
466
|
|
|
461
|
-
The dashboard is a browser SPA on top of the same `/api/v1/*` surface, so
|
|
467
|
+
The dashboard is a browser SPA on top of the same `/api/v1/*` surface, so nearly everything you can configure in the dashboard you can configure over HTTP. The exception is a short list of routes that only a **browser session** may call, marked † below. The mounted route groups include:
|
|
462
468
|
|
|
463
|
-
- **Auth & identity:** `/cli/exchange-token
|
|
464
|
-
- **Org & membership:** `/orgs
|
|
469
|
+
- **Auth & identity:** `/cli/exchange-token`†, `/pats`, `/user`, `/identity-links`, `/identity-links/:id` (DELETE)†, `/github-oauth`, `/auth/github/link`†, `/invites`, `/invites/pending`, `/invites/:inviteId/{accept,decline}`†
|
|
470
|
+
- **Org & membership:** `/orgs` (POST)†, `/orgs/:customerId`, `/orgs/:customerId/{members,roles,api-keys,orchestrator-keys,service-accounts,billing,trust-policies}`
|
|
465
471
|
- **Workflows & runs:** `/orgs/:customerId/{runs,registrations,workflows,held-runs,contexts,secrets,global-workflows}`
|
|
466
472
|
- **Webhooks & event log:** `/orgs/:customerId/{sources,webhook-endpoints,event-log}`
|
|
467
473
|
- **Diagnostics & activity:** `/orgs/:customerId/{diagnostics,activity,access-log}`
|
|
468
474
|
|
|
475
|
+
**† Browser session only.** These routes answer **403** with `This endpoint requires an interactive login` to a `kici_pat_`, `kici_sk_` or `kici_sa_` token. They are the routes that create an organization, join or decline one, unlink a provider identity, start a GitHub account link, and exchange your session for a personal access token. Each mints a credential wider than the credential presenting it, or changes which organizations and provider identities your account reaches. A `kici_sk_` or `kici_sa_` token is bound to one organization and one permission set, so letting it take those actions would hand it access it was never granted. Do them in the dashboard, or with `kici login`, which runs the browser flow for you.
|
|
476
|
+
|
|
477
|
+
`POST /pats` is the one route in between: it accepts a browser session **or** an existing `kici_pat_` (so `kici pat create` keeps working), and refuses `kici_sk_` and `kici_sa_`. A token minted from another token can never be wider than that token. The new token is capped by your own permissions **and** by the scopes of the token you called with, whether or not you pass `permissions` explicitly. Mint from an unscoped session when you need a broader token.
|
|
478
|
+
|
|
469
479
|
The full route tree is the source of truth — every method, request schema, and response schema is enumerated server-side. There is currently no auto-generated OpenAPI spec; the typed `DashboardApiType` export is the canonical contract for TypeScript clients.
|
|
470
480
|
|
|
471
481
|
### Calling the API
|
|
@@ -484,11 +494,16 @@ curl -sS \
|
|
|
484
494
|
|
|
485
495
|
**Browser console (after dashboard login):**
|
|
486
496
|
|
|
497
|
+
Mint a personal access token and pass it explicitly. Do not script against the
|
|
498
|
+
dashboard's own session token. That token is short-lived and tied to your
|
|
499
|
+
identity-provider session, so anything built on it stops working at the next
|
|
500
|
+
renewal or sign-out.
|
|
501
|
+
|
|
487
502
|
```js
|
|
488
|
-
|
|
489
|
-
const
|
|
503
|
+
// kici pat create --name console --expires-in-days 1 → prints the token
|
|
504
|
+
const token = 'kici_pat_...';
|
|
490
505
|
const res = await fetch('/<deployment-slug>/api/v1/orgs/<your-org-id>/runs?limit=5', {
|
|
491
|
-
headers: { Authorization: `Bearer ${
|
|
506
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
492
507
|
});
|
|
493
508
|
console.log(await res.json());
|
|
494
509
|
```
|
|
@@ -513,6 +528,12 @@ The CLI stores authentication data in `~/.kici/config` with `0600` permissions (
|
|
|
513
528
|
- Routing key for webhook source identification
|
|
514
529
|
- API key, when you logged in with `--token`
|
|
515
530
|
|
|
531
|
+
The web dashboard holds none of these. It keeps only the tokens of your current
|
|
532
|
+
sign-in, in browser storage for the tab's origin. It does not request offline
|
|
533
|
+
access, so those tokens die with your identity-provider session instead of
|
|
534
|
+
staying valid for weeks. Its origin also serves a Content-Security-Policy that
|
|
535
|
+
restricts which scripts run and which hosts the page may contact.
|
|
536
|
+
|
|
516
537
|
## Troubleshooting
|
|
517
538
|
|
|
518
539
|
### Browser doesn't open
|
|
@@ -717,10 +738,14 @@ kici init --private-registry https://npm.pkg.github.com/ \
|
|
|
717
738
|
types/ # Directory for generated type declarations (kici types)
|
|
718
739
|
package.json # Dependencies (@kici-dev/sdk)
|
|
719
740
|
tsconfig.json # TypeScript configuration (includes types/**/*.d.ts)
|
|
741
|
+
.gitignore # Keeps the generated types/ declarations untracked
|
|
742
|
+
.kiciignore # Paths the workflow content hash does not cover
|
|
720
743
|
AGENTS.md # LLM authoring context (skip with --no-agents-md)
|
|
721
744
|
.kiciignore # Default exclusion patterns for test uploads
|
|
722
745
|
```
|
|
723
746
|
|
|
747
|
+
The two `.kiciignore` files are unrelated. The one inside `.kici/` declares which paths the per-workflow content hash skips — see [Lock file and workflow drift](https://docs.kici.dev/user/lock-file-and-drift/#files-the-content-hash-skips-kicikiciignore). The one at the repo root selects which working-tree files a remote run uploads. Neither is overwritten when it already exists.
|
|
748
|
+
|
|
724
749
|
`AGENTS.md` is written by default (the interactive prompt defaults to yes, and CI / non-interactive runs write it). An existing `.kici/AGENTS.md` is never overwritten, so hand edits survive a re-run.
|
|
725
750
|
|
|
726
751
|
In interactive mode (TTY), `kici init` prompts you to:
|
|
@@ -1261,6 +1286,14 @@ to see it. The bundle holds your CLI, Node and orchestrator versions, your
|
|
|
1261
1286
|
redacted configuration, and your project's workflow and lock-file state. With
|
|
1262
1287
|
`--run` it also holds the failing run's detail and logs.
|
|
1263
1288
|
|
|
1289
|
+
For every orchestrator the probe returned the bundle also records where that
|
|
1290
|
+
orchestrator's own config files live — the paths only, never the contents. An
|
|
1291
|
+
orchestrator that is offline is still listed, with no paths: the CLI reads them
|
|
1292
|
+
from the live connection, so a disconnected one has none to report.
|
|
1293
|
+
Each path is a host path as that orchestrator sees it. A container deployment
|
|
1294
|
+
names a file on the container host, which is not a file you can open from the
|
|
1295
|
+
machine that read the bundle.
|
|
1296
|
+
|
|
1264
1297
|
```bash
|
|
1265
1298
|
kici report [options]
|
|
1266
1299
|
```
|
|
@@ -232,7 +232,7 @@ triggered, or the repo had no lock file at that commit.
|
|
|
232
232
|
source-registration mismatch).
|
|
233
233
|
2. **Did anything match?** Run `kici preview push --branch <your-branch>`
|
|
234
234
|
against your workflow. If it reports no matching workflow, your triggers don't
|
|
235
|
-
cover that event/branch — the push was delivered and
|
|
235
|
+
cover that event/branch — the push was delivered and matched nothing.
|
|
236
236
|
3. **Was there a lock file?** A repository with **no** `kici.lock.json` at the
|
|
237
237
|
pushed commit produces no run and is not an error. Confirm the lock file is
|
|
238
238
|
committed and current (see [Lock-file drift](https://docs.kici.dev/user/common-failures/#lock-file-drift)).
|
|
@@ -551,7 +551,7 @@ KiCI uses a **two-artifact model**: TypeScript workflows are the source of truth
|
|
|
551
551
|
## Why the lock file matters
|
|
552
552
|
|
|
553
553
|
- **Orchestrator** fetches the lock file at the commit SHA and uses it to evaluate triggers and to look up the cached `.kici/` source tarball + `node_modules` tarball. It never runs your TypeScript.
|
|
554
|
-
- **Agents** download the cached source tarball (or, on cold cache, the build agent clones + packs it), register the shared TypeScript loader hook, and dynamic-`import()` the workflow `.ts` directly. The lock file's per-workflow `contentHash` identifies the expected
|
|
554
|
+
- **Agents** download the cached source tarball (or, on cold cache, the build agent clones + packs it), register the shared TypeScript loader hook, and dynamic-`import()` the workflow `.ts` directly. The lock file's per-workflow `contentHash` identifies the expected contents of the whole `.kici/` directory and is verified against the extracted source before any step runs. The tarball's own bytes are verified against the digest the orchestrator dispatched, and the restored tree **replaces** `.kici/` rather than being unpacked over it, so a file you deleted does not survive a cache hit.
|
|
555
555
|
|
|
556
556
|
If you change a workflow file (`.ts`) but do **not** regenerate and commit the lock file, the repo at that commit has **drift**: the lock file no longer matches the source. Triggers and cache keys can be wrong, and runs can fail with a clear “stale lock file” error once the agent verifies the hash.
|
|
557
557
|
|
|
@@ -559,37 +559,38 @@ If you change a workflow file (`.ts`) but do **not** regenerate and commit the l
|
|
|
559
559
|
|
|
560
560
|
The lock file (`kici.lock.json`) is a JSON file with the following top-level fields:
|
|
561
561
|
|
|
562
|
-
| Field | Description
|
|
563
|
-
| ------------------ |
|
|
564
|
-
| `schemaVersion` | Lock file schema version, stamped by the compiler that produced the lock. Incremented on every format change. The orchestrator accepts a range of versions — see [schema compatibility window](https://docs.kici.dev/user/lock-file-and-drift/#schema-compatibility-window) — rather than requiring an exact match.
|
|
565
|
-
| `minReaderVersion` | The oldest orchestrator schema version that can read this lock (the newest breaking version at compile time). An orchestrator whose own schema is below this rejects the lock and asks you to upgrade it. Omitted on locks compiled before the compatibility window existed. See [schema compatibility window](https://docs.kici.dev/user/lock-file-and-drift/#schema-compatibility-window).
|
|
566
|
-
| `source` | Reference to the source file and export (e.g., `{ file: '.kici/workflows/ci.ts', export: '#default' }`).
|
|
567
|
-
| `contentHash` | SHA-256 of the serialized lock file content (excluding itself). Changes when any workflow, trigger, or job changes.
|
|
568
|
-
| `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.
|
|
569
|
-
| `
|
|
562
|
+
| Field | Description |
|
|
563
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
564
|
+
| `schemaVersion` | Lock file schema version, stamped by the compiler that produced the lock. Incremented on every format change. The orchestrator accepts a range of versions — see [schema compatibility window](https://docs.kici.dev/user/lock-file-and-drift/#schema-compatibility-window) — rather than requiring an exact match. |
|
|
565
|
+
| `minReaderVersion` | The oldest orchestrator schema version that can read this lock (the newest breaking version at compile time). An orchestrator whose own schema is below this rejects the lock and asks you to upgrade it. Omitted on locks compiled before the compatibility window existed. See [schema compatibility window](https://docs.kici.dev/user/lock-file-and-drift/#schema-compatibility-window). |
|
|
566
|
+
| `source` | Reference to the source file and export (e.g., `{ file: '.kici/workflows/ci.ts', export: '#default' }`). |
|
|
567
|
+
| `contentHash` | SHA-256 of the serialized lock file content (excluding itself). Changes when any workflow, trigger, or job changes. |
|
|
568
|
+
| `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. |
|
|
569
|
+
| `siblingsDigest` | SHA-256 over the git-tracked source of every in-repo `workspace:` / `file:` / `link:` / `portal:` sibling package `.kici` depends on, transitively. Part of the dependency cache key alongside `lockfileHash`, because editing a sibling's source moves no package manager lockfile. Omitted when `.kici` depends on no in-repo package, which is the common case. |
|
|
570
|
+
| `workflows` | Array of workflow entries, each with its own `contentHash`, `compileSchemaVersion`, triggers, and jobs. |
|
|
570
571
|
|
|
571
572
|
Each workflow entry includes:
|
|
572
573
|
|
|
573
|
-
| Field | Description
|
|
574
|
-
| ---------------------- |
|
|
575
|
-
| `name` | Workflow name.
|
|
576
|
-
| `source` | Per-workflow source file and export reference.
|
|
577
|
-
| `contentHash` | SHA-256 of the
|
|
578
|
-
| `compileSchemaVersion` | Compiler schema version used when computing `contentHash` (currently `
|
|
579
|
-
| `triggers` | Trigger definitions extracted from the workflow (used by the orchestrator for event matching).
|
|
580
|
-
| `jobs` | Job definitions with scheduling metadata (runsOn, needs, matrix, contexts, concurrency, container, checkout, gracePeriod, label routing, dynamic fields, etc.).
|
|
581
|
-
| `rules` | Workflow-level conditional rules (optional). Stored as dynamic references since rule functions cannot be serialized.
|
|
582
|
-
| `description` | Optional workflow description.
|
|
583
|
-
| `hashFiles` | Declared glob patterns for extra files included in the content hash (optional). See [extra files in the content hash](https://docs.kici.dev/user/lock-file-and-drift/#extra-files-in-the-content-hash-hashfiles).
|
|
584
|
-
| `resolvedHashFiles` | Resolved file paths from `hashFiles` at compile time (optional). Recorded so the agent can verify without re-discovering.
|
|
585
|
-
| `contexts` | Secret contexts declared by the workflow (optional). The orchestrator validates access to each context before dispatch.
|
|
586
|
-
| `registries` | Private npm registry declarations the agent authenticates against before install (optional): `url`, `scope`, `tokenSecret` reference, `alwaysAuth`. Resolved token bytes never appear in the lock file. See [private registries](https://docs.kici.dev/user/private-registries/).
|
|
587
|
-
| `installEnv` | Extra qualified secret refs (`<context>:<secret-name>`) projected as env vars on the install subprocess for use with a committed `.kici/.npmrc` (optional). See [private registries](https://docs.kici.dev/user/private-registries/).
|
|
588
|
-
| `concurrency` | Workflow-level concurrency config: `hasGroup`, `cancelInProgress`, `max` (optional). See [concurrency groups](https://docs.kici.dev/user/concurrency/).
|
|
589
|
-
| `timeout` | Whole-run wall-clock timeout in milliseconds (optional). The orchestrator reads this at run creation to set the run deadline.
|
|
590
|
-
| `approval` | Normalized approval gate (optional): `clauses`, `reason`, `timeoutSeconds`, `when`. When present the whole run is held before any job is dispatched. Job and step entries carry the same normalized block for job- and step-level gates. See [approval gates](https://docs.kici.dev/user/approvals/).
|
|
591
|
-
| `hasFilter` | `true` when the workflow declares a workflow-level `filter` predicate (optional; omitted rather than `false`). The predicate itself is never serialized — the flag tells the orchestrator an agent must evaluate the workflow before any of its jobs is dispatched. See [global workflows](https://docs.kici.dev/user/global-workflows/#narrowing-with-a-filter).
|
|
592
|
-
| Hook flags | Boolean flags (`hasOnCancel`, `hasCleanup`, `hasOnSuccess`, `hasOnFailure`) indicating which lifecycle hooks are defined. Job entries additionally have `hasBeforeStep` and `hasAfterStep`.
|
|
574
|
+
| Field | Description |
|
|
575
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
576
|
+
| `name` | Workflow name. |
|
|
577
|
+
| `source` | Per-workflow source file and export reference. |
|
|
578
|
+
| `contentHash` | SHA-256 of a digest over the whole `.kici/` directory mixed with `compileSchemaVersion` (and an `assetDigest` of declared `hashFiles` when present): `SHA-256(compileSchemaVersion + ":" + treeDigest [+ "\0" + assetDigest])`. The tree digest covers every file under `.kici/` except the paths declared in `.kici/.kiciignore` — see [files the content hash skips](https://docs.kici.dev/user/lock-file-and-drift/#files-the-content-hash-skips-kicikiciignore). Paths are sorted and line endings normalized. The orchestrator uses this as the source-tarball cache key and the agent re-computes it against the extracted tree to detect drift. |
|
|
579
|
+
| `compileSchemaVersion` | Compiler schema version used when computing `contentHash` (currently `7`). The hash input is line-ending-normalized (CRLF → LF) so a lock file produced on Linux matches the agent's hash on Windows where Git's `core.autocrlf=true` rewrites checked-out text to CRLF. Bumping the schema version invalidates every existing source cache entry even if source is unchanged, which is the correct behavior when the compile-time or runtime contract changes. |
|
|
580
|
+
| `triggers` | Trigger definitions extracted from the workflow (used by the orchestrator for event matching). |
|
|
581
|
+
| `jobs` | Job definitions with scheduling metadata (runsOn, needs, matrix, contexts, concurrency, container, checkout, gracePeriod, label routing, dynamic fields, etc.). |
|
|
582
|
+
| `rules` | Workflow-level conditional rules (optional). Stored as dynamic references since rule functions cannot be serialized. |
|
|
583
|
+
| `description` | Optional workflow description. |
|
|
584
|
+
| `hashFiles` | Declared glob patterns for extra files included in the content hash (optional). See [extra files in the content hash](https://docs.kici.dev/user/lock-file-and-drift/#extra-files-in-the-content-hash-hashfiles). |
|
|
585
|
+
| `resolvedHashFiles` | Resolved file paths from `hashFiles` at compile time (optional). Recorded so the agent can verify without re-discovering. |
|
|
586
|
+
| `contexts` | Secret contexts declared by the workflow (optional). The orchestrator validates access to each context before dispatch. |
|
|
587
|
+
| `registries` | Private npm registry declarations the agent authenticates against before install (optional): `url`, `scope`, `tokenSecret` reference, `alwaysAuth`. Resolved token bytes never appear in the lock file. See [private registries](https://docs.kici.dev/user/private-registries/). |
|
|
588
|
+
| `installEnv` | Extra qualified secret refs (`<context>:<secret-name>`) projected as env vars on the install subprocess for use with a committed `.kici/.npmrc` (optional). See [private registries](https://docs.kici.dev/user/private-registries/). |
|
|
589
|
+
| `concurrency` | Workflow-level concurrency config: `hasGroup`, `cancelInProgress`, `max` (optional). See [concurrency groups](https://docs.kici.dev/user/concurrency/). |
|
|
590
|
+
| `timeout` | Whole-run wall-clock timeout in milliseconds (optional). The orchestrator reads this at run creation to set the run deadline. |
|
|
591
|
+
| `approval` | Normalized approval gate (optional): `clauses`, `reason`, `timeoutSeconds`, `when`. When present the whole run is held before any job is dispatched. Job and step entries carry the same normalized block for job- and step-level gates. See [approval gates](https://docs.kici.dev/user/approvals/). |
|
|
592
|
+
| `hasFilter` | `true` when the workflow declares a workflow-level `filter` predicate (optional; omitted rather than `false`). The predicate itself is never serialized — the flag tells the orchestrator an agent must evaluate the workflow before any of its jobs is dispatched. See [global workflows](https://docs.kici.dev/user/global-workflows/#narrowing-with-a-filter). |
|
|
593
|
+
| Hook flags | Boolean flags (`hasOnCancel`, `hasCleanup`, `hasOnSuccess`, `hasOnFailure`) indicating which lifecycle hooks are defined. Job entries additionally have `hasBeforeStep` and `hasAfterStep`. |
|
|
593
594
|
|
|
594
595
|
Step entries carry their own capability flags, so the orchestrator can reason about a step without loading your TypeScript:
|
|
595
596
|
|
|
@@ -670,9 +671,40 @@ kici compile --check
|
|
|
670
671
|
|
|
671
672
|
This validates all workflows and generates the lock file in memory without writing it. If any workflow has syntax errors or invalid configuration, the command exits non-zero. Pair this with the agent-side hash verification (below) for full drift detection -- `--check` catches broken source, while the agent catches source-lock-file mismatches at run time.
|
|
672
673
|
|
|
674
|
+
## Files the content hash skips (`.kici/.kiciignore`)
|
|
675
|
+
|
|
676
|
+
The per-workflow content hash covers everything under `.kici/` except the paths declared in `.kici/.kiciignore`. `kici init` writes that file for you with this default set:
|
|
677
|
+
|
|
678
|
+
```
|
|
679
|
+
node_modules/
|
|
680
|
+
types/
|
|
681
|
+
.npmrc
|
|
682
|
+
package-lock.json
|
|
683
|
+
pnpm-lock.yaml
|
|
684
|
+
kici.lock.json
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
Every entry except `kici.lock.json` names something KiCI itself regenerates. The agent installs your workflow's dependencies before it re-checks the hash, and that install rewrites `package-lock.json`, `pnpm-lock.yaml`, `.npmrc` and `node_modules/`; `kici compile` refreshes `types/` after it has already hashed the tree. Hashing any of them would make the hash change on every run, and the drift gate would reject work that never changed.
|
|
688
|
+
|
|
689
|
+
`kici.lock.json` is different: the hash is written **into** that file, so hashing it would make it an input to itself. It stays excluded whatever your `.kiciignore` says.
|
|
690
|
+
|
|
691
|
+
Patterns are gitignore-style and are matched relative to `.kici/`. A trailing `/` matches a directory and everything beneath it, a bare name matches at any depth, and a pattern containing a slash is anchored at `.kici/`.
|
|
692
|
+
|
|
693
|
+
One rule differs from `git`: **a symlink to a directory counts as a directory**. So `node_modules/` covers a `.kici/node_modules` that is a symlink into a shared dependency tree, where `git` would treat that link as a file. The exclusion means "skip the dependency tree, whatever shape it takes on disk". Hashing the link instead produced a hash your build agent could not reproduce, because its own dependency install always writes a real directory there.
|
|
694
|
+
|
|
695
|
+
A symlink the exclusions do **not** cover is still hashed — as its link target, not as the bytes behind it. The source tarball has to carry that link unchanged for the agent to agree. So `kici compile` warns about a link it cannot carry: one whose target is absolute (extraction strips the leading `/`), or whose target points outside `.kici/`'s parent (extraction drops the link). Point the link inside `.kici/`, replace it with the files it names, or list it in `.kiciignore`.
|
|
696
|
+
|
|
697
|
+
:::caution[The file replaces the defaults — it does not add to them]
|
|
698
|
+
When `.kici/.kiciignore` exists, it **is** the exclusion list. A one-line file excludes one path and re-includes everything else, `package-lock.json` included. `kici compile` warns when your file omits a path a run rewrites, and names both the path and the instability it causes. Delete the file to fall back to the defaults.
|
|
699
|
+
:::
|
|
700
|
+
|
|
701
|
+
`.kiciignore` is itself covered by the hash. Which files define a workflow's identity is part of that identity, so editing the file forces a recompile — and nobody can change what a lock file attests to without changing the lock file.
|
|
702
|
+
|
|
703
|
+
> **Not the repo-root `.kiciignore`.** A `.kiciignore` at the root of your repository is a separate, unrelated file: it selects which working-tree files `kici run remote` uploads. Only the one inside `.kici/` affects the content hash.
|
|
704
|
+
|
|
673
705
|
## Extra files in the content hash (`hashFiles`)
|
|
674
706
|
|
|
675
|
-
|
|
707
|
+
A helper the workflow imports from `.kici/lib/` is already covered, so editing it invalidates the cache on its own. If your workflow depends on files **outside** `.kici/` -- configuration files, scripts, Dockerfiles, etc. -- changes to those files will **not** invalidate the cache unless you declare them.
|
|
676
708
|
|
|
677
709
|
Use the `hashFiles` option on a workflow to include additional paths or glob patterns (relative to the repo root) in the content hash:
|
|
678
710
|
|
|
@@ -683,14 +715,14 @@ export default workflow('deploy', {
|
|
|
683
715
|
});
|
|
684
716
|
```
|
|
685
717
|
|
|
686
|
-
When any of the matched files change, the content hash formula becomes `SHA-256(compileSchemaVersion + ":" +
|
|
718
|
+
When any of the matched files change, the content hash formula becomes `SHA-256(compileSchemaVersion + ":" + treeDigest + "\0" + assetDigest)` where `assetDigest` is a deterministic encoding of the resolved file paths and their contents. This busts the source-tarball cache and forces the build agent to pack and upload a fresh tarball. The resolved file paths are recorded in the lock file under `resolvedHashFiles` so the agent can verify without re-discovering the workflow.
|
|
687
719
|
|
|
688
720
|
## Agent-side safety net
|
|
689
721
|
|
|
690
722
|
If drift still occurs (e.g. someone committed only the `.ts` change), the agent detects it at run time before any step runs:
|
|
691
723
|
|
|
692
|
-
- After extracting the `.kici/` source tarball (or loading source from a `git clone` on the build path), the agent
|
|
693
|
-
- If the orchestrator sent a `contentHash` (from the lock file) and the computed hash does **not** match, the agent fails the run with an error like: **lock file is out of date** (workflow source changed without regenerating the lock file). The error includes the baked agent `@kici-dev/sdk` version + bundle hash so operators can debug cross-host compile mismatches.
|
|
724
|
+
- After extracting the `.kici/` source tarball (or loading source from a `git clone` on the build path), the agent walks the whole extracted `.kici/` tree and re-computes `contentHash = SHA-256(compileSchemaVersion + ":" + treeDigest [+ "\0" + assetDigest])` using the same implementation as the compiler. Because it covers the tree, an edit to any file the workflow imports is caught, not just an edit to the entry file.
|
|
725
|
+
- If the orchestrator sent a `contentHash` (from the lock file) and the computed hash does **not** match, the agent fails the run with an error like: **lock file is out of date** (workflow source changed without regenerating the lock file). The error includes the baked agent `@kici-dev/sdk` version + bundle hash so operators can debug cross-host compile mismatches. When the hashed tree carries symlinks, the error names them too — recompiling cannot reconcile a link the tarball omits or extraction rewrites, so the usual remedy would loop.
|
|
694
726
|
|
|
695
727
|
So even without a pre-commit or CI check, a stale lock file will cause the run to fail with a clear message instead of running with the wrong workflow.
|
|
696
728
|
|
|
@@ -702,6 +734,7 @@ So even without a pre-commit or CI check, a stale lock file will cause the run t
|
|
|
702
734
|
| Catch drift before commit | Install a pre-commit hook with `kici hook install`. |
|
|
703
735
|
| Catch broken source in CI | Run `kici compile --check` in CI. |
|
|
704
736
|
| Bust cache on external files | Add `hashFiles: ['config.json']` to include non-workflow files in the content hash. |
|
|
737
|
+
| Skip a path inside `.kici/` | List it in `.kici/.kiciignore` — remember the file replaces the defaults. |
|
|
705
738
|
| Fail fast when drift remains | Rely on the agent’s hash verification when it compiles from source. |
|
|
706
739
|
|
|
707
740
|
## See also
|
|
@@ -132,6 +132,8 @@ workflow('test', {
|
|
|
132
132
|
|
|
133
133
|
When `max: 1` (default), runs are fully serialized within the group.
|
|
134
134
|
|
|
135
|
+
`max` is enforced by the orchestrator's database, so the limit is cluster-wide and survives a restart. A run holding a slot keeps it across an orchestrator restart. Every orchestrator in a cluster counts against the same limit, so a group with `max: 1` runs one job at a time no matter which orchestrator dispatched it.
|
|
136
|
+
|
|
135
137
|
## Group key examples
|
|
136
138
|
|
|
137
139
|
### Deploy per environment
|
|
@@ -191,6 +193,14 @@ When a workflow has both `concurrency` and `context` protection rules:
|
|
|
191
193
|
|
|
192
194
|
This means a run that passed approval won't need re-approval if it gets queued by concurrency.
|
|
193
195
|
|
|
196
|
+
The two caps also differ in strength. A workflow-level concurrency group claims
|
|
197
|
+
its slot inside a single database transaction, so two runs that arrive together
|
|
198
|
+
cannot both take it. A context
|
|
199
|
+
[concurrency limit](https://docs.kici.dev/user/contexts/#concurrency-limits) is a throughput control:
|
|
200
|
+
simultaneous arrivals can each be admitted before either is recorded. Declare a
|
|
201
|
+
concurrency group for anything that must never run beside itself, whatever the
|
|
202
|
+
context limit says.
|
|
203
|
+
|
|
194
204
|
## Cancelling queued runs
|
|
195
205
|
|
|
196
206
|
Queued runs can be cancelled before they start executing. The cancel request removes them from the queue immediately -- they don't go through the grace period since no step is running.
|
|
@@ -401,6 +411,17 @@ Store the secrets first with `kici-admin secret set`. Pasting a token straight
|
|
|
401
411
|
into the workflow is rejected when the workflow is defined, because a token
|
|
402
412
|
written into `.kici/` would be committed to your repository.
|
|
403
413
|
|
|
414
|
+
The named context's protection rules run before the secret is read, exactly as
|
|
415
|
+
they do for [git credentials](https://docs.kici.dev/user/patterns/git-credentials/#what-a-job-may-ask-for).
|
|
416
|
+
A `prod:` reference from a branch the `prod` context restricts is refused, and
|
|
417
|
+
the job is dispatched with no registry credentials — so a private image fails to
|
|
418
|
+
pull rather than being pulled from a branch the context does not allow. The rule
|
|
419
|
+
that refused it is named in your orchestrator's log, not in the run.
|
|
420
|
+
|
|
421
|
+
**An untrusted ref receives no registry credentials.** A fork pull request is
|
|
422
|
+
dispatched without them, so a private base image fails to pull and a public one
|
|
423
|
+
is unaffected. The run's reduced-privilege note says so.
|
|
424
|
+
|
|
404
425
|
The username is not a secret, so you may write it directly:
|
|
405
426
|
|
|
406
427
|
```typescript
|
|
@@ -410,6 +431,30 @@ auth: { username: 'ci-bot', tokenSecret: 'prod:REGISTRY_TOKEN' }
|
|
|
410
431
|
Your orchestrator resolves these names at dispatch and sends only the resolved
|
|
411
432
|
credentials to the agent. The agent never reads your secret store.
|
|
412
433
|
|
|
434
|
+
### Naming the registry
|
|
435
|
+
|
|
436
|
+
`auth` also takes a `registry` — the registry host the credentials belong to,
|
|
437
|
+
such as `reg.internal:5000`.
|
|
438
|
+
|
|
439
|
+
With `image` it is optional, because KiCI reads the host off the image
|
|
440
|
+
reference. With `dockerfile` it is **required**: the base image is named inside
|
|
441
|
+
your Dockerfile, so there is nothing to read it from. A `dockerfile` job whose
|
|
442
|
+
`auth` omits `registry` is refused when you define the workflow.
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
container: {
|
|
446
|
+
dockerfile: '.kici/ci.Dockerfile',
|
|
447
|
+
auth: {
|
|
448
|
+
registry: 'reg.internal:5000',
|
|
449
|
+
usernameSecret: 'prod:REGISTRY_USER',
|
|
450
|
+
tokenSecret: 'prod:REGISTRY_TOKEN',
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
With `dockerfile`, these credentials pull the Dockerfile's own `FROM` base — not
|
|
456
|
+
a job image, since the job image is the one KiCI builds.
|
|
457
|
+
|
|
413
458
|
### Credentials that only exist at run time
|
|
414
459
|
|
|
415
460
|
A token fetched during the run — from a cloud registry's login command, for
|
|
@@ -560,10 +605,11 @@ The KiCI CLI reads the following environment variables to customize its behavior
|
|
|
560
605
|
|
|
561
606
|
## Development
|
|
562
607
|
|
|
563
|
-
| Variable
|
|
564
|
-
|
|
|
565
|
-
| `KICI_DEV`
|
|
566
|
-
| `
|
|
608
|
+
| Variable | Description | Default |
|
|
609
|
+
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
|
610
|
+
| `KICI_DEV` | Enable development mode. When `true`, uses prerelease-compatible version ranges (`>=0.0.1-0`) for dev dependencies and skips npm version resolution. | unset |
|
|
611
|
+
| `KICI_DEV_REGISTRY` | npm registry the `@kici-dev` scope points at when `KICI_DEV` is set. `kici init` writes it into `.npmrc`; with no value it writes no `.npmrc`. | unset |
|
|
612
|
+
| `KICI_DEBUG` | Enable debug logging. When `true`, prints verbose diagnostics (SDK alias resolution, step-level debug logs, stack traces on errors). Equivalent to the `--debug` CLI flag. | unset |
|
|
567
613
|
|
|
568
614
|
## Local dev plane
|
|
569
615
|
|
|
@@ -897,7 +943,7 @@ job('provision', {
|
|
|
897
943
|
|
|
898
944
|
### Trust tiers on internal triggers
|
|
899
945
|
|
|
900
|
-
An internally-triggered run resolves its [trust tier](https://docs.kici.dev/user/contexts/#minimum-trust) from the trigger. The tier decides the run's cache scope, whether it may run a [Dockerfile build](https://docs.kici.dev/user/container-jobs/#who-may-build), whether it receives [install secrets](https://docs.kici.dev/user/private-registries/), and whether a `minimumTrust` context holds it.
|
|
946
|
+
An internally-triggered run resolves its [trust tier](https://docs.kici.dev/user/contexts/#minimum-trust) from the trigger. The tier decides the run's cache scope, whether it may run a [Dockerfile build](https://docs.kici.dev/user/container-jobs/#who-may-build), whether it receives [install secrets](https://docs.kici.dev/user/private-registries/), and whether a `minimumTrust` context holds it. It also decides whether the run's jobs receive [container-registry credentials](https://docs.kici.dev/user/container-jobs/#private-images) and their declared [git credentials](https://docs.kici.dev/user/patterns/git-credentials/#what-a-job-may-ask-for).
|
|
901
947
|
|
|
902
948
|
Four rules resolve the tier, and KiCI applies them in this order:
|
|
903
949
|
|
|
@@ -1424,7 +1470,7 @@ The registration log line names the organization it decided against, so a refusa
|
|
|
1424
1470
|
|
|
1425
1471
|
### Secrets are not available
|
|
1426
1472
|
|
|
1427
|
-
A global workflow's job is dispatched with **no secret material** — not the source repo's, and not the workflow repo's own. The organization-wide dispatch path binds no secret contexts, so a `contexts:` declaration on a global workflow resolves to nothing and any secret the steps expect is
|
|
1473
|
+
A global workflow's job is dispatched with **no secret material** — not the source repo's, and not the workflow repo's own. The organization-wide dispatch path binds no secret contexts, so a `contexts:` declaration on a global workflow resolves to nothing and any secret the steps expect is absent. Plan for it: a global workflow is for checks, policy and reporting that need only the two checkouts, not for deploys that need credentials.
|
|
1428
1474
|
|
|
1429
1475
|
This is about your **stored secrets**, not about repository access: the job is still handed a short-lived clone token for each repo it checks out, which is how the dual checkout works at all. What it does not get is anything from a secret context.
|
|
1430
1476
|
|