@kici-dev/compiler 0.7.0 → 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 CHANGED
@@ -7,7 +7,7 @@ import { realpathSync } from "node:fs";
7
7
  import { Argument, Command } from "commander";
8
8
  import pc from "picocolors";
9
9
  //#region src/cli.ts
10
- const version = "0.7.0";
10
+ const version = "0.8.0";
11
11
  /**
12
12
  * Top-level commands that were removed, mapped to their current equivalent.
13
13
  * Consulted when the CLI hits an unknown command so the user gets a precise
@@ -312,7 +312,7 @@ Environment variables:
312
312
  ],
313
313
  [
314
314
  "logs",
315
- "Print the local dev plane orchestrator log path",
315
+ "Print the local dev plane log paths and rotation policy",
316
316
  "localLogsCommand"
317
317
  ],
318
318
  [
@@ -5,7 +5,7 @@
5
5
  * orchestrator plus a local Postgres (embedded, with a podman fallback) that
6
6
  * `kici run --local` dispatches through. `up` boots or reuses the plane,
7
7
  * `status` reports it plus the control commands, `down` stops it, and `logs`
8
- * prints the orchestrator log path.
8
+ * prints the plane log paths and rotation policy.
9
9
  */
10
10
  import type { PlaneStatus, PlaneMode } from '../local-plane/plane-manager.js';
11
11
  import type { PlaneState } from '../local-plane/plane-liveness.js';
@@ -52,7 +52,14 @@ export declare function localStatusCommand(options?: {
52
52
  }): Promise<boolean>;
53
53
  /** Stop the local dev plane, reporting success only once the port is released. */
54
54
  export declare function localDownCommand(): Promise<boolean>;
55
- /** Print the local dev plane orchestrator log path. */
55
+ /**
56
+ * Print the local dev plane orchestrator log path, plus the rotation policy.
57
+ *
58
+ * The path is the only line this command writes to stdout — the policy note
59
+ * goes to stderr — so a caller reading stdout never has to strip prose from
60
+ * it. The CLI version banner still precedes it, as for every command, so
61
+ * `$(kici local logs)` is NOT a bare path: read the last stdout line.
62
+ */
56
63
  export declare function localLogsCommand(): Promise<boolean>;
57
64
  /**
58
65
  * Attach the local dev plane to the hosted Platform so `kici run --local` uses
@@ -1,5 +1,6 @@
1
1
  import "../rolldown-runtime-ClRpJifh.js";
2
2
  import { loadGlobalConfig } from "../remote/config.js";
3
+ import "../local-plane/plane-log.js";
3
4
  import { attachPlane, detachPlane, planeDown, planeLogPath, planeStatus } from "../local-plane/plane-manager.js";
4
5
  import { resolvePlaneForRun } from "../local-plane/resolve-plane.js";
5
6
  import pc from "picocolors";
@@ -12,14 +13,14 @@ import { toErrorMessage } from "@kici-dev/core";
12
13
  * orchestrator plus a local Postgres (embedded, with a podman fallback) that
13
14
  * `kici run --local` dispatches through. `up` boots or reuses the plane,
14
15
  * `status` reports it plus the control commands, `down` stops it, and `logs`
15
- * prints the orchestrator log path.
16
+ * prints the plane log paths and rotation policy.
16
17
  */
17
18
  /** Print the control commands a user needs once the plane is running. */
18
19
  function printControlHints() {
19
20
  console.log("");
20
21
  console.log(pc.dim("Control commands:"));
21
22
  console.log(` ${pc.cyan("kici local status")} Show plane status`);
22
- console.log(` ${pc.cyan("kici local logs")} Print the orchestrator log path`);
23
+ console.log(` ${pc.cyan("kici local logs")} Print the plane log paths and rotation policy`);
23
24
  console.log(` ${pc.cyan("kici local down")} Stop the plane`);
24
25
  }
25
26
  /**
@@ -147,9 +148,18 @@ async function localDownCommand() {
147
148
  console.log(pc.green("✓") + " Local dev plane stopped.");
148
149
  return true;
149
150
  }
150
- /** Print the local dev plane orchestrator log path. */
151
+ /**
152
+ * Print the local dev plane orchestrator log path, plus the rotation policy.
153
+ *
154
+ * The path is the only line this command writes to stdout — the policy note
155
+ * goes to stderr — so a caller reading stdout never has to strip prose from
156
+ * it. The CLI version banner still precedes it, as for every command, so
157
+ * `$(kici local logs)` is NOT a bare path: read the last stdout line.
158
+ */
151
159
  async function localLogsCommand() {
152
- console.log(planeLogPath());
160
+ const logFile = planeLogPath();
161
+ console.log(logFile);
162
+ console.error(pc.dim(`Rotated to ${logFile}.1 when it reaches 50 MB, at the next plane start. When the plane runs embedded PostgreSQL, its log sits beside it at ${logFile}.pg and rotates the same way.`));
153
163
  return true;
154
164
  }
155
165
  /**
@@ -19,7 +19,7 @@ import { PROTOCOL_VERSION } from "@kici-dev/engine";
19
19
  */
20
20
  function collectIdentity(probe) {
21
21
  const identity = {
22
- kiciCliVersion: "0.7.0",
22
+ kiciCliVersion: "0.8.0",
23
23
  nodeVersion: process.version,
24
24
  platform: process.platform,
25
25
  arch: process.arch,
@@ -151,6 +151,9 @@ async function runRouted(options) {
151
151
  action: dispatch.action,
152
152
  clientPayload: dispatch.clientPayload
153
153
  }
154
+ }, {
155
+ repoBasePath: workdir.dir,
156
+ logPath: planeLogPath()
154
157
  });
155
158
  if (!quiet) logger.info(pc.green(`Run started: ${runId}`));
156
159
  const outcome = await followRun(plane.url, plane.adminToken, runId, {
@@ -9,6 +9,7 @@ import { buildEncryptedSecrets } from "../remote/secret-upload.js";
9
9
  import { createOverlayTarball, getSizeWarning, uploadTarball } from "../remote/uploader.js";
10
10
  import { formatJsonResult } from "../remote/output/json.js";
11
11
  import { formatJunitResult } from "../remote/output/junit.js";
12
+ import { unwrapStoredLogLine } from "../remote/output/streaming.js";
12
13
  import { formatErrorHighlight, formatMultiFixtureSummary, formatSummary } from "../remote/output/summary.js";
13
14
  import { compileFixtures, filterFixtures } from "../fixtures/compiler.js";
14
15
  import { describeEvent } from "../fixtures/describe-event.js";
@@ -500,7 +501,8 @@ async function pollRunToCompletion(ctx, runId, fixtureId, options) {
500
501
  throw err;
501
502
  }
502
503
  runSeen = true;
503
- for (const line of logs.lines) {
504
+ for (const raw of logs.lines) {
505
+ const line = unwrapStoredLogLine(raw);
504
506
  if (!options.quiet) process.stdout.write(line + "\n");
505
507
  tailLines.push(line);
506
508
  while (tailLines.length > MAX_TAIL) tailLines.shift();
@@ -585,7 +587,8 @@ function jobsFromStatus(status) {
585
587
  if (!status) return [];
586
588
  return status.jobs.map((j) => ({
587
589
  name: j.jobName,
588
- status: j.status
590
+ status: j.status,
591
+ ...typeof j.durationMs === "number" && { durationMs: j.durationMs }
589
592
  }));
590
593
  }
591
594
  /**
@@ -1,6 +1,7 @@
1
1
  import "../../rolldown-runtime-ClRpJifh.js";
2
2
  import { DashboardClient, DashboardClientError } from "../../remote/dashboard-client.js";
3
3
  import { colorStatus } from "../../remote/render.js";
4
+ import { unwrapStoredLogLine } from "../../remote/output/streaming.js";
4
5
  import pc from "picocolors";
5
6
  import { logger, toErrorMessage } from "@kici-dev/core";
6
7
  import { TERMINAL_RUN_STATES } from "@kici-dev/engine";
@@ -31,7 +32,7 @@ async function printAllLogs(client, runId, jobs, jobFilter) {
31
32
  for (const j of selectJobs(jobs, jobFilter)) for (const s of j.steps ?? []) {
32
33
  console.log(pc.bold(`\n=== ${j.jobName} › ${s.stepName} `) + colorStatus(s.status) + pc.bold(" ==="));
33
34
  const logs = await client.getStepLogs(runId, j.jobId, s.stepIndex);
34
- for (const line of logs.lines) console.log(line);
35
+ for (const line of logs.lines) console.log(unwrapStoredLogLine(line));
35
36
  }
36
37
  }
37
38
  async function collectAllLogs(client, runId, jobs, jobFilter) {
@@ -50,7 +51,7 @@ async function followLogs(client, runId, jobFilter) {
50
51
  const seen = printed[key] ?? 0;
51
52
  if (logs.lines.length > seen) {
52
53
  if (seen === 0) console.log(pc.bold(`\n=== ${j.jobName} › ${s.stepName} ===`));
53
- for (const line of logs.lines.slice(seen)) console.log(line);
54
+ for (const line of logs.lines.slice(seen)) console.log(unwrapStoredLogLine(line));
54
55
  printed[key] = logs.lines.length;
55
56
  }
56
57
  }
@@ -1465,7 +1465,7 @@ KiCI uses three WebSocket layers for real-time communication.
1465
1465
 
1466
1466
  ### Platform ↔ Orchestrator
1467
1467
 
1468
- The orchestrator connects outbound to the Platform WebSocket endpoint. After authentication (API key validated via SHA-256 hash lookup), the connection is used for webhook relay, execution telemetry (events, status, logs), source registration, and peer discovery. The Platform can also relay `job.reroute` messages between orchestrators that cannot reach each other directly.
1468
+ The orchestrator connects outbound to the Platform WebSocket endpoint. After authentication (API key validated via SHA-256 hash lookup), the connection is used for webhook relay, execution telemetry (events, status, logs), source registration, and peer discovery. Peer discovery is matchmaking only: the Platform pushes a `peer.update` membership list to every orchestrator sharing a routing key, and the orchestrators then connect to each other directly. Inter-orchestrator traffic such as `job.reroute` never transits the Platform.
1469
1469
 
1470
1470
  ### Orchestrator ↔ Orchestrator (P2P)
1471
1471
 
@@ -761,7 +761,7 @@ In interactive mode (TTY), `kici init` prompts you to:
761
761
 
762
762
  **Standalone vs workspace integration:** by default `kici init` scaffolds a self-contained `.kici/` with its own `package.json`. When run inside a pnpm, npm, or yarn workspace, it offers an **integrate** option (or pass `--workspace`): `.kici/` joins the workspace, `@kici-dev/sdk` is added to your workspace-root `package.json`, and your workflows can `import` your other workspace packages (e.g. shared build or deploy utilities). In this mode there is no `.kici/package.json` — the workspace root manages dependencies, and the root install resolves the SDK. Your workflows resolve sibling packages through the workspace-root `node_modules`: under npm and yarn every workspace member is hoisted there automatically, while pnpm links only your root's declared dependencies, so under pnpm add the package you want to import to your workspace-root `dependencies` (this is how KiCI's own repository imports its packages from workflows). Pass `--standalone` to force the self-contained layout even inside a workspace. In CI / non-interactive runs the default is standalone; use `--workspace` to opt in explicitly. `--workspace` and `--standalone` are mutually exclusive, and `--workspace` errors if no workspace is found at or above the current directory.
763
763
 
764
- **Development mode:** When `KICI_DEV=true` or `package.json` has `"kici": { "development": true }`, the generated `package.json` uses prerelease-compatible version ranges (`>=0.0.1-0`) so npm resolves Verdaccio's prerelease builds.
764
+ **Development mode:** When `KICI_DEV=true` or `package.json` has `"kici": { "development": true }`, the generated `package.json` pins `@kici-dev/sdk` to the `latest` dist-tag so npm resolves Verdaccio's newest prerelease build.
765
765
 
766
766
  ### kici org
767
767
 
@@ -1707,6 +1707,18 @@ Secrets are always sourced from your real `.kici/` directory, not from the isola
1707
1707
 
1708
1708
  Pass `--in-place` to run against the real working directory instead — useful when you explicitly want in-tree execution. `--in-place` requires no git repository; the default isolated mode does, and fails with an actionable error pointing at `--in-place` when the directory is not a git repository.
1709
1709
 
1710
+ **If the trigger times out:**
1711
+
1712
+ The command waits up to 60 seconds for the local dev plane to create the run. If nothing appears in that window it stops and names the cause it read back from the plane:
1713
+
1714
+ | Message | What it means |
1715
+ | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1716
+ | `plane is not leader yet (election grace period)` | The plane had not finished electing itself. A single-machine plane elects within seconds; if it does not, read the plane log (`kici local logs`). |
1717
+ | `no kici.lock.json at <commit> in <path>` | The plane resolved no lock file for the commit the run packed, so nothing matched. Commit `.kici/kici.lock.json`, or make sure it is present in the working tree. |
1718
+ | `the plane recorded delivery <id> as "<status>"` | The plane processed the trigger and wrote down a terminal status for it without creating a run. The status names the stage that stopped. |
1719
+ | `no run appeared for delivery <id> — see <log>` | Neither the plane's cluster state nor its delivery record explained the timeout. The named log is the next place to look. |
1720
+ | `no run appeared for this trigger — the plane never accepted the webhook` | Every webhook POST came back without a delivery id, so the plane never queued the trigger. Check the plane is up (`kici local status`), then read the named log. |
1721
+
1710
1722
  **Examples:**
1711
1723
 
1712
1724
  ```bash
@@ -1365,7 +1365,7 @@ Manage the **local dev plane** — the warm, per-user orchestrator (plus its own
1365
1365
  kici local up [--offline | --connected] # Start, or reuse an already-running plane
1366
1366
  kici local status [--json] # Port, pid, PostgreSQL backend, attachment mode, readiness
1367
1367
  kici local down # Stop the orchestrator and its PostgreSQL, verifying the port is freed
1368
- kici local logs # Print the orchestrator log file path
1368
+ kici local logs # Print the plane log paths and rotation policy
1369
1369
  kici local attach # Attach to the Platform (hybrid mode)
1370
1370
  kici local detach # Return the plane to offline (independent) mode
1371
1371
  kici local trust-root <file> # Export the dev-signed trust root for offline verification
@@ -1382,6 +1382,8 @@ The plane runs in one of two modes:
1382
1382
 
1383
1383
  `kici local status` reports a plane whose process is alive but whose readiness probe fails — for example when its PostgreSQL has stopped — as running but not ready, together with its readiness checks, rather than as not running. When the holder is a KiCI plane orchestrator that this config directory did not start — a plane belonging to another `KICI_CONFIG_DIR`, or one whose record here was lost — status names it as such rather than as not ready, since its readiness is never probed, and points at `kici local down`, which does reclaim it. When the port is held by a process that is not a KiCI plane orchestrator, `kici local status` names that holder instead and points at `KICI_LOCAL_ORCH_PORT`, because `kici local down` will not stop it.
1384
1384
 
1385
+ The plane writes its orchestrator log to `orchestrator.log` in its state directory and, when it runs embedded PostgreSQL, the PostgreSQL log to `orchestrator.log.pg` beside it. `kici local logs` prints their location. Each is rotated to a `.1` sibling when it reaches 50 MB, at the next plane start — the plane keeps the current log and one previous generation, so neither can grow without bound.
1386
+
1385
1387
  Pass `--json` for machine-readable output. It prints one object and exits 0 for
1386
1388
  every state, including when the plane is stopped — the state is in the payload,
1387
1389
  not the exit code:
@@ -1674,7 +1676,7 @@ Synopsis: `kici local down`
1674
1676
 
1675
1677
  ### `kici local logs`
1676
1678
 
1677
- Print the local dev plane orchestrator log path
1679
+ Print the local dev plane log paths and rotation policy
1678
1680
 
1679
1681
  Synopsis: `kici local logs`
1680
1682
 
@@ -607,7 +607,7 @@ The KiCI CLI reads the following environment variables to customize its behavior
607
607
 
608
608
  | Variable | Description | Default |
609
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 |
610
+ | `KICI_DEV` | Enable development mode. When `true`, pins `@kici-dev/sdk` to the `latest` dist-tag (so a dev registry's prerelease build resolves) and skips npm version resolution. | unset |
611
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
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 |
613
613
 
@@ -590,7 +590,7 @@ Point to a local SDK checkout for IDE autocompletion:
590
590
  {
591
591
  "name": "my-project-kici",
592
592
  "devDependencies": {
593
- "@kici-dev/sdk": ">=0.0.1-0"
593
+ "@kici-dev/sdk": "latest"
594
594
  },
595
595
  "kici": {
596
596
  "sdkPath": "../../packages/sdk"
@@ -602,7 +602,7 @@ The `sdkPath` field tells the compiler where to resolve TypeScript path mappings
602
602
 
603
603
  ### KICI_DEV environment variable
604
604
 
605
- Set `KICI_DEV=true` to use a prerelease-compatible version range (`>=0.0.1-0`) in generated files, which resolves prerelease builds from a local Verdaccio registry:
605
+ 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:
606
606
 
607
607
  ```bash
608
608
  KICI_DEV=true npx kici init
@@ -6502,6 +6502,17 @@ interface StepContext<TInputs = Record<string, unknown>> {
6502
6502
  provider?: string;
6503
6503
  /** Whether this execution was triggered by `kici run remote` (developer-initiated remote run) */
6504
6504
  isTestRun: boolean;
6505
+ /** 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/) */
6506
+ repo?: {
6507
+ identifier: string;
6508
+ path: string;
6509
+ ref?: string;
6510
+ sha?: string;
6511
+ withWrite(
6512
+ opts: { permissions?: Record<string, string>; credential?: string },
6513
+ fn: () => Promise<void>,
6514
+ ): Promise<void>;
6515
+ };
6505
6516
  /** Registering repo of a global workflow (undefined for non-global workflows) */
6506
6517
  workflowRepo?: RepoInfo;
6507
6518
  /** Repo where the triggering event occurred (undefined for non-global workflows) */
@@ -8573,7 +8584,7 @@ Manage the **local dev plane** — the warm, per-user orchestrator (plus its own
8573
8584
  kici local up [--offline | --connected] # Start, or reuse an already-running plane
8574
8585
  kici local status [--json] # Port, pid, PostgreSQL backend, attachment mode, readiness
8575
8586
  kici local down # Stop the orchestrator and its PostgreSQL, verifying the port is freed
8576
- kici local logs # Print the orchestrator log file path
8587
+ kici local logs # Print the plane log paths and rotation policy
8577
8588
  kici local attach # Attach to the Platform (hybrid mode)
8578
8589
  kici local detach # Return the plane to offline (independent) mode
8579
8590
  kici local trust-root <file> # Export the dev-signed trust root for offline verification
@@ -8590,6 +8601,8 @@ The plane runs in one of two modes:
8590
8601
 
8591
8602
  `kici local status` reports a plane whose process is alive but whose readiness probe fails — for example when its PostgreSQL has stopped — as running but not ready, together with its readiness checks, rather than as not running. When the holder is a KiCI plane orchestrator that this config directory did not start — a plane belonging to another `KICI_CONFIG_DIR`, or one whose record here was lost — status names it as such rather than as not ready, since its readiness is never probed, and points at `kici local down`, which does reclaim it. When the port is held by a process that is not a KiCI plane orchestrator, `kici local status` names that holder instead and points at `KICI_LOCAL_ORCH_PORT`, because `kici local down` will not stop it.
8592
8603
 
8604
+ The plane writes its orchestrator log to `orchestrator.log` in its state directory and, when it runs embedded PostgreSQL, the PostgreSQL log to `orchestrator.log.pg` beside it. `kici local logs` prints their location. Each is rotated to a `.1` sibling when it reaches 50 MB, at the next plane start — the plane keeps the current log and one previous generation, so neither can grow without bound.
8605
+
8593
8606
  Pass `--json` for machine-readable output. It prints one object and exits 0 for
8594
8607
  every state, including when the plane is stopped — the state is in the payload,
8595
8608
  not the exit code:
@@ -8882,7 +8895,7 @@ Synopsis: `kici local down`
8882
8895
 
8883
8896
  ### `kici local logs`
8884
8897
 
8885
- Print the local dev plane orchestrator log path
8898
+ Print the local dev plane log paths and rotation policy
8886
8899
 
8887
8900
  Synopsis: `kici local logs`
8888
8901
 
@@ -9747,7 +9760,7 @@ In interactive mode (TTY), `kici init` prompts you to:
9747
9760
 
9748
9761
  **Standalone vs workspace integration:** by default `kici init` scaffolds a self-contained `.kici/` with its own `package.json`. When run inside a pnpm, npm, or yarn workspace, it offers an **integrate** option (or pass `--workspace`): `.kici/` joins the workspace, `@kici-dev/sdk` is added to your workspace-root `package.json`, and your workflows can `import` your other workspace packages (e.g. shared build or deploy utilities). In this mode there is no `.kici/package.json` — the workspace root manages dependencies, and the root install resolves the SDK. Your workflows resolve sibling packages through the workspace-root `node_modules`: under npm and yarn every workspace member is hoisted there automatically, while pnpm links only your root's declared dependencies, so under pnpm add the package you want to import to your workspace-root `dependencies` (this is how KiCI's own repository imports its packages from workflows). Pass `--standalone` to force the self-contained layout even inside a workspace. In CI / non-interactive runs the default is standalone; use `--workspace` to opt in explicitly. `--workspace` and `--standalone` are mutually exclusive, and `--workspace` errors if no workspace is found at or above the current directory.
9749
9762
 
9750
- **Development mode:** When `KICI_DEV=true` or `package.json` has `"kici": { "development": true }`, the generated `package.json` uses prerelease-compatible version ranges (`>=0.0.1-0`) so npm resolves Verdaccio's prerelease builds.
9763
+ **Development mode:** When `KICI_DEV=true` or `package.json` has `"kici": { "development": true }`, the generated `package.json` pins `@kici-dev/sdk` to the `latest` dist-tag so npm resolves Verdaccio's newest prerelease build.
9751
9764
 
9752
9765
  ### kici org
9753
9766
 
@@ -10693,6 +10706,18 @@ Secrets are always sourced from your real `.kici/` directory, not from the isola
10693
10706
 
10694
10707
  Pass `--in-place` to run against the real working directory instead — useful when you explicitly want in-tree execution. `--in-place` requires no git repository; the default isolated mode does, and fails with an actionable error pointing at `--in-place` when the directory is not a git repository.
10695
10708
 
10709
+ **If the trigger times out:**
10710
+
10711
+ The command waits up to 60 seconds for the local dev plane to create the run. If nothing appears in that window it stops and names the cause it read back from the plane:
10712
+
10713
+ | Message | What it means |
10714
+ | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
10715
+ | `plane is not leader yet (election grace period)` | The plane had not finished electing itself. A single-machine plane elects within seconds; if it does not, read the plane log (`kici local logs`). |
10716
+ | `no kici.lock.json at <commit> in <path>` | The plane resolved no lock file for the commit the run packed, so nothing matched. Commit `.kici/kici.lock.json`, or make sure it is present in the working tree. |
10717
+ | `the plane recorded delivery <id> as "<status>"` | The plane processed the trigger and wrote down a terminal status for it without creating a run. The status names the stage that stopped. |
10718
+ | `no run appeared for delivery <id> — see <log>` | Neither the plane's cluster state nor its delivery record explained the timeout. The named log is the next place to look. |
10719
+ | `no run appeared for this trigger — the plane never accepted the webhook` | Every webhook POST came back without a delivery id, so the plane never queued the trigger. Check the plane is up (`kici local status`), then read the named log. |
10720
+
10696
10721
  **Examples:**
10697
10722
 
10698
10723
  ```bash
@@ -13664,7 +13689,7 @@ The KiCI CLI reads the following environment variables to customize its behavior
13664
13689
 
13665
13690
  | Variable | Description | Default |
13666
13691
  | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
13667
- | `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 |
13692
+ | `KICI_DEV` | Enable development mode. When `true`, pins `@kici-dev/sdk` to the `latest` dist-tag (so a dev registry's prerelease build resolves) and skips npm version resolution. | unset |
13668
13693
  | `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 |
13669
13694
  | `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 |
13670
13695
 
@@ -17394,7 +17419,7 @@ KiCI uses three WebSocket layers for real-time communication.
17394
17419
 
17395
17420
  ### Platform ↔ Orchestrator
17396
17421
 
17397
- The orchestrator connects outbound to the Platform WebSocket endpoint. After authentication (API key validated via SHA-256 hash lookup), the connection is used for webhook relay, execution telemetry (events, status, logs), source registration, and peer discovery. The Platform can also relay `job.reroute` messages between orchestrators that cannot reach each other directly.
17422
+ The orchestrator connects outbound to the Platform WebSocket endpoint. After authentication (API key validated via SHA-256 hash lookup), the connection is used for webhook relay, execution telemetry (events, status, logs), source registration, and peer discovery. Peer discovery is matchmaking only: the Platform pushes a `peer.update` membership list to every orchestrator sharing a routing key, and the orchestrators then connect to each other directly. Inter-orchestrator traffic such as `job.reroute` never transits the Platform.
17398
17423
 
17399
17424
  ### Orchestrator ↔ Orchestrator (P2P)
17400
17425
 
@@ -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": ">=0.0.1-0"
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 use a prerelease-compatible version range (`>=0.0.1-0`) in generated files, which resolves prerelease builds from a local Verdaccio registry:
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
@@ -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) */
@@ -15,7 +15,7 @@ Each bundle below is a self-contained markdown file for one authoring task. Fetc
15
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
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
17
  - [cli](https://docs.kici.dev/llms-cli.txt) (97 KB) — Running the CLI locally: compile, test, run local, hooks, lock-file drift, common failures
18
- - [cli-remote](https://docs.kici.dev/llms-cli-remote.txt) (109 KB) — Auth, org and orchestrator selection, runs, approvals, notifications, diagnostics, and the MCP server a coding agent connects to
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
19
  - [features](https://docs.kici.dev/llms-features.txt) (97 KB) — Deployment contexts, scoped secrets, private registries, approvals, provenance, dashboard and account access
20
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://
@@ -1,5 +1,6 @@
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";
@@ -48,6 +49,7 @@ function spawnOrchestratorProcess(databaseUrl, opts) {
48
49
  const { logFile, root, cacheDir } = planePaths();
49
50
  fs.mkdirSync(root, { recursive: true });
50
51
  fs.mkdirSync(cacheDir, { recursive: true });
52
+ rotatePlaneLogIfOversized(logFile);
51
53
  const out = fs.openSync(logFile, "a");
52
54
  const baseEnv = {};
53
55
  for (const [key, value] of Object.entries(process.env)) if (!key.startsWith("KICI_")) baseEnv[key] = value;
@@ -58,6 +60,7 @@ function spawnOrchestratorProcess(databaseUrl, opts) {
58
60
  KICI_AGENT_AUTH: "none",
59
61
  KICI_HOST: "127.0.0.1",
60
62
  KICI_AUTO_MIGRATE: "true",
63
+ KICI_CLUSTER_SINGLE_NODE: "true",
61
64
  KICI_DATA_DIR: root,
62
65
  KICI_BOOTSTRAP_ADMIN_TOKEN: opts.adminToken,
63
66
  KICI_SECRET_KEY: opts.secretKey,
@@ -33,6 +33,7 @@ export declare function planePaths(): {
33
33
  pidfile: string;
34
34
  stampFile: string;
35
35
  logFile: string;
36
+ pgLogFile: string;
36
37
  socketDir: string;
37
38
  adminTokenFile: string;
38
39
  platformTokenFile: string;
@@ -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
@@ -19,8 +19,8 @@ import { generateKeyPairSync, randomBytes } from "node:crypto";
19
19
  */
20
20
  function currentBuildIdentity() {
21
21
  return {
22
- version: "0.7.0",
23
- buildCommit: "31843a21c"
22
+ version: "0.8.0",
23
+ buildCommit: "57712173f"
24
24
  };
25
25
  }
26
26
  /**
@@ -11,6 +11,9 @@
11
11
  * The source's bundle hot-reload is debounced, so a first trigger can land
12
12
  * before the plane has registered the (re-pointed) local source. This resends
13
13
  * the webhook after a grace window until a run appears or the timeout elapses.
14
+ * When the budget still runs out, the timeout is explained rather than merely
15
+ * reported: the plane's own Raft role and its event-log record for this
16
+ * delivery decide which cause is named.
14
17
  */
15
18
  /** A minimal admin-read client (AdminApiClient.get) — injectable for tests. */
16
19
  export interface RunDiscoveryClient {
@@ -58,7 +61,32 @@ export interface TriggerRunOptions {
58
61
  pollIntervalMs?: number;
59
62
  resendAfterMs?: number;
60
63
  timeoutMs?: number;
64
+ /** Workdir the plane's local source points at — named in the lock-file diagnosis. */
65
+ repoBasePath?: string;
66
+ /** Plane log path — named when nothing else explains the timeout. */
67
+ logPath?: string;
61
68
  }
69
+ /** What the diagnosis knows about the trigger that just timed out. */
70
+ export interface TriggerTimeoutContext {
71
+ /** Routing-key-scoped delivery id, or null when the plane never accepted the webhook. */
72
+ deliveryId: string | null;
73
+ orgId: string;
74
+ /** Commit the synthetic push carried (the workdir's overlay commit). */
75
+ sha: string;
76
+ /** Absolute path the plane's local source points at — the run's workdir. */
77
+ repoBasePath?: string;
78
+ /** Plane log path, named in the fallback so the developer has somewhere to look. */
79
+ logPath?: string;
80
+ }
81
+ /**
82
+ * Explain a trigger timeout in one line, from what the plane itself recorded.
83
+ *
84
+ * Ordered most-specific-first, and every branch is a fact read back off the
85
+ * plane rather than an inference: the Raft role it reports, then the status it
86
+ * wrote for THIS delivery, then the delivery id + log path so the developer has
87
+ * a thread to pull even when neither surface answered.
88
+ */
89
+ export declare function diagnoseTriggerTimeout(client: RunDiscoveryClient, ctx: TriggerTimeoutContext): Promise<string>;
62
90
  /**
63
91
  * Trigger the run and resolve its runId. Sends the synthetic push, then polls
64
92
  * the admin runs list filtered by this webhook's routing-key-scoped delivery id