@kici-dev/compiler 0.1.14 → 0.1.15

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.
@@ -20,7 +20,9 @@ The full markdown bundle of every page indexed here is available at https://docs
20
20
 
21
21
  ## SDK reference
22
22
 
23
+ - [Caching](https://docs.kici.dev/user/sdk/caching/): Cache files and directories across runs with declarative job/step cache or the imperative ctx.cache API
23
24
  - [SDK reference: core](https://docs.kici.dev/user/sdk/core/): Factory functions (workflow, job, step) and authoring patterns: needs, output chaining, dynamic groups
25
+ - [Event payload reference](https://docs.kici.dev/user/sdk/event-payloads/): Generated schema of the normalized event envelope passed to rules and dynamic functions.
24
26
  - [SDK reference: idempotent](https://docs.kici.dev/user/sdk/idempotent/): Idempotent helpers for declarative check / apply patterns inside workflow steps
25
27
  - [SDK reference: rules, matrix, dynamic jobs](https://docs.kici.dev/user/sdk/rules-matrix-dynamic/): rule(), skip(), matrix builds (static + dynamic), and dynamicJob / dynamicGroup
26
28
  - [SDK reference: runtime](https://docs.kici.dev/user/sdk/runtime/): Types index, StepContext, secrets, fixtures
@@ -58,5 +60,4 @@ The full markdown bundle of every page indexed here is available at https://docs
58
60
  ## Architecture overview
59
61
 
60
62
  - [Data flows](https://docs.kici.dev/architecture/data-flows/): End-to-end data flows through the KiCI three-tier architecture
61
- - [Design decisions](https://docs.kici.dev/architecture/design-decisions/): Why KiCI's architecture is shaped the way it is
62
63
  - [Architecture overview](https://docs.kici.dev/architecture/overview/): Three-tier relay model, package structure, and component responsibilities
@@ -2,14 +2,14 @@ import "../chunk-gOLHoazu.js";
2
2
  import { discoverWorkflows, resolveKiciDir } from "../execution/executor.js";
3
3
  import "../execution/index.js";
4
4
  import { transformTriggers } from "../lockfile/generator.js";
5
+ import { loadLocalSecrets } from "./secret-loader.js";
5
6
  import { executeDag, resolveJobFilter } from "./dag-scheduler.js";
6
7
  import { compileCommand } from "../commands/compile.js";
7
- import { loadLocalSecrets } from "./secret-loader.js";
8
8
  import { generateEventPayload } from "./payload-generator.js";
9
9
  import { executeResolvedJob, resolveJobs } from "./job-runner.js";
10
10
  import { displayLocalSummary, formatLocalJsonResult, formatLocalJunitResult } from "./output-streamer.js";
11
11
  import { PickerCancelledError, runPicker } from "./picker.js";
12
- import { materializeCheckout } from "./materializer.js";
12
+ import { gcStaleRunCheckouts, materializeCheckout } from "./materializer.js";
13
13
  import { ConcurrencyKeyEvaluationError, acquireWorkflowLock } from "./workflow-lock.js";
14
14
  import pc from "picocolors";
15
15
  import { writeFile } from "node:fs/promises";
@@ -107,7 +107,7 @@ async function runOneMatchedWorkflow(workflow, ctx) {
107
107
  lockHandle = await acquireWorkflowLock({
108
108
  workflowName: workflow.name,
109
109
  workflow,
110
- event: event.payload,
110
+ event,
111
111
  branch: deriveBranchForGroupCtx(event),
112
112
  debug: options.debug
113
113
  });
@@ -272,6 +272,7 @@ async function executeLocal(options) {
272
272
  let materialized = null;
273
273
  let execDir = repoRoot;
274
274
  if (!options.inPlace) {
275
+ await gcStaleRunCheckouts(process.env.KICI_RUN_DIR ?? os.tmpdir());
275
276
  materialized = await materializeCheckout(repoRoot, { runDir: process.env.KICI_RUN_DIR });
276
277
  execDir = materialized.path;
277
278
  logger.info(pc.gray(`running in ${execDir}`));
@@ -3,6 +3,7 @@ import { ensureTsLoaderHook } from "../execution/ts-loader.js";
3
3
  import { formatter } from "../test-runner/output-formatter.js";
4
4
  import { createStepContext } from "../test-runner/step-context.js";
5
5
  import { createRuleContext, evaluateRules as evaluateRulesWithFormatting } from "../test-runner/rule-evaluator.js";
6
+ import { toEventPayload } from "./to-event-payload.js";
6
7
  import path from "node:path";
7
8
  import { pathToFileURL } from "node:url";
8
9
  import { applyIncludeExclude, expandMatrix, isDynamicJobFn, setJobOutputsMap, setStepOutputsMap, setStepRefMap } from "@kici-dev/sdk";
@@ -72,10 +73,7 @@ async function resolveJobs(workflow, event) {
72
73
  $: (await import("zx")).$,
73
74
  ctx: {
74
75
  workflow: { name: workflow.name },
75
- event: {
76
- type: event.type,
77
- ...event.payload
78
- }
76
+ event: toEventPayload(event)
79
77
  },
80
78
  log: {
81
79
  info: () => {},
@@ -36,4 +36,9 @@ export interface MaterializeOptions {
36
36
  * @throws If `repoRoot` is not inside a git work tree
37
37
  */
38
38
  export declare function materializeCheckout(repoRoot: string, opts?: MaterializeOptions): Promise<MaterializedCheckout>;
39
+ /**
40
+ * Collect stale retained checkouts under the run base. Invoked on every
41
+ * `kici run local`; never throws (the GC must not break the run).
42
+ */
43
+ export declare function gcStaleRunCheckouts(base: string): Promise<string[]>;
39
44
  //# sourceMappingURL=materializer.d.ts.map
@@ -2,9 +2,11 @@ import "../chunk-gOLHoazu.js";
2
2
  import { selectOverlayFiles } from "../remote/uploader.js";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
+ import { logger } from "@kici-dev/core";
5
6
  import { execSync } from "node:child_process";
6
7
  import os from "node:os";
7
8
  import { randomBytes } from "node:crypto";
9
+ import { gcStaleTmpDirs } from "@kici-dev/core/tmp-gc";
8
10
  //#region src/local-executor/materializer.ts
9
11
  /**
10
12
  * Materialize an isolated tmp checkout of the repo for `kici run local`.
@@ -52,6 +54,25 @@ async function materializeCheckout(repoRoot, opts) {
52
54
  };
53
55
  }
54
56
  /**
57
+ * Checkouts retained for inspection (failed runs, --keep) stay around this
58
+ * long before the next `kici run local` invocation collects them.
59
+ */
60
+ const RUN_CHECKOUT_GC_MAX_AGE_MS = 4320 * 60 * 1e3;
61
+ /** Matches the kici-run-<6 hex> dirs materializeCheckout creates — nothing else. */
62
+ const RUN_CHECKOUT_PATTERN = /^kici-run-[0-9a-f]{6}$/;
63
+ /**
64
+ * Collect stale retained checkouts under the run base. Invoked on every
65
+ * `kici run local`; never throws (the GC must not break the run).
66
+ */
67
+ async function gcStaleRunCheckouts(base) {
68
+ return gcStaleTmpDirs({
69
+ base,
70
+ pattern: RUN_CHECKOUT_PATTERN,
71
+ maxAgeMs: RUN_CHECKOUT_GC_MAX_AGE_MS,
72
+ log: (m) => logger.debug(m)
73
+ });
74
+ }
75
+ /**
55
76
  * Throw an actionable error if `repoRoot` is not a git work tree.
56
77
  */
57
78
  function requireGitRepo(repoRoot) {
@@ -106,6 +127,6 @@ function shellQuote(value) {
106
127
  return `'${value.replace(/'/g, `'\\''`)}'`;
107
128
  }
108
129
  //#endregion
109
- export { materializeCheckout };
130
+ export { gcStaleRunCheckouts, materializeCheckout };
110
131
 
111
132
  //# sourceMappingURL=materializer.js.map
@@ -0,0 +1,16 @@
1
+ import type { SimulatedEvent } from '@kici-dev/engine';
2
+ import type { EventPayload } from '@kici-dev/sdk';
3
+ /**
4
+ * Map a {@link SimulatedEvent} into the normalized SDK {@link EventPayload}
5
+ * envelope that every user-authored dynamic function (dynamic job generators,
6
+ * `concurrency.group()`) receives as its `event` argument.
7
+ *
8
+ * `SimulatedEvent` already carries the normalized fields (`type`, `action`,
9
+ * `targetBranch`, …) alongside the raw provider body under `payload`, so the
10
+ * single cast at this boundary is a deliberate type assertion over a
11
+ * structurally-identical value: it asserts the discriminated-union view
12
+ * without reshaping anything. Raw provider fields stay nested under
13
+ * `payload.<field>`; normalized fields live at the top level.
14
+ */
15
+ export declare function toEventPayload(event: SimulatedEvent): EventPayload;
16
+ //# sourceMappingURL=to-event-payload.d.ts.map
@@ -0,0 +1,21 @@
1
+ import "../chunk-gOLHoazu.js";
2
+ //#region src/local-executor/to-event-payload.ts
3
+ /**
4
+ * Map a {@link SimulatedEvent} into the normalized SDK {@link EventPayload}
5
+ * envelope that every user-authored dynamic function (dynamic job generators,
6
+ * `concurrency.group()`) receives as its `event` argument.
7
+ *
8
+ * `SimulatedEvent` already carries the normalized fields (`type`, `action`,
9
+ * `targetBranch`, …) alongside the raw provider body under `payload`, so the
10
+ * single cast at this boundary is a deliberate type assertion over a
11
+ * structurally-identical value: it asserts the discriminated-union view
12
+ * without reshaping anything. Raw provider fields stay nested under
13
+ * `payload.<field>`; normalized fields live at the top level.
14
+ */
15
+ function toEventPayload(event) {
16
+ return event;
17
+ }
18
+ //#endregion
19
+ export { toEventPayload };
20
+
21
+ //# sourceMappingURL=to-event-payload.js.map
@@ -20,6 +20,7 @@
20
20
  * `os.tmpdir()/kici-local-locks-<uid>/`).
21
21
  */
22
22
  import type { Workflow } from '@kici-dev/sdk';
23
+ import type { SimulatedEvent } from '@kici-dev/engine';
23
24
  /** Sidecar metadata persisted next to the lock dir for diagnostics + reclamation. */
24
25
  export interface LockHolderMetadata {
25
26
  pid: number;
@@ -35,8 +36,8 @@ export interface WorkflowLockHandle {
35
36
  export interface AcquireWorkflowLockOptions {
36
37
  workflowName: string;
37
38
  workflow: Workflow;
38
- /** The simulated-event payload object passed as `groupCtx.event` to the user's group fn. */
39
- event: Record<string, unknown>;
39
+ /** The simulated event mapped to the normalized envelope passed as `groupCtx.event`. */
40
+ event: SimulatedEvent;
40
41
  /** Branch derived from the simulated event for `groupCtx.branch`. */
41
42
  branch: string;
42
43
  debug?: boolean;
@@ -68,7 +69,7 @@ export declare function resolveLockDir(): Promise<string>;
68
69
  * Returns `null` when the workflow has no `concurrency` block. Throws
69
70
  * {@link ConcurrencyKeyEvaluationError} when the group function throws or rejects.
70
71
  */
71
- export declare function resolveConcurrencyKey(workflow: Workflow, event: Record<string, unknown>, branch: string): Promise<string | null>;
72
+ export declare function resolveConcurrencyKey(workflow: Workflow, event: SimulatedEvent, branch: string): Promise<string | null>;
72
73
  /**
73
74
  * Acquire the workflow-level concurrency lock for a `kici run local` invocation.
74
75
  *
@@ -5,7 +5,7 @@ import { resolveHashFiles } from "./hash-files.js";
5
5
  import { analyzePurity } from "./purity-analyzer.js";
6
6
  import { readFileSync } from "node:fs";
7
7
  import path from "node:path";
8
- import { getDynamicJobGroup, isDynamicFunction, isDynamicGroupRef, isDynamicJobFn, isStaticArray, isStaticObject } from "@kici-dev/sdk";
8
+ import { getDynamicJobGroup, isDynamicFunction, isDynamicGroupRef, isDynamicJobFn, isStaticArray, isStaticObject, normalizeCacheSpecs } from "@kici-dev/sdk";
9
9
  import { sha256 } from "@kici-dev/core";
10
10
  import { PackageManager, detectPackageManagerSync } from "@kici-dev/core/package-manager";
11
11
  import { validateResourceRequest } from "@kici-dev/engine";
@@ -151,7 +151,8 @@ function transformWorkflow(workflow, sourceFile, exportRef, bundleSource, gitRoo
151
151
  hasGroup: !!workflow.concurrency.group,
152
152
  ...workflow.concurrency.cancelInProgress !== void 0 && { cancelInProgress: workflow.concurrency.cancelInProgress },
153
153
  ...workflow.concurrency.max !== void 0 && { max: workflow.concurrency.max }
154
- } }
154
+ } },
155
+ ...workflow.timeout !== void 0 && { timeout: workflow.timeout }
155
156
  };
156
157
  }
157
158
  /**
@@ -528,6 +529,7 @@ function transformJob(job, configPath, index, gitRoot, uuidToName) {
528
529
  rules: job.rules ? transformRules(job.rules, configPath, index) : void 0,
529
530
  description: job.description,
530
531
  ...job.checkout !== void 0 && { checkout: job.checkout },
532
+ ...job.cache !== void 0 && { cache: normalizeCacheSpecs(job.cache) },
531
533
  ...job.container !== void 0 && { container: job.container },
532
534
  ...environmentFields,
533
535
  ...envFields,
@@ -539,7 +541,9 @@ function transformJob(job, configPath, index, gitRoot, uuidToName) {
539
541
  ...job.beforeStep !== void 0 && { hasBeforeStep: true },
540
542
  ...job.afterStep !== void 0 && { hasAfterStep: true },
541
543
  ...job.gracePeriod !== void 0 && { gracePeriod: job.gracePeriod },
542
- ...job.resources !== void 0 && { resources: job.resources }
544
+ ...job.timeout !== void 0 && { timeout: job.timeout },
545
+ ...job.resources !== void 0 && { resources: job.resources },
546
+ ...job.init !== void 0 && { init: job.init }
543
547
  };
544
548
  }
545
549
  /**
@@ -603,6 +607,7 @@ function transformSteps(steps, gitRoot) {
603
607
  hasOutputs: !!step.outputs && Object.keys(step.outputs).length > 0,
604
608
  ...step.continueOnError !== void 0 && { continueOnError: step.continueOnError },
605
609
  ...step.timeout !== void 0 && { timeout: step.timeout },
610
+ ...step.cache !== void 0 && { cache: normalizeCacheSpecs(step.cache) },
606
611
  ...step._sourceLocation && { sourceLocation: {
607
612
  file: makeRelativePath(step._sourceLocation.file, gitRoot),
608
613
  line: step._sourceLocation.line,
@@ -33,6 +33,10 @@ export interface TestTriggerInput {
33
33
  /** CLI's ephemeral public key (base64, SPKI/DER) for overlay decryption */
34
34
  cliPublicKey?: string;
35
35
  secrets?: Record<string, string>;
36
+ /** Base64 X25519+AES-GCM blob of the developer's local secrets ({flat, contexts}). */
37
+ encryptedSecrets?: string;
38
+ /** Base64 ephemeral CLI public key that encrypted `encryptedSecrets`. */
39
+ encryptedSecretsKey?: string;
36
40
  workflowName?: string;
37
41
  /** JSON-stringified lock file content for repos with no remote. */
38
42
  inlineLockFile?: string;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Production defaults for the `kici login` OAuth flow. When the matching
3
+ * env var (or the `--platform-endpoint` flag) is unset, login resolves
4
+ * these so a developer targeting the hosted KiCI Platform authenticates
5
+ * with no setup. Setting the env var overrides the default — staging E2E
6
+ * and self-hosted Platforms depend on that override path.
7
+ *
8
+ * Resolution is login-local: these are read into locals in `oauthLogin`,
9
+ * never written back into `process.env`, so the orchestrator's separate
10
+ * `wss://…/ws` meaning of `KICI_PLATFORM_URL` is unaffected.
11
+ */
12
+ /** Platform API base URL — login POSTs to `${value}/api/v1/cli/exchange-token`. */
13
+ export declare const PROD_PLATFORM_URL = "https://api.kici.dev";
14
+ /** OIDC issuer for the hosted Platform's Keycloak realm. */
15
+ export declare const PROD_OIDC_ISSUER = "https://auth.kici.dev/realms/kici-internal";
16
+ /** Public OIDC client id registered for the `kici` CLI. */
17
+ export declare const PROD_OIDC_CLIENT_ID = "kici-cli";
18
+ //# sourceMappingURL=prod-defaults.d.ts.map
@@ -0,0 +1,23 @@
1
+ import "../chunk-gOLHoazu.js";
2
+ //#region src/remote/prod-defaults.ts
3
+ /**
4
+ * Production defaults for the `kici login` OAuth flow. When the matching
5
+ * env var (or the `--platform-endpoint` flag) is unset, login resolves
6
+ * these so a developer targeting the hosted KiCI Platform authenticates
7
+ * with no setup. Setting the env var overrides the default — staging E2E
8
+ * and self-hosted Platforms depend on that override path.
9
+ *
10
+ * Resolution is login-local: these are read into locals in `oauthLogin`,
11
+ * never written back into `process.env`, so the orchestrator's separate
12
+ * `wss://…/ws` meaning of `KICI_PLATFORM_URL` is unaffected.
13
+ */
14
+ /** Platform API base URL — login POSTs to `${value}/api/v1/cli/exchange-token`. */
15
+ const PROD_PLATFORM_URL = "https://api.kici.dev";
16
+ /** OIDC issuer for the hosted Platform's Keycloak realm. */
17
+ const PROD_OIDC_ISSUER = "https://auth.kici.dev/realms/kici-internal";
18
+ /** Public OIDC client id registered for the `kici` CLI. */
19
+ const PROD_OIDC_CLIENT_ID = "kici-cli";
20
+ //#endregion
21
+ export { PROD_OIDC_CLIENT_ID, PROD_OIDC_ISSUER, PROD_PLATFORM_URL };
22
+
23
+ //# sourceMappingURL=prod-defaults.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Parse `--context ctx.key=value` flag values into a nested
3
+ * `{ context: { key: value } }` map. The first `.` splits the context name
4
+ * from `key=value`; the first `=` splits key from value (values may contain
5
+ * `=`). Malformed entries (missing `.` or `=`, empty context/key) are skipped.
6
+ */
7
+ export declare function parseContextFlags(flags: string[] | undefined): Record<string, Record<string, string>>;
8
+ /**
9
+ * Load the developer's local secrets (same sources as `kici run local`: the
10
+ * `.kici` secret files plus `--env` flat flags) and `--context` namespaced
11
+ * flags, and encrypt them to the orchestrator's per-upload X25519 public key.
12
+ * `--context` values override `.kici/.secrets` file contexts for the same key.
13
+ * Returns the base64 ciphertext and the ephemeral CLI public key the
14
+ * orchestrator needs to decrypt, or null when there is nothing to send.
15
+ */
16
+ export declare function buildEncryptedSecrets(kiciDir: string, envFlags: string[] | undefined, contextFlags: string[] | undefined, orchestratorPublicKeyB64: string): Promise<{
17
+ encryptedSecrets: string;
18
+ cliPublicKey: string;
19
+ } | null>;
20
+ //# sourceMappingURL=secret-upload.d.ts.map
@@ -0,0 +1,58 @@
1
+ import "../chunk-gOLHoazu.js";
2
+ import { loadLocalSecrets } from "../local-executor/secret-loader.js";
3
+ import { encryptJson } from "@kici-dev/core";
4
+ //#region src/remote/secret-upload.ts
5
+ /**
6
+ * Parse `--context ctx.key=value` flag values into a nested
7
+ * `{ context: { key: value } }` map. The first `.` splits the context name
8
+ * from `key=value`; the first `=` splits key from value (values may contain
9
+ * `=`). Malformed entries (missing `.` or `=`, empty context/key) are skipped.
10
+ */
11
+ function parseContextFlags(flags) {
12
+ const contexts = {};
13
+ for (const flag of flags ?? []) {
14
+ const dotIndex = flag.indexOf(".");
15
+ if (dotIndex === -1) continue;
16
+ const contextName = flag.slice(0, dotIndex).trim();
17
+ const rest = flag.slice(dotIndex + 1);
18
+ const eqIndex = rest.indexOf("=");
19
+ if (eqIndex === -1) continue;
20
+ const key = rest.slice(0, eqIndex).trim();
21
+ const value = rest.slice(eqIndex + 1).trim();
22
+ if (!contextName || !key) continue;
23
+ (contexts[contextName] ??= {})[key] = value;
24
+ }
25
+ return contexts;
26
+ }
27
+ /**
28
+ * Load the developer's local secrets (same sources as `kici run local`: the
29
+ * `.kici` secret files plus `--env` flat flags) and `--context` namespaced
30
+ * flags, and encrypt them to the orchestrator's per-upload X25519 public key.
31
+ * `--context` values override `.kici/.secrets` file contexts for the same key.
32
+ * Returns the base64 ciphertext and the ephemeral CLI public key the
33
+ * orchestrator needs to decrypt, or null when there is nothing to send.
34
+ */
35
+ async function buildEncryptedSecrets(kiciDir, envFlags, contextFlags, orchestratorPublicKeyB64) {
36
+ const local = await loadLocalSecrets(kiciDir, envFlags);
37
+ const contexts = {};
38
+ for (const [ctxName, vals] of Object.entries(local.contexts)) contexts[ctxName] = { ...vals };
39
+ for (const [ctxName, vals] of Object.entries(parseContextFlags(contextFlags))) contexts[ctxName] = {
40
+ ...contexts[ctxName] ?? {},
41
+ ...vals
42
+ };
43
+ const hasFlat = Object.keys(local.flat).length > 0;
44
+ const hasContexts = Object.values(contexts).some((c) => Object.keys(c).length > 0);
45
+ if (!hasFlat && !hasContexts) return null;
46
+ const { ciphertextB64, senderPublicKeyB64 } = encryptJson({
47
+ flat: local.flat,
48
+ contexts
49
+ }, Buffer.from(orchestratorPublicKeyB64, "base64"));
50
+ return {
51
+ encryptedSecrets: ciphertextB64,
52
+ cliPublicKey: senderPublicKeyB64
53
+ };
54
+ }
55
+ //#endregion
56
+ export { buildEncryptedSecrets, parseContextFlags };
57
+
58
+ //# sourceMappingURL=secret-upload.js.map
@@ -1,6 +1,6 @@
1
1
  import "../chunk-gOLHoazu.js";
2
2
  //#region src/templates/package-json.ts
3
- const sdkVersion = "0.1.14";
3
+ const sdkVersion = "0.1.15";
4
4
  /**
5
5
  * Generate package.json content for .kici/ directory
6
6
  *
@@ -1,5 +1,5 @@
1
1
  import "../../chunk-gOLHoazu.js";
2
- import { job, pr, rule, skip, step, workflow } from "@kici-dev/sdk";
2
+ import { isEventType, job, pr, rule, skip, step, workflow } from "@kici-dev/sdk";
3
3
  //#region src/templates/workflows/pr-checks.ts
4
4
  const prChecksWorkflow = workflow("pr-checks", {
5
5
  on: pr({
@@ -7,7 +7,8 @@ const prChecksWorkflow = workflow("pr-checks", {
7
7
  paths: ["src/**"]
8
8
  }),
9
9
  rules: [skip("skip-draft-prs", async (ctx) => {
10
- return ctx.event.pull_request?.draft === true;
10
+ if (!isEventType(ctx.event, "pull_request")) return false;
11
+ return ctx.event.payload.pull_request.draft === true;
11
12
  }), rule("require-src-changes", async (ctx) => {
12
13
  return ctx.changedFiles.some((file) => file.startsWith("src/"));
13
14
  })],
@@ -2,7 +2,7 @@
2
2
  // Docs: https://kici.dev/docs/sdk-reference
3
3
  // Patterns: https://kici.dev/docs/workflow-patterns
4
4
 
5
- import { workflow, job, step, pr, rule, skip } from '@kici-dev/sdk';
5
+ import { workflow, job, step, pr, rule, skip, isEventType } from '@kici-dev/sdk';
6
6
 
7
7
  export const prChecksWorkflow = workflow('pr-checks', {
8
8
  // Trigger: pr() matches pull request events
@@ -13,7 +13,9 @@ export const prChecksWorkflow = workflow('pr-checks', {
13
13
  // skip() = skip if true, rule() = run only if true
14
14
  rules: [
15
15
  skip('skip-draft-prs', async (ctx) => {
16
- return (ctx.event as any).pull_request?.draft === true;
16
+ // Raw provider fields live under `ctx.event.payload`; narrow first for typing.
17
+ if (!isEventType(ctx.event, 'pull_request')) return false;
18
+ return ctx.event.payload.pull_request.draft === true;
17
19
  }),
18
20
 
19
21
  rule('require-src-changes', async (ctx) => {
@@ -131,7 +131,11 @@ function createStepContext(workflowInfo, jobInfo, repoRoot, inputs = {}, matrix,
131
131
  kici: { infrastructure: { list: () => Promise.resolve({
132
132
  scalers: [],
133
133
  agents: []
134
- }) } }
134
+ }) } },
135
+ cache: {
136
+ restore: async () => ({ hit: false }),
137
+ save: async () => {}
138
+ }
135
139
  };
136
140
  }
137
141
  //#endregion
package/dist/types.d.ts CHANGED
@@ -9,10 +9,11 @@
9
9
  * v7 adds hook flags, step rules, gracePeriod, and workflow concurrency config.
10
10
  * v8 adds runsOn polymorphic type (string | string[] | selector) and excludeLabels.
11
11
  * v11 adds LockInlineValue type for pure function inline evaluation.
12
+ * v15 adds per-job init config(s).
12
13
  */
13
14
  import type { ResourceRequest } from '@kici-dev/engine';
14
15
  /** Schema version - re-exported from engine as single source of truth */
15
- export declare const SCHEMA_VERSION: 12;
16
+ export declare const SCHEMA_VERSION: 15;
16
17
  /**
17
18
  * Source file reference with meaningful path.
18
19
  * Format: file is relative path from git root, export uses hash syntax.
@@ -307,6 +308,8 @@ export interface LockStep {
307
308
  readonly continueOnError?: boolean;
308
309
  /** Step-level timeout in milliseconds. */
309
310
  readonly timeout?: number;
311
+ /** Declarative cache specs (normalized to an array). Restored before / saved after the step. */
312
+ readonly cache?: readonly import('@kici-dev/sdk').CacheSpec[];
310
313
  /** Source location of the step() call in the original TypeScript file (for annotations). */
311
314
  readonly sourceLocation?: {
312
315
  readonly file: string;
@@ -369,6 +372,8 @@ export interface LockJob {
369
372
  readonly description?: string;
370
373
  /** When false, agent skips git clone (default: true). */
371
374
  readonly checkout?: boolean;
375
+ /** Declarative cache specs (normalized to an array). Restored before steps / saved after the job. */
376
+ readonly cache?: readonly import('@kici-dev/sdk').CacheSpec[];
372
377
  /** Docker image for job execution. All steps run inside the container. */
373
378
  readonly container?: string | {
374
379
  image: string;
@@ -400,12 +405,20 @@ export interface LockJob {
400
405
  readonly hasAfterStep?: boolean;
401
406
  /** Seconds before SIGKILL after SIGTERM during cancellation. */
402
407
  readonly gracePeriod?: number;
408
+ /** Total job wall-clock timeout in milliseconds (init + all steps + hooks). Agent reads this from jobConfig to arm a job-level deadline. */
409
+ readonly timeout?: number;
403
410
  /**
404
411
  * Resource request and limit for this job.
405
412
  * Threaded from SDK `Job.resources` to the orchestrator scaler for cap accounting
406
413
  * (`requests`) and kernel-side enforcement (`limits`) on the spawned agent.
407
414
  */
408
415
  readonly resources?: ResourceRequest;
416
+ /**
417
+ * Per-job init config(s) run after clone, before steps. `false` is an explicit
418
+ * opt-out. Threaded verbatim from the SDK `Job.init` -- the agent reads it from
419
+ * the loaded module, the lock copy is for orchestrator/dashboard visibility.
420
+ */
421
+ readonly init?: import('@kici-dev/sdk').GenericInitConfig | readonly import('@kici-dev/sdk').GenericInitConfig[] | false;
409
422
  }
410
423
  /**
411
424
  * Dynamic job generator reference.
@@ -480,6 +493,8 @@ export interface LockWorkflow {
480
493
  readonly cancelInProgress?: boolean;
481
494
  readonly max?: number;
482
495
  };
496
+ /** Whole-run wall-clock timeout in milliseconds. Orchestrator reads this at run creation to set the run deadline. */
497
+ readonly timeout?: number;
483
498
  }
484
499
  /**
485
500
  * Complete lock file structure.
@@ -492,6 +507,7 @@ export interface LockWorkflow {
492
507
  * v7 adds hook flags, step rules, gracePeriod, and workflow concurrency config.
493
508
  * v8 adds runsOn polymorphic type (string | string[] | selector) and excludeLabels.
494
509
  * v11 adds LockInlineValue type for pure function inline evaluation.
510
+ * v13 adds job-level and workflow-level timeout.
495
511
  */
496
512
  export interface LockFile {
497
513
  readonly schemaVersion: typeof SCHEMA_VERSION;
package/dist/types.js CHANGED
@@ -12,6 +12,7 @@ import { SCHEMA_VERSION as SCHEMA_VERSION$1 } from "@kici-dev/engine";
12
12
  * v7 adds hook flags, step rules, gracePeriod, and workflow concurrency config.
13
13
  * v8 adds runsOn polymorphic type (string | string[] | selector) and excludeLabels.
14
14
  * v11 adds LockInlineValue type for pure function inline evaluation.
15
+ * v15 adds per-job init config(s).
15
16
  */
16
17
  /** Schema version - re-exported from engine as single source of truth */
17
18
  const SCHEMA_VERSION = SCHEMA_VERSION$1;
@@ -2,7 +2,7 @@
2
2
  // Docs: https://kici.dev/docs/sdk-reference
3
3
  // Patterns: https://kici.dev/docs/workflow-patterns
4
4
 
5
- import { workflow, job, step, pr, rule, skip } from '@kici-dev/sdk';
5
+ import { workflow, job, step, pr, rule, skip, isEventType } from '@kici-dev/sdk';
6
6
 
7
7
  export const prChecksWorkflow = workflow('pr-checks', {
8
8
  // Trigger: pr() matches pull request events
@@ -13,7 +13,9 @@ export const prChecksWorkflow = workflow('pr-checks', {
13
13
  // skip() = skip if true, rule() = run only if true
14
14
  rules: [
15
15
  skip('skip-draft-prs', async (ctx) => {
16
- return (ctx.event as any).pull_request?.draft === true;
16
+ // Raw provider fields live under `ctx.event.payload`; narrow first for typing.
17
+ if (!isEventType(ctx.event, 'pull_request')) return false;
18
+ return ctx.event.payload.pull_request.draft === true;
17
19
  }),
18
20
 
19
21
  rule('require-src-changes', async (ctx) => {
package/package.json CHANGED
@@ -1,18 +1,22 @@
1
1
  {
2
2
  "name": "@kici-dev/compiler",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "Compiler and CLI for KiCI workflows. Compiles `.kici/workflows/*.ts` to a `kici.lock.json` file consumed by the orchestrator and agents, and runs workflows locally or against a remote orchestrator.",
5
5
  "keywords": [
6
- "kici",
7
6
  "ci",
8
- "cd",
9
- "ci-cd",
7
+ "cicd",
8
+ "continuous-integration",
10
9
  "typescript",
11
- "workflows",
12
- "devops",
10
+ "workflow",
13
11
  "cli",
14
12
  "compiler"
15
13
  ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/kici-dev/kici-public.git",
17
+ "directory": "packages/compiler"
18
+ },
19
+ "bugs": "https://github.com/kici-dev/kici-public/issues",
16
20
  "homepage": "https://kici.dev",
17
21
  "author": {
18
22
  "name": "KiCI",
@@ -58,11 +62,11 @@
58
62
  "ws": "^8.20.0",
59
63
  "yaml": "^2.8.3",
60
64
  "zx": "^8.8.5",
61
- "@kici-dev/core": "0.1.14",
62
- "@kici-dev/engine": "0.1.14"
65
+ "@kici-dev/core": "0.1.15",
66
+ "@kici-dev/engine": "0.1.15"
63
67
  },
64
68
  "peerDependencies": {
65
- "@kici-dev/sdk": "0.1.14"
69
+ "@kici-dev/sdk": "0.1.15"
66
70
  },
67
71
  "devDependencies": {
68
72
  "@types/proper-lockfile": "^4.1.4"