@kici-dev/compiler 0.6.1 → 0.8.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 +2 -2
- 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/local.d.ts +9 -2
- package/dist/commands/local.js +14 -4
- 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 +4 -0
- package/dist/commands/run.js +5 -2
- package/dist/commands/runs/logs.js +3 -2
- 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 +73 -87
- package/dist/llm-context/llms-cli-remote.txt +53 -8
- package/dist/llm-context/llms-cli.txt +71 -36
- 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 +558 -180
- package/dist/llm-context/llms-getting-started.txt +5 -5
- package/dist/llm-context/llms-patterns.txt +81 -5
- package/dist/llm-context/llms-providers.txt +6 -2
- package/dist/llm-context/llms-sdk-runtime.txt +33 -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 +6 -14
- package/dist/local-plane/paths.d.ts +1 -0
- package/dist/local-plane/paths.js +1 -0
- package/dist/local-plane/plane-log.d.ts +27 -0
- package/dist/local-plane/plane-log.js +39 -0
- package/dist/local-plane/plane-manager.js +2 -2
- package/dist/local-plane/plane-trigger.d.ts +28 -0
- package/dist/local-plane/plane-trigger.js +57 -2
- package/dist/local-plane/postgres.js +9 -6
- package/dist/local-plane/run-follow.js +2 -1
- 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/remote/output/streaming.d.ts +12 -0
- package/dist/remote/output/streaming.js +20 -1
- package/dist/remote/platform-client.d.ts +2 -0
- package/dist/templates/package-json.d.ts +9 -7
- package/dist/templates/package-json.js +11 -9
- 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
|
@@ -46,7 +46,7 @@ The lock file is the seam. Everything left of it is decided once at compile time
|
|
|
46
46
|
|
|
47
47
|
`kici compile` loads your `.kici/workflows/*.ts`, validates dependencies (no cycles, no missing `needs`), assigns compile-time step IDs (unnamed steps become `step-1`, `step-2`, …), and writes `kici.lock.json`.
|
|
48
48
|
|
|
49
|
-
The compiler runs your module's **top-level code** to build the workflow object — but that execution's side effects and in-memory state do not travel. Only the resulting workflow structure lands in the lock. Anything your top-level code computes that isn't part of the returned workflow object
|
|
49
|
+
The compiler runs your module's **top-level code** to build the workflow object — but that execution's side effects and in-memory state do not travel. Only the resulting workflow structure lands in the lock. Anything your top-level code computes that isn't part of the returned workflow object doesn't exist past this point.
|
|
50
50
|
|
|
51
51
|
See [compile the workflow](https://docs.kici.dev/user/getting-started/#compile-the-workflow) for the command in context.
|
|
52
52
|
|
|
@@ -202,7 +202,7 @@ kici runs logs <run-id>
|
|
|
202
202
|
When you cannot resolve it yourself, gather a diagnostic bundle:
|
|
203
203
|
|
|
204
204
|
```bash
|
|
205
|
-
kici report --run <run-id>
|
|
205
|
+
kici report --run <run-id>
|
|
206
206
|
```
|
|
207
207
|
|
|
208
208
|
The command writes a ZIP and prints its path and `sha256`. It sends nothing.
|
|
@@ -309,7 +309,7 @@ npx kici init
|
|
|
309
309
|
This will:
|
|
310
310
|
|
|
311
311
|
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.
|
|
312
|
-
2. Create
|
|
312
|
+
2. Create two `.kiciignore` files with sensible defaults: one at the repo root, which selects the working-tree files a remote run uploads, and one inside `.kici/`, which declares the paths the per-workflow content hash skips
|
|
313
313
|
3. Let you choose from starter workflow templates (hello-world, pr-checks)
|
|
314
314
|
4. Install dependencies using the package manager detected for your repo (npm, pnpm, or yarn)
|
|
315
315
|
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)
|
|
@@ -588,7 +588,7 @@ Point to a local SDK checkout for IDE autocompletion:
|
|
|
588
588
|
{
|
|
589
589
|
"name": "my-project-kici",
|
|
590
590
|
"devDependencies": {
|
|
591
|
-
"@kici-dev/sdk": "
|
|
591
|
+
"@kici-dev/sdk": "latest"
|
|
592
592
|
},
|
|
593
593
|
"kici": {
|
|
594
594
|
"sdkPath": "../../packages/sdk"
|
|
@@ -600,7 +600,7 @@ The `sdkPath` field tells the compiler where to resolve TypeScript path mappings
|
|
|
600
600
|
|
|
601
601
|
### KICI_DEV environment variable
|
|
602
602
|
|
|
603
|
-
Set `KICI_DEV=true` to
|
|
603
|
+
Set `KICI_DEV=true` to pin the SDK to the `latest` dist-tag in generated files, which resolves prerelease builds from a local Verdaccio registry:
|
|
604
604
|
|
|
605
605
|
```bash
|
|
606
606
|
KICI_DEV=true npx kici init
|
|
@@ -725,6 +725,74 @@ For a **minted app token**, prefer re-deriving over transporting — those expir
|
|
|
725
725
|
after an hour, so one minted in an earlier job is often already dead by the time
|
|
726
726
|
a later job reads it. Have the later job name the same secret, or mint its own.
|
|
727
727
|
|
|
728
|
+
## What a job may ask for
|
|
729
|
+
|
|
730
|
+
A credential is authorized against the workflow you wrote, not against the code
|
|
731
|
+
running in the job. Three things must all hold before the orchestrator resolves
|
|
732
|
+
one:
|
|
733
|
+
|
|
734
|
+
1. **The job declared it.** The orchestrator records the job's `gitCredentials`
|
|
735
|
+
map when it dispatches the job, and compares every request against that
|
|
736
|
+
record. A request naming a credential the job did not declare is refused.
|
|
737
|
+
This is why you pass `credential: 'forge'` — a name — rather than building a
|
|
738
|
+
credential reference in step code.
|
|
739
|
+
2. **The named context admits the run.** A `prod:` reference runs the `prod`
|
|
740
|
+
context's own protection rules first: its branch restrictions, its
|
|
741
|
+
`minimumTrust`, its approval requirement. A credential named from a branch
|
|
742
|
+
the context does not allow is refused, and the git operation fails. The rule
|
|
743
|
+
that refused it is named in your orchestrator's log, not in the run — the
|
|
744
|
+
orchestrator returns a fixed error to the job rather than describing its own
|
|
745
|
+
configuration to code it does not trust.
|
|
746
|
+
3. **The contributor is trusted.** A run from an untrusted ref — a fork pull
|
|
747
|
+
request — gets no declared credential at all. It still clones with the
|
|
748
|
+
source credential, so the build runs; only the declared credentials are
|
|
749
|
+
withheld. The reduced-privilege note on the run says so.
|
|
750
|
+
|
|
751
|
+
The context in a reference does **not** have to appear in the job's `contexts:`
|
|
752
|
+
list. The reference names its own context, and that context's rules are what
|
|
753
|
+
authorize it.
|
|
754
|
+
|
|
755
|
+
## Generated jobs
|
|
756
|
+
|
|
757
|
+
A job produced by a `dynamicJob` generator has no entry in the lock file, so it
|
|
758
|
+
cannot declare credentials of its own. The **generator** declares them, and every
|
|
759
|
+
job it produces inherits that map:
|
|
760
|
+
|
|
761
|
+
```typescript
|
|
762
|
+
dynamicJob('shards', {
|
|
763
|
+
gitCredentials: {
|
|
764
|
+
forge: { kind: 'token', tokenSecret: 'ci:FORGE_PAT' },
|
|
765
|
+
},
|
|
766
|
+
generate: async ({ ctx }) =>
|
|
767
|
+
ctx.event.payload.targets.map((target) =>
|
|
768
|
+
job(`publish-${target}`, {
|
|
769
|
+
runsOn: 'linux',
|
|
770
|
+
run: async ({ $, repo }) => {
|
|
771
|
+
await repo.withWrite(
|
|
772
|
+
{ permissions: { contents: 'write' }, credential: 'forge' },
|
|
773
|
+
async () => {
|
|
774
|
+
await $`git push origin HEAD`;
|
|
775
|
+
},
|
|
776
|
+
);
|
|
777
|
+
},
|
|
778
|
+
}),
|
|
779
|
+
),
|
|
780
|
+
});
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
Three points follow from where the declaration lives:
|
|
784
|
+
|
|
785
|
+
- **All generated jobs share one map.** The generator is granted one ceiling, and
|
|
786
|
+
every job it produces gets exactly that ceiling. Use a second generator when
|
|
787
|
+
two sets of jobs need different credentials.
|
|
788
|
+
- **A `gitCredentials` map on a generated job is ignored.** The generator's
|
|
789
|
+
declaration is committed source that KiCI reads from the lock file. A generated
|
|
790
|
+
job's own declaration would come from the code that produced it, which is what
|
|
791
|
+
the authorization check above exists to be independent of.
|
|
792
|
+
- **The options form is required.** `dynamicJob('shards', async () => …)` — the
|
|
793
|
+
bare function form — has nowhere to put the declaration. Pass
|
|
794
|
+
`{ generate, gitCredentials }` instead; `needs` stays optional.
|
|
795
|
+
|
|
728
796
|
## How it works, and why long jobs still push
|
|
729
797
|
|
|
730
798
|
An app token expires an hour after it is issued, and cannot be renewed. Rather
|
|
@@ -741,6 +809,10 @@ does at the start, and no credential is ever written into `.git/config`, into
|
|
|
741
809
|
unaffected.
|
|
742
810
|
- **The reserved `needs:` context is not resolvable yet** on a deployed
|
|
743
811
|
orchestrator; naming it produces a clear error rather than a wrong credential.
|
|
812
|
+
- **A credential reference built in step code is refused.** The SDK takes a
|
|
813
|
+
credential _name_; there is no way to pass a reference. Code that constructs
|
|
814
|
+
one and sends it directly is rejected by the agent and, if it reaches the
|
|
815
|
+
orchestrator, by the declaration check above.
|
|
744
816
|
- **A write window is bounded by the repository and the callback, not the step.**
|
|
745
817
|
Steps running concurrently in the same job can push to the same repository
|
|
746
818
|
while it is open. They cannot reach a different one.
|
|
@@ -1075,8 +1147,10 @@ KiCI has no native provider for Gogs, Forgejo, or Gitea, but these forges send H
|
|
|
1075
1147
|
```bash
|
|
1076
1148
|
# Forgejo / Gitea send event name in X-Gitea-Event and signature in X-Gitea-Signature.
|
|
1077
1149
|
# Gogs uses X-Gogs-Event and X-Gogs-Signature (same HMAC-SHA256 hex-digest format).
|
|
1150
|
+
# --org must be your Platform organization id: a generic source's routing key
|
|
1151
|
+
# embeds it, and the Platform refuses to register a key naming another org.
|
|
1078
1152
|
kici-admin source add generic \
|
|
1079
|
-
--org
|
|
1153
|
+
--org <platform-org-id> \
|
|
1080
1154
|
--name forgejo-main \
|
|
1081
1155
|
--verification hmac_sha256 \
|
|
1082
1156
|
--secret @/path/to/webhook-secret.txt \
|
|
@@ -1084,7 +1158,7 @@ kici-admin source add generic \
|
|
|
1084
1158
|
--rate-limit 120
|
|
1085
1159
|
```
|
|
1086
1160
|
|
|
1087
|
-
Note the returned source ID, then register a webhook in the forge pointing at `https://<platform>/
|
|
1161
|
+
Note the returned source ID, then register a webhook in the forge pointing at `https://<platform>/webhook/<orgId>/generic/<sourceId>` (or the orchestrator's direct URL, which takes the source **name** in place of the id). Set content type to `application/json` and paste the same secret.
|
|
1088
1162
|
|
|
1089
1163
|
**Workflow:**
|
|
1090
1164
|
|
|
@@ -1151,14 +1225,16 @@ HTTPS with a forge PAT works the same way — store the token as a secret, `awai
|
|
|
1151
1225
|
|
|
1152
1226
|
## Plain GitHub repo webhooks (no GitHub App)
|
|
1153
1227
|
|
|
1154
|
-
The Gogs/Forgejo/Gitea pattern above also applies when you want to trigger workflows from a GitHub repository **without installing the KiCI GitHub App
|
|
1228
|
+
The Gogs/Forgejo/Gitea pattern above also applies when you want to trigger workflows from a GitHub repository **without installing the KiCI GitHub App**. You may lack org-admin rights, sit on a restricted GitHub Enterprise tenant, or not want an App installation. Model the repo-level webhook as a generic source, accepting the same `genericWebhook()`-only ergonomics.
|
|
1155
1229
|
|
|
1156
1230
|
**Operator setup:**
|
|
1157
1231
|
|
|
1158
1232
|
```bash
|
|
1159
1233
|
# GitHub sends event name in X-GitHub-Event and HMAC-SHA256 signature in X-Hub-Signature-256.
|
|
1234
|
+
# --org must be your Platform organization id: a generic source's routing key
|
|
1235
|
+
# embeds it, and the Platform refuses to register a key naming another org.
|
|
1160
1236
|
kici-admin source add generic \
|
|
1161
|
-
--org
|
|
1237
|
+
--org <platform-org-id> \
|
|
1162
1238
|
--name gh-repo-foo \
|
|
1163
1239
|
--verification hmac_sha256 \
|
|
1164
1240
|
--secret @/path/to/webhook-secret.txt \
|
|
@@ -1175,7 +1251,7 @@ curl -X PATCH https://<orchestrator>/api/v1/admin/generic-sources/<sourceId> \
|
|
|
1175
1251
|
|
|
1176
1252
|
Then in the GitHub repo, go to **Settings → Webhooks → Add webhook**, set:
|
|
1177
1253
|
|
|
1178
|
-
- **Payload URL:** `https://<platform>/
|
|
1254
|
+
- **Payload URL:** `https://<platform>/webhook/<orgId>/generic/<sourceId>` (or the orchestrator's direct URL, which takes the source **name** in place of the id)
|
|
1179
1255
|
- **Content type:** `application/json`
|
|
1180
1256
|
- **Secret:** the same secret
|
|
1181
1257
|
- **Events:** pick what you care about (e.g., `push`, `pull_request`)
|
|
@@ -627,10 +627,14 @@ kici-admin secret set <orgId> "__source__/<sourceId>" pat --value "<your-forgejo
|
|
|
627
627
|
Finally, configure the forge to deliver webhooks to:
|
|
628
628
|
|
|
629
629
|
```
|
|
630
|
-
https://<platform-host>/webhook/<orgId>/generic/<
|
|
630
|
+
https://<platform-host>/webhook/<orgId>/generic/<sourceId>
|
|
631
631
|
```
|
|
632
632
|
|
|
633
|
-
with the same secret you passed to `--secret`.
|
|
633
|
+
with the same secret you passed to `--secret`. `<orgId>` is your Platform
|
|
634
|
+
organization id — the same value you passed to `--org`, which the source's
|
|
635
|
+
routing key embeds — and `<sourceId>` is the id `source add` printed. The
|
|
636
|
+
orchestrator's own ingest URL has the same shape but takes the source **name**
|
|
637
|
+
in place of the id.
|
|
634
638
|
|
|
635
639
|
## SSH deploy key
|
|
636
640
|
|
|
@@ -617,7 +617,7 @@ The SDK exposes three idempotency helpers — a generic function `idempotent()`,
|
|
|
617
617
|
2. **Apply** the change only when drift is detected.
|
|
618
618
|
3. **Surface** the resource (or its identifier) on both branches, so downstream steps don't need to know whether work happened or was skipped.
|
|
619
619
|
|
|
620
|
-
`idempotent()` and `idempotentStep()` wrap the same underlying runner and always apply on drift. Pick `idempotentStep()` when the operation is the whole job of a step; use `idempotent()` from anywhere — inside a multi-action step, a hook, or a bare async function. Pick `checkStep()` when the step should respect the run-level check mode — `kici run --check` previews the drift without applying it.
|
|
620
|
+
`idempotent()` and `idempotentStep()` wrap the same underlying runner and always apply on drift. Pick `idempotentStep()` when the operation is the whole job of a step; use `idempotent()` from anywhere — inside a multi-action step, a hook, or a bare async function. Pick `checkStep()` when the step should respect the run-level check mode — `kici run remote --check` previews the drift without applying it.
|
|
621
621
|
|
|
622
622
|
## `idempotent(options)`
|
|
623
623
|
|
|
@@ -712,30 +712,30 @@ export const setup = job('setup', {
|
|
|
712
712
|
|
|
713
713
|
## `checkStep(name, options)`
|
|
714
714
|
|
|
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`):
|
|
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 remote --check`):
|
|
716
716
|
|
|
717
|
-
| Factory | Behavior under `kici run --check`
|
|
717
|
+
| Factory | Behavior under `kici run remote --check` |
|
|
718
718
|
| ---------------- | ----------------------------------------- |
|
|
719
719
|
| `idempotentStep` | always applies on drift |
|
|
720
720
|
| `checkStep` | reports drift, applies only in apply mode |
|
|
721
721
|
|
|
722
|
-
Use `checkStep()` for deploy-style steps where you want a dry-run preview of pending changes before committing them, and `idempotentStep()` for steps that must always converge (for example inside a hook). A `checkStep()` desugars to the first-class step check facet (`check` / `summarize` / `run(ctx, drift)` / `whenInSync`), so it participates in run-level check mode automatically: `kici run --check` reports the drift and skips `apply`, `kici run --check --fail-on-drift` exits non-zero when drift is detected, and apply mode applies the change.
|
|
722
|
+
Use `checkStep()` for deploy-style steps where you want a dry-run preview of pending changes before committing them, and `idempotentStep()` for steps that must always converge (for example inside a hook). A `checkStep()` desugars to the first-class step check facet (`check` / `summarize` / `run(ctx, drift)` / `whenInSync`), so it participates in run-level check mode automatically: `kici run remote --check` reports the drift and skips `apply`, `kici run remote --check --fail-on-drift` exits non-zero when drift is detected, and apply mode applies the change.
|
|
723
723
|
|
|
724
724
|
### Parameters
|
|
725
725
|
|
|
726
|
-
| Name | Type | Required | Description
|
|
727
|
-
| ----------------- | ------------------------------------------- | -------- |
|
|
728
|
-
| `name` | `string` | Yes | Step name. Appears in the run timeline and in log lines.
|
|
729
|
-
| `check` | `(ctx) => Promise<TDrift \| null>` | Yes | Read-only inspection. Return `null` when the system is already in the desired state.
|
|
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
|
-
| `summarize` | `(drift: TDrift) => string` | Yes | Human-readable summary of what `apply()` would do; shown in check-mode drift output.
|
|
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.
|
|
734
|
-
| `continueOnError` | `boolean` | No | When true, the job proceeds even if this step fails.
|
|
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.
|
|
726
|
+
| Name | Type | Required | Description |
|
|
727
|
+
| ----------------- | ------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------- |
|
|
728
|
+
| `name` | `string` | Yes | Step name. Appears in the run timeline and in log lines. |
|
|
729
|
+
| `check` | `(ctx) => Promise<TDrift \| null>` | Yes | Read-only inspection. Return `null` when the system is already in the desired state. |
|
|
730
|
+
| `apply` | `(ctx, drift: TDrift) => Promise<TApplied>` | Yes | Brings the system to the desired state. Runs only in apply mode (skipped under `kici run remote --check`). |
|
|
731
|
+
| `summarize` | `(drift: TDrift) => string` | Yes | Human-readable summary of what `apply()` would do; shown in check-mode drift output. |
|
|
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. |
|
|
734
|
+
| `continueOnError` | `boolean` | No | When true, the job proceeds even if this step fails. |
|
|
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. |
|
|
739
739
|
|
|
740
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
741
|
|
|
@@ -769,7 +769,7 @@ export const deploy = job('deploy', {
|
|
|
769
769
|
});
|
|
770
770
|
```
|
|
771
771
|
|
|
772
|
-
Run `kici run --check` against this workflow to see the drift summary without touching DNS; run it without `--check` to apply.
|
|
772
|
+
Run `kici run remote --check` against this workflow to see the drift summary without touching DNS; run it without `--check` to apply.
|
|
773
773
|
|
|
774
774
|
## Worked example: create-if-missing returning a resource id
|
|
775
775
|
|
|
@@ -1332,6 +1332,17 @@ interface StepContext<TInputs = Record<string, unknown>> {
|
|
|
1332
1332
|
provider?: string;
|
|
1333
1333
|
/** Whether this execution was triggered by `kici run remote` (developer-initiated remote run) */
|
|
1334
1334
|
isTestRun: boolean;
|
|
1335
|
+
/** The job's own checked-out repository (present for every job that checks out); `withWrite` opens a write window for it — see [Git credentials](https://docs.kici.dev/user/patterns/git-credentials/) */
|
|
1336
|
+
repo?: {
|
|
1337
|
+
identifier: string;
|
|
1338
|
+
path: string;
|
|
1339
|
+
ref?: string;
|
|
1340
|
+
sha?: string;
|
|
1341
|
+
withWrite(
|
|
1342
|
+
opts: { permissions?: Record<string, string>; credential?: string },
|
|
1343
|
+
fn: () => Promise<void>,
|
|
1344
|
+
): Promise<void>;
|
|
1345
|
+
};
|
|
1335
1346
|
/** Registering repo of a global workflow (undefined for non-global workflows) */
|
|
1336
1347
|
workflowRepo?: RepoInfo;
|
|
1337
1348
|
/** Repo where the triggering event occurred (undefined for non-global workflows) */
|
|
@@ -1532,6 +1543,8 @@ const deploy = job('deploy', {
|
|
|
1532
1543
|
|
|
1533
1544
|
Request a short-lived OIDC ID token for the current job, bound to an `audience`. The token is a signed JWT whose identity claims (`repository`, `ref`, `sha`, `kici_run_id`, `kici_job_id`) are derived by your orchestrator from the run context — a step cannot spoof them. Use it to authenticate the build to an external service that trusts the orchestrator's OIDC issuer (for example, when generating build provenance).
|
|
1534
1545
|
|
|
1546
|
+
The token also carries the event context a cloud trust policy needs to tell a fork pull request from a trusted push — `is_fork`, `head_repository`, `trust_tier`, `event_name`, and a pull-request-specific `sub`. See [ID-token claims and cloud trust policies](https://docs.kici.dev/user/provenance/#id-token-claims-and-cloud-trust-policies) for the full claim table and a worked AWS policy.
|
|
1547
|
+
|
|
1535
1548
|
```typescript
|
|
1536
1549
|
const publish = job('publish', {
|
|
1537
1550
|
steps: [
|
|
@@ -1571,6 +1584,8 @@ interface HostInventoryEntry {
|
|
|
1571
1584
|
|
|
1572
1585
|
Two dimensions describe a host. **Labels** are flat strings used for grouping and targeting (the same labels `runsOn` / `runsOnAll` match). **Properties** are typed host-vars (`string | number | boolean`) — the place for facts like `region`, `cores`, or `gpu`. A host reports its own properties via the agent's `KICI_PROPERTIES` config, and an operator can pre-declare them with `kici-admin host declare --prop key=value`; the two are shallow-merged (agent-reported keys win).
|
|
1573
1586
|
|
|
1587
|
+
`labels` and `hostname` come back lowercase — KiCI folds both, so a pool declaring `Docker` reports `docker`. Compare against a lowercase value: `h.labels.includes('gpu')`, not `h.labels.includes('GPU')`. A label selector passed to `query()` folds too, so `{ include: [['GPU']] }` matches. `agentId` and `properties` keep their case.
|
|
1588
|
+
|
|
1574
1589
|
```typescript
|
|
1575
1590
|
// All hosts:
|
|
1576
1591
|
const all = await ctx.kici.inventory.query();
|
|
@@ -142,6 +142,7 @@ runsOn: { labels: ['kici:os:linux'], exclude: ['kici:host:box-01'] }
|
|
|
142
142
|
|
|
143
143
|
- **Required labels:** The agent must have every label in the `labels` array (or the string/array form).
|
|
144
144
|
- **Excluded labels:** The agent must NOT have any label in the `exclude` array. This includes auto-derived labels like `kici:arch:arm64`, `kici:os:linux`, etc.
|
|
145
|
+
- **Case:** Label matching is **case-insensitive** at every step. `runsOn: 'gpu'` matches an agent that reports `GPU`, and a pool declaring `["Docker"]` serves a `runsOn: ["docker"]` job (see [auto-scaler matching rules](https://docs.kici.dev/operator/orchestrator/auto-scaler/operations/#matching-rules)). KiCI stores and displays every label in lowercase, so the dashboard, `kici-admin agent list`, and `ctx.kici.inventory[…].labels` report the folded form. Compare against a lowercase value when you read a label back in workflow code: `h.labels.includes('gpu')`, not `h.labels.includes('GPU')`.
|
|
145
146
|
- **Compile-time validation:** The compiler will error if any label appears in both `labels` and `exclude` (overlap detection).
|
|
146
147
|
- **Operator-declared mandatory labels:** Operators may mark a scaler with `mandatoryLabels` (Kubernetes-taint-style opt-in). When a scaler declares a mandatory label, a job is only allowed to land on it if `runsOn.labels` includes that label. A workflow targeting such a scaler must explicitly list the mandatory label in `runsOn`. See the [auto-scaler mandatory labels](https://docs.kici.dev/operator/orchestrator/auto-scaler/common-config/#mandatory--exclude-labels) for details.
|
|
147
148
|
|
|
@@ -194,6 +195,12 @@ Every selector element — in `runsOn`, in `runsOnAll`, on both the include and
|
|
|
194
195
|
- **String with glob metacharacters (`*`, `?`, `[]`, `{}`) → glob.** `'kici:host:web-*'` matches every host label starting with `kici:host:web-`. `'kici:host:box-0[1-3]'` matches `box-01`, `box-02`, `box-03`.
|
|
195
196
|
- **`RegExp` literal → regular expression.** `/kici:host:box-0[1-3]/` matches any label the expression matches.
|
|
196
197
|
|
|
198
|
+
All three forms match case-insensitively. `'GPU'`, `'kici:host:Web-*'` and `/kici:host:BOX-0[1-3]/` each match a label of any case, and a `RegExp` you write with the `i` flag behaves the same. The `g` and `y` flags are dropped — a selector asks one question per label, so a sticky match would resume part-way through the next one.
|
|
199
|
+
|
|
200
|
+
`kici:host:` carries the machine's hostname folded to lowercase. A host that calls itself `Build-Box-01` advertises `kici:host:build-box-01`, and both `runsOn: 'kici:host:build-box-01'` and `runsOn: 'kici:host:Build-Box-01'` match it.
|
|
201
|
+
|
|
202
|
+
Case folding covers labels and hostnames only. An **agent ID** stays an opaque identifier and compares exactly, which is what keeps a per-host secret binding on `prod-01` away from an agent named `PROD-01` — see [per-host secret scoping](https://docs.kici.dev/operator/security/secrets/#per-host-secret-scoping).
|
|
203
|
+
|
|
197
204
|
Both the required (include) side and the excluded side accept all three forms:
|
|
198
205
|
|
|
199
206
|
```typescript
|
|
@@ -852,11 +859,19 @@ Tag a dynamic job generator with a group name so other jobs can reference it via
|
|
|
852
859
|
```typescript
|
|
853
860
|
function dynamicJob(
|
|
854
861
|
groupName: string,
|
|
855
|
-
fnOrConfig:
|
|
862
|
+
fnOrConfig:
|
|
863
|
+
| DynamicJobFn
|
|
864
|
+
| {
|
|
865
|
+
needs?: DynamicJobNeed[];
|
|
866
|
+
generate: DynamicJobFn;
|
|
867
|
+
gitCredentials?: GitCredentialMap;
|
|
868
|
+
},
|
|
856
869
|
): TaggedDynamicJobFn;
|
|
857
870
|
```
|
|
858
871
|
|
|
859
|
-
The second argument is either a plain generator (event-only, evaluated at webhook time) or
|
|
872
|
+
The second argument is either a plain generator (event-only, evaluated at webhook time) or an options config. An options config that declares `needs` is result-aware: it defers the generator until those upstreams complete and exposes their frozen outputs as `ctx.needs`. See [Rules, matrix, dynamic jobs](https://docs.kici.dev/user/sdk/rules-matrix-dynamic/#dynamicjob--result-aware-generation) for the result-aware form.
|
|
873
|
+
|
|
874
|
+
`needs` is optional. An options config without it is evaluated at webhook time, like the plain generator form. Use that form to declare `gitCredentials` on an event-only generator: every job the generator produces inherits the map, which is the only way a generated job gets named credentials. See [Git credentials](https://docs.kici.dev/user/patterns/git-credentials/#generated-jobs).
|
|
860
875
|
|
|
861
876
|
The generator runs twice: once in the init phase (to register expected job names) and once inside the executing agent (to produce the actual jobs). Mismatches between the two evaluations are detected as determinism drift -- see [dynamic-jobs](https://docs.kici.dev/architecture/execution/dynamic-jobs/).
|
|
862
877
|
|
|
@@ -1035,6 +1050,11 @@ The throw is a `ChangedFilesUnavailableError` (exported from `@kici-dev/sdk`, ca
|
|
|
1035
1050
|
|
|
1036
1051
|
### evaluateRules(rules, context, label, onRuleResult?)
|
|
1037
1052
|
|
|
1053
|
+
The agent calls this on your behalf. A workflow does not call it. It lives on
|
|
1054
|
+
`@kici-dev/sdk/internal`, outside semver, and stays exported from the root barrel as
|
|
1055
|
+
`@deprecated` until v1.0.0 — see [deprecations](https://docs.kici.dev/user/deprecations/). It is described here
|
|
1056
|
+
because its return shape is what a rule's outcome looks like in the run log.
|
|
1057
|
+
|
|
1038
1058
|
Evaluate an array of rules sequentially with fail-fast behavior. Stops on the first failure.
|
|
1039
1059
|
|
|
1040
1060
|
```typescript
|
|
@@ -1102,7 +1122,7 @@ Every variant carries the shared `EventBase` fields — `type`, `action`, `targe
|
|
|
1102
1122
|
|
|
1103
1123
|
Matrix configurations expand a single job into multiple instances, one per parameter combination. Maximum 256 combinations.
|
|
1104
1124
|
|
|
1105
|
-
Combinations must be **unique**. Two combinations that would produce the same instance name —
|
|
1125
|
+
Combinations must be **unique**. Two combinations that would produce the same instance name — in the simplest case, the same value listed twice — fail the job instead of quietly running it twice.
|
|
1106
1126
|
|
|
1107
1127
|
Expansion happens at **dispatch time**: the orchestrator materializes the matrix into N execution jobs — one per combination, each dispatched to its own agent — before any job runs. Each instance receives its combination as `ctx.matrix`. This is identical whether the workflow runs via `kici run <event> --local` or remotely through a webhook trigger, and the dashboard groups the N instances under one parent node.
|
|
1108
1128
|
|
|
@@ -1285,8 +1305,13 @@ isDynamicFunction(matrix); // true if async function
|
|
|
1285
1305
|
|
|
1286
1306
|
### Matrix expansion utilities
|
|
1287
1307
|
|
|
1308
|
+
The agent expands a matrix for you. A workflow does not call these. They live on
|
|
1309
|
+
`@kici-dev/sdk/internal`, outside semver, and stay exported from the root barrel as
|
|
1310
|
+
`@deprecated` until v1.0.0 — see [deprecations](https://docs.kici.dev/user/deprecations/). They are described
|
|
1311
|
+
here because they define the combinations a matrix job actually produces.
|
|
1312
|
+
|
|
1288
1313
|
```typescript
|
|
1289
|
-
import { expandMatrix, applyIncludeExclude } from '@kici-dev/sdk';
|
|
1314
|
+
import { expandMatrix, applyIncludeExclude } from '@kici-dev/sdk/internal';
|
|
1290
1315
|
```
|
|
1291
1316
|
|
|
1292
1317
|
`expandMatrix(matrix)` takes a string array or an object of string arrays and returns all combinations as `MatrixValues[]`. For a single-dimension array, each value becomes `{ value: '...' }`. For multi-dimensional objects, it produces the Cartesian product. Anything else throws a `MatrixShapeError` naming the expected shape; numbers and booleans inside the values are accepted and converted to strings.
|
|
@@ -1344,7 +1369,8 @@ export default workflow('ci', {
|
|
|
1344
1369
|
`dynamicJob(group, fnOrConfig)` tags a generator with a group name (so static jobs can depend on it via `needs: [dynamicGroup('group')]`). It is polymorphic:
|
|
1345
1370
|
|
|
1346
1371
|
- **Function form** — event-only, dispatched at webhook time: `dynamicJob('shards', async ({ ctx }) => [...])`.
|
|
1347
|
-
- **Options-object form** — result-aware
|
|
1372
|
+
- **Options-object form** — `dynamicJob('reports', { needs, generate })`. With `needs`, it is result-aware: deferred until those upstreams complete, then run with their frozen outputs as `ctx.needs`.
|
|
1373
|
+
- `needs` is optional. Without it the generator is dispatched at webhook time, like the function form. That form is how a generator declares `gitCredentials`, which every job it produces inherits — see [Git credentials](https://docs.kici.dev/user/patterns/git-credentials/#generated-jobs).
|
|
1348
1374
|
|
|
1349
1375
|
```typescript
|
|
1350
1376
|
import { workflow, job, step, dynamicJob, dynamicGroup, z } from '@kici-dev/sdk';
|
|
@@ -1517,7 +1543,7 @@ those files contain**. It is a declarative filter on the `pr()`, `push()`, and
|
|
|
1517
1543
|
at the event's commit. The orchestrator evaluates it as pure data before
|
|
1518
1544
|
dispatching — it reads only the referenced files, never clones the whole
|
|
1519
1545
|
repository, and never runs any of your workflow code. A workflow whose `requires`
|
|
1520
|
-
does not pass is
|
|
1546
|
+
does not pass is not dispatched.
|
|
1521
1547
|
|
|
1522
1548
|
Each entry is a `ContentRequirement`:
|
|
1523
1549
|
|
|
@@ -1782,7 +1808,7 @@ dispatch({ types: ['deploy', 'rollback'] }); // Specific event types
|
|
|
1782
1808
|
#### Typed dispatch inputs
|
|
1783
1809
|
|
|
1784
1810
|
A `dispatch()` trigger can declare a typed `inputs` schema. Operators supply
|
|
1785
|
-
values with `kici run --input key=value`; KiCI validates, coerces, defaults, and
|
|
1811
|
+
values with `kici run remote --input key=value`; KiCI validates, coerces, defaults, and
|
|
1786
1812
|
exposes them to steps and rules as `ctx.dispatchInputs`. The values are validated
|
|
1787
1813
|
on the orchestrator from the compiled lock file — a missing required input or a
|
|
1788
1814
|
bad value is rejected before any agent runs, without cloning the repository.
|
|
@@ -2497,6 +2523,20 @@ import { workflow, job, step, pr, push, rule, defineEvent } from '@kici-dev/sdk'
|
|
|
2497
2523
|
|
|
2498
2524
|
For the complete list of every named export (factory functions, triggers, rules, validation, hook factories, types), see the per-topic pages above.
|
|
2499
2525
|
|
|
2526
|
+
## `@kici-dev/sdk/internal` is not a supported surface
|
|
2527
|
+
|
|
2528
|
+
The package also publishes an `@kici-dev/sdk/internal` subpath. It carries the runtime
|
|
2529
|
+
contract between the SDK and the KiCI agent. Those are the functions that install the maps
|
|
2530
|
+
a `.result` proxy reads, build the step context your workflow body receives, evaluate its
|
|
2531
|
+
rules, and expand its matrix. The agent drives all of it on your behalf.
|
|
2532
|
+
|
|
2533
|
+
It is **not covered by semver** and may change shape in any release. Do not import it from
|
|
2534
|
+
a workflow. Everything a workflow author needs is on the root entry point above.
|
|
2535
|
+
|
|
2536
|
+
Those same symbols are also still exported from the root barrel, marked `@deprecated`, so
|
|
2537
|
+
an older SDK in a repository keeps working. They are removed from the root at v1.0.0 — see
|
|
2538
|
+
[deprecations](https://docs.kici.dev/user/deprecations/).
|
|
2539
|
+
|
|
2500
2540
|
## See also
|
|
2501
2541
|
|
|
2502
2542
|
- [Getting started](https://docs.kici.dev/user/getting-started/) -- install the SDK, write your first workflow, test locally
|
|
@@ -11,15 +11,15 @@ Found a mismatch between what these docs advertise and what KiCI does? Read http
|
|
|
11
11
|
Each bundle below is a self-contained markdown file for one authoring task. Fetch only the one your task needs instead of the full bundle:
|
|
12
12
|
|
|
13
13
|
- [getting-started](https://docs.kici.dev/llms-getting-started.txt) (65 KB) — Adopt KiCI: why it exists, how workflows execute, migrating from GitHub Actions, installing the SDK, and writing/compiling/testing your first workflow
|
|
14
|
-
- [patterns](https://docs.kici.dev/llms-patterns.txt) (
|
|
15
|
-
- [sdk](https://docs.kici.dev/llms-sdk.txt) (
|
|
16
|
-
- [sdk-runtime](https://docs.kici.dev/llms-sdk-runtime.txt) (
|
|
17
|
-
- [cli](https://docs.kici.dev/llms-cli.txt) (
|
|
18
|
-
- [cli-remote](https://docs.kici.dev/llms-cli-remote.txt) (
|
|
19
|
-
- [features](https://docs.kici.dev/llms-features.txt) (
|
|
20
|
-
- [features-execution](https://docs.kici.dev/llms-features-execution.txt) (
|
|
14
|
+
- [patterns](https://docs.kici.dev/llms-patterns.txt) (61 KB) — Copy-paste workflow recipes: triggers, conditionals, matrix, scheduling, integrations
|
|
15
|
+
- [sdk](https://docs.kici.dev/llms-sdk.txt) (131 KB) — Core authoring API: workflow/job/step factories, triggers, rules, matrix, dynamic jobs, cross-job outputs
|
|
16
|
+
- [sdk-runtime](https://docs.kici.dev/llms-sdk-runtime.txt) (115 KB) — Runtime and advanced authoring: step runtime context, event payloads, host fan-out, idempotent steps, caching, artifacts, waiting
|
|
17
|
+
- [cli](https://docs.kici.dev/llms-cli.txt) (97 KB) — Running the CLI locally: compile, test, run local, hooks, lock-file drift, common failures
|
|
18
|
+
- [cli-remote](https://docs.kici.dev/llms-cli-remote.txt) (111 KB) — Auth, org and orchestrator selection, runs, approvals, notifications, diagnostics, and the MCP server a coding agent connects to
|
|
19
|
+
- [features](https://docs.kici.dev/llms-features.txt) (97 KB) — Deployment contexts, scoped secrets, private registries, approvals, provenance, dashboard and account access
|
|
20
|
+
- [features-execution](https://docs.kici.dev/llms-features-execution.txt) (124 KB) — Concurrency, dynamic values, events, container jobs, environment variables, global workflows, idempotent steps
|
|
21
21
|
- [providers](https://docs.kici.dev/llms-providers.txt) (35 KB) — Connecting sources: GitHub App, universal-git (Forgejo/Gitea/GitLab), local file://
|
|
22
|
-
- [architecture](https://docs.kici.dev/llms-architecture.txt) (
|
|
22
|
+
- [architecture](https://docs.kici.dev/llms-architecture.txt) (100 KB) — How the runtime works: three-tier relay model, data flows, configuration
|
|
23
23
|
|
|
24
24
|
## Getting started
|
|
25
25
|
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resolve a host address the plane's co-located bare-metal agent can reach for
|
|
3
|
-
* cache-blob fetches. The orchestrator refuses to hand a scaler a loopback
|
|
4
|
-
* storage URL (it assumes remote agents), so the plane advertises its first
|
|
5
|
-
* non-internal IPv4 (still this machine — the agent is co-located). Falls back
|
|
6
|
-
* to the hostname when no external interface is present.
|
|
7
|
-
*/
|
|
8
|
-
export declare function resolveAgentReachableHost(): string;
|
|
9
1
|
/**
|
|
10
2
|
* Resolve the built `@kici-dev/orchestrator/standalone` entry the local plane
|
|
11
3
|
* launches in independent (offline) mode.
|
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
2
|
import { planePaths, planePorts } from "./paths.js";
|
|
3
|
+
import { rotatePlaneLogIfOversized } from "./plane-log.js";
|
|
3
4
|
import { createRequire } from "node:module";
|
|
4
5
|
import fs from "node:fs";
|
|
5
6
|
import { spawn } from "node:child_process";
|
|
6
|
-
import os from "node:os";
|
|
7
7
|
//#region src/local-plane/orchestrator-process.ts
|
|
8
8
|
/**
|
|
9
|
-
* Resolve a host address the plane's co-located bare-metal agent can reach for
|
|
10
|
-
* cache-blob fetches. The orchestrator refuses to hand a scaler a loopback
|
|
11
|
-
* storage URL (it assumes remote agents), so the plane advertises its first
|
|
12
|
-
* non-internal IPv4 (still this machine — the agent is co-located). Falls back
|
|
13
|
-
* to the hostname when no external interface is present.
|
|
14
|
-
*/
|
|
15
|
-
function resolveAgentReachableHost() {
|
|
16
|
-
for (const addrs of Object.values(os.networkInterfaces())) for (const a of addrs ?? []) if (a.family === "IPv4" && !a.internal) return a.address;
|
|
17
|
-
return os.hostname();
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
9
|
* Resolve the built `@kici-dev/orchestrator/standalone` entry the local plane
|
|
21
10
|
* launches in independent (offline) mode.
|
|
22
11
|
*/
|
|
@@ -60,6 +49,7 @@ function spawnOrchestratorProcess(databaseUrl, opts) {
|
|
|
60
49
|
const { logFile, root, cacheDir } = planePaths();
|
|
61
50
|
fs.mkdirSync(root, { recursive: true });
|
|
62
51
|
fs.mkdirSync(cacheDir, { recursive: true });
|
|
52
|
+
rotatePlaneLogIfOversized(logFile);
|
|
63
53
|
const out = fs.openSync(logFile, "a");
|
|
64
54
|
const baseEnv = {};
|
|
65
55
|
for (const [key, value] of Object.entries(process.env)) if (!key.startsWith("KICI_")) baseEnv[key] = value;
|
|
@@ -68,7 +58,9 @@ function spawnOrchestratorProcess(databaseUrl, opts) {
|
|
|
68
58
|
KICI_PORT: String(port),
|
|
69
59
|
KICI_DATABASE_URL: databaseUrl,
|
|
70
60
|
KICI_AGENT_AUTH: "none",
|
|
61
|
+
KICI_HOST: "127.0.0.1",
|
|
71
62
|
KICI_AUTO_MIGRATE: "true",
|
|
63
|
+
KICI_CLUSTER_SINGLE_NODE: "true",
|
|
72
64
|
KICI_DATA_DIR: root,
|
|
73
65
|
KICI_BOOTSTRAP_ADMIN_TOKEN: opts.adminToken,
|
|
74
66
|
KICI_SECRET_KEY: opts.secretKey,
|
|
@@ -76,7 +68,7 @@ function spawnOrchestratorProcess(databaseUrl, opts) {
|
|
|
76
68
|
KICI_WEBHOOK_PUBLIC_URL: `http://127.0.0.1:${port}`,
|
|
77
69
|
KICI_STORAGE_TYPE: "filesystem",
|
|
78
70
|
KICI_STORAGE_FS_PATH: cacheDir,
|
|
79
|
-
KICI_STORAGE_FS_BASE_URL: `http
|
|
71
|
+
KICI_STORAGE_FS_BASE_URL: `http://127.0.0.1:${port}`
|
|
80
72
|
};
|
|
81
73
|
let entry;
|
|
82
74
|
let modeEnv;
|
|
@@ -124,6 +116,6 @@ async function awaitOrchestratorReady(port, attempts = 120, intervalMs = 500) {
|
|
|
124
116
|
throw new Error("local orchestrator did not become ready");
|
|
125
117
|
}
|
|
126
118
|
//#endregion
|
|
127
|
-
export { awaitOrchestratorReady, orchestratorReady,
|
|
119
|
+
export { awaitOrchestratorReady, orchestratorReady, resolveServerEntry, resolveStandaloneEntry, spawnOrchestratorProcess };
|
|
128
120
|
|
|
129
121
|
//# sourceMappingURL=orchestrator-process.js.map
|
|
@@ -48,6 +48,7 @@ function planePaths() {
|
|
|
48
48
|
pidfile: path.join(root, "plane.pid"),
|
|
49
49
|
stampFile: path.join(root, "stamp.json"),
|
|
50
50
|
logFile: path.join(root, "orchestrator.log"),
|
|
51
|
+
pgLogFile: path.join(root, "orchestrator.log.pg"),
|
|
51
52
|
socketDir: path.join(root, "sock"),
|
|
52
53
|
adminTokenFile: path.join(root, "admin-token"),
|
|
53
54
|
platformTokenFile: path.join(root, "platform-token"),
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Size at which a local dev plane log is rotated on the next plane start.
|
|
3
|
+
*
|
|
4
|
+
* The plane's orchestrator writes one JSON line per request and per
|
|
5
|
+
* scheduled-job tick, and nothing truncates the file between boots, so an
|
|
6
|
+
* unrotated log grows without bound. 50 MB is roughly 200k lines — more history
|
|
7
|
+
* than a local debugging session reads.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PLANE_LOG_MAX_BYTES: number;
|
|
10
|
+
/** The cap expressed in whole megabytes, for the messages that name it. */
|
|
11
|
+
export declare const PLANE_LOG_MAX_MB: number;
|
|
12
|
+
/**
|
|
13
|
+
* Rotate `file` to `<file>.1` once it reaches `PLANE_LOG_MAX_BYTES`, keeping two
|
|
14
|
+
* generations: any previous `.1` is discarded.
|
|
15
|
+
*
|
|
16
|
+
* Call this only where nothing holds the file open. A rename under a live
|
|
17
|
+
* writer leaves that writer appending to the renamed inode, so the live log
|
|
18
|
+
* would stay empty for the rest of the process's life.
|
|
19
|
+
*
|
|
20
|
+
* Every failure is swallowed on purpose. Both callers are on a plane-boot path:
|
|
21
|
+
* `startPlanePostgres` reads a throw as "embedded Postgres is unavailable" and
|
|
22
|
+
* silently falls back to a Podman container, and `spawnOrchestratorProcess`
|
|
23
|
+
* would fail the boot outright. A log that cannot be rotated must not cost the
|
|
24
|
+
* user their plane.
|
|
25
|
+
*/
|
|
26
|
+
export declare function rotatePlaneLogIfOversized(file: string): void;
|
|
27
|
+
//# sourceMappingURL=plane-log.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
//#region src/local-plane/plane-log.ts
|
|
4
|
+
/**
|
|
5
|
+
* Size at which a local dev plane log is rotated on the next plane start.
|
|
6
|
+
*
|
|
7
|
+
* The plane's orchestrator writes one JSON line per request and per
|
|
8
|
+
* scheduled-job tick, and nothing truncates the file between boots, so an
|
|
9
|
+
* unrotated log grows without bound. 50 MB is roughly 200k lines — more history
|
|
10
|
+
* than a local debugging session reads.
|
|
11
|
+
*/
|
|
12
|
+
const PLANE_LOG_MAX_BYTES = 52428800;
|
|
13
|
+
/** The cap expressed in whole megabytes, for the messages that name it. */
|
|
14
|
+
const PLANE_LOG_MAX_MB = PLANE_LOG_MAX_BYTES / 1048576;
|
|
15
|
+
/**
|
|
16
|
+
* Rotate `file` to `<file>.1` once it reaches `PLANE_LOG_MAX_BYTES`, keeping two
|
|
17
|
+
* generations: any previous `.1` is discarded.
|
|
18
|
+
*
|
|
19
|
+
* Call this only where nothing holds the file open. A rename under a live
|
|
20
|
+
* writer leaves that writer appending to the renamed inode, so the live log
|
|
21
|
+
* would stay empty for the rest of the process's life.
|
|
22
|
+
*
|
|
23
|
+
* Every failure is swallowed on purpose. Both callers are on a plane-boot path:
|
|
24
|
+
* `startPlanePostgres` reads a throw as "embedded Postgres is unavailable" and
|
|
25
|
+
* silently falls back to a Podman container, and `spawnOrchestratorProcess`
|
|
26
|
+
* would fail the boot outright. A log that cannot be rotated must not cost the
|
|
27
|
+
* user their plane.
|
|
28
|
+
*/
|
|
29
|
+
function rotatePlaneLogIfOversized(file) {
|
|
30
|
+
try {
|
|
31
|
+
if (fs.statSync(file).size < 52428800) return;
|
|
32
|
+
fs.rmSync(`${file}.1`, { force: true });
|
|
33
|
+
fs.renameSync(file, `${file}.1`);
|
|
34
|
+
} catch {}
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { PLANE_LOG_MAX_BYTES, PLANE_LOG_MAX_MB, rotatePlaneLogIfOversized };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=plane-log.js.map
|