@kici-dev/compiler 0.5.0 → 0.6.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 +37 -7
- package/dist/commands/approve.d.ts +12 -0
- package/dist/commands/approve.js +5 -2
- package/dist/commands/compile.js +4 -2
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/endpoints.js +4 -6
- package/dist/commands/held-run-client.d.ts +21 -1
- package/dist/commands/held-run-client.js +34 -15
- package/dist/commands/hook.js +22 -20
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +2 -1
- package/dist/commands/init.d.ts +9 -2
- package/dist/commands/init.js +43 -16
- package/dist/commands/login.js +1 -1
- package/dist/commands/orchestrators.js +3 -2
- package/dist/commands/reject.d.ts +12 -0
- package/dist/commands/reject.js +5 -2
- package/dist/commands/report/collect.d.ts +82 -0
- package/dist/commands/report/collect.js +234 -0
- package/dist/commands/report/identity.d.ts +48 -0
- package/dist/commands/report/identity.js +49 -0
- package/dist/commands/report/index.d.ts +63 -0
- package/dist/commands/report/index.js +119 -0
- package/dist/commands/report/upload.d.ts +38 -0
- package/dist/commands/report/upload.js +64 -0
- package/dist/commands/run-hold-watch.js +2 -2
- package/dist/commands/run.js +6 -3
- package/dist/commands/runs/show.js +80 -1
- package/dist/commands/types.js +51 -8
- package/dist/execution/sdk-alias.js +4 -2
- package/dist/fixtures/compiler.js +2 -1
- package/dist/format.js +3 -3
- package/dist/generators/secrets-dts.d.ts +8 -2
- package/dist/generators/secrets-dts.js +3 -2
- package/dist/hooks/installer.js +2 -1
- package/dist/llm-context/llms-architecture.txt +35 -13
- package/dist/llm-context/llms-cli.txt +142 -28
- package/dist/llm-context/llms-features-execution.txt +2017 -0
- package/dist/llm-context/llms-features.txt +298 -1483
- package/dist/llm-context/llms-full.txt +3008 -1698
- package/dist/llm-context/llms-getting-started.txt +145 -12
- package/dist/llm-context/llms-patterns.txt +176 -1
- package/dist/llm-context/llms-providers.txt +11 -27
- package/dist/llm-context/llms-sdk-runtime.txt +25 -4
- package/dist/llm-context/llms-sdk.txt +31 -1
- package/dist/llm-context/llms.txt +22 -14
- package/dist/local-plane/paths.d.ts +15 -0
- package/dist/local-plane/paths.js +22 -1
- package/dist/local-plane/plane-manager.js +2 -2
- package/dist/local-plane/port-holder.js +1 -1
- package/dist/local-plane/postgres.d.ts +3 -16
- package/dist/local-plane/postgres.js +10 -15
- package/dist/lockfile/generator.d.ts +12 -0
- package/dist/lockfile/generator.js +47 -14
- package/dist/postinstall.js +2 -1
- package/dist/remote/config.d.ts +2 -15
- package/dist/remote/config.js +2 -16
- package/dist/remote/dashboard-client.d.ts +39 -0
- package/dist/remote/dashboard-client.js +41 -0
- package/dist/remote/oauth.js +7 -5
- package/dist/remote/uploader.js +2 -2
- package/dist/templates/package-json.js +1 -1
- package/dist/test-runner/dry-run.js +4 -2
- package/dist/test-runner/git-detector.js +2 -1
- package/dist/test-runner/job-executor.js +2 -1
- package/dist/test-runner/payload-builder.js +11 -17
- package/dist/types.d.ts +33 -3
- package/dist/validation/validator.js +23 -6
- package/package.json +16 -11
- package/sbom.spdx.json +953 -901
|
@@ -76,6 +76,8 @@ Trigger matching can query the **contents** of individual source files, not just
|
|
|
76
76
|
|
|
77
77
|
The orchestrator also does **not** run `dynamicJob` generator bodies itself: for the event-only (function) form it dispatches a dedicated dynamic-evaluation job to an agent at event time; the generator function then runs agent-side (see below).
|
|
78
78
|
|
|
79
|
+
A workflow-level `filter` predicate is the same shape: the orchestrator sees only the lock's `hasFilter` flag, never the predicate, so it dispatches an evaluation job and lets an agent decide whether the workflow applies. Prefer the declarative filters where they answer the question — `commitMessage` on the trigger and `requires` over source files cost no evaluation job at all.
|
|
80
|
+
|
|
79
81
|
See [dynamic values](https://docs.kici.dev/user/dynamic-values/) for how dynamic `context`, `env`, and `concurrencyGroup` functions resolve.
|
|
80
82
|
|
|
81
83
|
## Agent time
|
|
@@ -86,20 +88,33 @@ After dispatch, each job runs in its own ephemeral agent sandbox: a shallow clon
|
|
|
86
88
|
2. **Step-level rules**, then each step's `run()` body and its hooks.
|
|
87
89
|
3. **Dynamic values** (`context`, `env`, `concurrencyGroup` functions) are resolved here, via a short `__init__` job that runs the function before the real job runs; this shows in the run timeline as an `Init:` entry.
|
|
88
90
|
4. **`dynamicJob` generators run here — both forms.** The event-only (function) form runs in a dedicated evaluation job dispatched at event time; the result-aware (options) form is deferred until its declared `needs` complete, then run with the upstream outputs frozen as `ctx.needs`.
|
|
91
|
+
5. **A workflow-level `filter` predicate runs here too**, before the jobs it gates. A global workflow evaluates it once per (event × workflow repo), before any run row exists, so a `false` verdict leaves no run at all. A same-repo workflow evaluates it once per job that reaches dispatch and once per job generator, after the run row exists, so a `false` verdict leaves a run whose only entries are the evaluation jobs. Keep the predicate cheap, pure, and side-effect free — a ten-job workflow calls it ten times for one event. See [narrowing with a filter](https://docs.kici.dev/user/global-workflows/#narrowing-with-a-filter).
|
|
89
92
|
|
|
90
93
|
See [job execution](https://docs.kici.dev/architecture/execution/job-execution/) and [hooks and rules](https://docs.kici.dev/user/hooks/) for the details.
|
|
91
94
|
|
|
95
|
+
## How a reused agent stays clean between jobs
|
|
96
|
+
|
|
97
|
+
An ephemeral agent is discarded after one job, so residue never matters. A **reused agent** — a long-lived process on a shared host (the bare-metal profile) — serves many jobs in turn. Between them, the agent runs a supervisor-owned cleanup phase so one job's leftovers never reach the next:
|
|
98
|
+
|
|
99
|
+
1. **It reaps the finished job's process tree.** A step may background a daemon that outlives the job. The agent runs each job's process in its own process group and signals the whole group when the job ends, so a stray daemon does not survive into the next job. Set `KICI_AGENT_ORPHAN_CLEANUP=false` to keep only the runner and leave a backgrounded process alive on purpose.
|
|
100
|
+
2. **It re-runs declared cleanup after a hard kill.** A job's `cleanup` / `onFailure` hooks normally run in the job process. If that process is killed hard (out of memory, forced stop), the agent re-runs the declared cleanup against the preserved work directory.
|
|
101
|
+
3. **It deletes the work directory.**
|
|
102
|
+
4. **It runs an optional operator reset command.** Set `KICI_AGENT_BETWEEN_JOBS_RESET_COMMAND` to a host-reset command (for example, pruning a container cache). It runs after the reap and work-directory deletion. A failure never fails the finished job.
|
|
103
|
+
|
|
104
|
+
This phase is the primary cross-job cleanup. The agent's startup temp-directory sweep stays as a backstop for anything a between-jobs phase missed. See [agent configuration](https://docs.kici.dev/operator/agent/configuration/) for the full env-var reference.
|
|
105
|
+
|
|
92
106
|
## What re-evaluates where
|
|
93
107
|
|
|
94
|
-
| Construct | Runs on | When
|
|
95
|
-
| ---------------------------- | ---------------- |
|
|
96
|
-
| Static value | Compile → lock | Never re-evaluated
|
|
97
|
-
| Dynamic value | Agent init step | Per event
|
|
98
|
-
| Job-level rules | Agent | After clone
|
|
99
|
-
| Step-level rules | Agent | Per step
|
|
100
|
-
| `dynamicJob` (function form) | Agent (eval job) | Dispatched at event time
|
|
101
|
-
| `dynamicJob` (options form) | Agent | Deferred until `needs` complete
|
|
102
|
-
|
|
|
108
|
+
| Construct | Runs on | When |
|
|
109
|
+
| ---------------------------- | ---------------- | ---------------------------------------------------- |
|
|
110
|
+
| Static value | Compile → lock | Never re-evaluated |
|
|
111
|
+
| Dynamic value | Agent init step | Per event |
|
|
112
|
+
| Job-level rules | Agent | After clone |
|
|
113
|
+
| Step-level rules | Agent | Per step |
|
|
114
|
+
| `dynamicJob` (function form) | Agent (eval job) | Dispatched at event time |
|
|
115
|
+
| `dynamicJob` (options form) | Agent | Deferred until `needs` complete |
|
|
116
|
+
| Workflow `filter` predicate | Agent (eval job) | Per event (global) / per job + generator (same-repo) |
|
|
117
|
+
| Step / job body + hooks | Agent | Per job |
|
|
103
118
|
|
|
104
119
|
**Determinism note.** `ctx.event` and `ctx.needs` are frozen snapshots — captured once and replayed unchanged on any re-evaluation. A generator that derives its output from them is stable across re-evaluations; one that reads the wall clock (`Date.now()`) or a random source (`Math.random()`) is not.
|
|
105
120
|
|
|
@@ -149,6 +164,124 @@ Outputs are typed across the job boundary too: reading `jobRef.result.…` or `c
|
|
|
149
164
|
|
|
150
165
|
---
|
|
151
166
|
|
|
167
|
+
## Getting help
|
|
168
|
+
|
|
169
|
+
Source: https://docs.kici.dev/user/getting-help/
|
|
170
|
+
|
|
171
|
+
When something goes wrong, the fastest path to a fix is a report that already
|
|
172
|
+
carries the context. This page covers what to try first, what to send, and how
|
|
173
|
+
to send it privately.
|
|
174
|
+
|
|
175
|
+
## Diagnose it yourself first
|
|
176
|
+
|
|
177
|
+
Two commands answer most problems without anyone else involved:
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# Check your own setup: login, org, lock file, orchestrator, agent labels
|
|
181
|
+
kici doctor
|
|
182
|
+
|
|
183
|
+
# Look at the org's infrastructure: orchestrators, scalers, agents
|
|
184
|
+
kici diagnostics
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
`kici doctor` prints the next command to run for each problem it finds. Work
|
|
188
|
+
down its output before reporting — a stale lock file or an expired login is a
|
|
189
|
+
one-command fix.
|
|
190
|
+
|
|
191
|
+
If your workflow ran and failed, read its logs:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
kici runs show <run-id>
|
|
195
|
+
kici runs logs <run-id>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
[Common failures](https://docs.kici.dev/user/common-failures/) covers the errors people hit most.
|
|
199
|
+
|
|
200
|
+
## Report a problem
|
|
201
|
+
|
|
202
|
+
When you cannot resolve it yourself, gather a diagnostic bundle:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
kici report --run <run-id> --message "what you expected, and what happened"
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
The command writes a ZIP and prints its path and `sha256`. It sends nothing.
|
|
209
|
+
The bundle holds:
|
|
210
|
+
|
|
211
|
+
- your CLI, Node, and orchestrator versions,
|
|
212
|
+
- your redacted KiCI configuration,
|
|
213
|
+
- your project's workflow list and lock-file state,
|
|
214
|
+
- the failing run's detail and logs, when you pass `--run`,
|
|
215
|
+
- a collection report saying which of those the command could and could not
|
|
216
|
+
read.
|
|
217
|
+
|
|
218
|
+
Open the file and read it. It is yours until you decide to share it.
|
|
219
|
+
|
|
220
|
+
### What gets redacted
|
|
221
|
+
|
|
222
|
+
KiCI removes known secret shapes before anything enters the bundle:
|
|
223
|
+
|
|
224
|
+
- API keys and access tokens (AWS, GitHub, Slack, KiCI agent tokens),
|
|
225
|
+
- `Authorization` headers and JSON web tokens,
|
|
226
|
+
- passwords inside connection URLs,
|
|
227
|
+
- private keys and encrypted-value blocks,
|
|
228
|
+
- values assigned to a secret-named key, such as `api_key=` or `password=`.
|
|
229
|
+
|
|
230
|
+
Configuration is redacted twice: an allowlist keeps only known-safe fields, and
|
|
231
|
+
the free-text scrubber runs over what remains.
|
|
232
|
+
|
|
233
|
+
**Redaction is best effort.** A secret in a format KiCI does not recognize can
|
|
234
|
+
survive it. Review the bundle before you share it. `--no-redact` turns
|
|
235
|
+
redaction off and prints a warning — use it only for a bundle you keep.
|
|
236
|
+
|
|
237
|
+
## Send it privately
|
|
238
|
+
|
|
239
|
+
Reports contain your data, so there is no public tracker for them. Add
|
|
240
|
+
`--upload` to send the bundle to KiCI directly:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
kici report --run <run-id> --upload --message "matrix job hangs on macOS"
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The command prints a reference id. Quote it in any conversation about the
|
|
247
|
+
problem. The bundle goes straight from your machine to KiCI storage over a
|
|
248
|
+
one-time upload link — it never passes through the dashboard.
|
|
249
|
+
|
|
250
|
+
Add `--email` if you want a reply address attached to the report.
|
|
251
|
+
|
|
252
|
+
## Manage what you have sent
|
|
253
|
+
|
|
254
|
+
An upload is not permanent, and it is yours to revoke:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# See the reports you have uploaded
|
|
258
|
+
kici report list
|
|
259
|
+
|
|
260
|
+
# Delete an uploaded bundle
|
|
261
|
+
kici report withdraw <ref>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Uploaded bundles expire automatically after 90 days.
|
|
265
|
+
|
|
266
|
+
Anyone in your organization can upload a report. By default you see and
|
|
267
|
+
withdraw your own. A member with the `support:admin` permission can manage
|
|
268
|
+
every report in the organization, which is how an owner cleans up on behalf of
|
|
269
|
+
someone who has left.
|
|
270
|
+
|
|
271
|
+
## Reporting a security issue
|
|
272
|
+
|
|
273
|
+
Do not use `kici report` for a suspected vulnerability. Follow the disclosure
|
|
274
|
+
process in [SECURITY.md](https://github.com/kici-dev/kici/blob/master/SECURITY.md)
|
|
275
|
+
instead.
|
|
276
|
+
|
|
277
|
+
## See also
|
|
278
|
+
|
|
279
|
+
- [Common failures](https://docs.kici.dev/user/common-failures/) — the errors people hit most, and their fixes
|
|
280
|
+
- [CLI reference](https://docs.kici.dev/user/cli-reference/) — every `kici` command
|
|
281
|
+
- [Dashboard](https://docs.kici.dev/user/dashboard/) — inspecting runs in the browser
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
152
285
|
## Getting started with workflows
|
|
153
286
|
|
|
154
287
|
Source: https://docs.kici.dev/user/getting-started/
|
|
@@ -171,11 +304,11 @@ npx kici init
|
|
|
171
304
|
|
|
172
305
|
This will:
|
|
173
306
|
|
|
174
|
-
1. Create `.kici/` directory with `workflows/`, `tests/`, `types/`, `package.json`, and `tsconfig.json`
|
|
307
|
+
1. Create `.kici/` directory with `workflows/`, `tests/`, `types/`, `package.json`, and `tsconfig.json`. The `types/` folder holds a local development aid — TypeScript declarations that `kici types` (and an authenticated `kici compile`) generate from your orchestrator's secret contexts. Its content is a snapshot of one org's secret keys, so it is not committed.
|
|
175
308
|
2. Create a `.kiciignore` file with sensible defaults
|
|
176
309
|
3. Let you choose from starter workflow templates (hello-world, pr-checks)
|
|
177
310
|
4. Install dependencies using the package manager detected for your repo (npm, pnpm, or yarn)
|
|
178
|
-
5. Update `.gitignore` to exclude `.kici/node_modules/`
|
|
311
|
+
5. Update `.gitignore` to exclude `.kici/node_modules/`, and write `.kici/.gitignore` to keep the generated `types/` declarations untracked (`kici.lock.json` stays tracked — the orchestrator fetches it from your repo)
|
|
179
312
|
6. Optionally install a pre-commit hook to auto-compile workflows
|
|
180
313
|
|
|
181
314
|
The package manager is detected from your repo's `packageManager` field, lockfile, or the manager that invoked `kici`, defaulting to npm. Pass `--package-manager <npm|pnpm|yarn>` to override it.
|
|
@@ -496,7 +629,7 @@ For coding agents that want the entire documentation set up front, KiCI follows
|
|
|
496
629
|
|
|
497
630
|
- `https://kici.dev/llms.txt` — curated link index grouped by SDK / patterns / CLI / architecture.
|
|
498
631
|
- `https://kici.dev/llms-full.txt` — concatenated markdown of every page indexed above.
|
|
499
|
-
- `kici docs llm` — print the same `llms
|
|
632
|
+
- `kici docs llm` — print the same curated `llms.txt` index to stdout, offline, straight from the installed `@kici-dev/compiler` package. `kici docs llm <topic>` prints one task bundle (`getting-started`, `patterns`, `sdk`, `sdk-runtime`, `cli`, `features`, `features-execution`, `providers`, `architecture`), and `kici docs llm full` prints the whole `llms-full.txt`. Add `--out <path>` to write to a file. The agent can pipe the output into its own context buffer with no network call.
|
|
500
633
|
- `kici docs` — open the docs site in your browser.
|
|
501
634
|
|
|
502
635
|
The offline bundle is regenerated from `docs/` every time the package is built, so it always matches the version of KiCI you've installed.
|
|
@@ -577,6 +577,181 @@ This workflow:
|
|
|
577
577
|
|
|
578
578
|
---
|
|
579
579
|
|
|
580
|
+
## Git credentials
|
|
581
|
+
|
|
582
|
+
Source: https://docs.kici.dev/user/patterns/git-credentials/
|
|
583
|
+
|
|
584
|
+
Two facts drive everything on this page, and neither is guessable:
|
|
585
|
+
|
|
586
|
+
- **Cloning your own repository needs no credential.** The framework checks it
|
|
587
|
+
out for you, and the app KiCI installs already holds read access.
|
|
588
|
+
- **Pushing always needs a credential you supply.** The KiCI app holds read
|
|
589
|
+
access only, so every push — including to the job's own repository — needs one.
|
|
590
|
+
|
|
591
|
+
## Declare credentials once, by name
|
|
592
|
+
|
|
593
|
+
Credentials are declared on the job as a named map. **Every value is the name of
|
|
594
|
+
a secret**, in `<context>:<secret-name>` form — never the credential itself:
|
|
595
|
+
|
|
596
|
+
```typescript
|
|
597
|
+
job('release', {
|
|
598
|
+
runsOn: 'linux',
|
|
599
|
+
gitCredentials: {
|
|
600
|
+
// `default` is used whenever a call names no credential
|
|
601
|
+
default: {
|
|
602
|
+
kind: 'app',
|
|
603
|
+
appIdSecret: 'ci:ACME_APP_ID',
|
|
604
|
+
installationIdSecret: 'ci:ACME_INSTALL_ID',
|
|
605
|
+
privateKeySecret: 'ci:ACME_APP_KEY',
|
|
606
|
+
},
|
|
607
|
+
forge: { kind: 'token', tokenSecret: 'ci:FORGE_PAT' },
|
|
608
|
+
vendor: { kind: 'ssh', privateKeySecret: 'ci:VENDOR_DEPLOY_KEY' },
|
|
609
|
+
},
|
|
610
|
+
steps: [build, tagAndPush],
|
|
611
|
+
});
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
Store the secrets first with `kici-admin secret set`. Pasting a private key
|
|
615
|
+
straight into the workflow is rejected when the workflow is defined, naming the
|
|
616
|
+
field — a key written into `.kici/` would be committed to your repository.
|
|
617
|
+
|
|
618
|
+
## Push
|
|
619
|
+
|
|
620
|
+
Your checkout is read-only by default. Opening a write window is explicit:
|
|
621
|
+
|
|
622
|
+
```typescript
|
|
623
|
+
step('tag', async ({ $, repo }) => {
|
|
624
|
+
await $`git tag v${version}`;
|
|
625
|
+
await repo.withWrite({ permissions: { contents: 'write' } }, async () => {
|
|
626
|
+
await $`git push origin v${version}`;
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
Inside the callback, git operations on that repository use a write credential.
|
|
632
|
+
Outside it they do not, so an accidental push elsewhere in the job fails.
|
|
633
|
+
|
|
634
|
+
Pass `credential: 'forge'` to use a named entry instead of `default`.
|
|
635
|
+
|
|
636
|
+
**KiCI never guesses the permission set.** What a push needs depends on what is
|
|
637
|
+
being pushed — changing anything under `.github/workflows/` additionally requires
|
|
638
|
+
`workflows`:
|
|
639
|
+
|
|
640
|
+
```typescript
|
|
641
|
+
await repo.withWrite({ permissions: { contents: 'write', workflows: 'write' } }, async () => {
|
|
642
|
+
await $`git push origin HEAD`;
|
|
643
|
+
});
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
If the app was not granted a permission you request, the forge refuses to issue
|
|
647
|
+
the credential at all. The error names the repository and the permissions you
|
|
648
|
+
asked for, **before any git command runs** — not at the end of a long build.
|
|
649
|
+
|
|
650
|
+
## Clone more than one repository
|
|
651
|
+
|
|
652
|
+
A workflow often needs several repositories, not just its own. Mint one token
|
|
653
|
+
that covers all of them, then clone each with it:
|
|
654
|
+
|
|
655
|
+
```typescript
|
|
656
|
+
const { token } = await kici.git.github.getToken({
|
|
657
|
+
repositories: ['acme/app', 'acme/shared-lib'],
|
|
658
|
+
permissions: { contents: 'read' },
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
for (const repo of ['acme/app', 'acme/shared-lib']) {
|
|
662
|
+
await $`git clone https://x-access-token:${token}@github.com/${repo}.git`;
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
A GitHub App token is issued per installation, so one call covers every
|
|
667
|
+
repository you name. Each repository must be inside the app's installation. If
|
|
668
|
+
one is not, the forge refuses the whole request and the error names it.
|
|
669
|
+
|
|
670
|
+
The credential helper is installed on your own checkout only, so a repository
|
|
671
|
+
you clone yourself does not inherit it. That is why this case mints a token
|
|
672
|
+
rather than relying on the helper.
|
|
673
|
+
|
|
674
|
+
## Call the forge API
|
|
675
|
+
|
|
676
|
+
`gh` does not read git credential helpers, so this is the one case that wants the
|
|
677
|
+
token as a value:
|
|
678
|
+
|
|
679
|
+
```typescript
|
|
680
|
+
const { token } = await kici.git.github.getToken({
|
|
681
|
+
repositories: ['acme/app'],
|
|
682
|
+
permissions: { contents: 'write' },
|
|
683
|
+
});
|
|
684
|
+
await $({ env: { ...process.env, GH_TOKEN: token } })`gh release create v${version}`;
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
The token is masked in step logs. Prefer `withWrite` for git itself, which never
|
|
688
|
+
places a credential in the step environment.
|
|
689
|
+
|
|
690
|
+
## Credentials that only exist at run time
|
|
691
|
+
|
|
692
|
+
A credential fetched during the run — from a vault, or a cloud secret store via
|
|
693
|
+
the job's OIDC identity — cannot be named ahead of time. Use the `*Value` half of
|
|
694
|
+
the pair, which says "this is the credential, not a name for one":
|
|
695
|
+
|
|
696
|
+
```typescript
|
|
697
|
+
gitCredentials: { default: { kind: 'token', tokenValue: fetchedAtRuntime } }
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
To pass a derived credential to a **later** job, publish it with
|
|
701
|
+
`ctx.setSecretOutput()` and name it with the reserved `needs:` context — that
|
|
702
|
+
path is encrypted, scoped to the run, and deleted when the run ends:
|
|
703
|
+
|
|
704
|
+
```typescript
|
|
705
|
+
const mint = job('mint', {
|
|
706
|
+
runsOn: 'linux',
|
|
707
|
+
run: async (ctx) => {
|
|
708
|
+
const token = (await ctx.$`vault write -f auth/token/create`).stdout.trim();
|
|
709
|
+
ctx.setSecretOutput('FORGE_TOKEN', token);
|
|
710
|
+
},
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
const build = job('build', {
|
|
714
|
+
runsOn: 'linux',
|
|
715
|
+
needs: [mint],
|
|
716
|
+
gitCredentials: { default: { kind: 'token', tokenSecret: 'needs:FORGE_TOKEN' } },
|
|
717
|
+
steps: [cloneAndPush],
|
|
718
|
+
});
|
|
719
|
+
```
|
|
720
|
+
|
|
721
|
+
Never put a credential in a regular job output: regular outputs are not masked,
|
|
722
|
+
are stored, and are shown in the dashboard.
|
|
723
|
+
|
|
724
|
+
For a **minted app token**, prefer re-deriving over transporting — those expire
|
|
725
|
+
after an hour, so one minted in an earlier job is often already dead by the time
|
|
726
|
+
a later job reads it. Have the later job name the same secret, or mint its own.
|
|
727
|
+
|
|
728
|
+
## How it works, and why long jobs still push
|
|
729
|
+
|
|
730
|
+
An app token expires an hour after it is issued, and cannot be renewed. Rather
|
|
731
|
+
than capture one at checkout time, the agent installs a git credential helper on
|
|
732
|
+
the checkout: git asks it on every network operation, and it obtains a fresh
|
|
733
|
+
credential each time. A push at the end of a three-hour build works exactly as it
|
|
734
|
+
does at the start, and no credential is ever written into `.git/config`, into
|
|
735
|
+
`git remote -v`, or into the step's environment.
|
|
736
|
+
|
|
737
|
+
## Limits worth knowing
|
|
738
|
+
|
|
739
|
+
- **Container jobs cannot use this yet.** A container job runs git inside the
|
|
740
|
+
container, which has no route to the credential service. Bare-metal jobs are
|
|
741
|
+
unaffected.
|
|
742
|
+
- **The reserved `needs:` context is not resolvable yet** on a deployed
|
|
743
|
+
orchestrator; naming it produces a clear error rather than a wrong credential.
|
|
744
|
+
- **A write window is bounded by the repository and the callback, not the step.**
|
|
745
|
+
Steps running concurrently in the same job can push to the same repository
|
|
746
|
+
while it is open. They cannot reach a different one.
|
|
747
|
+
- **A credential you supply yourself cannot be narrowed.** A personal access
|
|
748
|
+
token or SSH key grants whatever it was created with, so a requested permission
|
|
749
|
+
set is reported as unscoped rather than pretended to be enforced.
|
|
750
|
+
- **Being allowed to push is not the same as the push succeeding.** A branch
|
|
751
|
+
protection rule or repository ruleset can still reject it.
|
|
752
|
+
|
|
753
|
+
---
|
|
754
|
+
|
|
580
755
|
## Host restart & wait-for-alive
|
|
581
756
|
|
|
582
757
|
Source: https://docs.kici.dev/user/patterns/host-restart/
|
|
@@ -939,7 +1114,7 @@ export default workflow('on-forgejo-push', {
|
|
|
939
1114
|
**Caveat — cloning:** generic webhook sources deliver only the payload; they do not carry a clone token, and KiCI's automatic pre-step clone (`packages/agent/src/checkout/git-clone.ts`) is GitHub-only today (HTTPS + `http.extraHeader` Basic auth with a GitHub installation token). Three practical patterns:
|
|
940
1115
|
|
|
941
1116
|
- **Mirror to GitHub and fan out.** Keep the repo on GitHub, register the workflow via a GitHub default-branch push, and have Gogs/Forgejo webhooks fan out via [cross-source delivery](https://docs.kici.dev/architecture/webhooks/webhook-delivery/#cross-source-delivery). The clone runs against the GitHub mirror using the GitHub App's token.
|
|
942
|
-
- **Clone yourself using a
|
|
1117
|
+
- **Clone yourself using a declared credential.** Set `checkout: false` on the job to skip the framework clone, declare the forge credential on the job as a named `gitCredentials` entry (values are **secret names**, never the credential), and run `git clone` in the first step — the agent authenticates it for you, and the credential never appears in workflow source. See [Git credentials](https://docs.kici.dev/user/patterns/git-credentials/). This works for any forge the agent can reach, no mirror needed. You still need a way to **register** the workflow — either keep a one-file GitHub repo whose only job is to own the registration, or bootstrap the registration manually against the orchestrator DB.
|
|
943
1118
|
- **Self-contained workflow.** No clone at all. The step reads whatever it needs from `rawPayload` (e.g., `rawPayload.after`, `rawPayload.repository.clone_url`) and drives external systems — notifications, deploys, third-party CI triggers.
|
|
944
1119
|
|
|
945
1120
|
Manual-clone example (pattern 2) using an SSH deploy key:
|
|
@@ -144,38 +144,22 @@ the manifest flow — follow these steps.
|
|
|
144
144
|
|
|
145
145
|
5. **Pick permissions.** Minimum required:
|
|
146
146
|
|
|
147
|
-
| Scope | Access | Why
|
|
148
|
-
| --------------------------- | ------------ |
|
|
149
|
-
| Repository -> Contents | Read | Clone the repo to read the lock file
|
|
150
|
-
| Repository -> Metadata | Read (auto) | Default for every App
|
|
151
|
-
| Repository -> Pull requests | Read | Match `pull_request` triggers
|
|
152
|
-
| Repository -> Checks | Read & write | Post KiCI's enriched Check runs
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
on an org-level install — see the event note below.
|
|
147
|
+
| Scope | Access | Why |
|
|
148
|
+
| --------------------------- | ------------ | ------------------------------------ |
|
|
149
|
+
| Repository -> Contents | Read | Clone the repo to read the lock file |
|
|
150
|
+
| Repository -> Metadata | Read (auto) | Default for every App |
|
|
151
|
+
| Repository -> Pull requests | Read | Match `pull_request` triggers |
|
|
152
|
+
| Repository -> Checks | Read & write | Post KiCI's enriched Check runs |
|
|
153
|
+
|
|
154
|
+
Those four cover the whole flow: clone, trigger matching, Check runs.
|
|
155
|
+
[CI trust](https://docs.kici.dev/architecture/security/ci-security/) needs no
|
|
156
|
+
permission of its own — it reads the fork relationship straight out of
|
|
157
|
+
the webhook payload and calls no GitHub API.
|
|
159
158
|
|
|
160
159
|
6. **Subscribe to events.** At minimum: `push`, `pull_request`,
|
|
161
160
|
`check_run`, `check_suite`. Add others (`issues`, `release`, ...) if
|
|
162
161
|
your workflows use those triggers.
|
|
163
162
|
|
|
164
|
-
**For CI trust (optional but recommended on org installs):** also
|
|
165
|
-
subscribe to `member`, `organization`, `membership`, and `team`.
|
|
166
|
-
KiCI caches each pull-request author's repository access level (used
|
|
167
|
-
to decide whether workflow changes take effect immediately or are
|
|
168
|
-
held for approval — see
|
|
169
|
-
[CI security](https://docs.kici.dev/architecture/security/ci-security/)). These
|
|
170
|
-
events let the orchestrator drop stale cache entries the moment a
|
|
171
|
-
contributor's access changes. They are not required for correctness:
|
|
172
|
-
without them the cache simply ages out on its own 15-minute TTL, so a
|
|
173
|
-
permission change can take up to 15 minutes to take effect. The
|
|
174
|
-
`organization` / `membership` / `team` events require the
|
|
175
|
-
**Organization -> Members** read permission and an org-level
|
|
176
|
-
installation; `member` is a repository event covered by the default
|
|
177
|
-
Metadata permission.
|
|
178
|
-
|
|
179
163
|
7. **Generate a private key.** Scroll to the bottom of the App settings
|
|
180
164
|
and click _Generate a private key_. A `.pem` file downloads —
|
|
181
165
|
store it safely; you cannot redownload it.
|
|
@@ -109,7 +109,7 @@ An orchestrator enforces a few limits, each surfaced as a clear step error when
|
|
|
109
109
|
- A **per-org storage quota** (20 GiB by default) across all non-expired artifacts.
|
|
110
110
|
- An **expiry** (30 days by default) after which an artifact is no longer downloadable or listed.
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
Each of the four is a cluster-wide default an operator can raise or lower per organization; see the [orchestrator storage layout](https://docs.kici.dev/operator/orchestrator/storage-layout/#artifacts) for the operator-facing knobs.
|
|
113
113
|
|
|
114
114
|
When an upload or download fails for a reason that is **not** one of these limits — the orchestrator has no artifact storage configured, or it could not service the request — the step error says exactly that, instead of reporting a quota rejection or a missing artifact. So an error that names a limit really is a limit you can act on, and an error that names an orchestrator problem is one to take to whoever runs it.
|
|
115
115
|
|
|
@@ -266,7 +266,7 @@ Source: https://docs.kici.dev/user/sdk/event-payloads/
|
|
|
266
266
|
|
|
267
267
|
## The envelope
|
|
268
268
|
|
|
269
|
-
The normalized event envelope is the single event contract in KiCI. Rules receive it as `ctx.event`, and every dynamic function — `
|
|
269
|
+
The normalized event envelope is the single event contract in KiCI. Rules receive it as `ctx.event`, and every dynamic function — `context:`, `env:`, and `concurrencyGroup:` resolvers, generated jobs, and a workflow's `concurrency.group` — receives the same envelope as its argument.
|
|
270
270
|
|
|
271
271
|
Narrow on the `type` field to branch per trigger kind (`if (event.type === 'push')`). The raw provider webhook body is nested at `payload`; the typed variants below describe its shape per event type.
|
|
272
272
|
|
|
@@ -712,7 +712,7 @@ export const setup = job('setup', {
|
|
|
712
712
|
|
|
713
713
|
## `checkStep(name, options)`
|
|
714
714
|
|
|
715
|
-
The check-mode-aware sibling of `idempotentStep()`. It takes
|
|
715
|
+
The check-mode-aware sibling of `idempotentStep()`. It takes a closely related option shape, but behaves differently when a run is started in check mode (`kici run --check`):
|
|
716
716
|
|
|
717
717
|
| Factory | Behavior under `kici run --check` |
|
|
718
718
|
| ---------------- | ----------------------------------------- |
|
|
@@ -730,10 +730,16 @@ Use `checkStep()` for deploy-style steps where you want a dry-run preview of pen
|
|
|
730
730
|
| `apply` | `(ctx, drift: TDrift) => Promise<TApplied>` | Yes | Brings the system to the desired state. Runs only in apply mode (skipped under `kici run --check`). |
|
|
731
731
|
| `summarize` | `(drift: TDrift) => string` | Yes | Human-readable summary of what `apply()` would do; shown in check-mode drift output. |
|
|
732
732
|
| `whenInSync` | `(ctx) => Promise<TInSync>` | No | Runs when `check()` returned `null` (already in sync). |
|
|
733
|
+
| `outputs` | `OutputSchema` | No | Zod schema validating the step's outputs at runtime. |
|
|
733
734
|
| `continueOnError` | `boolean` | No | When true, the job proceeds even if this step fails. |
|
|
734
735
|
| `timeout` | `number` | No | Step-level timeout in milliseconds. |
|
|
736
|
+
| `retry` | `number \| RetryConfig` | No | Retry policy for the step; `retry: N` is shorthand for `{ maxAttempts: N }`. |
|
|
737
|
+
| `cache` | `CacheInput` | No | Declarative cache restored before the step and saved after it succeeds. |
|
|
738
|
+
| `rules` | `Rule[]` | No | Step-level conditional rules, evaluated agent-side. |
|
|
735
739
|
|
|
736
|
-
|
|
740
|
+
Everything from `outputs` down is a plain [`step()` option](https://docs.kici.dev/user/sdk/core/) forwarded to the underlying step. The three step options `checkStep` does **not** accept are `onCancel`, `cleanup`, and `approval`.
|
|
741
|
+
|
|
742
|
+
There are two signature differences from `idempotentStep`. First, `apply` and `whenInSync` receive `ctx` as their first argument, so the apply logic has access to `ctx.$`, `ctx.log`, and `ctx.secrets`. Second, `summarize` is **required** here, because it is what check mode prints; `idempotentStep` defaults it to a JSON dump of the drift. `idempotentStep` also takes none of the step-option passthroughs above.
|
|
737
743
|
|
|
738
744
|
### Result
|
|
739
745
|
|
|
@@ -841,6 +847,21 @@ const patch = job('patch', {
|
|
|
841
847
|
});
|
|
842
848
|
```
|
|
843
849
|
|
|
850
|
+
### Which hosts are targets
|
|
851
|
+
|
|
852
|
+
`runsOnAll` targets the hosts you declare as members of your fleet. A host becomes
|
|
853
|
+
a target when it registers with the orchestrator under a stable agent identity, or
|
|
854
|
+
when an operator declares it with `kici-admin host declare`.
|
|
855
|
+
|
|
856
|
+
Agents that an auto-scaler starts are **not** targets, even when their labels match
|
|
857
|
+
the predicate. An auto-scaler starts an agent at the fixed shape of its pool, so a
|
|
858
|
+
pinned child would run at that shape and not at its own. Auto-scaler agents stay
|
|
859
|
+
available to `runsOn` and to the queue, and `ctx.kici.inventory` continues to list
|
|
860
|
+
them.
|
|
861
|
+
|
|
862
|
+
The fleet preview and the host detail page in the web UI apply the same rule, so
|
|
863
|
+
they show the hosts a run targets.
|
|
864
|
+
|
|
844
865
|
### Input forms
|
|
845
866
|
|
|
846
867
|
`runsOnAll` accepts three shapes:
|
|
@@ -961,7 +961,7 @@ Source: https://docs.kici.dev/user/sdk/rules-matrix-dynamic/
|
|
|
961
961
|
|
|
962
962
|
Rules control conditional execution of workflows and jobs. A rule that returns `false` (or whose check function returns `false`) prevents execution.
|
|
963
963
|
|
|
964
|
-
A rule's check function that **returns `false`** cleanly skips the job or step. A check function that **throws**
|
|
964
|
+
A rule's check function that **returns `false`** cleanly skips the job or step. A check function that **throws** counts as an evaluation failure, not a skip. The job or step **fails** with the error surfaced (both on a remote run and when running locally with `kici run --local`), so a broken rule can never silently pass as a green run. For example, `rule('main only', (ctx) => ctx.event.ref.endsWith('main'))` throws on an event whose `ref` is undefined — that run fails with the error instead of quietly skipping every step. Fix the thrown error (guard the access) rather than relying on the skip.
|
|
965
965
|
|
|
966
966
|
### rule(label) / rule(label, check)
|
|
967
967
|
|
|
@@ -2435,6 +2435,36 @@ workflow('post-deploy', {
|
|
|
2435
2435
|
|
|
2436
2436
|
The orchestrator automatically emits system events for workflow and job completions. You do not need to call `ctx.emit()` for these -- they are generated by the orchestrator after execution. Listen for them with `workflowComplete()` and `jobComplete()` triggers.
|
|
2437
2437
|
|
|
2438
|
+
### Event scaler events
|
|
2439
|
+
|
|
2440
|
+
The [event scaler backend](https://docs.kici.dev/operator/orchestrator/event-scaler/) emits two reserved events that your provisioning and teardown workflows subscribe to. The SDK exports their names and payload schemas, so a workflow imports the contract instead of re-declaring it.
|
|
2441
|
+
|
|
2442
|
+
| Export | What it is |
|
|
2443
|
+
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
|
|
2444
|
+
| `SCALER_EVENT_NAMES` | The two reserved event names: `SCALER_EVENT_NAMES.scaleUp` and `SCALER_EVENT_NAMES.scaleDown`. |
|
|
2445
|
+
| `ScalerScaleUpPayload` | Schema of the scale-up payload. `.parse(ctx.rawPayload)` returns the typed payload and rejects a malformed one. |
|
|
2446
|
+
| `ScalerScaleDownPayload` | Schema of the scale-down payload, including the narrowed `reason`. |
|
|
2447
|
+
| `ScaleDownReason` | Why the scaler asked for a teardown: `idle`, `job-complete`, `heartbeat-timeout`, `spawn-timeout`, `drain`, `shutdown`. |
|
|
2448
|
+
|
|
2449
|
+
```ts
|
|
2450
|
+
import { workflow, job, kiciEvent, SCALER_EVENT_NAMES, ScalerScaleUpPayload } from '@kici-dev/sdk';
|
|
2451
|
+
|
|
2452
|
+
export default workflow('provision', {
|
|
2453
|
+
on: [kiciEvent({ name: SCALER_EVENT_NAMES.scaleUp, match: { '$.scalerName': 'hetzner' } })],
|
|
2454
|
+
jobs: [
|
|
2455
|
+
job('provision', {
|
|
2456
|
+
runsOn: ['kici:os:linux'],
|
|
2457
|
+
run: async (ctx) => {
|
|
2458
|
+
const payload = ScalerScaleUpPayload.parse(ctx.rawPayload);
|
|
2459
|
+
ctx.log.info(`provision agent ${payload.agentId}`);
|
|
2460
|
+
},
|
|
2461
|
+
}),
|
|
2462
|
+
],
|
|
2463
|
+
});
|
|
2464
|
+
```
|
|
2465
|
+
|
|
2466
|
+
These names are reserved. `ctx.emit()` rejects any event name that starts with `kici.`, so a workflow step cannot forge a scaler event. For every payload field, see the [event contract reference](https://docs.kici.dev/operator/orchestrator/event-scaler-events/). For complete provisioning and teardown workflows, see [autoscaling workflows](https://docs.kici.dev/user/workflows/autoscaling-workflows/).
|
|
2467
|
+
|
|
2438
2468
|
---
|
|
2439
2469
|
|
|
2440
2470
|
## SDK reference
|