@kici-dev/compiler 0.1.27 → 0.2.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 (153) hide show
  1. package/dist/auth/headless-detect.d.ts +14 -5
  2. package/dist/auth/headless-detect.js +50 -9
  3. package/dist/cli.js +120 -11
  4. package/dist/commands/compile.js +28 -4
  5. package/dist/commands/diagnostics.js +14 -2
  6. package/dist/commands/doctor.d.ts +64 -0
  7. package/dist/commands/doctor.js +413 -0
  8. package/dist/commands/endpoints.js +6 -0
  9. package/dist/commands/fixture.js +13 -0
  10. package/dist/commands/hook.js +3 -2
  11. package/dist/commands/index.d.ts +8 -0
  12. package/dist/commands/index.js +5 -1
  13. package/dist/commands/init-host-os.d.ts +34 -0
  14. package/dist/commands/init-host-os.js +44 -0
  15. package/dist/commands/init.d.ts +4 -0
  16. package/dist/commands/init.js +159 -36
  17. package/dist/commands/local.d.ts +28 -2
  18. package/dist/commands/local.js +74 -5
  19. package/dist/commands/login.js +1 -1
  20. package/dist/commands/notifications.d.ts +61 -0
  21. package/dist/commands/notifications.js +274 -0
  22. package/dist/commands/preview.js +8 -1
  23. package/dist/commands/run-routed.js +10 -1
  24. package/dist/commands/run.js +41 -12
  25. package/dist/commands/runs/artifacts/download.d.ts +24 -0
  26. package/dist/commands/runs/artifacts/download.js +154 -0
  27. package/dist/commands/runs/artifacts/list.d.ts +5 -0
  28. package/dist/commands/runs/artifacts/list.js +47 -0
  29. package/dist/commands/runs/cancel.js +2 -2
  30. package/dist/commands/runs/list.js +8 -2
  31. package/dist/commands/verify-attestation.d.ts +3 -1
  32. package/dist/commands/verify-attestation.js +18 -7
  33. package/dist/errors/formatter.d.ts +14 -1
  34. package/dist/errors/formatter.js +13 -3
  35. package/dist/errors/index.d.ts +2 -1
  36. package/dist/errors/index.js +3 -2
  37. package/dist/errors/source-location.d.ts +20 -0
  38. package/dist/errors/source-location.js +45 -0
  39. package/dist/execution/executor.js +25 -5
  40. package/dist/fixtures/defaults/index.js +2 -1
  41. package/dist/llm-context/llms-architecture.txt +48 -19
  42. package/dist/llm-context/llms-cli.txt +2461 -1582
  43. package/dist/llm-context/llms-features.txt +178 -106
  44. package/dist/llm-context/llms-full.txt +6124 -4423
  45. package/dist/llm-context/llms-getting-started.txt +306 -42
  46. package/dist/llm-context/llms-patterns.txt +13 -7
  47. package/dist/llm-context/llms-sdk-runtime.txt +2013 -0
  48. package/dist/llm-context/llms-sdk.txt +591 -2152
  49. package/dist/llm-context/llms.txt +30 -16
  50. package/dist/local-plane/orchestrator-process.d.ts +12 -7
  51. package/dist/local-plane/orchestrator-process.js +28 -14
  52. package/dist/local-plane/os-mismatch-hint.d.ts +16 -0
  53. package/dist/local-plane/os-mismatch-hint.js +34 -0
  54. package/dist/local-plane/plane-liveness.d.ts +68 -0
  55. package/dist/local-plane/plane-liveness.js +145 -0
  56. package/dist/local-plane/plane-manager.d.ts +60 -18
  57. package/dist/local-plane/plane-manager.js +210 -70
  58. package/dist/local-plane/plane-trigger.d.ts +13 -4
  59. package/dist/local-plane/plane-trigger.js +27 -14
  60. package/dist/local-plane/platform-attach.js +1 -5
  61. package/dist/local-plane/port-holder.d.ts +78 -0
  62. package/dist/local-plane/port-holder.js +227 -0
  63. package/dist/local-plane/postgres.d.ts +15 -3
  64. package/dist/local-plane/postgres.js +34 -12
  65. package/dist/local-plane/run-follow.d.ts +20 -0
  66. package/dist/local-plane/run-follow.js +51 -4
  67. package/dist/local-plane/scaler-config.d.ts +28 -14
  68. package/dist/local-plane/scaler-config.js +59 -41
  69. package/dist/local-plane/source-provider.js +4 -10
  70. package/dist/lockfile/generator.d.ts +14 -1
  71. package/dist/lockfile/generator.js +99 -33
  72. package/dist/lockfile/index.d.ts +3 -1
  73. package/dist/lockfile/index.js +3 -2
  74. package/dist/lockfile/purity-diagnostics.d.ts +31 -0
  75. package/dist/lockfile/purity-diagnostics.js +52 -0
  76. package/dist/postinstall.js +2 -1
  77. package/dist/remote/artifact-extract.d.ts +8 -0
  78. package/dist/remote/artifact-extract.js +58 -0
  79. package/dist/remote/config.d.ts +21 -3
  80. package/dist/remote/config.js +24 -5
  81. package/dist/remote/dashboard-client.d.ts +39 -2
  82. package/dist/remote/dashboard-client.js +47 -4
  83. package/dist/remote/fs-case.d.ts +21 -0
  84. package/dist/remote/fs-case.js +45 -0
  85. package/dist/remote/history.d.ts +9 -3
  86. package/dist/remote/history.js +79 -10
  87. package/dist/remote/local-repo-identity.d.ts +1 -1
  88. package/dist/remote/local-repo-identity.js +2 -1
  89. package/dist/remote/notifications-client.d.ts +149 -0
  90. package/dist/remote/notifications-client.js +103 -0
  91. package/dist/remote/oauth.d.ts +13 -0
  92. package/dist/remote/oauth.js +31 -5
  93. package/dist/remote/output/summary.js +76 -14
  94. package/dist/remote/render.js +30 -9
  95. package/dist/remote/uploader.js +5 -4
  96. package/dist/templates/agents-md.d.ts +1 -1
  97. package/dist/templates/agents-md.js +6 -6
  98. package/dist/templates/index.d.ts +1 -1
  99. package/dist/templates/index.js +2 -2
  100. package/dist/templates/package-json.d.ts +15 -0
  101. package/dist/templates/package-json.js +26 -5
  102. package/dist/test-runner/dry-run.d.ts +2 -1
  103. package/dist/test-runner/dry-run.js +18 -1
  104. package/dist/test-runner/event-types.d.ts +2 -0
  105. package/dist/test-runner/event-types.js +3 -0
  106. package/dist/test-runner/job-executor.d.ts +1 -0
  107. package/dist/test-runner/job-executor.js +81 -43
  108. package/dist/test-runner/output-formatter.d.ts +5 -2
  109. package/dist/test-runner/output-formatter.js +6 -4
  110. package/dist/test-runner/payload-builder.d.ts +7 -1
  111. package/dist/test-runner/payload-builder.js +11 -2
  112. package/dist/test-runner/rule-evaluator.d.ts +7 -3
  113. package/dist/test-runner/rule-evaluator.js +13 -12
  114. package/dist/test-runner/step-context.d.ts +23 -5
  115. package/dist/test-runner/step-context.js +28 -118
  116. package/dist/types.d.ts +21 -2
  117. package/dist/types.js +4 -2
  118. package/dist/validation/index.d.ts +2 -0
  119. package/dist/validation/index.js +2 -1
  120. package/dist/validation/typecheck.d.ts +28 -0
  121. package/dist/validation/typecheck.js +110 -0
  122. package/dist/validation/validator.d.ts +9 -4
  123. package/dist/validation/validator.js +38 -44
  124. package/package.json +12 -15
  125. package/sbom.spdx.json +1322 -1964
  126. package/dist/assets/api-TJJVHrjC.json +0 -118
  127. package/dist/assets/descriptor-BTtjzN9L.json +0 -1382
  128. package/dist/assets/package-BpQF9kR8.json +0 -74
  129. package/dist/assets/package-Ceo2h27X.json +0 -89
  130. package/dist/assets/source_context-D0atuL28.json +0 -20
  131. package/dist/assets/type-BFqO8SCZ.json +0 -202
  132. package/dist/commands/cancel.d.ts +0 -22
  133. package/dist/commands/detect-package-manager.d.ts +0 -42
  134. package/dist/commands/held-run-resolve.d.ts +0 -50
  135. package/dist/commands/status.d.ts +0 -34
  136. package/dist/commands/test.d.ts +0 -88
  137. package/dist/errors/codes.d.ts +0 -25
  138. package/dist/local-executor/dag-scheduler.d.ts +0 -44
  139. package/dist/local-executor/index.d.ts +0 -23
  140. package/dist/local-executor/job-runner.d.ts +0 -47
  141. package/dist/local-executor/materializer.d.ts +0 -44
  142. package/dist/local-executor/output-streamer.d.ts +0 -31
  143. package/dist/local-executor/payload-generator.d.ts +0 -16
  144. package/dist/local-executor/picker.d.ts +0 -33
  145. package/dist/local-executor/runs-on-display.d.ts +0 -9
  146. package/dist/local-executor/secret-loader.d.ts +0 -18
  147. package/dist/local-executor/to-event-payload.d.ts +0 -16
  148. package/dist/local-executor/types.d.ts +0 -93
  149. package/dist/local-executor/workflow-lock.d.ts +0 -82
  150. package/dist/package-F7UXSDHW.json +0 -74
  151. package/dist/remote/client.d.ts +0 -210
  152. package/dist/remote/observer.d.ts +0 -81
  153. package/dist/test-runner/summary.d.ts +0 -7
@@ -1,7 +1,7 @@
1
1
  import "../rolldown-runtime-ClRpJifh.js";
2
2
  import { formatter } from "./output-formatter.js";
3
3
  import { createStepContext } from "./step-context.js";
4
- import { createRuleContext, evaluateRules as evaluateRulesWithFormatting } from "./rule-evaluator.js";
4
+ import { createRuleContext as createRuleContext$1, evaluateRules as evaluateRulesWithFormatting } from "./rule-evaluator.js";
5
5
  import { localRunsOnString } from "./runs-on-display.js";
6
6
  import { pathToFileURL } from "node:url";
7
7
  import path from "node:path";
@@ -73,11 +73,23 @@ async function executeJob(job, workflowName, event, repoRoot, testSecrets, sdkSe
73
73
  const stepResults = [];
74
74
  let ruleResults;
75
75
  if (job.rules && job.rules.length > 0) {
76
- const ruleContext = createRuleContext(event, event.changedFiles);
76
+ const ruleContext = createRuleContext$1({
77
+ event,
78
+ changedFiles: event.changedFiles,
79
+ changedFilesStatus: event.changedFilesStatus ?? "fetched"
80
+ });
77
81
  const ruleEval = await evaluateRulesWithFormatting(job.rules, ruleContext, job.name);
78
82
  ruleResults = ruleEval.results;
79
83
  if (!ruleEval.allPassed) {
80
84
  const durationMs = Date.now() - startTime;
85
+ if (ruleEval.evaluationError) return {
86
+ name: job.name,
87
+ status: "failure",
88
+ durationMs,
89
+ steps: [],
90
+ ruleResults,
91
+ error: /* @__PURE__ */ new Error(`rule '${ruleEval.evaluationError.label}' errored: ${ruleEval.evaluationError.message}`)
92
+ };
81
93
  return {
82
94
  name: job.name,
83
95
  status: "skipped",
@@ -93,51 +105,61 @@ async function executeJob(job, workflowName, event, repoRoot, testSecrets, sdkSe
93
105
  setters.setStepRefMap(refMap);
94
106
  setStepOutputsMap(outputsMap);
95
107
  setStepRefMap(refMap);
96
- const ctx = createStepContext({ name: workflowName }, {
108
+ const { ctx, dispose } = createStepContext({ name: workflowName }, {
97
109
  name: job.name,
98
110
  runsOn: localRunsOnString(job.runsOn)
99
111
  }, repoRoot, void 0, void 0, testSecrets, void 0, event.payload, event.provider);
100
- let stepCounter = 0;
101
- for (const stepOrFn of job.steps) {
102
- let normalizedStep;
103
- if (typeof stepOrFn === "function") {
104
- stepCounter++;
105
- const name = `step-${stepCounter}`;
106
- refMap.set(stepOrFn, name);
107
- normalizedStep = {
108
- _tag: "Step",
109
- name,
110
- run: stepOrFn
111
- };
112
- } else {
113
- normalizedStep = stepOrFn;
114
- if (!normalizedStep.name) stepCounter++;
115
- }
116
- const result = await executeStep(normalizedStep, ctx, job.name);
117
- if (result.status === "success" && result.outputs != null) outputsMap.set(normalizedStep.name, result.outputs);
118
- stepResults.push(result);
119
- if (result.status === "failure") {
120
- const durationMs = Date.now() - startTime;
121
- formatter.logJobFailure(job.name, durationMs, result.error);
122
- return {
123
- name: job.name,
124
- status: "failure",
125
- durationMs,
126
- steps: stepResults,
127
- ruleResults,
128
- error: result.error
129
- };
112
+ try {
113
+ let stepCounter = 0;
114
+ for (const stepOrFn of job.steps) {
115
+ let normalizedStep;
116
+ if (typeof stepOrFn === "function") {
117
+ stepCounter++;
118
+ const name = `step-${stepCounter}`;
119
+ refMap.set(stepOrFn, name);
120
+ normalizedStep = {
121
+ _tag: "Step",
122
+ name,
123
+ run: stepOrFn
124
+ };
125
+ } else {
126
+ normalizedStep = stepOrFn;
127
+ if (!normalizedStep.name) {
128
+ stepCounter++;
129
+ normalizedStep.name = `step-${stepCounter}`;
130
+ }
131
+ }
132
+ const result = await executeStep(normalizedStep, ctx, job.name);
133
+ if (result.status === "success" && result.outputs != null) outputsMap.set(normalizedStep.name, result.outputs);
134
+ stepResults.push(result);
135
+ if (result.status === "failure") {
136
+ const durationMs = Date.now() - startTime;
137
+ formatter.logJobFailure(job.name, durationMs, result.error);
138
+ return {
139
+ name: job.name,
140
+ status: "failure",
141
+ durationMs,
142
+ steps: stepResults,
143
+ ruleResults,
144
+ error: result.error
145
+ };
146
+ }
130
147
  }
148
+ const durationMs = Date.now() - startTime;
149
+ formatter.logJobComplete(job.name, durationMs);
150
+ return {
151
+ name: job.name,
152
+ status: "success",
153
+ durationMs,
154
+ steps: stepResults,
155
+ ruleResults
156
+ };
157
+ } finally {
158
+ await dispose().catch((err) => {
159
+ const message = err instanceof Error ? err.message : String(err);
160
+ logger.warn(`local job temp scope cleanup failed: ${message}`);
161
+ });
131
162
  }
132
- const durationMs = Date.now() - startTime;
133
- formatter.logJobComplete(job.name, durationMs);
134
- return {
135
- name: job.name,
136
- status: "success",
137
- durationMs,
138
- steps: stepResults,
139
- ruleResults
140
- };
141
163
  }
142
164
  /**
143
165
  * Execute a workflow with parallel job execution.
@@ -149,11 +171,27 @@ async function executeWorkflow(workflow, event, repoRoot, testSecrets, kiciDir)
149
171
  const sdkSetters = await resolveSdkSetters(kiciDir);
150
172
  let ruleResults;
151
173
  if (workflow.rules && workflow.rules.length > 0) {
152
- const ruleContext = createRuleContext(event, event.changedFiles);
174
+ const ruleContext = createRuleContext$1({
175
+ event,
176
+ changedFiles: event.changedFiles,
177
+ changedFilesStatus: event.changedFilesStatus ?? "fetched"
178
+ });
153
179
  const ruleEval = await evaluateRulesWithFormatting(workflow.rules, ruleContext, workflow.name);
154
180
  ruleResults = ruleEval.results;
155
181
  if (!ruleEval.allPassed) {
156
182
  const durationMs = Date.now() - startTime;
183
+ if (ruleEval.evaluationError) {
184
+ const error = /* @__PURE__ */ new Error(`rule '${ruleEval.evaluationError.label}' errored: ${ruleEval.evaluationError.message}`);
185
+ logger.info(pc.red(`\n✗ Workflow failed: ${error.message}\n`));
186
+ return {
187
+ name: workflow.name,
188
+ status: "failure",
189
+ durationMs,
190
+ jobs: [],
191
+ ruleResults,
192
+ error
193
+ };
194
+ }
157
195
  logger.info(pc.yellow(`\n⚠ Workflow skipped due to rule failure\n`));
158
196
  return {
159
197
  name: workflow.name,
@@ -1,3 +1,4 @@
1
+ import type { RuleResult } from '@kici-dev/sdk';
1
2
  type ColorFn = (text: string) => string;
2
3
  /**
3
4
  * Output formatter that prefixes messages with colored job labels.
@@ -38,9 +39,11 @@ declare class OutputFormatter {
38
39
  */
39
40
  logJobFailure(jobName: string, durationMs: number, error: Error): void;
40
41
  /**
41
- * Log rule evaluation.
42
+ * Log rule evaluation. A rule whose `check()` threw carries an `error`; it
43
+ * prints `✗ errored: <message>` so the author sees the crash reason instead of
44
+ * a bare `✗ failed`. A clean pass/false prints `✓ passed` / `✗ failed`.
42
45
  */
43
- logRuleResult(jobName: string, ruleLabel: string, passed: boolean): void;
46
+ logRuleResult(jobName: string, result: RuleResult): void;
44
47
  }
45
48
  export declare const formatter: OutputFormatter;
46
49
  export {};
@@ -77,12 +77,14 @@ var OutputFormatter = class {
77
77
  logger.error(color(`[${jobName}]`) + pc.red(` ✗ Failed: ${error.message}`) + pc.gray(` (${durationMs}ms)`));
78
78
  }
79
79
  /**
80
- * Log rule evaluation.
80
+ * Log rule evaluation. A rule whose `check()` threw carries an `error`; it
81
+ * prints `✗ errored: <message>` so the author sees the crash reason instead of
82
+ * a bare `✗ failed`. A clean pass/false prints `✓ passed` / `✗ failed`.
81
83
  */
82
- logRuleResult(jobName, ruleLabel, passed) {
84
+ logRuleResult(jobName, result) {
83
85
  const color = this.getJobColor(jobName);
84
- const status = passed ? pc.green("✓ passed") : pc.red("✗ failed");
85
- logger.info(color(`[${jobName}]`) + pc.gray(` rule "${ruleLabel}": `) + status);
86
+ const status = result.error ? pc.red(`✗ errored: ${result.error}`) : result.passed ? pc.green("✓ passed") : pc.red("✗ failed");
87
+ logger.info(color(`[${jobName}]`) + pc.gray(` rule "${result.label}": `) + status);
86
88
  }
87
89
  };
88
90
  const formatter = new OutputFormatter();
@@ -1,4 +1,4 @@
1
- import type { SimulatedEvent } from '@kici-dev/engine';
1
+ import type { SimulatedEvent, ChangedFilesStatus } from '@kici-dev/engine';
2
2
  export interface PayloadOptions {
3
3
  /** Path to custom fixture file */
4
4
  payload?: string;
@@ -12,6 +12,12 @@ export interface PayloadOptions {
12
12
  sha?: string;
13
13
  /** Simulate changed file paths for onChangedFiles trigger matching */
14
14
  files?: string[];
15
+ /**
16
+ * Simulate `changedFiles` availability for rule evaluation. Defaults to
17
+ * `'fetched'` (the author's file list is a genuine diff); set `'unavailable'`
18
+ * to test a rule's `ctx.changedFilesStatus` guard on a diff-less event.
19
+ */
20
+ changedFilesStatus?: ChangedFilesStatus;
15
21
  }
16
22
  /**
17
23
  * Build event payload from default fixture, custom file, and CLI overrides.
@@ -147,7 +147,8 @@ async function buildEventPayload(eventArg, options) {
147
147
  payload: mergedPayload,
148
148
  targetBranch,
149
149
  sourceBranch,
150
- changedFiles: options.files ?? []
150
+ changedFiles: options.files ?? [],
151
+ changedFilesStatus: options.changedFilesStatus ?? "fetched"
151
152
  };
152
153
  }
153
154
  /**
@@ -157,6 +158,7 @@ function isInternalEventType(eventType) {
157
158
  return [
158
159
  "kici_event",
159
160
  "workflow_complete",
161
+ "workflows_failed_batch",
160
162
  "job_complete",
161
163
  "generic_webhook",
162
164
  "schedule",
@@ -182,6 +184,12 @@ async function buildInternalEventPayload(eventType, options) {
182
184
  status: eventType.status
183
185
  };
184
186
  break;
187
+ case "workflows_failed_batch":
188
+ payload = {
189
+ total: 0,
190
+ runs: []
191
+ };
192
+ break;
185
193
  case "job_complete":
186
194
  payload = {
187
195
  workflowName: eventType.workflowName,
@@ -207,7 +215,8 @@ async function buildInternalEventPayload(eventType, options) {
207
215
  action: void 0,
208
216
  payload,
209
217
  targetBranch: "main",
210
- changedFiles: options.files ?? []
218
+ changedFiles: options.files ?? [],
219
+ changedFilesStatus: options.changedFilesStatus ?? "fetched"
211
220
  };
212
221
  }
213
222
  //#endregion
@@ -1,9 +1,13 @@
1
- import type { Rule, RuleContext, EventPayload } from '@kici-dev/sdk';
1
+ import type { Rule, RuleContext, CreateRuleContextInput } from '@kici-dev/sdk';
2
2
  import type { RuleEvaluationResult } from '@kici-dev/sdk';
3
3
  /**
4
- * Create RuleContext for rule evaluation.
4
+ * Create a RuleContext for `kici test` rule evaluation. Delegates to the shared
5
+ * SDK factory (single source of truth), defaulting `env` to the process
6
+ * environment — the test-runner exposes the developer's real env to rules — and
7
+ * `changedFilesStatus` to `'fetched'` so an author-supplied (or empty) test
8
+ * changeset is a genuine diff and `ctx.changedFiles` never throws.
5
9
  */
6
- export declare function createRuleContext(event: EventPayload, changedFiles?: string[], dispatchInputs?: Readonly<Record<string, string | number | boolean | null>>): RuleContext;
10
+ export declare function createRuleContext(input: CreateRuleContextInput): RuleContext;
7
11
  /**
8
12
  * Evaluate rules with formatting output.
9
13
  * Wraps the SDK's evaluateRules() with a callback that logs each rule result.
@@ -1,21 +1,22 @@
1
1
  import "../rolldown-runtime-ClRpJifh.js";
2
2
  import { formatter } from "./output-formatter.js";
3
- import { evaluateRules } from "@kici-dev/sdk";
3
+ import { createRuleContext as createRuleContext$1, evaluateRules } from "@kici-dev/sdk";
4
4
  import { initZx } from "@kici-dev/core";
5
- import { $ } from "zx";
6
5
  //#region src/test-runner/rule-evaluator.ts
7
6
  initZx();
8
7
  /**
9
- * Create RuleContext for rule evaluation.
8
+ * Create a RuleContext for `kici test` rule evaluation. Delegates to the shared
9
+ * SDK factory (single source of truth), defaulting `env` to the process
10
+ * environment — the test-runner exposes the developer's real env to rules — and
11
+ * `changedFilesStatus` to `'fetched'` so an author-supplied (or empty) test
12
+ * changeset is a genuine diff and `ctx.changedFiles` never throws.
10
13
  */
11
- function createRuleContext(event, changedFiles = [], dispatchInputs = {}) {
12
- return {
13
- event,
14
- changedFiles,
15
- env: { ...process.env },
16
- dispatchInputs,
17
- $
18
- };
14
+ function createRuleContext(input) {
15
+ return createRuleContext$1({
16
+ ...input,
17
+ env: input.env ?? { ...process.env },
18
+ changedFilesStatus: input.changedFilesStatus ?? "fetched"
19
+ });
19
20
  }
20
21
  /**
21
22
  * Evaluate rules with formatting output.
@@ -23,7 +24,7 @@ function createRuleContext(event, changedFiles = [], dispatchInputs = {}) {
23
24
  */
24
25
  async function evaluateRulesWithFormatting(rules, context, jobName) {
25
26
  return evaluateRules(rules, context, jobName, (result) => {
26
- formatter.logRuleResult(jobName, result.label, result.passed);
27
+ formatter.logRuleResult(jobName, result);
27
28
  });
28
29
  }
29
30
  //#endregion
@@ -1,15 +1,33 @@
1
1
  import type { StepContext, WorkflowInfo, JobInfo, MatrixValues } from '@kici-dev/sdk';
2
+ /** Handle returned by {@link createStepContext}. */
3
+ export interface LocalStepContext {
4
+ /** The built step context, threaded into every step of the job. */
5
+ ctx: StepContext;
6
+ /**
7
+ * Drain the job's temp scope (and secrets state). Call once at job end —
8
+ * success, failure, or throw — to reclaim every `ctx.mktemp`/`ctx.mktempFile`
9
+ * allocation the job made. Delegates to the shared builder's `dispose`.
10
+ */
11
+ dispose: () => Promise<void>;
12
+ }
2
13
  /**
3
14
  * Create a step context for local test execution.
4
15
  *
5
- * `repoRoot` pins `ctx.$` to the workflow's repository root so steps running
6
- * via local-dispatch behave the same as on the agent path (see
16
+ * `repoRoot` pins `ctx.$` to the workflow's repository root so steps running via
17
+ * local-dispatch behave the same as on the agent path (see
7
18
  * `packages/agent/src/execution/sandbox/workflow-runner.ts`). Without this,
8
- * `ctx.$` would inherit `process.cwd()` — i.e. wherever the user invoked
9
- * `kici` — which silently breaks any step that uses relative paths.
19
+ * `ctx.$` would inherit `process.cwd()` — i.e. wherever the user invoked `kici`
20
+ * — which silently breaks any step that uses relative paths.
21
+ *
22
+ * This is a thin adapter over the shared `@kici-dev/sdk/testing` builder — the
23
+ * single source of truth for constructing a test step context — supplying the
24
+ * local runner's formatter-backed logger. The shared builder owns the
25
+ * job-scoped temp allocator backing `ctx.mktemp`/`ctx.mktempFile`; the returned
26
+ * `dispose` surfaces its drain so the caller (`executeJob`) can reclaim the
27
+ * job's temp dirs at job end rather than only via the exit backstop.
10
28
  */
11
29
  export declare function createStepContext(workflowInfo: WorkflowInfo, jobInfo: JobInfo, repoRoot: string, inputs?: Record<string, unknown>, matrix?: MatrixValues, testSecrets?: {
12
30
  flat: Record<string, string>;
13
31
  contexts: Record<string, Record<string, string>>;
14
- }, context?: string, rawPayload?: Record<string, unknown>, provider?: string, dispatchInputs?: Readonly<Record<string, string | number | boolean | null>>, signal?: AbortSignal): StepContext;
32
+ }, context?: string, rawPayload?: Record<string, unknown>, provider?: string, dispatchInputs?: Readonly<Record<string, string | number | boolean | null>>, signal?: AbortSignal): LocalStepContext;
15
33
  //# sourceMappingURL=step-context.d.ts.map
@@ -1,15 +1,9 @@
1
1
  import "../rolldown-runtime-ClRpJifh.js";
2
2
  import { formatter } from "./output-formatter.js";
3
- import { join } from "node:path";
4
- import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
5
- import { createStepSecrets, resolveJobOutputs, resolveStepOutputs } from "@kici-dev/sdk";
6
- import { initZx } from "@kici-dev/core";
7
- import { tmpdir } from "node:os";
8
- import { $ } from "zx";
3
+ import { createTestStepContext } from "@kici-dev/sdk/testing";
9
4
  //#region src/test-runner/step-context.ts
10
- initZx();
11
5
  /**
12
- * Create a logger that prefixes output with job name.
6
+ * Create a logger that prefixes output with the job name.
13
7
  */
14
8
  function createTestLogger(jobName) {
15
9
  return {
@@ -36,121 +30,37 @@ function createTestLogger(jobName) {
36
30
  /**
37
31
  * Create a step context for local test execution.
38
32
  *
39
- * `repoRoot` pins `ctx.$` to the workflow's repository root so steps running
40
- * via local-dispatch behave the same as on the agent path (see
33
+ * `repoRoot` pins `ctx.$` to the workflow's repository root so steps running via
34
+ * local-dispatch behave the same as on the agent path (see
41
35
  * `packages/agent/src/execution/sandbox/workflow-runner.ts`). Without this,
42
- * `ctx.$` would inherit `process.cwd()` — i.e. wherever the user invoked
43
- * `kici` — which silently breaks any step that uses relative paths.
36
+ * `ctx.$` would inherit `process.cwd()` — i.e. wherever the user invoked `kici`
37
+ * — which silently breaks any step that uses relative paths.
38
+ *
39
+ * This is a thin adapter over the shared `@kici-dev/sdk/testing` builder — the
40
+ * single source of truth for constructing a test step context — supplying the
41
+ * local runner's formatter-backed logger. The shared builder owns the
42
+ * job-scoped temp allocator backing `ctx.mktemp`/`ctx.mktempFile`; the returned
43
+ * `dispose` surfaces its drain so the caller (`executeJob`) can reclaim the
44
+ * job's temp dirs at job end rather than only via the exit backstop.
44
45
  */
45
46
  function createStepContext(workflowInfo, jobInfo, repoRoot, inputs = {}, matrix, testSecrets, context, rawPayload, provider, dispatchInputs = {}, signal = new AbortController().signal) {
46
- const flat = testSecrets?.flat ?? {};
47
- const namespacedSecrets = testSecrets?.contexts ?? {};
48
- const mergedFlat = { ...flat };
49
- for (const contextSecrets of Object.values(namespacedSecrets)) Object.assign(mergedFlat, contextSecrets);
50
- const env = { ...process.env };
51
- const scoped$ = $({ cwd: repoRoot });
52
- let tmpdirPath = null;
53
- const exposedEnvVars = /* @__PURE__ */ new Set();
54
- let mountCounter = 0;
55
- let exitHandlerRegistered = false;
56
- const onProcessExit = () => {
57
- for (const envVar of exposedEnvVars) {
58
- delete env[envVar];
59
- delete process.env[envVar];
60
- }
61
- exposedEnvVars.clear();
62
- if (tmpdirPath !== null) {
63
- try {
64
- rmSync(tmpdirPath, {
65
- recursive: true,
66
- force: true
67
- });
68
- } catch {}
69
- tmpdirPath = null;
70
- }
71
- };
72
- const secretsHandle = createStepSecrets(mergedFlat, env, void 0, {
73
- host: {
74
- async writeMountedFile(args) {
75
- if (tmpdirPath === null) tmpdirPath = mkdtempSync(join(tmpdir(), "kici-secret-files-"));
76
- if (!exitHandlerRegistered) {
77
- process.once("exit", onProcessExit);
78
- exitHandlerRegistered = true;
79
- }
80
- mountCounter += 1;
81
- const filename = args.name ?? `secret-${mountCounter}`;
82
- const filePath = join(tmpdirPath, filename);
83
- writeFileSync(filePath, args.content);
84
- chmodSync(filePath, args.mode);
85
- return filePath;
86
- },
87
- trackExposedEnv(envVar) {
88
- exposedEnvVars.add(envVar);
89
- }
90
- },
91
- cleanup: async () => {
92
- if (exitHandlerRegistered) {
93
- process.off("exit", onProcessExit);
94
- exitHandlerRegistered = false;
95
- }
96
- onProcessExit();
97
- }
98
- });
99
- return {
100
- $: scoped$,
101
- log: createTestLogger(jobInfo.name),
102
- signal,
103
- env,
104
- setEnv: (key, value) => {
105
- env[key] = value;
106
- process.env[key] = value;
107
- },
108
- addPath: (dir) => {
109
- const updated = `${dir}:${env.PATH ?? process.env.PATH ?? ""}`;
110
- env.PATH = updated;
111
- process.env.PATH = updated;
112
- },
113
- inputs,
114
- dispatchInputs,
47
+ const { ctx, dispose } = createTestStepContext({
115
48
  workflow: workflowInfo,
116
49
  job: jobInfo,
117
- matrix,
118
- isTestRun: false,
119
- context,
120
- ...rawPayload && { rawPayload },
121
- ...provider && { provider },
122
- secrets: secretsHandle.secrets,
123
- emit: async () => {
124
- return { deliveryId: "local-test-noop" };
125
- },
126
- outputsOf: (ref) => {
127
- return resolveStepOutputs(ref);
128
- },
129
- jobOutputs: (ref) => {
130
- return resolveJobOutputs(ref);
131
- },
132
- setSecretOutput: () => {},
133
- kici: {
134
- infrastructure: { list: () => Promise.resolve({
135
- scalers: [],
136
- agents: []
137
- }) },
138
- inventory: {
139
- query: () => Promise.resolve([]),
140
- get: () => Promise.resolve(null)
141
- },
142
- oidc: { token: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.oidc.token() is not available in the local test runner")) },
143
- host: { requestReboot: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.host.requestReboot() is not available in the local test runner")) },
144
- bootstrap: {
145
- ensureInitRunner: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.bootstrap.ensureInitRunner() is not available in the local test runner")),
146
- preBootSend: () => Promise.reject(/* @__PURE__ */ new Error("ctx.kici.bootstrap.preBootSend() is not available in the local test runner"))
147
- }
148
- },
149
- cache: {
150
- restore: async () => ({ hit: false }),
151
- save: async () => {}
152
- },
153
- attestProvenance: () => Promise.reject(/* @__PURE__ */ new Error("ctx.attestProvenance() is not available in the local test runner"))
50
+ repoRoot,
51
+ inputs,
52
+ dispatchInputs,
53
+ signal,
54
+ log: createTestLogger(jobInfo.name),
55
+ secrets: testSecrets,
56
+ ...matrix !== void 0 && { matrix },
57
+ ...context !== void 0 && { context },
58
+ ...rawPayload !== void 0 && { rawPayload },
59
+ ...provider !== void 0 && { provider }
60
+ });
61
+ return {
62
+ ctx,
63
+ dispose
154
64
  };
155
65
  }
156
66
  //#endregion
package/dist/types.d.ts CHANGED
@@ -28,7 +28,9 @@ export interface LockApproval {
28
28
  readonly when: 'always' | 'drift';
29
29
  }
30
30
  /** Schema version - re-exported from engine as single source of truth */
31
- export declare const SCHEMA_VERSION: 30;
31
+ export declare const SCHEMA_VERSION: 32;
32
+ /** Lock compatibility-window floor - re-exported from engine as single source of truth */
33
+ export declare const BREAKING_FLOOR: 30;
32
34
  /**
33
35
  * Source file reference with meaningful path.
34
36
  * Format: file is relative path from git root, export uses hash syntax.
@@ -229,6 +231,17 @@ export interface LockWorkflowCompleteTrigger {
229
231
  readonly status?: readonly string[];
230
232
  readonly source?: string;
231
233
  }
234
+ /**
235
+ * Workflows-failed-batch trigger in lock file.
236
+ * Accumulates failed workflow completions over a window and fires the
237
+ * subscribing workflow once per window with the batched run list.
238
+ */
239
+ export interface LockWorkflowsFailedBatchTrigger {
240
+ readonly _type: 'workflows_failed_batch';
241
+ readonly accumulateFor: number;
242
+ readonly name?: string;
243
+ readonly source?: string;
244
+ }
232
245
  /**
233
246
  * Job completion trigger in lock file.
234
247
  * Matches when a specific job within a workflow finishes execution.
@@ -289,7 +302,7 @@ export interface LockLifecycleTrigger {
289
302
  readonly description?: string;
290
303
  }
291
304
  /** Union of all trigger types */
292
- export type LockTrigger = LockPrTrigger | LockPushTrigger | LockTagTrigger | LockCommentTrigger | LockReviewTrigger | LockReviewCommentTrigger | LockReleaseTrigger | LockDispatchTrigger | LockCreateTrigger | LockDeleteTrigger | LockStatusTrigger | LockWorkflowRunTrigger | LockForkTrigger | LockStarTrigger | LockWatchTrigger | LockWebhookTrigger | LockKiciEventTrigger | LockWorkflowCompleteTrigger | LockJobCompleteTrigger | LockGenericWebhookTrigger | LockScheduleTrigger | LockLifecycleTrigger;
305
+ export type LockTrigger = LockPrTrigger | LockPushTrigger | LockTagTrigger | LockCommentTrigger | LockReviewTrigger | LockReviewCommentTrigger | LockReleaseTrigger | LockDispatchTrigger | LockCreateTrigger | LockDeleteTrigger | LockStatusTrigger | LockWorkflowRunTrigger | LockForkTrigger | LockStarTrigger | LockWatchTrigger | LockWebhookTrigger | LockKiciEventTrigger | LockWorkflowCompleteTrigger | LockWorkflowsFailedBatchTrigger | LockJobCompleteTrigger | LockGenericWebhookTrigger | LockScheduleTrigger | LockLifecycleTrigger;
293
306
  /**
294
307
  * Matrix configuration in lock file.
295
308
  * Static matrices are expanded at orchestrator, dynamic at agent runtime.
@@ -592,6 +605,12 @@ export interface LockWorkflow {
592
605
  */
593
606
  export interface LockFile {
594
607
  readonly schemaVersion: typeof SCHEMA_VERSION;
608
+ /**
609
+ * Newest breaking schema version at emit time (`BREAKING_FLOOR`). Readers
610
+ * whose own SCHEMA_VERSION is below this must reject the lock. Mirrors the
611
+ * engine `LockFile.minReaderVersion` field.
612
+ */
613
+ readonly minReaderVersion?: number;
595
614
  readonly source: LockSource;
596
615
  /** SHA-256 hash of the serialized lock file content (excluding this field). Changes only when workflows, triggers, jobs, or bundle hashes change. */
597
616
  readonly contentHash: string;
package/dist/types.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./rolldown-runtime-ClRpJifh.js";
2
- import { SCHEMA_VERSION as SCHEMA_VERSION$1 } from "@kici-dev/engine";
2
+ import { BREAKING_FLOOR as BREAKING_FLOOR$1, SCHEMA_VERSION as SCHEMA_VERSION$1 } from "@kici-dev/engine";
3
3
  //#region src/types.ts
4
4
  /**
5
5
  * Lock file schema version 11.
@@ -17,6 +17,8 @@ import { SCHEMA_VERSION as SCHEMA_VERSION$1 } from "@kici-dev/engine";
17
17
  */
18
18
  /** Schema version - re-exported from engine as single source of truth */
19
19
  const SCHEMA_VERSION = SCHEMA_VERSION$1;
20
+ /** Lock compatibility-window floor - re-exported from engine as single source of truth */
21
+ const BREAKING_FLOOR = BREAKING_FLOOR$1;
20
22
  /** Type guard distinguishing a parallel group from an ordinary lock step. */
21
23
  function isLockParallelStep(entry) {
22
24
  return entry.kind === "parallel";
@@ -34,6 +36,6 @@ function isLockDynamicJobFn(job) {
34
36
  return job._type === "dynamic";
35
37
  }
36
38
  //#endregion
37
- export { SCHEMA_VERSION, isLockDynamicJobFn, isLockInlineValue, isLockParallelStep, isLockStaticJob };
39
+ export { BREAKING_FLOOR, SCHEMA_VERSION, isLockDynamicJobFn, isLockInlineValue, isLockParallelStep, isLockStaticJob };
38
40
 
39
41
  //# sourceMappingURL=types.js.map
@@ -1,3 +1,5 @@
1
1
  export { validateConfig } from './validator.js';
2
2
  export type { ValidationResult } from './validator.js';
3
+ export { runTypecheck, parseTscOutput } from './typecheck.js';
4
+ export type { TypecheckResult } from './typecheck.js';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,4 @@
1
1
  import "../rolldown-runtime-ClRpJifh.js";
2
2
  import { validateConfig } from "./validator.js";
3
- export { validateConfig };
3
+ import { parseTscOutput, runTypecheck } from "./typecheck.js";
4
+ export { parseTscOutput, runTypecheck, validateConfig };
@@ -0,0 +1,28 @@
1
+ import { type CompilerError } from '../errors/index.js';
2
+ /**
3
+ * Result of the opt-in `tsc --noEmit` type-check pass.
4
+ *
5
+ * `ran: false` means the pass was skipped (no `tsconfig.json` — a
6
+ * JavaScript-only / `--mjs` workspace has no types to check). `ran: true` with
7
+ * an empty `errors` array means the sources type-check cleanly.
8
+ */
9
+ export interface TypecheckResult {
10
+ ran: boolean;
11
+ errors: CompilerError[];
12
+ }
13
+ /**
14
+ * Turn `tsc --noEmit --pretty false` output into located `E120` compiler errors.
15
+ * Handles both located diagnostics (`file(line,col): error TS…`) and global
16
+ * ones (`error TS…`, e.g. a bad tsconfig). Non-diagnostic noise lines are
17
+ * ignored. Paths are left as tsc emits them (kiciDir-relative), readable in the
18
+ * GNU error format.
19
+ */
20
+ export declare function parseTscOutput(stdout: string): CompilerError[];
21
+ /**
22
+ * Run an opt-in `tsc --noEmit` type-check over the workflow sources in
23
+ * `kiciDir`. Skips (returns `ran: false`) when there is no `tsconfig.json`.
24
+ * Prefers the workspace-local `tsc` binary, falling back to `npx typescript`;
25
+ * returns a single `E121` when the compiler cannot be resolved.
26
+ */
27
+ export declare function runTypecheck(kiciDir: string): Promise<TypecheckResult>;
28
+ //# sourceMappingURL=typecheck.d.ts.map