@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.
Files changed (70) hide show
  1. package/dist/cli.js +37 -7
  2. package/dist/commands/approve.d.ts +12 -0
  3. package/dist/commands/approve.js +5 -2
  4. package/dist/commands/compile.js +4 -2
  5. package/dist/commands/doctor.js +2 -2
  6. package/dist/commands/endpoints.js +4 -6
  7. package/dist/commands/held-run-client.d.ts +21 -1
  8. package/dist/commands/held-run-client.js +34 -15
  9. package/dist/commands/hook.js +22 -20
  10. package/dist/commands/index.d.ts +2 -0
  11. package/dist/commands/index.js +2 -1
  12. package/dist/commands/init.d.ts +9 -2
  13. package/dist/commands/init.js +43 -16
  14. package/dist/commands/login.js +1 -1
  15. package/dist/commands/orchestrators.js +3 -2
  16. package/dist/commands/reject.d.ts +12 -0
  17. package/dist/commands/reject.js +5 -2
  18. package/dist/commands/report/collect.d.ts +82 -0
  19. package/dist/commands/report/collect.js +234 -0
  20. package/dist/commands/report/identity.d.ts +48 -0
  21. package/dist/commands/report/identity.js +49 -0
  22. package/dist/commands/report/index.d.ts +63 -0
  23. package/dist/commands/report/index.js +119 -0
  24. package/dist/commands/report/upload.d.ts +38 -0
  25. package/dist/commands/report/upload.js +64 -0
  26. package/dist/commands/run-hold-watch.js +2 -2
  27. package/dist/commands/run.js +6 -3
  28. package/dist/commands/runs/show.js +80 -1
  29. package/dist/commands/types.js +51 -8
  30. package/dist/execution/sdk-alias.js +4 -2
  31. package/dist/fixtures/compiler.js +2 -1
  32. package/dist/format.js +3 -3
  33. package/dist/generators/secrets-dts.d.ts +8 -2
  34. package/dist/generators/secrets-dts.js +3 -2
  35. package/dist/hooks/installer.js +2 -1
  36. package/dist/llm-context/llms-architecture.txt +35 -13
  37. package/dist/llm-context/llms-cli.txt +142 -28
  38. package/dist/llm-context/llms-features-execution.txt +2017 -0
  39. package/dist/llm-context/llms-features.txt +298 -1483
  40. package/dist/llm-context/llms-full.txt +3008 -1698
  41. package/dist/llm-context/llms-getting-started.txt +145 -12
  42. package/dist/llm-context/llms-patterns.txt +176 -1
  43. package/dist/llm-context/llms-providers.txt +11 -27
  44. package/dist/llm-context/llms-sdk-runtime.txt +25 -4
  45. package/dist/llm-context/llms-sdk.txt +31 -1
  46. package/dist/llm-context/llms.txt +22 -14
  47. package/dist/local-plane/paths.d.ts +15 -0
  48. package/dist/local-plane/paths.js +22 -1
  49. package/dist/local-plane/plane-manager.js +2 -2
  50. package/dist/local-plane/port-holder.js +1 -1
  51. package/dist/local-plane/postgres.d.ts +3 -16
  52. package/dist/local-plane/postgres.js +10 -15
  53. package/dist/lockfile/generator.d.ts +12 -0
  54. package/dist/lockfile/generator.js +47 -14
  55. package/dist/postinstall.js +2 -1
  56. package/dist/remote/config.d.ts +2 -15
  57. package/dist/remote/config.js +2 -16
  58. package/dist/remote/dashboard-client.d.ts +39 -0
  59. package/dist/remote/dashboard-client.js +41 -0
  60. package/dist/remote/oauth.js +7 -5
  61. package/dist/remote/uploader.js +2 -2
  62. package/dist/templates/package-json.js +1 -1
  63. package/dist/test-runner/dry-run.js +4 -2
  64. package/dist/test-runner/git-detector.js +2 -1
  65. package/dist/test-runner/job-executor.js +2 -1
  66. package/dist/test-runner/payload-builder.js +11 -17
  67. package/dist/types.d.ts +33 -3
  68. package/dist/validation/validator.js +23 -6
  69. package/package.json +16 -11
  70. package/sbom.spdx.json +953 -901
@@ -8,19 +8,21 @@ The full markdown bundle of every page indexed here is available at https://docs
8
8
 
9
9
  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:
10
10
 
11
- - [getting-started](https://docs.kici.dev/llms-getting-started.txt) (51 KB) — Adopt KiCI: why it exists, how workflows execute, migrating from GitHub Actions, installing the SDK, and writing/compiling/testing your first workflow
12
- - [patterns](https://docs.kici.dev/llms-patterns.txt) (50 KB) — Copy-paste workflow recipes: triggers, conditionals, matrix, scheduling, integrations
13
- - [sdk](https://docs.kici.dev/llms-sdk.txt) (125 KB) — Core authoring API: workflow/job/step factories, triggers, rules, matrix, dynamic jobs, cross-job outputs
14
- - [sdk-runtime](https://docs.kici.dev/llms-sdk-runtime.txt) (112 KB) — Runtime and advanced authoring: step runtime context, event payloads, host fan-out, idempotent steps, caching, artifacts, waiting
15
- - [cli](https://docs.kici.dev/llms-cli.txt) (187 KB) — Running the CLI: compile, test, run local/remote, auth, hooks, lock-file drift
16
- - [features](https://docs.kici.dev/llms-features.txt) (158 KB) — Workflow features: concurrency, contexts, secrets, approvals, provenance, events
17
- - [providers](https://docs.kici.dev/llms-providers.txt) (37 KB) — Connecting sources: GitHub App, universal-git (Forgejo/Gitea/GitLab), local file://
18
- - [architecture](https://docs.kici.dev/llms-architecture.txt) (95 KB) — How the runtime works: three-tier relay model, data flows, configuration
11
+ - [getting-started](https://docs.kici.dev/llms-getting-started.txt) (58 KB) — Adopt KiCI: why it exists, how workflows execute, migrating from GitHub Actions, installing the SDK, and writing/compiling/testing your first workflow
12
+ - [patterns](https://docs.kici.dev/llms-patterns.txt) (57 KB) — Copy-paste workflow recipes: triggers, conditionals, matrix, scheduling, integrations
13
+ - [sdk](https://docs.kici.dev/llms-sdk.txt) (127 KB) — Core authoring API: workflow/job/step factories, triggers, rules, matrix, dynamic jobs, cross-job outputs
14
+ - [sdk-runtime](https://docs.kici.dev/llms-sdk-runtime.txt) (114 KB) — Runtime and advanced authoring: step runtime context, event payloads, host fan-out, idempotent steps, caching, artifacts, waiting
15
+ - [cli](https://docs.kici.dev/llms-cli.txt) (194 KB) — Running the CLI: compile, test, run local/remote, auth, hooks, lock-file drift
16
+ - [features](https://docs.kici.dev/llms-features.txt) (90 KB) — Deployment contexts, scoped secrets, private registries, approvals, provenance, dashboard and account access
17
+ - [features-execution](https://docs.kici.dev/llms-features-execution.txt) (120 KB) — Concurrency, dynamic values, events, container jobs, environment variables, global workflows, idempotent steps
18
+ - [providers](https://docs.kici.dev/llms-providers.txt) (35 KB) — Connecting sources: GitHub App, universal-git (Forgejo/Gitea/GitLab), local file://
19
+ - [architecture](https://docs.kici.dev/llms-architecture.txt) (99 KB) — How the runtime works: three-tier relay model, data flows, configuration
19
20
 
20
21
  ## Getting started
21
22
 
22
23
  - [User guide](https://docs.kici.dev/user/): Writing and testing CI/CD workflows in TypeScript, running on infrastructure you control
23
24
  - [How your workflow code executes](https://docs.kici.dev/user/execution-model/): When and where your workflow TypeScript runs — compile time, orchestrator time, and agent time
25
+ - [Getting help](https://docs.kici.dev/user/getting-help/): How to report a problem to KiCI and send the diagnostic context privately
24
26
  - [Getting started with workflows](https://docs.kici.dev/user/getting-started/): Install the SDK, write your first workflow, compile and test locally
25
27
  - [Migrating from GitHub Actions](https://docs.kici.dev/user/migrating-from-github-actions/): Map GitHub Actions concepts to KiCI, translate a real workflow side-by-side, and see what has no equivalent yet
26
28
  - [5-minute quickstart](https://docs.kici.dev/user/quickstart/): Stand up a KiCI orchestrator + agent and run your first workflow
@@ -30,6 +32,7 @@ Each bundle below is a self-contained markdown file for one authoring task. Fetc
30
32
 
31
33
  - [Basic workflow patterns](https://docs.kici.dev/user/patterns/basic/): Basic CI, PR-only / push-only filters, multiple triggers, manual-only workflows
32
34
  - [Conditionals & matrix patterns](https://docs.kici.dev/user/patterns/conditionals-matrix/): Conditional execution with rules, matrix builds (static + dynamic), dynamic job generation
35
+ - [Git credentials](https://docs.kici.dev/user/patterns/git-credentials/): Authenticated git in a workflow — declaring credentials from the secrets backend, and pushing
33
36
  - [Host restart & wait-for-alive](https://docs.kici.dev/user/patterns/host-restart/): Reboot the host a workflow runs on and continue after it comes back
34
37
  - [Integration patterns](https://docs.kici.dev/user/patterns/integrations/): Workflow chaining, generic webhooks, Stripe, self-hosted git forges, plain GitHub repos
35
38
  - [Pattern reference](https://docs.kici.dev/user/patterns/reference/): Step context, examples repository, GitHub check run output — cross-cutting reference for all patterns
@@ -59,7 +62,7 @@ Each bundle below is a self-contained markdown file for one authoring task. Fetc
59
62
 
60
63
  - [Drive KiCI from your coding agent](https://docs.kici.dev/user/ai-agents/): Point a coding agent at KiCI's MCP server and let it trigger, read, and re-run your CI under your own identity.
61
64
  - [CLI authentication](https://docs.kici.dev/user/cli-auth/): Authenticate the KiCI CLI with browser OAuth, device flow, or API key paste
62
- - [CLI reference](https://docs.kici.dev/user/cli-reference/): All CLI commands: compile, preview, local, fixture, types, workflows, hook, docs, run, runs, approve, reject, login, logout, init, org, pat, secrets, admin, orchestrators, endpoints, notifications, verify-attestation, diagnostics, doctor
65
+ - [CLI reference](https://docs.kici.dev/user/cli-reference/): All CLI commands: compile, preview, local, fixture, types, workflows, hook, docs, run, runs, approve, reject, login, logout, init, org, pat, secrets, admin, orchestrators, endpoints, notifications, verify-attestation, diagnostics, doctor, report
63
66
  - [Common failures](https://docs.kici.dev/user/common-failures/): Symptom-to-fix reference for the failures workflow authors hit most — no jobs dispatched, lock-file drift, missing webhooks, and agents that won't connect
64
67
  - [Lifecycle hooks](https://docs.kici.dev/user/hooks/): SDK hook API for cancel, cleanup, success, failure, and step-level callbacks
65
68
  - [Lock file and workflow drift](https://docs.kici.dev/user/lock-file-and-drift/): Keep the lock file in sync with your workflow source and avoid drift
@@ -70,21 +73,26 @@ Each bundle below is a self-contained markdown file for one authoring task. Fetc
70
73
  - [kici: notifications & diagnostics](https://docs.kici.dev/user/cli/notifications-and-diagnostics/): Notification channels, attestation verification, and diagnostics
71
74
  - [kici: runs & approvals](https://docs.kici.dev/user/cli/runs-and-approvals/): Run push and inspection plus approval / rejection of held runs
72
75
 
73
- ## Workflow features
76
+ ## Workflow features: access and approval
74
77
 
75
78
  - [Account and sign-in](https://docs.kici.dev/user/account-and-login/): How your KiCI account relates to sign-in methods, and how to change the way you sign in.
76
79
  - [Approval gates](https://docs.kici.dev/user/approvals/): Pause a workflow for human sign-off at step, job, or workflow granularity with approval
77
- - [Concurrency groups](https://docs.kici.dev/user/concurrency/): Control parallel execution with auto-cancel and queue modes
78
80
  - [Contexts](https://docs.kici.dev/user/contexts/): Configure deployment contexts with variables, secrets, and protection rules
79
81
  - [Dashboard](https://docs.kici.dev/user/dashboard/): Web UI for monitoring workflow runs, managing sources, secrets, and organization settings.
82
+ - [Private npm registries](https://docs.kici.dev/user/private-registries/): Authenticate `npm install` against private registries (CodeArtifact, GitHub Packages, Verdaccio, …) from a workflow's `.kici/package.json`
83
+ - [Build provenance and attestations](https://docs.kici.dev/user/provenance/): Generate and verify signed SLSA provenance for the artifacts your workflows build
84
+ - [Secrets](https://docs.kici.dev/user/secrets/): How to access secrets in KiCI workflow steps
85
+
86
+ ## Workflow features: execution
87
+
88
+ - [Concurrency groups](https://docs.kici.dev/user/concurrency/): Control parallel execution with auto-cancel and queue modes
89
+ - [Container jobs](https://docs.kici.dev/user/container-jobs/): Run a job inside any container image, including a private one, without that image shipping Node or git
80
90
  - [Dynamic values](https://docs.kici.dev/user/dynamic-values/): Compute a job's context, env, and concurrencyGroup at runtime from the incoming event
81
91
  - [Environment variables](https://docs.kici.dev/user/env-vars/): Environment variable reference for the CLI
82
92
  - [Event system](https://docs.kici.dev/user/events/): How KiCI's event model works -- event types, the registration model, event matching, and circuit breaker protection
83
93
  - [Global workflows](https://docs.kici.dev/user/global-workflows/): Cross-repo workflows that run on events from any repo in the same org
84
94
  - [Idempotent steps and check mode](https://docs.kici.dev/user/idempotent-steps/): Declare desired state with a step check facet, then run in apply or --check preview mode
85
- - [Private npm registries](https://docs.kici.dev/user/private-registries/): Authenticate `npm install` against private registries (CodeArtifact, GitHub Packages, Verdaccio, …) from a workflow's `.kici/package.json`
86
- - [Build provenance and attestations](https://docs.kici.dev/user/provenance/): Generate and verify signed SLSA provenance for the artifacts your workflows build
87
- - [Secrets](https://docs.kici.dev/user/secrets/): How to access secrets in KiCI workflow steps
95
+ - [Autoscaling workflows](https://docs.kici.dev/user/workflows/autoscaling-workflows/): Write TypeScript provisioning and teardown workflows that boot and delete ephemeral cloud agents in response to the event scaler
88
96
 
89
97
  ## Providers
90
98
 
@@ -7,6 +7,21 @@ export declare const PLANE_STAMP_VERSION = 3;
7
7
  /**
8
8
  * Root directory of the local dev plane's state, following the same
9
9
  * `KICI_CONFIG_DIR` → `~/.kici` convention the rest of the CLI uses.
10
+ *
11
+ * RESOLVED THROUGH SYMLINKS, deliberately. The plane is a singleton on fixed
12
+ * ports, so a caller may reach it through a config dir that only symlinks
13
+ * `local` at the durable one — `pnpm deploy:stg` does exactly that, to run
14
+ * against a throwaway config dir carrying no credentials while still reusing
15
+ * the warm plane.
16
+ *
17
+ * Without resolving, the plane's Postgres is started with a data directory
18
+ * addressed through that ephemeral path and keeps it open. When the caller
19
+ * removes its temp dir, Postgres PANICs — `could not open file
20
+ * "<tmp>/local/pgdata/global/pg_control"` — and shuts the whole plane down,
21
+ * taking every later phase with it. Resolving first means Postgres only ever
22
+ * sees the durable path, so a caller's temp dir can come and go beneath it.
23
+ *
24
+ * A path that does not exist yet resolves to itself: a fresh plane creates it.
10
25
  */
11
26
  export declare function planeRoot(): string;
12
27
  /**
@@ -1,5 +1,6 @@
1
1
  import "../rolldown-runtime-ClRpJifh.js";
2
2
  import path from "node:path";
3
+ import fs from "node:fs";
3
4
  import os from "node:os";
4
5
  //#region src/local-plane/paths.ts
5
6
  /**
@@ -11,10 +12,30 @@ const PLANE_STAMP_VERSION = 3;
11
12
  /**
12
13
  * Root directory of the local dev plane's state, following the same
13
14
  * `KICI_CONFIG_DIR` → `~/.kici` convention the rest of the CLI uses.
15
+ *
16
+ * RESOLVED THROUGH SYMLINKS, deliberately. The plane is a singleton on fixed
17
+ * ports, so a caller may reach it through a config dir that only symlinks
18
+ * `local` at the durable one — `pnpm deploy:stg` does exactly that, to run
19
+ * against a throwaway config dir carrying no credentials while still reusing
20
+ * the warm plane.
21
+ *
22
+ * Without resolving, the plane's Postgres is started with a data directory
23
+ * addressed through that ephemeral path and keeps it open. When the caller
24
+ * removes its temp dir, Postgres PANICs — `could not open file
25
+ * "<tmp>/local/pgdata/global/pg_control"` — and shuts the whole plane down,
26
+ * taking every later phase with it. Resolving first means Postgres only ever
27
+ * sees the durable path, so a caller's temp dir can come and go beneath it.
28
+ *
29
+ * A path that does not exist yet resolves to itself: a fresh plane creates it.
14
30
  */
15
31
  function planeRoot() {
16
32
  const base = process.env.KICI_CONFIG_DIR ?? path.join(os.homedir(), ".kici");
17
- return path.join(base, "local");
33
+ const root = path.join(base, "local");
34
+ try {
35
+ return fs.realpathSync(root);
36
+ } catch {
37
+ return root;
38
+ }
18
39
  }
19
40
  /**
20
41
  * Absolute paths for every artefact the plane persists under its state root.
@@ -19,8 +19,8 @@ import { generateKeyPairSync, randomBytes } from "node:crypto";
19
19
  */
20
20
  function currentBuildIdentity() {
21
21
  return {
22
- version: "0.5.0",
23
- buildCommit: "cb51c7d1e"
22
+ version: "0.6.0",
23
+ buildCommit: "e4a936029"
24
24
  };
25
25
  }
26
26
  /**
@@ -15,7 +15,7 @@ import net from "node:net";
15
15
  const defaultExec = (cmd, args) => new Promise((resolve) => {
16
16
  execFile(cmd, args, {
17
17
  timeout: 5e3,
18
- maxBuffer: 8 * 1024 * 1024
18
+ maxBuffer: 8388608
19
19
  }, (err, stdout) => {
20
20
  const raw = err?.code;
21
21
  resolve({
@@ -3,7 +3,7 @@
3
3
  * `TS_CONST_PINS` in hack/containers-lock.ts (identity
4
4
  * docker.io/library/postgres+alpine), so a lock bump rewrites it in place.
5
5
  */
6
- export declare const PLANE_PG_IMAGE = "docker.io/library/postgres:18.4-alpine";
6
+ export declare const PLANE_PG_IMAGE = "docker.io/library/postgres:18.6-alpine";
7
7
  /** Name of the fallback podman Postgres container. */
8
8
  export declare const PLANE_PG_CONTAINER = "kici-local-postgres";
9
9
  export type PlanePgHandle = {
@@ -11,13 +11,6 @@ export type PlanePgHandle = {
11
11
  kind: 'embedded' | 'podman';
12
12
  stop(): Promise<void>;
13
13
  };
14
- declare let readyPoller: typeof defaultReadyPoller;
15
- /** Test seam: override the podman readiness poller. */
16
- export declare function __setReadyPollerForTest(fn: typeof readyPoller): void;
17
- declare let embeddedDaemon: typeof defaultEmbeddedDaemon;
18
- /** Test seam: override the embedded postmaster daemonizer. */
19
- export declare function __setEmbeddedDaemonForTest(fn: typeof embeddedDaemon): void;
20
- declare function defaultReadyPoller(_port: number): Promise<boolean>;
21
14
  /**
22
15
  * Whether this plane's embedded postmaster is already serving `port`.
23
16
  *
@@ -31,13 +24,6 @@ declare function defaultReadyPoller(_port: number): Promise<boolean>;
31
24
  * one this plane can reuse.
32
25
  */
33
26
  export declare function embeddedClusterIsServing(port: number): Promise<boolean>;
34
- /**
35
- * Start a detached embedded postmaster via `pg_ctl` so it survives the exit of
36
- * this CLI process (embedded-postgres's in-process server is killed by its own
37
- * exit hook, so it cannot back a warm plane). A cluster already serving this
38
- * plane's port is reused as-is.
39
- */
40
- declare function defaultEmbeddedDaemon(port: number): Promise<void>;
41
27
  /**
42
28
  * Stop the plane's Postgres by backend kind. Handle-independent so a separate
43
29
  * CLI invocation (`kici local down`) can tear down what `up` started.
@@ -51,6 +37,7 @@ export declare function stopPlanePostgres(kind: 'embedded' | 'podman'): Promise<
51
37
  */
52
38
  export declare function startPlanePostgres(opts?: {
53
39
  forcePodman?: boolean;
40
+ readyPoller?: (port: number) => Promise<boolean>;
41
+ embeddedDaemon?: (port: number) => Promise<void>;
54
42
  }): Promise<PlanePgHandle>;
55
- export {};
56
43
  //# sourceMappingURL=postgres.d.ts.map
@@ -13,19 +13,9 @@ import EmbeddedPostgres from "embedded-postgres";
13
13
  * `TS_CONST_PINS` in hack/containers-lock.ts (identity
14
14
  * docker.io/library/postgres+alpine), so a lock bump rewrites it in place.
15
15
  */
16
- const PLANE_PG_IMAGE = "docker.io/library/postgres:18.4-alpine";
16
+ const PLANE_PG_IMAGE = "docker.io/library/postgres:18.6-alpine";
17
17
  /** Name of the fallback podman Postgres container. */
18
18
  const PLANE_PG_CONTAINER = "kici-local-postgres";
19
- let readyPoller = defaultReadyPoller;
20
- /** Test seam: override the podman readiness poller. */
21
- function __setReadyPollerForTest(fn) {
22
- readyPoller = fn;
23
- }
24
- let embeddedDaemon = defaultEmbeddedDaemon;
25
- /** Test seam: override the embedded postmaster daemonizer. */
26
- function __setEmbeddedDaemonForTest(fn) {
27
- embeddedDaemon = fn;
28
- }
29
19
  async function defaultReadyPoller(_port) {
30
20
  for (let i = 0; i < 60; i++) try {
31
21
  await $`podman exec ${PLANE_PG_CONTAINER} pg_isready -U kici`.quiet();
@@ -56,7 +46,8 @@ function resolvePgCtl() {
56
46
  "win32:x64": "@embedded-postgres/windows-x64"
57
47
  }[`${platform}:${arch}`];
58
48
  if (!pkg) throw new Error(`unsupported platform for embedded Postgres: ${platform}/${arch}`);
59
- const entry = createRequire(createRequire(import.meta.url).resolve("embedded-postgres")).resolve(pkg);
49
+ const require = createRequire(import.meta.url);
50
+ const entry = createRequire(require.resolve("embedded-postgres")).resolve(pkg);
60
51
  const binName = platform === "win32" ? "pg_ctl.exe" : "pg_ctl";
61
52
  return path.resolve(path.dirname(entry), "..", "native", "bin", binName);
62
53
  }
@@ -119,13 +110,15 @@ async function embeddedClusterIsServing(port) {
119
110
  async function defaultEmbeddedDaemon(port) {
120
111
  const { pgData, logFile } = planePaths();
121
112
  if (await embeddedClusterIsServing(port)) return;
122
- await $`${resolvePgCtl()} -D ${pgData} -o ${`-p ${port}`} -l ${`${logFile}.pg`} -w start`.quiet();
113
+ const pgCtl = resolvePgCtl();
114
+ await $`${pgCtl} -D ${pgData} -o ${`-p ${port}`} -l ${`${logFile}.pg`} -w start`.quiet();
123
115
  }
124
116
  /** Stop the detached embedded postmaster (handle-independent, reads the data dir). */
125
117
  async function stopEmbeddedDaemon() {
126
118
  const { pgData } = planePaths();
127
119
  if (!fs.existsSync(path.join(pgData, "postmaster.pid"))) return;
128
- await $`${resolvePgCtl()} -D ${pgData} stop -m fast`.quiet().catch(() => {});
120
+ const pgCtl = resolvePgCtl();
121
+ await $`${pgCtl} -D ${pgData} stop -m fast`.quiet().catch(() => {});
129
122
  }
130
123
  /**
131
124
  * Stop the plane's Postgres by backend kind. Handle-independent so a separate
@@ -142,6 +135,8 @@ async function stopPlanePostgres(kind) {
142
135
  * (or when forced via `forcePodman` / `KICI_LOCAL_PG_MODE=podman`).
143
136
  */
144
137
  async function startPlanePostgres(opts = {}) {
138
+ const readyPoller = opts.readyPoller ?? defaultReadyPoller;
139
+ const embeddedDaemon = opts.embeddedDaemon ?? defaultEmbeddedDaemon;
145
140
  const { postgres: port } = planePorts();
146
141
  const url = `postgres://kici:kici@127.0.0.1:${port}/kici_local`;
147
142
  if (!(opts.forcePodman || process.env.KICI_LOCAL_PG_MODE === "podman")) try {
@@ -182,6 +177,6 @@ async function startPlanePostgres(opts = {}) {
182
177
  };
183
178
  }
184
179
  //#endregion
185
- export { PLANE_PG_CONTAINER, PLANE_PG_IMAGE, __setEmbeddedDaemonForTest, __setReadyPollerForTest, embeddedClusterIsServing, startPlanePostgres, stopPlanePostgres };
180
+ export { PLANE_PG_CONTAINER, PLANE_PG_IMAGE, embeddedClusterIsServing, startPlanePostgres, stopPlanePostgres };
186
181
 
187
182
  //# sourceMappingURL=postgres.js.map
@@ -36,6 +36,18 @@ export declare function detectGitRoot(): string;
36
36
  * For yarn the prefix also carries the flavor (`yarn-classic` / `yarn-berry`),
37
37
  * so a classic-layout dep-cache tarball is never restored into a berry install.
38
38
  *
39
+ * The detected manager only orders the search; it never restricts it. Detection
40
+ * falls back to the AMBIENT `npm_config_user_agent` when a directory carries no
41
+ * signal of its own, so a repo whose root holds neither a `package.json` nor a
42
+ * lockfile is reported as whatever manager happened to invoke the compiler. A
43
+ * repo compiled under pnpm that keeps an npm `.kici/package-lock.json` was
44
+ * therefore searched for `pnpm-lock.yaml` only, found none, and got no hash at
45
+ * all — which silently disables the orchestrator's dependency cache for it, so
46
+ * every agent installs from the registry and an agent with no registry route
47
+ * cannot run the job at all. Searching every manager's candidates after the
48
+ * detected one's keeps the detected manager authoritative where it has real
49
+ * evidence, and still finds the lockfile that is actually on disk.
50
+ *
39
51
  * @param gitRoot - Absolute path to git repository root
40
52
  * @returns Hex SHA-256 hash string, or null if no lockfile is found
41
53
  */
@@ -66,16 +66,39 @@ function detectGitRoot() {
66
66
  * For yarn the prefix also carries the flavor (`yarn-classic` / `yarn-berry`),
67
67
  * so a classic-layout dep-cache tarball is never restored into a berry install.
68
68
  *
69
+ * The detected manager only orders the search; it never restricts it. Detection
70
+ * falls back to the AMBIENT `npm_config_user_agent` when a directory carries no
71
+ * signal of its own, so a repo whose root holds neither a `package.json` nor a
72
+ * lockfile is reported as whatever manager happened to invoke the compiler. A
73
+ * repo compiled under pnpm that keeps an npm `.kici/package-lock.json` was
74
+ * therefore searched for `pnpm-lock.yaml` only, found none, and got no hash at
75
+ * all — which silently disables the orchestrator's dependency cache for it, so
76
+ * every agent installs from the registry and an agent with no registry route
77
+ * cannot run the job at all. Searching every manager's candidates after the
78
+ * detected one's keeps the detected manager authoritative where it has real
79
+ * evidence, and still finds the lockfile that is actually on disk.
80
+ *
69
81
  * @param gitRoot - Absolute path to git repository root
70
82
  * @returns Hex SHA-256 hash string, or null if no lockfile is found
71
83
  */
72
84
  function computeLockfileHash(gitRoot) {
73
85
  const pm = detectPackageManagerSync(gitRoot) === PackageManager.Npm ? detectPackageManagerSync(path.join(gitRoot, ".kici")) : detectPackageManagerSync(gitRoot);
74
- const candidates = pm === PackageManager.Pnpm ? [path.join(gitRoot, "pnpm-lock.yaml"), path.join(gitRoot, ".kici", "pnpm-lock.yaml")] : pm === PackageManager.Yarn ? [path.join(gitRoot, "yarn.lock"), path.join(gitRoot, ".kici", "yarn.lock")] : [path.join(gitRoot, ".kici", "package-lock.json")];
75
- const prefix = pm === PackageManager.Yarn ? `${pm}-${detectYarnFlavorSync(detectPackageManagerSync(gitRoot) === PackageManager.Yarn ? gitRoot : path.join(gitRoot, ".kici"))}` : pm;
76
- for (const lockfilePath of candidates) try {
77
- return sha256(`${prefix}\n${readFileSync(lockfilePath, "utf-8")}`);
78
- } catch {}
86
+ const byManager = (m) => m === PackageManager.Pnpm ? [path.join(gitRoot, "pnpm-lock.yaml"), path.join(gitRoot, ".kici", "pnpm-lock.yaml")] : m === PackageManager.Yarn ? [path.join(gitRoot, "yarn.lock"), path.join(gitRoot, ".kici", "yarn.lock")] : [path.join(gitRoot, ".kici", "package-lock.json")];
87
+ const order = [pm, ...[
88
+ PackageManager.Npm,
89
+ PackageManager.Pnpm,
90
+ PackageManager.Yarn
91
+ ].filter((m) => m !== pm)];
92
+ for (const manager of order) for (const lockfilePath of byManager(manager)) {
93
+ let content;
94
+ try {
95
+ content = readFileSync(lockfilePath, "utf-8");
96
+ } catch {
97
+ continue;
98
+ }
99
+ const prefix = manager === PackageManager.Yarn ? `${manager}-${detectYarnFlavorSync(path.dirname(lockfilePath))}` : manager;
100
+ return sha256(`${prefix}\n${content}`);
101
+ }
79
102
  return null;
80
103
  }
81
104
  /**
@@ -112,16 +135,17 @@ function generateLockFile(workflowsWithSource) {
112
135
  const workflows = workflowsWithSource.map(({ workflow, source, bundleSource }) => {
113
136
  return transformWorkflow(workflow, path.relative(gitRoot, source.file).replaceAll("\\", "/"), formatExportRef(source), bundleSource, gitRoot);
114
137
  });
138
+ const contentHash = sha256(JSON.stringify({
139
+ schemaVersion: SCHEMA_VERSION$1,
140
+ minReaderVersion: BREAKING_FLOOR$1,
141
+ source: topLevelSource,
142
+ workflows
143
+ }));
115
144
  return {
116
145
  schemaVersion: SCHEMA_VERSION$1,
117
146
  minReaderVersion: BREAKING_FLOOR$1,
118
147
  source: topLevelSource,
119
- contentHash: sha256(JSON.stringify({
120
- schemaVersion: SCHEMA_VERSION$1,
121
- minReaderVersion: BREAKING_FLOOR$1,
122
- source: topLevelSource,
123
- workflows
124
- })),
148
+ contentHash,
125
149
  ...lockfileHash && { lockfileHash },
126
150
  workflows
127
151
  };
@@ -209,12 +233,14 @@ function serializeRegexEntry(entry, ctx) {
209
233
  try {
210
234
  re = new RegExp(pattern, flags);
211
235
  } catch (err) {
212
- throw compilerError("E123", `${ctx}: invalid regex — ${err instanceof Error ? err.message : String(err)}`);
236
+ const reason = err instanceof Error ? err.message : String(err);
237
+ throw compilerError("E123", `${ctx}: invalid regex — ${reason}`);
213
238
  }
214
239
  try {
215
240
  assertSafeRegex(re.source, re.flags, ctx);
216
241
  } catch (err) {
217
- throw compilerError("E123", `${ctx}: ${err instanceof Error ? err.message : String(err)}`);
242
+ const reason = err instanceof Error ? err.message : String(err);
243
+ throw compilerError("E123", `${ctx}: ${reason}`);
218
244
  }
219
245
  return `/${re.source}/${re.flags}`;
220
246
  }
@@ -660,7 +686,7 @@ function transformJob(job, configPath, index, gitRoot, uuidToName) {
660
686
  location: jobLocation,
661
687
  suggestion: "Set exactly one of runsOn (single agent) or runsOnAll (fan-out to every matching agent)."
662
688
  });
663
- if (job.runsOn === void 0 && job.runsOnAll === void 0) throw compilerError("E109", `job '${job.name}': one of runsOn or runsOnAll is required`, {
689
+ if (job.invoke === void 0 && job.runsOn === void 0 && job.runsOnAll === void 0) throw compilerError("E109", `job '${job.name}': one of runsOn or runsOnAll is required`, {
664
690
  location: jobLocation,
665
691
  suggestion: "Add runsOn: \"kici:os:linux\" (or another agent label) to the job."
666
692
  });
@@ -700,8 +726,15 @@ function transformJob(job, configPath, index, gitRoot, uuidToName) {
700
726
  ...job.runsOnAll !== void 0 && { runsOnAll: normalizeRunsOnAllToMatchers(job.runsOnAll, `job '${job.name}' runsOnAll`) },
701
727
  ...job.onUnreachable !== void 0 && { onUnreachable: job.onUnreachable },
702
728
  ...job.includeUninitialized !== void 0 && { includeUninitialized: job.includeUninitialized },
729
+ ...job.gitCredentials !== void 0 && { gitCredentials: job.gitCredentials },
703
730
  ...job.maxParallel !== void 0 && { maxParallel: job.maxParallel },
704
731
  ...job.failFast !== void 0 && { failFast: job.failFast },
732
+ ...job.invoke !== void 0 && { invoke: {
733
+ event: job.invoke.event,
734
+ scope: job.invoke.scope,
735
+ ...job.invoke.payload !== void 0 && { payload: job.invoke.payload },
736
+ ...job.invoke.optional === true && { optional: true }
737
+ } },
705
738
  ...resolveNeedsForLock(job.needs, uuidToName),
706
739
  steps: transformSteps(job.steps, gitRoot, jobLocation),
707
740
  matrix: job.matrix ? transformMatrix(job.matrix, job.name, configPath) : void 0,
@@ -26,7 +26,8 @@ async function detectDevelopmentMode() {
26
26
  if (process.env.KICI_DEV === "true") return true;
27
27
  try {
28
28
  const rootDir = process.env.INIT_CWD || process.cwd();
29
- const content = await readFile(path.join(rootDir, "package.json"), "utf-8");
29
+ const rootPkgPath = path.join(rootDir, "package.json");
30
+ const content = await readFile(rootPkgPath, "utf-8");
30
31
  return JSON.parse(content).kici?.development === true;
31
32
  } catch {
32
33
  return false;
@@ -35,22 +35,9 @@ export interface GlobalConfig {
35
35
  *
36
36
  * Resolution order:
37
37
  * 1. `KICI_CONFIG_DIR`, when set to a non-empty value.
38
- * 2. A hard refusal when `KICI_TEST_ISOLATION` is present. A developer
39
- * machine's `~/.kici/config` names a real endpoint and carries a live
40
- * PAT, so a test process that reads it authenticates against whatever
41
- * that config points at — in practice, production. Every test that needs
42
- * a config must name its own isolated directory.
43
- * 3. `~/.kici`.
38
+ * 2. `~/.kici`.
44
39
  *
45
- * `KICI_TEST_ISOLATION` is set by this repository's own vitest configs, via
46
- * `hack/lib/vitest-isolation.ts`. It is deliberately a KiCI-owned name rather
47
- * than the runner's `VITEST`: `kici` is a compat-protected CLI, and `VITEST`
48
- * is a third-party marker that propagates into spawned children, so keying off
49
- * it would break a customer whose vitest test shells out to `kici`.
50
- *
51
- * `env` is injectable so this module's own tests can exercise every branch.
52
- * It is NOT an opt-out: production call sites pass no argument and inherit the
53
- * guard.
40
+ * `env` is injectable so this module's own tests can exercise both branches.
54
41
  */
55
42
  export declare function getConfigDir(env?: NodeJS.ProcessEnv): string;
56
43
  /**
@@ -33,26 +33,12 @@ function sanitizeConfig(raw) {
33
33
  *
34
34
  * Resolution order:
35
35
  * 1. `KICI_CONFIG_DIR`, when set to a non-empty value.
36
- * 2. A hard refusal when `KICI_TEST_ISOLATION` is present. A developer
37
- * machine's `~/.kici/config` names a real endpoint and carries a live
38
- * PAT, so a test process that reads it authenticates against whatever
39
- * that config points at — in practice, production. Every test that needs
40
- * a config must name its own isolated directory.
41
- * 3. `~/.kici`.
36
+ * 2. `~/.kici`.
42
37
  *
43
- * `KICI_TEST_ISOLATION` is set by this repository's own vitest configs, via
44
- * `hack/lib/vitest-isolation.ts`. It is deliberately a KiCI-owned name rather
45
- * than the runner's `VITEST`: `kici` is a compat-protected CLI, and `VITEST`
46
- * is a third-party marker that propagates into spawned children, so keying off
47
- * it would break a customer whose vitest test shells out to `kici`.
48
- *
49
- * `env` is injectable so this module's own tests can exercise every branch.
50
- * It is NOT an opt-out: production call sites pass no argument and inherit the
51
- * guard.
38
+ * `env` is injectable so this module's own tests can exercise both branches.
52
39
  */
53
40
  function getConfigDir(env = process.env) {
54
41
  if (env.KICI_CONFIG_DIR) return env.KICI_CONFIG_DIR;
55
- if (env.KICI_TEST_ISOLATION) throw new Error("Refusing to read the ambient ~/.kici config from a test process. Set KICI_CONFIG_DIR to an isolated directory for this run — the developer machine config may point at production.");
56
42
  return path.join(os.homedir(), ".kici");
57
43
  }
58
44
  /**
@@ -37,6 +37,18 @@ export declare const webhookActivitySchema: z.ZodObject<{
37
37
  orchestratorUnavailable: z.ZodBoolean;
38
38
  }, z.core.$strip>;
39
39
  export type WebhookActivity = z.infer<typeof webhookActivitySchema>;
40
+ declare const issueReportListSchema: z.ZodObject<{
41
+ reports: z.ZodArray<z.ZodObject<{
42
+ ref: z.ZodString;
43
+ bundleId: z.ZodString;
44
+ byteSize: z.ZodNumber;
45
+ status: z.ZodString;
46
+ createdAt: z.ZodString;
47
+ userId: z.ZodString;
48
+ message: z.ZodNullable<z.ZodString>;
49
+ }, z.core.$strip>>;
50
+ }, z.core.$strip>;
51
+ export type IssueReportListResponse = z.infer<typeof issueReportListSchema>;
40
52
  /** Result of {@link DashboardClient.listRegistrations}. */
41
53
  export interface RegistrationsListResult {
42
54
  registrations: RegistrationItem[];
@@ -104,6 +116,33 @@ export declare class DashboardClient {
104
116
  * relays it without re-signing, so the artifact bytes never transit Platform.
105
117
  */
106
118
  listArtifacts(runId: string): Promise<ArtifactsListResult>;
119
+ /**
120
+ * Reserve an issue-report upload slot.
121
+ *
122
+ * Returns a presigned PUT the caller uploads the bundle bytes to directly —
123
+ * the Platform never proxies them — plus the reference id the customer
124
+ * quotes to support.
125
+ */
126
+ createIssueReport(body: {
127
+ bundleId: string;
128
+ byteSize: number;
129
+ sha256: string;
130
+ message?: string;
131
+ email?: string;
132
+ }): Promise<{
133
+ ref: string;
134
+ uploadUrl: string;
135
+ }>;
136
+ /** Tell the Platform the bytes landed, so it can verify and mark the report received. */
137
+ confirmIssueReport(ref: string): Promise<{
138
+ ref: string;
139
+ status: string;
140
+ }>;
141
+ listIssueReports(): Promise<IssueReportListResponse>;
142
+ withdrawIssueReport(ref: string): Promise<{
143
+ ref: string;
144
+ deleted: boolean;
145
+ }>;
107
146
  rerunRun(runId: string): Promise<{
108
147
  newRunId: string;
109
148
  }>;
@@ -31,6 +31,27 @@ const webhookActivitySchema = z.object({
31
31
  lockfileMissing: z.number().optional(),
32
32
  orchestratorUnavailable: z.boolean()
33
33
  });
34
+ const issueReportCreateSchema = z.object({
35
+ ref: z.string(),
36
+ uploadUrl: z.string()
37
+ });
38
+ const issueReportConfirmSchema = z.object({
39
+ ref: z.string(),
40
+ status: z.string()
41
+ });
42
+ const issueReportDeleteSchema = z.object({
43
+ ref: z.string(),
44
+ deleted: z.boolean()
45
+ });
46
+ const issueReportListSchema = z.object({ reports: z.array(z.object({
47
+ ref: z.string(),
48
+ bundleId: z.string(),
49
+ byteSize: z.number(),
50
+ status: z.string(),
51
+ createdAt: z.string(),
52
+ userId: z.string(),
53
+ message: z.string().nullable()
54
+ })) });
34
55
  const rerunResponseSchema = z.object({ newRunId: z.string() });
35
56
  const cancelResponseSchema = z.object({
36
57
  cancelledJobs: z.number().optional(),
@@ -176,6 +197,26 @@ var DashboardClient = class DashboardClient {
176
197
  async listArtifacts(runId) {
177
198
  return artifactsListResultSchema.parse(await this.getJson(`/runs/${runId}/artifacts`));
178
199
  }
200
+ /**
201
+ * Reserve an issue-report upload slot.
202
+ *
203
+ * Returns a presigned PUT the caller uploads the bundle bytes to directly —
204
+ * the Platform never proxies them — plus the reference id the customer
205
+ * quotes to support.
206
+ */
207
+ async createIssueReport(body) {
208
+ return issueReportCreateSchema.parse(await this.postJson("/issue-reports", body));
209
+ }
210
+ /** Tell the Platform the bytes landed, so it can verify and mark the report received. */
211
+ async confirmIssueReport(ref) {
212
+ return issueReportConfirmSchema.parse(await this.postJson(`/issue-reports/${encodeURIComponent(ref)}/confirm`));
213
+ }
214
+ async listIssueReports() {
215
+ return issueReportListSchema.parse(await this.getJson("/issue-reports"));
216
+ }
217
+ async withdrawIssueReport(ref) {
218
+ return issueReportDeleteSchema.parse(await this.deleteJson(`/issue-reports/${encodeURIComponent(ref)}`));
219
+ }
179
220
  async rerunRun(runId) {
180
221
  return rerunResponseSchema.parse(await this.postJson(`/runs/${runId}/rerun`));
181
222
  }
@@ -106,7 +106,7 @@ function describeListenFailure(err, listenPort) {
106
106
  * @returns OIDC access token
107
107
  */
108
108
  async function pkceFlow(opts) {
109
- const { issuer, clientId, nudgeAfterMs = 9e4, timeoutMs = 300 * 1e3 } = opts;
109
+ const { issuer, clientId, nudgeAfterMs = 9e4, timeoutMs = 3e5 } = opts;
110
110
  let listenPort = 0;
111
111
  const callbackPortEnv = process.env.KICI_CALLBACK_PORT;
112
112
  if (callbackPortEnv) {
@@ -211,10 +211,12 @@ async function pkceFlow(opts) {
211
211
  if (browserCmd === "none") {
212
212
  console.log(`KICI_AUTH_URL=${authUrl.toString()}`);
213
213
  console.log(pc.cyan(` Waiting for browser callback on port ${actualPort}...`));
214
- } else if (browserCmd) exec(browserCmd.replace("{url}", authUrl.toString()), (err) => {
215
- if (err) console.log(pc.yellow(` Custom browser command failed: ${err.message}\n Please visit:\n ${authUrl.toString()}`));
216
- });
217
- else {
214
+ } else if (browserCmd) {
215
+ const cmd = browserCmd.replace("{url}", authUrl.toString());
216
+ exec(cmd, (err) => {
217
+ if (err) console.log(pc.yellow(` Custom browser command failed: ${err.message}\n Please visit:\n ${authUrl.toString()}`));
218
+ });
219
+ } else {
218
220
  console.log(pc.cyan(" Opening browser for authentication..."));
219
221
  console.log(pc.dim(` If it does not open, visit:\n ${authUrl.toString()}`));
220
222
  open(authUrl.toString()).catch(() => {
@@ -9,9 +9,9 @@ import { makeTempDir } from "@kici-dev/core/tmp";
9
9
  import picomatch from "picomatch";
10
10
  //#region src/remote/uploader.ts
11
11
  /** Size threshold for warning (50MB) */
12
- const SIZE_WARN_THRESHOLD = 50 * 1024 * 1024;
12
+ const SIZE_WARN_THRESHOLD = 52428800;
13
13
  /** Size threshold for hard error (500MB) */
14
- const SIZE_ERROR_THRESHOLD = 500 * 1024 * 1024;
14
+ const SIZE_ERROR_THRESHOLD = 524288e3;
15
15
  /** Maximum upload retry attempts */
16
16
  const MAX_RETRIES = 3;
17
17
  /**
@@ -1,6 +1,6 @@
1
1
  import "../rolldown-runtime-ClRpJifh.js";
2
2
  //#region src/templates/package-json.ts
3
- const sdkVersion = "0.5.0";
3
+ const sdkVersion = "0.6.0";
4
4
  /**
5
5
  * The npm version range the scaffold pins `@kici-dev/sdk` to.
6
6
  *