@kici-dev/compiler 0.0.0 → 0.1.2

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 (221) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +1 -6
  3. package/dist/assets/api-TJJVHrjC.json +118 -0
  4. package/dist/assets/descriptor-BTtjzN9L.json +1382 -0
  5. package/dist/assets/package-BpQF9kR8.json +74 -0
  6. package/dist/assets/package-Ceo2h27X.json +89 -0
  7. package/dist/assets/source_context-D0atuL28.json +20 -0
  8. package/dist/assets/type-BFqO8SCZ.json +202 -0
  9. package/dist/auth/headless-detect.d.ts +14 -0
  10. package/dist/auth/headless-detect.js +25 -0
  11. package/dist/chunk-gOLHoazu.js +4 -0
  12. package/dist/cli.d.ts +3 -0
  13. package/dist/cli.js +193 -0
  14. package/dist/commands/cancel.d.ts +22 -0
  15. package/dist/commands/cancel.js +124 -0
  16. package/dist/commands/compile.d.ts +17 -0
  17. package/dist/commands/compile.js +99 -0
  18. package/dist/commands/docs.d.ts +26 -0
  19. package/dist/commands/docs.js +60 -0
  20. package/dist/commands/drain-worker.d.ts +19 -0
  21. package/dist/commands/drain-worker.js +45 -0
  22. package/dist/commands/endpoints.d.ts +53 -0
  23. package/dist/commands/endpoints.js +185 -0
  24. package/dist/commands/fixture.d.ts +14 -0
  25. package/dist/commands/fixture.js +104 -0
  26. package/dist/commands/hook.d.ts +20 -0
  27. package/dist/commands/hook.js +97 -0
  28. package/dist/commands/index.d.ts +33 -0
  29. package/dist/commands/index.js +20 -0
  30. package/dist/commands/init.d.ts +35 -0
  31. package/dist/commands/init.js +393 -0
  32. package/dist/commands/login.d.ts +23 -0
  33. package/dist/commands/login.js +144 -0
  34. package/dist/commands/logout.d.ts +18 -0
  35. package/dist/commands/logout.js +55 -0
  36. package/dist/commands/org.d.ts +27 -0
  37. package/dist/commands/org.js +126 -0
  38. package/dist/commands/run.d.ts +21 -0
  39. package/dist/commands/run.js +562 -0
  40. package/dist/commands/secrets-list.d.ts +16 -0
  41. package/dist/commands/secrets-list.js +85 -0
  42. package/dist/commands/status.d.ts +30 -0
  43. package/dist/commands/status.js +210 -0
  44. package/dist/commands/test.d.ts +61 -0
  45. package/dist/commands/test.js +220 -0
  46. package/dist/commands/types.d.ts +18 -0
  47. package/dist/commands/types.js +74 -0
  48. package/dist/commands/watch.d.ts +14 -0
  49. package/dist/commands/watch.js +97 -0
  50. package/dist/commands/workflows.d.ts +28 -0
  51. package/dist/commands/workflows.js +154 -0
  52. package/dist/comment-created.json +30 -0
  53. package/dist/create-branch.json +17 -0
  54. package/dist/delete-branch.json +15 -0
  55. package/dist/dispatch.json +15 -0
  56. package/dist/errors/capability-gap.d.ts +50 -0
  57. package/dist/errors/capability-gap.js +54 -0
  58. package/dist/errors/codes.d.ts +25 -0
  59. package/dist/errors/formatter.d.ts +32 -0
  60. package/dist/errors/formatter.js +35 -0
  61. package/dist/errors/index.d.ts +5 -0
  62. package/dist/errors/index.js +4 -0
  63. package/dist/execution/executor.d.ts +61 -0
  64. package/dist/execution/executor.js +230 -0
  65. package/dist/execution/index.d.ts +4 -0
  66. package/dist/execution/index.js +3 -0
  67. package/dist/execution/sdk-alias.d.ts +33 -0
  68. package/dist/execution/sdk-alias.js +102 -0
  69. package/dist/fixtures/compiler.d.ts +47 -0
  70. package/dist/fixtures/compiler.js +177 -0
  71. package/dist/fixtures/defaults/comment-created.json +30 -0
  72. package/dist/fixtures/defaults/create-branch.json +17 -0
  73. package/dist/fixtures/defaults/delete-branch.json +15 -0
  74. package/dist/fixtures/defaults/dispatch.json +15 -0
  75. package/dist/fixtures/defaults/fork.json +19 -0
  76. package/dist/fixtures/defaults/index.d.ts +16 -0
  77. package/dist/fixtures/defaults/index.js +105 -0
  78. package/dist/fixtures/defaults/index.ts +145 -0
  79. package/dist/fixtures/defaults/pr-closed.json +28 -0
  80. package/dist/fixtures/defaults/pr-opened.json +27 -0
  81. package/dist/fixtures/defaults/pr-reopened.json +27 -0
  82. package/dist/fixtures/defaults/pr-synchronize.json +27 -0
  83. package/dist/fixtures/defaults/push.json +16 -0
  84. package/dist/fixtures/defaults/release-published.json +23 -0
  85. package/dist/fixtures/defaults/review-comment-created.json +36 -0
  86. package/dist/fixtures/defaults/review-submitted.json +35 -0
  87. package/dist/fixtures/defaults/star-created.json +15 -0
  88. package/dist/fixtures/defaults/status.json +26 -0
  89. package/dist/fixtures/defaults/tag-push.json +20 -0
  90. package/dist/fixtures/defaults/watch-started.json +15 -0
  91. package/dist/fixtures/defaults/workflow-run-completed.json +23 -0
  92. package/dist/fork.json +19 -0
  93. package/dist/format.d.ts +5 -0
  94. package/dist/format.js +20 -0
  95. package/dist/generators/secrets-dts.d.ts +29 -0
  96. package/dist/generators/secrets-dts.js +67 -0
  97. package/dist/hooks/detector.d.ts +39 -0
  98. package/dist/hooks/detector.js +133 -0
  99. package/dist/hooks/index.d.ts +6 -0
  100. package/dist/hooks/index.js +5 -0
  101. package/dist/hooks/installer.d.ts +24 -0
  102. package/dist/hooks/installer.js +328 -0
  103. package/dist/hooks/templates.d.ts +26 -0
  104. package/dist/hooks/templates.js +63 -0
  105. package/dist/index.d.ts +12 -0
  106. package/dist/index.js +12 -0
  107. package/dist/llm-context/llms-full.txt +9974 -0
  108. package/dist/llm-context/llms.txt +61 -0
  109. package/dist/local-executor/dag-scheduler.d.ts +44 -0
  110. package/dist/local-executor/dag-scheduler.js +183 -0
  111. package/dist/local-executor/index.d.ts +23 -0
  112. package/dist/local-executor/index.js +309 -0
  113. package/dist/local-executor/job-runner.d.ts +40 -0
  114. package/dist/local-executor/job-runner.js +307 -0
  115. package/dist/local-executor/output-streamer.d.ts +31 -0
  116. package/dist/local-executor/output-streamer.js +166 -0
  117. package/dist/local-executor/payload-generator.d.ts +16 -0
  118. package/dist/local-executor/payload-generator.js +138 -0
  119. package/dist/local-executor/picker.d.ts +33 -0
  120. package/dist/local-executor/picker.js +109 -0
  121. package/dist/local-executor/secret-loader.d.ts +18 -0
  122. package/dist/local-executor/secret-loader.js +105 -0
  123. package/dist/local-executor/types.d.ts +80 -0
  124. package/dist/local-executor/types.js +2 -0
  125. package/dist/local-executor/workflow-lock.d.ts +81 -0
  126. package/dist/local-executor/workflow-lock.js +0 -0
  127. package/dist/lockfile/generator.d.ts +39 -0
  128. package/dist/lockfile/generator.js +688 -0
  129. package/dist/lockfile/hash-files.d.ts +14 -0
  130. package/dist/lockfile/hash-files.js +50 -0
  131. package/dist/lockfile/hasher.d.ts +36 -0
  132. package/dist/lockfile/hasher.js +46 -0
  133. package/dist/lockfile/index.d.ts +3 -0
  134. package/dist/lockfile/index.js +4 -0
  135. package/dist/lockfile/purity-analyzer.d.ts +25 -0
  136. package/dist/lockfile/purity-analyzer.js +204 -0
  137. package/dist/package-F7UXSDHW.json +74 -0
  138. package/dist/postinstall.d.ts +9 -0
  139. package/dist/postinstall.js +60 -0
  140. package/dist/pr-closed.json +28 -0
  141. package/dist/pr-opened.json +27 -0
  142. package/dist/pr-reopened.json +27 -0
  143. package/dist/pr-synchronize.json +27 -0
  144. package/dist/push.json +16 -0
  145. package/dist/release-published.json +23 -0
  146. package/dist/remote/client.d.ts +204 -0
  147. package/dist/remote/client.js +203 -0
  148. package/dist/remote/config.d.ts +54 -0
  149. package/dist/remote/config.js +92 -0
  150. package/dist/remote/encryption.d.ts +35 -0
  151. package/dist/remote/encryption.js +87 -0
  152. package/dist/remote/history.d.ts +91 -0
  153. package/dist/remote/history.js +146 -0
  154. package/dist/remote/oauth.d.ts +65 -0
  155. package/dist/remote/oauth.js +302 -0
  156. package/dist/remote/observer.d.ts +81 -0
  157. package/dist/remote/observer.js +174 -0
  158. package/dist/remote/output/json.d.ts +13 -0
  159. package/dist/remote/output/json.js +36 -0
  160. package/dist/remote/output/junit.d.ts +18 -0
  161. package/dist/remote/output/junit.js +50 -0
  162. package/dist/remote/output/streaming.d.ts +57 -0
  163. package/dist/remote/output/streaming.js +124 -0
  164. package/dist/remote/output/summary.d.ts +39 -0
  165. package/dist/remote/output/summary.js +99 -0
  166. package/dist/remote/uploader.d.ts +87 -0
  167. package/dist/remote/uploader.js +197 -0
  168. package/dist/review-comment-created.json +36 -0
  169. package/dist/review-submitted.json +35 -0
  170. package/dist/star-created.json +15 -0
  171. package/dist/status.json +26 -0
  172. package/dist/tag-push.json +20 -0
  173. package/dist/templates/agents-md.d.ts +8 -0
  174. package/dist/templates/agents-md.js +130 -0
  175. package/dist/templates/index.d.ts +43 -0
  176. package/dist/templates/index.js +48 -0
  177. package/dist/templates/package-json.d.ts +18 -0
  178. package/dist/templates/package-json.js +34 -0
  179. package/dist/templates/tsconfig-json.d.ts +9 -0
  180. package/dist/templates/tsconfig-json.js +26 -0
  181. package/dist/templates/workflows/hello-world.d.ts +2 -0
  182. package/dist/templates/workflows/hello-world.js +16 -0
  183. package/dist/templates/workflows/hello-world.ts +21 -0
  184. package/dist/templates/workflows/pr-checks.d.ts +2 -0
  185. package/dist/templates/workflows/pr-checks.js +40 -0
  186. package/dist/templates/workflows/pr-checks.ts +57 -0
  187. package/dist/test-runner/dry-run.d.ts +12 -0
  188. package/dist/test-runner/dry-run.js +66 -0
  189. package/dist/test-runner/event-types.d.ts +86 -0
  190. package/dist/test-runner/event-types.js +330 -0
  191. package/dist/test-runner/git-detector.d.ts +15 -0
  192. package/dist/test-runner/git-detector.js +30 -0
  193. package/dist/test-runner/index.d.ts +13 -0
  194. package/dist/test-runner/index.js +11 -0
  195. package/dist/test-runner/job-executor.d.ts +48 -0
  196. package/dist/test-runner/job-executor.js +245 -0
  197. package/dist/test-runner/output-formatter.d.ts +47 -0
  198. package/dist/test-runner/output-formatter.js +92 -0
  199. package/dist/test-runner/payload-builder.d.ts +20 -0
  200. package/dist/test-runner/payload-builder.js +216 -0
  201. package/dist/test-runner/rule-evaluator.d.ts +14 -0
  202. package/dist/test-runner/rule-evaluator.js +31 -0
  203. package/dist/test-runner/secrets-file.d.ts +24 -0
  204. package/dist/test-runner/secrets-file.js +62 -0
  205. package/dist/test-runner/step-context.d.ts +15 -0
  206. package/dist/test-runner/step-context.js +140 -0
  207. package/dist/test-runner/summary.d.ts +7 -0
  208. package/dist/types.d.ts +525 -0
  209. package/dist/types.js +33 -0
  210. package/dist/validation/index.d.ts +3 -0
  211. package/dist/validation/index.js +3 -0
  212. package/dist/validation/validator.d.ts +26 -0
  213. package/dist/validation/validator.js +164 -0
  214. package/dist/watch-started.json +15 -0
  215. package/dist/workflow-run-completed.json +23 -0
  216. package/dist/workflows/hello-world.ts +21 -0
  217. package/dist/workflows/pr-checks.ts +57 -0
  218. package/hack/postinstall.mjs +105 -0
  219. package/package.json +60 -6
  220. package/sbom.spdx.json +10997 -0
  221. package/index.js +0 -3
@@ -0,0 +1,61 @@
1
+ # KiCI
2
+
3
+ > KiCI is a TypeScript-native CI/CD workflow engine. Workflows are defined in TypeScript (not YAML), compiled into a portable lock file, and executed by self-hosted agents. The docs below cover the SDK, the CLI, workflow patterns, and the runtime architecture an LLM coding agent needs in order to author and test KiCI workflows.
4
+
5
+ The full markdown bundle of every page indexed here is available at https://kici.dev/llms-full.txt.
6
+
7
+ ## Getting started
8
+
9
+ - [User guide](https://kici.dev/docs/user/): Writing and testing CI/CD workflows in TypeScript
10
+ - [Getting started with KiCI](https://kici.dev/docs/user/getting-started/): Install the SDK, write your first workflow, compile and test locally
11
+
12
+ ## Workflow patterns
13
+
14
+ - [Basic workflow patterns](https://kici.dev/docs/user/patterns/basic/): Basic CI, PR-only / push-only filters, multiple triggers, manual-only workflows
15
+ - [Conditionals & matrix patterns](https://kici.dev/docs/user/patterns/conditionals-matrix/): Conditional execution with rules, matrix builds (static + dynamic), dynamic job generation
16
+ - [Integration patterns](https://kici.dev/docs/user/patterns/integrations/): Workflow chaining, generic webhooks, Stripe, self-hosted git forges, plain GitHub repos
17
+ - [Pattern reference](https://kici.dev/docs/user/patterns/reference/): Step context, examples repository, GitHub check run output — cross-cutting reference for all patterns
18
+ - [Scheduling & event patterns](https://kici.dev/docs/user/patterns/scheduling-and-events/): Nightly cron, workflow-complete-triggered deploys, custom event chaining
19
+
20
+ ## SDK reference
21
+
22
+ - [SDK reference: core](https://kici.dev/docs/user/sdk/core/): Factory functions (workflow, job, step) and authoring patterns: needs, output chaining, dynamic groups
23
+ - [SDK reference: idempotent](https://kici.dev/docs/user/sdk/idempotent/): Idempotent helpers for declarative check / apply patterns inside workflow steps
24
+ - [SDK reference: rules, matrix, dynamic jobs](https://kici.dev/docs/user/sdk/rules-matrix-dynamic/): rule(), skip(), matrix builds (static + dynamic), and dynamicJob / dynamicGroup
25
+ - [SDK reference: runtime](https://kici.dev/docs/user/sdk/runtime/): Types index, StepContext, secrets, fixtures
26
+ - [SDK reference: triggers](https://kici.dev/docs/user/sdk/triggers/): GitHub event triggers, kiciEvent, workflowComplete, jobComplete, genericWebhook, schedule, lifecycle
27
+ - [SDK reference: validation & events](https://kici.dev/docs/user/sdk/validation-events/): validateDag(), event definitions, and event emission
28
+ - [SDK reference: waitFor](https://kici.dev/docs/user/sdk/wait-for/): Polling helpers for steps that pause until an external condition becomes true
29
+ - [SDK reference](https://kici.dev/docs/user/sdk-reference/): Complete API reference for @kici-dev/sdk -- workflows, jobs, steps, triggers, rules, matrix, validation, runtime
30
+
31
+ ## CLI and authoring
32
+
33
+ - [CLI authentication](https://kici.dev/docs/user/cli-auth/): Authenticate the KiCI CLI with browser OAuth, device flow, or API key paste
34
+ - [CLI reference](https://kici.dev/docs/user/cli-reference/): All CLI commands: compile, run (local/remote), test, login, logout, org, status, cancel, secrets, types, fixture, init, hook, endpoints, workflows, docs, admin
35
+ - [Lifecycle hooks](https://kici.dev/docs/user/hooks/): SDK hook API for cancel, cleanup, success, failure, and step-level callbacks
36
+ - [Lock file and workflow drift](https://kici.dev/docs/user/lock-file-and-drift/): Keep the lock file in sync with your workflow source and avoid drift
37
+ - [Testing guide](https://kici.dev/docs/user/testing-guide/): Running remote test fixtures with kici run remote
38
+ - [Workflow patterns](https://kici.dev/docs/user/workflow-patterns/): Common patterns: monorepo builds, conditional jobs, dynamic matrices, generic webhooks, scheduling
39
+
40
+ ## Workflow features
41
+
42
+ - [Concurrency groups](https://kici.dev/docs/user/concurrency/): Control parallel execution with auto-cancel and queue modes
43
+ - [Dashboard](https://kici.dev/docs/user/dashboard/)
44
+ - [Dynamic values](https://kici.dev/docs/user/dynamic-values/)
45
+ - [Environment variables](https://kici.dev/docs/user/env-vars/): KICI_* environment variable reference for the CLI
46
+ - [Environments](https://kici.dev/docs/user/environments/): Configure deployment environments with variables, secrets, and protection rules
47
+ - [Event system](https://kici.dev/docs/user/events/): How KiCI's event model works -- event types, the registration model, event matching, and circuit breaker protection
48
+ - [Global workflows](https://kici.dev/docs/user/global-workflows/): Cross-repo workflows that run on events from any repo in the same org
49
+ - [Private npm registries](https://kici.dev/docs/user/private-registries/): Authenticate `npm install` against private registries (CodeArtifact, GitHub Packages, Verdaccio, …) from a workflow's `.kici/package.json`
50
+ - [Secrets](https://kici.dev/docs/user/secrets/): How to access secrets in KiCI workflow steps
51
+
52
+ ## Providers
53
+
54
+ - [GitHub App provider](https://kici.dev/docs/user/providers/github/): Connect KiCI to GitHub via a GitHub App — full Checks API, installation-token clones, and cross-org dispatch
55
+ - [Universal-git provider](https://kici.dev/docs/user/providers/universal-git/): Connect Forgejo, Gitea, Gogs, GitLab, or plain GitHub via webhook + PAT or SSH deploy key
56
+
57
+ ## Architecture overview
58
+
59
+ - [Data flows](https://kici.dev/docs/architecture/data-flows/): End-to-end data flows through the KiCI three-tier architecture
60
+ - [Design decisions](https://kici.dev/docs/architecture/design-decisions/): Why KiCI's architecture is shaped the way it is
61
+ - [Architecture overview](https://kici.dev/docs/architecture/overview/): Three-tier relay model, package structure, and component responsibilities
@@ -0,0 +1,44 @@
1
+ /**
2
+ * DAG-based parallel job scheduler with concurrency control and fail-fast.
3
+ *
4
+ * Generic scheduler that takes nodes with dependency relationships and
5
+ * executes them via callbacks, respecting concurrency limits and
6
+ * providing abort signals for fail-fast cancellation.
7
+ */
8
+ export interface DagNode {
9
+ name: string;
10
+ needs: string[];
11
+ }
12
+ interface DagExecutionCallbacks<T> {
13
+ execute: (name: string, signal: AbortSignal) => Promise<T>;
14
+ isSuccess: (result: T) => boolean;
15
+ }
16
+ interface DagOptions {
17
+ maxConcurrency: number;
18
+ failFast: boolean;
19
+ }
20
+ interface DagResult<T> {
21
+ results: Map<string, T>;
22
+ skipped: string[];
23
+ cancelled: string[];
24
+ status: 'success' | 'failure';
25
+ }
26
+ /**
27
+ * Execute a DAG of nodes with bounded concurrency and fail-fast support.
28
+ *
29
+ * Algorithm:
30
+ * 1. Detect cycles
31
+ * 2. Initialize ready queue with zero-dependency nodes
32
+ * 3. Event loop: launch ready nodes up to concurrency limit,
33
+ * await first completion, update state, repeat
34
+ * 4. On failure: if failFast, abort all running and skip pending;
35
+ * if keep-going, only skip transitive dependents
36
+ */
37
+ export declare function executeDag<T>(nodes: DagNode[], callbacks: DagExecutionCallbacks<T>, options: DagOptions): Promise<DagResult<T>>;
38
+ /**
39
+ * Given a list of DAG nodes and a target job name, return the target
40
+ * plus all transitive dependencies in topological order.
41
+ */
42
+ export declare function resolveJobFilter(nodes: DagNode[], targetName: string): DagNode[];
43
+ export {};
44
+ //# sourceMappingURL=dag-scheduler.d.ts.map
@@ -0,0 +1,183 @@
1
+ import "../chunk-gOLHoazu.js";
2
+ //#region src/local-executor/dag-scheduler.ts
3
+ /**
4
+ * Detect cycles using Kahn's algorithm.
5
+ * Returns true if a cycle exists.
6
+ */
7
+ function detectCycle(nodes) {
8
+ const inDegree = /* @__PURE__ */ new Map();
9
+ for (const n of nodes) {
10
+ if (!inDegree.has(n.name)) inDegree.set(n.name, 0);
11
+ for (const dep of n.needs) {
12
+ inDegree.set(n.name, (inDegree.get(n.name) ?? 0) + 1);
13
+ if (!inDegree.has(dep)) inDegree.set(dep, 0);
14
+ }
15
+ }
16
+ const queue = [];
17
+ for (const [name, deg] of inDegree) if (deg === 0) queue.push(name);
18
+ let processed = 0;
19
+ while (queue.length > 0) {
20
+ const name = queue.shift();
21
+ processed++;
22
+ for (const n of nodes) if (n.needs.includes(name)) {
23
+ const newDeg = inDegree.get(n.name) - 1;
24
+ inDegree.set(n.name, newDeg);
25
+ if (newDeg === 0) queue.push(n.name);
26
+ }
27
+ }
28
+ return processed < nodes.length;
29
+ }
30
+ /**
31
+ * Execute a DAG of nodes with bounded concurrency and fail-fast support.
32
+ *
33
+ * Algorithm:
34
+ * 1. Detect cycles
35
+ * 2. Initialize ready queue with zero-dependency nodes
36
+ * 3. Event loop: launch ready nodes up to concurrency limit,
37
+ * await first completion, update state, repeat
38
+ * 4. On failure: if failFast, abort all running and skip pending;
39
+ * if keep-going, only skip transitive dependents
40
+ */
41
+ async function executeDag(nodes, callbacks, options) {
42
+ if (nodes.length === 0) return {
43
+ results: /* @__PURE__ */ new Map(),
44
+ skipped: [],
45
+ cancelled: [],
46
+ status: "success"
47
+ };
48
+ if (detectCycle(nodes)) throw new Error("Circular dependency detected in job graph");
49
+ const nodeMap = new Map(nodes.map((n) => [n.name, n]));
50
+ const results = /* @__PURE__ */ new Map();
51
+ const skipped = [];
52
+ const cancelled = [];
53
+ const failedNames = /* @__PURE__ */ new Set();
54
+ const pending = new Set(nodes.map((n) => n.name));
55
+ const running = /* @__PURE__ */ new Map();
56
+ let hasFailure = false;
57
+ let abortAll = false;
58
+ /**
59
+ * Check if a node is ready to execute:
60
+ * all dependencies completed successfully.
61
+ */
62
+ function isReady(name) {
63
+ return nodeMap.get(name).needs.every((dep) => results.has(dep) && callbacks.isSuccess(results.get(dep)));
64
+ }
65
+ /**
66
+ * Check if a node should be skipped because a dependency failed.
67
+ */
68
+ function shouldSkip(name) {
69
+ return nodeMap.get(name).needs.some((dep) => failedNames.has(dep) || skipped.includes(dep) || cancelled.includes(dep));
70
+ }
71
+ while (pending.size > 0 || running.size > 0) {
72
+ if (abortAll) {
73
+ for (const name of pending) skipped.push(name);
74
+ pending.clear();
75
+ if (running.size > 0) {
76
+ const entries = [...running.entries()];
77
+ const settled = await Promise.allSettled(entries.map(([, v]) => v.promise));
78
+ for (let i = 0; i < entries.length; i++) {
79
+ const [name] = entries[i];
80
+ const s = settled[i];
81
+ if (s.status === "fulfilled") {
82
+ results.set(name, s.value);
83
+ if (!callbacks.isSuccess(s.value)) cancelled.push(name);
84
+ } else cancelled.push(name);
85
+ running.delete(name);
86
+ }
87
+ }
88
+ break;
89
+ }
90
+ let skipChanged = true;
91
+ while (skipChanged) {
92
+ skipChanged = false;
93
+ const toSkip = [];
94
+ for (const name of pending) if (shouldSkip(name)) toSkip.push(name);
95
+ for (const name of toSkip) {
96
+ pending.delete(name);
97
+ skipped.push(name);
98
+ skipChanged = true;
99
+ }
100
+ }
101
+ const readyNodes = [];
102
+ for (const name of pending) {
103
+ if (running.size + readyNodes.length >= options.maxConcurrency) break;
104
+ if (isReady(name)) readyNodes.push(name);
105
+ }
106
+ for (const name of readyNodes) {
107
+ pending.delete(name);
108
+ const controller = new AbortController();
109
+ const promise = callbacks.execute(name, controller.signal);
110
+ running.set(name, {
111
+ promise,
112
+ controller
113
+ });
114
+ }
115
+ if (running.size === 0) break;
116
+ const wrappedPromises = [...running.entries()].map(async ([name, { promise }]) => {
117
+ return {
118
+ name,
119
+ result: await promise
120
+ };
121
+ });
122
+ for (const p of wrappedPromises) p.catch(() => {});
123
+ const { name: completedName, result: completedResult } = await Promise.race(wrappedPromises);
124
+ running.delete(completedName);
125
+ results.set(completedName, completedResult);
126
+ if (!callbacks.isSuccess(completedResult)) {
127
+ hasFailure = true;
128
+ failedNames.add(completedName);
129
+ if (options.failFast) {
130
+ for (const [, { controller }] of running) controller.abort();
131
+ abortAll = true;
132
+ }
133
+ }
134
+ }
135
+ return {
136
+ results,
137
+ skipped,
138
+ cancelled,
139
+ status: hasFailure || skipped.length > 0 || cancelled.length > 0 ? "failure" : "success"
140
+ };
141
+ }
142
+ /**
143
+ * Given a list of DAG nodes and a target job name, return the target
144
+ * plus all transitive dependencies in topological order.
145
+ */
146
+ function resolveJobFilter(nodes, targetName) {
147
+ const nodeMap = new Map(nodes.map((n) => [n.name, n]));
148
+ if (!nodeMap.has(targetName)) return [];
149
+ const needed = /* @__PURE__ */ new Set();
150
+ const queue = [targetName];
151
+ while (queue.length > 0) {
152
+ const name = queue.shift();
153
+ if (needed.has(name)) continue;
154
+ needed.add(name);
155
+ const node = nodeMap.get(name);
156
+ if (node) {
157
+ for (const dep of node.needs) if (!needed.has(dep)) queue.push(dep);
158
+ }
159
+ }
160
+ const subset = nodes.filter((n) => needed.has(n.name));
161
+ const inDegree = /* @__PURE__ */ new Map();
162
+ for (const n of subset) {
163
+ if (!inDegree.has(n.name)) inDegree.set(n.name, 0);
164
+ for (const dep of n.needs) if (needed.has(dep)) inDegree.set(n.name, (inDegree.get(n.name) ?? 0) + 1);
165
+ }
166
+ const result = [];
167
+ const topoQueue = [];
168
+ for (const [name, deg] of inDegree) if (deg === 0) topoQueue.push(name);
169
+ while (topoQueue.length > 0) {
170
+ const name = topoQueue.shift();
171
+ result.push(nodeMap.get(name));
172
+ for (const n of subset) if (n.needs.includes(name) && needed.has(n.name)) {
173
+ const newDeg = inDegree.get(n.name) - 1;
174
+ inDegree.set(n.name, newDeg);
175
+ if (newDeg === 0) topoQueue.push(n.name);
176
+ }
177
+ }
178
+ return result;
179
+ }
180
+ //#endregion
181
+ export { executeDag, resolveJobFilter };
182
+
183
+ //# sourceMappingURL=dag-scheduler.js.map
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Public API: executeLocal() orchestrating compile, match, resolve, schedule, report.
3
+ *
4
+ * Pipeline:
5
+ * 1. Compile workflows from .kici/ sources
6
+ * 2. Load workflows (discover runtime modules)
7
+ * 3. Load secrets from local files
8
+ * 4. Build event payload from git state + CLI overrides
9
+ * 5. Match triggers to find active workflows
10
+ * 6. Apply --workflow and --job filters
11
+ * 7. Resolve jobs (matrix expansion, dynamic evaluation)
12
+ * 8. Execute via DAG scheduler with concurrency control
13
+ * 9. Return success/failure
14
+ */
15
+ import type { RunLocalOptions } from './types.js';
16
+ /**
17
+ * Execute workflows locally -- the full compile-match-resolve-execute pipeline.
18
+ *
19
+ * @param options - CLI options from `kici run local`
20
+ * @returns true if all workflows succeeded, false otherwise
21
+ */
22
+ export declare function executeLocal(options: RunLocalOptions): Promise<boolean>;
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,309 @@
1
+ import "../chunk-gOLHoazu.js";
2
+ import { discoverWorkflows, resolveKiciDir } from "../execution/executor.js";
3
+ import "../execution/index.js";
4
+ import { transformTriggers } from "../lockfile/generator.js";
5
+ import { compileCommand } from "../commands/compile.js";
6
+ import { executeDag, resolveJobFilter } from "./dag-scheduler.js";
7
+ import { loadLocalSecrets } from "./secret-loader.js";
8
+ import { generateEventPayload } from "./payload-generator.js";
9
+ import { executeResolvedJob, resolveJobs } from "./job-runner.js";
10
+ import { displayLocalSummary, formatLocalJsonResult, formatLocalJunitResult } from "./output-streamer.js";
11
+ import { PickerCancelledError, runPicker } from "./picker.js";
12
+ import { ConcurrencyKeyEvaluationError, acquireWorkflowLock } from "./workflow-lock.js";
13
+ import pc from "picocolors";
14
+ import { writeFile } from "node:fs/promises";
15
+ import { logger } from "@kici-dev/shared";
16
+ import { matchAllWorkflows } from "@kici-dev/engine";
17
+ import os from "node:os";
18
+ //#region src/local-executor/index.ts
19
+ /**
20
+ * Public API: executeLocal() orchestrating compile, match, resolve, schedule, report.
21
+ *
22
+ * Pipeline:
23
+ * 1. Compile workflows from .kici/ sources
24
+ * 2. Load workflows (discover runtime modules)
25
+ * 3. Load secrets from local files
26
+ * 4. Build event payload from git state + CLI overrides
27
+ * 5. Match triggers to find active workflows
28
+ * 6. Apply --workflow and --job filters
29
+ * 7. Resolve jobs (matrix expansion, dynamic evaluation)
30
+ * 8. Execute via DAG scheduler with concurrency control
31
+ * 9. Return success/failure
32
+ */
33
+ /**
34
+ * Convert SDK workflows to lock-file-like structure for trigger matching.
35
+ * Simplified version of the one in test.ts -- only needs trigger info.
36
+ */
37
+ function workflowsToLockFormat(workflows) {
38
+ return workflows.map((w) => ({
39
+ name: w.name,
40
+ contentHash: "",
41
+ compileSchemaVersion: 0,
42
+ triggers: transformTriggers(w.on),
43
+ jobs: w.jobs.map((j, i) => {
44
+ if (typeof j === "function") return {
45
+ _type: "dynamic",
46
+ source: {
47
+ file: "",
48
+ index: i
49
+ }
50
+ };
51
+ return {
52
+ _type: "static",
53
+ name: j.name,
54
+ runsOn: j.runsOn,
55
+ needs: j.needs?.map((n) => typeof n === "string" ? n : n.name) ?? [],
56
+ steps: j.steps.map((s) => {
57
+ if (typeof s === "function") return {
58
+ name: "",
59
+ hasOutputs: false
60
+ };
61
+ return {
62
+ name: s.name,
63
+ hasOutputs: !!s.outputs
64
+ };
65
+ })
66
+ };
67
+ }),
68
+ rules: w.rules?.map((r, i) => ({
69
+ _type: "dynamic",
70
+ label: r.label,
71
+ source: {
72
+ file: "",
73
+ index: i
74
+ }
75
+ }))
76
+ }));
77
+ }
78
+ /**
79
+ * Derive `groupCtx.branch` for the workflow concurrency `group` callback.
80
+ * Mirrors the agent's semantics (`request.branch ?? request.ref`) so a
81
+ * `group({ branch })` function works identically locally and remotely.
82
+ */
83
+ function deriveBranchForGroupCtx(event) {
84
+ if (event.targetBranch) return event.targetBranch;
85
+ if (event.sourceBranch) return event.sourceBranch;
86
+ const ref = event.payload?.ref;
87
+ if (typeof ref === "string") {
88
+ const m = /^refs\/heads\/(.+)$/.exec(ref);
89
+ if (m) return m[1];
90
+ }
91
+ return "";
92
+ }
93
+ /**
94
+ * Execute one matched workflow under its concurrency lock (if any).
95
+ *
96
+ * Returns the result and a flag indicating whether it should count towards
97
+ * `allSucceeded`. Returns `null` when the workflow was filtered out (e.g. by
98
+ * `--job` removing every node).
99
+ */
100
+ async function runOneMatchedWorkflow(workflow, ctx) {
101
+ const { options, isQuiet, event, secrets, kiciDir, concurrency, failFast } = ctx;
102
+ if (!isQuiet) logger.info(`\n${pc.bold("Running workflow:")} ${workflow.name}\n`);
103
+ let lockHandle = null;
104
+ try {
105
+ lockHandle = await acquireWorkflowLock({
106
+ workflowName: workflow.name,
107
+ workflow,
108
+ event: event.payload,
109
+ branch: deriveBranchForGroupCtx(event),
110
+ debug: options.debug
111
+ });
112
+ } catch (err) {
113
+ if (err instanceof ConcurrencyKeyEvaluationError) {
114
+ logger.error(pc.red(`Concurrency group evaluation failed for workflow "${workflow.name}": ${err.cause?.message ?? err.message}`));
115
+ return {
116
+ result: {
117
+ name: workflow.name,
118
+ status: "failure",
119
+ jobs: [],
120
+ durationMs: 0
121
+ },
122
+ succeeded: false
123
+ };
124
+ }
125
+ throw err;
126
+ }
127
+ try {
128
+ return await runWorkflowBody(workflow, ctx, options, secrets, kiciDir, concurrency, failFast);
129
+ } finally {
130
+ await lockHandle?.release();
131
+ }
132
+ }
133
+ /**
134
+ * The actual DAG-execution body for one workflow. Split out so
135
+ * {@link runOneMatchedWorkflow} can wrap it in a lock acquire/release.
136
+ */
137
+ async function runWorkflowBody(workflow, ctx, options, secrets, kiciDir, concurrency, failFast) {
138
+ const { event } = ctx;
139
+ const resolvedJobs = await resolveJobs(workflow, event);
140
+ let dagNodes = resolvedJobs.map((r) => ({
141
+ name: r.expandedName,
142
+ needs: r.resolvedNeeds
143
+ }));
144
+ if (options.job) {
145
+ dagNodes = resolveJobFilter(dagNodes, options.job);
146
+ if (dagNodes.length === 0) return null;
147
+ }
148
+ const resolvedJobMap = new Map(resolvedJobs.map((r) => [r.expandedName, r]));
149
+ const dagNodeNames = new Set(dagNodes.map((n) => n.name));
150
+ const filteredResolvedJobs = resolvedJobs.filter((r) => dagNodeNames.has(r.expandedName));
151
+ const jobOutputsMap = /* @__PURE__ */ new Map();
152
+ const startTime = Date.now();
153
+ const dagResult = await executeDag(dagNodes, {
154
+ execute: async (name, signal) => {
155
+ const resolved = resolvedJobMap.get(name);
156
+ if (!resolved) return {
157
+ name,
158
+ status: "failure",
159
+ durationMs: 0,
160
+ steps: [],
161
+ error: /* @__PURE__ */ new Error(`Job "${name}" not found in resolved jobs`)
162
+ };
163
+ return executeResolvedJob(resolved, {
164
+ workflowName: workflow.name,
165
+ event,
166
+ secrets,
167
+ kiciDir,
168
+ jobOutputsMap,
169
+ signal
170
+ });
171
+ },
172
+ isSuccess: (result) => result.status === "success" || result.status === "skipped"
173
+ }, {
174
+ maxConcurrency: concurrency,
175
+ failFast
176
+ });
177
+ const durationMs = Date.now() - startTime;
178
+ const jobResults = [];
179
+ for (const rj of filteredResolvedJobs) {
180
+ const result = dagResult.results.get(rj.expandedName);
181
+ if (result) jobResults.push({
182
+ ...result,
183
+ matrixValues: Object.keys(rj.matrixValues).length > 0 ? rj.matrixValues : void 0
184
+ });
185
+ else if (dagResult.skipped.includes(rj.expandedName)) jobResults.push({
186
+ name: rj.expandedName,
187
+ status: "skipped",
188
+ durationMs: 0,
189
+ steps: [],
190
+ matrixValues: Object.keys(rj.matrixValues).length > 0 ? rj.matrixValues : void 0
191
+ });
192
+ else if (dagResult.cancelled.includes(rj.expandedName)) jobResults.push({
193
+ name: rj.expandedName,
194
+ status: "cancelled",
195
+ durationMs: 0,
196
+ steps: [],
197
+ cancelled: true,
198
+ matrixValues: Object.keys(rj.matrixValues).length > 0 ? rj.matrixValues : void 0
199
+ });
200
+ }
201
+ return {
202
+ result: {
203
+ name: workflow.name,
204
+ status: dagResult.status === "success" ? "success" : "failure",
205
+ jobs: jobResults,
206
+ durationMs
207
+ },
208
+ succeeded: dagResult.status === "success"
209
+ };
210
+ }
211
+ /**
212
+ * Execute workflows locally -- the full compile-match-resolve-execute pipeline.
213
+ *
214
+ * @param options - CLI options from `kici run local`
215
+ * @returns true if all workflows succeeded, false otherwise
216
+ */
217
+ async function executeLocal(options) {
218
+ const kiciDir = resolveKiciDir(options.kiciDir);
219
+ if (!await compileCommand({
220
+ kiciDir,
221
+ check: false,
222
+ verbose: options.debug ?? false
223
+ })) {
224
+ process.exitCode = 2;
225
+ return false;
226
+ }
227
+ const { workflows: workflowsWithSource } = await discoverWorkflows(kiciDir);
228
+ const workflows = workflowsWithSource.map((w) => w.workflow);
229
+ if (workflows.length === 0) {
230
+ logger.info(pc.yellow("No workflows found"));
231
+ return true;
232
+ }
233
+ if (options.pick) try {
234
+ const picked = await runPicker(workflows, { filterEvent: options.event });
235
+ options = {
236
+ ...options,
237
+ event: picked.event,
238
+ workflow: picked.workflow
239
+ };
240
+ } catch (err) {
241
+ if (err instanceof PickerCancelledError) {
242
+ logger.info(pc.yellow(err.message));
243
+ return true;
244
+ }
245
+ throw err;
246
+ }
247
+ if (!options.event) {
248
+ logger.error(pc.red("Missing event argument. Pass an event (e.g. \"push\") or use --pick to select one."));
249
+ process.exitCode = 2;
250
+ return false;
251
+ }
252
+ const secrets = await loadLocalSecrets(kiciDir, options.env);
253
+ const event = await generateEventPayload(options.event, options);
254
+ const decisions = matchAllWorkflows(workflowsToLockFormat(workflows), event);
255
+ let matchedWorkflows = workflows.filter((_, i) => decisions[i]?.matched);
256
+ if (matchedWorkflows.length === 0) {
257
+ logger.info(pc.gray("No workflows matched the event"));
258
+ return true;
259
+ }
260
+ if (options.workflow) {
261
+ matchedWorkflows = matchedWorkflows.filter((w) => w.name === options.workflow);
262
+ if (matchedWorkflows.length === 0) {
263
+ logger.info(pc.yellow(`No workflow named "${options.workflow}" matched the event`));
264
+ return true;
265
+ }
266
+ }
267
+ const isQuiet = Boolean(options.quiet || options.json);
268
+ const concurrency = options.concurrency ?? getDefaultConcurrency();
269
+ const failFast = !options.keepGoing;
270
+ let allSucceeded = true;
271
+ const workflowResults = [];
272
+ const ctx = {
273
+ options,
274
+ isQuiet,
275
+ event,
276
+ secrets,
277
+ kiciDir,
278
+ concurrency,
279
+ failFast
280
+ };
281
+ for (const workflow of matchedWorkflows) {
282
+ const outcome = await runOneMatchedWorkflow(workflow, ctx);
283
+ if (outcome === null) continue;
284
+ workflowResults.push(outcome.result);
285
+ if (!outcome.succeeded) allSucceeded = false;
286
+ }
287
+ if (options.json) console.log(formatLocalJsonResult(workflowResults));
288
+ else if (options.junit) {
289
+ const junitXml = formatLocalJunitResult(workflowResults);
290
+ await writeFile(options.junit, junitXml);
291
+ if (!isQuiet) logger.info(pc.green(`JUnit XML written to ${options.junit}`));
292
+ }
293
+ if (!isQuiet) displayLocalSummary(workflowResults);
294
+ return allSucceeded;
295
+ }
296
+ /**
297
+ * Get default concurrency based on available CPU parallelism.
298
+ */
299
+ function getDefaultConcurrency() {
300
+ try {
301
+ return os.availableParallelism();
302
+ } catch {
303
+ return os.cpus().length || 4;
304
+ }
305
+ }
306
+ //#endregion
307
+ export { executeLocal };
308
+
309
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Job resolution (matrix/dynamic) and single-job execution with step context.
3
+ *
4
+ * Handles:
5
+ * - Static job resolution
6
+ * - Matrix expansion into multiple ResolvedJob instances
7
+ * - Dynamic job function evaluation
8
+ * - Single-job step-by-step execution with rules, abort, and output chaining
9
+ */
10
+ import type { Workflow, OutputsMap } from '@kici-dev/sdk';
11
+ import type { SimulatedEvent } from '@kici-dev/engine';
12
+ import type { ParsedSecrets } from '../test-runner/secrets-file.js';
13
+ import type { ResolvedJob, LocalJobResult } from './types.js';
14
+ /**
15
+ * Context passed to executeResolvedJob for single-job execution.
16
+ */
17
+ interface JobExecutionContext {
18
+ workflowName: string;
19
+ event: SimulatedEvent;
20
+ secrets: ParsedSecrets;
21
+ kiciDir: string;
22
+ jobOutputsMap: OutputsMap;
23
+ signal: AbortSignal;
24
+ }
25
+ /**
26
+ * Resolve all jobs in a workflow, expanding matrix jobs and evaluating dynamic jobs.
27
+ *
28
+ * - Static jobs: create ResolvedJob with name, needs, job reference
29
+ * - Matrix jobs: use expandMatrix() from SDK, create one ResolvedJob per combination
30
+ * - Dynamic job functions: evaluate the function, recursively resolve results
31
+ * - Fan-in: if a job needs a matrix-expanded job, it needs ALL instances
32
+ */
33
+ export declare function resolveJobs(workflow: Workflow, event: SimulatedEvent): Promise<ResolvedJob[]>;
34
+ /**
35
+ * Execute a single resolved job: evaluate rules, run steps sequentially,
36
+ * check abort signal between steps, collect outputs.
37
+ */
38
+ export declare function executeResolvedJob(resolvedJob: ResolvedJob, context: JobExecutionContext): Promise<LocalJobResult>;
39
+ export {};
40
+ //# sourceMappingURL=job-runner.d.ts.map