@kici-dev/sdk 0.1.26 → 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 (56) hide show
  1. package/dist/api-types.d.ts +28 -0
  2. package/dist/api-types.js +3 -1
  3. package/dist/approval.d.ts +3 -8
  4. package/dist/approval.js +9 -0
  5. package/dist/artifacts-types.d.ts +45 -0
  6. package/dist/artifacts-types.js +3 -0
  7. package/dist/context.d.ts +73 -23
  8. package/dist/events/define-event.d.ts +11 -3
  9. package/dist/events/define-event.js +14 -4
  10. package/dist/events/emit-typing.test-d.d.ts +2 -0
  11. package/dist/events/emit-typing.test-d.js +36 -0
  12. package/dist/events/event-payloads.d.ts +4 -0
  13. package/dist/events/index.d.ts +1 -1
  14. package/dist/events/index.js +2 -2
  15. package/dist/fleet/agent-version-converge.d.ts +23 -0
  16. package/dist/fleet/agent-version-converge.js +58 -0
  17. package/dist/idempotent.d.ts +2 -2
  18. package/dist/idempotent.js +2 -2
  19. package/dist/index.d.ts +9 -3
  20. package/dist/index.js +6 -2
  21. package/dist/job-outputs.test-d.d.ts +2 -0
  22. package/dist/job-outputs.test-d.js +164 -0
  23. package/dist/job.d.ts +71 -9
  24. package/dist/job.js +19 -5
  25. package/dist/matrix/expand.d.ts +1 -1
  26. package/dist/matrix/expand.js +2 -2
  27. package/dist/needs-context.d.ts +13 -6
  28. package/dist/outputs.d.ts +19 -4
  29. package/dist/outputs.js +21 -7
  30. package/dist/parallel.d.ts +1 -1
  31. package/dist/parallel.js +1 -1
  32. package/dist/provenance-types.d.ts +3 -3
  33. package/dist/rules/context.d.ts +33 -0
  34. package/dist/rules/context.js +51 -0
  35. package/dist/rules/evaluator.d.ts +10 -0
  36. package/dist/rules/evaluator.js +9 -1
  37. package/dist/rules/index.d.ts +2 -0
  38. package/dist/rules/index.js +2 -1
  39. package/dist/rules/types.d.ts +11 -1
  40. package/dist/secrets.d.ts +1 -1
  41. package/dist/step.d.ts +6 -6
  42. package/dist/step.js +6 -3
  43. package/dist/testing/index.d.ts +2 -0
  44. package/dist/testing/index.js +3 -0
  45. package/dist/testing/step-context.d.ts +56 -0
  46. package/dist/testing/step-context.js +259 -0
  47. package/dist/triggers/index.d.ts +2 -1
  48. package/dist/triggers/index.js +2 -1
  49. package/dist/triggers/types.d.ts +23 -1
  50. package/dist/triggers/workflows-failed-batch.d.ts +20 -0
  51. package/dist/triggers/workflows-failed-batch.js +25 -0
  52. package/dist/types.d.ts +163 -30
  53. package/dist/validation/dag.js +11 -3
  54. package/dist/workflow.js +3 -3
  55. package/package.json +10 -5
  56. package/sbom.spdx.json +192 -192
package/dist/types.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { $ as Shell } from 'zx';
3
3
  import type { RetryBackoff } from '@kici-dev/core';
4
4
  import type { ResourceRequest, RunsOnAllInput, OnUnreachableMode, NeedsWhen, ExecutionJobStatus } from '@kici-dev/engine';
5
5
  import type { StepContext, Logger } from './context.js';
6
+ import type { SingleNeedEntry, NeedsContext } from './needs-context.js';
6
7
  import type { TriggerConfig } from './triggers/types.js';
7
8
  import type { Rule } from './rules/types.js';
8
9
  import type { Matrix, MatrixInclude, MatrixExclude } from './matrix/types.js';
@@ -33,6 +34,78 @@ export interface SourceLocation {
33
34
  export type OutputProxy<T> = {
34
35
  readonly [K in keyof T]: T[K];
35
36
  };
37
+ /** Collapse a union of object types into their intersection (`{a} | {b}` -> `{a} & {b}`). */
38
+ type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
39
+ /**
40
+ * A single step's contribution to its job's merged output map. A named,
41
+ * non-void step contributes `{ [name]: result }`; an id-less step (empty name)
42
+ * or a void step contributes nothing; a bare function / parallel group in the
43
+ * steps array contributes nothing. Used by {@link InferJobOutputsFromSteps}.
44
+ */
45
+ type StepOutputEntry<TStep> = TStep extends Step<infer TResult, infer TName> ? TName extends '' ? object : [TResult] extends [void] ? object : {
46
+ readonly [K in TName]: TResult;
47
+ } : object;
48
+ /**
49
+ * Merge every named, non-void step's outputs into the job's nested output map,
50
+ * keyed by step name — reproducing the runtime shape `jobRef.result.<step>.<field>`
51
+ * for a multi-step job. When no step contributes a name (all id-less or void),
52
+ * falls back to the loose `Record<string, unknown>` so untyped code keeps
53
+ * compiling. The `run:` shorthand does NOT go through this helper: it infers a
54
+ * flat shape from the run function's return type at the `job()` overload.
55
+ */
56
+ export type InferJobOutputsFromSteps<TSteps extends readonly unknown[]> = UnionToIntersection<{
57
+ [I in keyof TSteps]: StepOutputEntry<TSteps[I]>;
58
+ }[number]> extends infer M ? keyof M extends never ? Record<string, unknown> : {
59
+ readonly [K in keyof M]: M[K];
60
+ } : never;
61
+ /**
62
+ * One `needs` tuple element's contribution to the typed `ctx.needs` map. A job
63
+ * reference with a literal name, or a `'name'` string / `{ name }` object,
64
+ * contributes `{ [name]: SingleNeedEntry<T> }` (outputs typed for a job ref,
65
+ * loose for a string/name); a name-less job, a group ref, or a `{ group }`
66
+ * entry contributes nothing typed (they fall back to the loose map).
67
+ */
68
+ type NeedFromTupleEntry<E> = E extends Job<infer TOut, infer TName> ? string extends TName ? object : TName extends '' ? object : {
69
+ readonly [K in TName]: SingleNeedEntry<TOut>;
70
+ } : E extends string ? string extends E ? object : {
71
+ readonly [K in E]: SingleNeedEntry<Record<string, unknown>>;
72
+ } : E extends {
73
+ name: infer N;
74
+ } ? N extends string ? string extends N ? object : {
75
+ readonly [K in N]: SingleNeedEntry<Record<string, unknown>>;
76
+ } : object : object;
77
+ /**
78
+ * True when at least one `needs` tuple element cannot be keyed (a group ref, a
79
+ * `{ group }` entry, or a name-less job) — i.e. its {@link NeedFromTupleEntry}
80
+ * contributes no key. Such an element would be silently dropped from a strict
81
+ * closed map, so its presence forces the loose fallback.
82
+ */
83
+ type HasUnkeyableNeed<TNeeds extends readonly unknown[]> = true extends {
84
+ [I in keyof TNeeds]: keyof NeedFromTupleEntry<TNeeds[I]> extends never ? true : false;
85
+ }[number] ? true : false;
86
+ /**
87
+ * Derive the typed `ctx.needs` map from a job's `needs` tuple. The strict, closed
88
+ * map (keyed by upstream name — job references carry their inferred outputs,
89
+ * string / `{ name }` entries key loose) is produced ONLY when every entry is
90
+ * keyable, so reading an undeclared need is a compile error. If any entry cannot
91
+ * be keyed (a group reference, a `{ group }` entry, or a name-less job), the whole
92
+ * map falls back to the loose {@link NeedsContext} — the group's key stays
93
+ * accessible and no valid read regresses to a type error.
94
+ */
95
+ export type NeedsFromTuple<TNeeds extends readonly unknown[]> = HasUnkeyableNeed<TNeeds> extends true ? NeedsContext : UnionToIntersection<{
96
+ [I in keyof TNeeds]: NeedFromTupleEntry<TNeeds[I]>;
97
+ }[number]> extends infer M ? keyof M extends never ? NeedsContext : {
98
+ readonly [K in keyof M]: M[K];
99
+ } : never;
100
+ /**
101
+ * The step/run context with its `needs` narrowed to the typed map derived from
102
+ * the enclosing job's `needs` tuple. Used as the contextual type of the `run:`
103
+ * shorthand so `ctx.needs.<job>.result.<field>` is checked. `needs` is required
104
+ * here (a job with declared needs always has them at runtime).
105
+ */
106
+ export type StepContextWithNeeds<TNeeds extends readonly unknown[]> = Omit<StepContext, 'needs'> & {
107
+ needs: NeedsFromTuple<TNeeds>;
108
+ };
36
109
  /** Output schema type - record of Zod types */
37
110
  export type OutputSchema = Record<string, z.ZodTypeAny>;
38
111
  /** Infer the output type from an output schema */
@@ -43,10 +116,15 @@ export type InferOutputs<T extends OutputSchema> = z.infer<z.ZodObject<T>>;
43
116
  * TResult is the inferred return type of the run function (defaults to void for backward compat).
44
117
  * The optional `outputs` field holds a Zod schema for runtime validation but does NOT drive the generic.
45
118
  */
46
- export interface Step<TResult = void> {
119
+ export interface Step<TResult = void, TName extends string = string> {
47
120
  readonly _tag: 'Step';
48
- /** Step name. Empty string for id-less steps (compiler assigns counter IDs). */
49
- readonly name: string;
121
+ /**
122
+ * Step name, carried as a literal type. Empty string (`''`) for id-less steps
123
+ * (the compiler assigns counter IDs at lock generation). The literal name is
124
+ * what lets a named step contribute a typed key to its job's merged output map
125
+ * (see {@link InferJobOutputsFromSteps}).
126
+ */
127
+ readonly name: TName;
50
128
  /** Optional Zod schema for runtime output validation. */
51
129
  readonly outputs?: OutputSchema;
52
130
  readonly run: (ctx: StepContext, drift?: any) => Promise<TResult>;
@@ -324,6 +402,29 @@ export interface ContainerConfig {
324
402
  /** Additional environment variables for the container */
325
403
  env?: Record<string, string>;
326
404
  }
405
+ /**
406
+ * Per-job sandbox escape hatch for container-sandbox jobs (a job with a
407
+ * `container:` image). Requests are granted only within the operator's
408
+ * allow-list (`sandboxAllowedCapabilities` / `sandboxAllowHostNetwork`); a
409
+ * request for anything not allow-listed FAILS the run at dispatch with a reason
410
+ * naming the offending capability or knob — never a silent downgrade.
411
+ */
412
+ export interface SandboxOptions {
413
+ /**
414
+ * Extra Linux capabilities to add back over the hardened `CapDrop:['ALL']`
415
+ * baseline (e.g. `['NET_ADMIN']`). Each requested capability must be
416
+ * allow-listed by the operator via `sandboxAllowedCapabilities`. Unknown
417
+ * capability names are rejected at author time.
418
+ */
419
+ capabilities?: string[];
420
+ /**
421
+ * Container network posture. `'host'` shares the host network namespace and
422
+ * is the escalation — it requires the operator to enable
423
+ * `sandboxAllowHostNetwork`. `'none'` (loopback-only) and `'default'` (bridge)
424
+ * are always allowed (they do not escalate).
425
+ */
426
+ network?: 'default' | 'none' | 'host';
427
+ }
327
428
  /**
328
429
  * Generic per-job initialization config. Runs a hand-written command after the
329
430
  * repo is cloned and before the job's steps execute, so a repo-declared
@@ -424,10 +525,25 @@ export type RunsOnPick = 'deterministic' | 'any';
424
525
  * for portable pool targeting. Users still cannot *set* `kici:` labels on agents.
425
526
  */
426
527
  export type RunsOn = string | RegExp | (string | RegExp)[] | RunsOnSelector;
427
- /** Job definition returned by job() factory */
428
- export interface Job {
528
+ /**
529
+ * Job definition returned by job() factory.
530
+ *
531
+ * `TOutputs` is the job's inferred output shape, threaded onto `result` so
532
+ * cross-job reads (`jobRef.result.<step>.<field>`) are type-checked. It is
533
+ * inferred by {@link InferJobOutputsFromSteps} for a `steps:` job (nested by
534
+ * step name) or from the run function's return type for a `run:` shorthand job
535
+ * (flat), with an explicit override available (`job<T>(...)`). Defaults to the
536
+ * loose `Record<string, unknown>` so a bare `Job` and untyped code keep
537
+ * compiling.
538
+ *
539
+ * `TName` is the job's name carried as a literal (from the `job('name', …)`
540
+ * argument), which lets a typed `needs: [jobRef]` key the reference under its
541
+ * name in `ctx.needs`. Name-less jobs (auto-generated id) keep the loose
542
+ * `string` default.
543
+ */
544
+ export interface Job<TOutputs = Record<string, unknown>, TName extends string = string> {
429
545
  readonly _tag: 'Job';
430
- readonly name: string;
546
+ readonly name: TName;
431
547
  /** Single-agent targeting. Mutually exclusive with `runsOnAll`. */
432
548
  readonly runsOn?: RunsOn;
433
549
  /**
@@ -471,18 +587,20 @@ export interface Job {
471
587
  readonly checkout?: boolean;
472
588
  /** Docker image for job execution. All steps run inside the container. */
473
589
  readonly container?: string | ContainerConfig;
474
- /** Deployment environment for this job. String for static, or a function of the normalized event envelope for dynamic (resolved at orchestrator two-phase eval). */
475
- readonly environment?: string | ((event: EventPayload) => string | Promise<string>);
590
+ /** Per-job sandbox escape hatch (container jobs only); granted within the operator allow-list. */
591
+ readonly sandbox?: SandboxOptions;
592
+ /** Bound context for this job. String for static, or a function of the normalized event envelope for dynamic (resolved at orchestrator two-phase eval). */
593
+ readonly context?: string | ((event: EventPayload) => string | Promise<string>);
476
594
  /**
477
- * Deployment environments for this job, in merge order (later entries override
478
- * earlier on name collisions). Mutually exclusive with `environment`. Each entry
595
+ * Bound contexts for this job, in merge order (later entries override
596
+ * earlier on name collisions). Mutually exclusive with `context`. Each entry
479
597
  * is a static name or a function of the event (dynamic, resolved per element at
480
598
  * two-phase eval).
481
599
  */
482
- readonly environments?: readonly (string | ((event: EventPayload) => string | Promise<string>))[];
600
+ readonly contexts?: readonly (string | ((event: EventPayload) => string | Promise<string>))[];
483
601
  /** Environment variables. Static object or a function of the normalized event envelope (resolved at orchestrator two-phase eval). */
484
602
  readonly env?: Record<string, string> | ((event: EventPayload) => Record<string, string> | Promise<Record<string, string>>);
485
- /** Concurrency group name. Defaults to the first bound environment's name if not set. String or a function of the normalized event envelope. */
603
+ /** Concurrency group name. Defaults to the first bound context's name if not set. String or a function of the normalized event envelope. */
486
604
  readonly concurrencyGroup?: string | ((event: EventPayload) => string | Promise<string>);
487
605
  /** Runs on cancellation. */
488
606
  readonly onCancel?: HookInput;
@@ -530,9 +648,18 @@ export interface Job {
530
648
  * Type-safe proxy for accessing this job's outputs.
531
649
  * For multi-step jobs: jobRef.result.stepName.field
532
650
  * For single-step (run shorthand) jobs: jobRef.result.field
533
- * At runtime, resolves against the shared job outputs map populated by the workflow runner.
534
- */
535
- readonly result: OutputProxy<any>;
651
+ * At runtime, resolves against the shared job outputs map populated by the
652
+ * workflow runner.
653
+ *
654
+ * The `[TOutputs] extends [void]` guard mirrors `Step.result` so a void
655
+ * run-shorthand job resolves `result` to `never` — which keeps `Job<void>`
656
+ * assignable to `JobOrFactory` (`never` is assignable to
657
+ * `OutputProxy<Record<string, unknown>>`). The tuple wrap is deliberate and
658
+ * non-distributive: it must not distribute over a union output map. Do not
659
+ * "align" this with `Step.result`'s naked form — that would reintroduce
660
+ * distribution over unions.
661
+ */
662
+ readonly result: [TOutputs] extends [void] ? never : OutputProxy<TOutputs>;
536
663
  }
537
664
  /** Options for job() factory */
538
665
  export interface JobOptions {
@@ -619,18 +746,24 @@ export interface JobOptions {
619
746
  * When set, all steps run inside the container.
620
747
  */
621
748
  container?: string | ContainerConfig;
622
- /** Deployment environment for this job. String for static, or a function of the normalized event envelope for dynamic (resolved at orchestrator two-phase eval). */
623
- environment?: string | ((event: EventPayload) => string | Promise<string>);
624
749
  /**
625
- * Deployment environments for this job, in merge order (later entries override
626
- * earlier on name collisions). Mutually exclusive with `environment`. Each entry
750
+ * Per-job sandbox escape hatch (container jobs only). Request extra Linux
751
+ * capabilities / host networking; granted only within the operator's
752
+ * `org_settings` allow-list, else the run fails loudly at dispatch.
753
+ */
754
+ sandbox?: SandboxOptions;
755
+ /** Bound context for this job. String for static, or a function of the normalized event envelope for dynamic (resolved at orchestrator two-phase eval). */
756
+ context?: string | ((event: EventPayload) => string | Promise<string>);
757
+ /**
758
+ * Bound contexts for this job, in merge order (later entries override
759
+ * earlier on name collisions). Mutually exclusive with `context`. Each entry
627
760
  * is a static name or a function of the event (dynamic, resolved per element at
628
761
  * two-phase eval).
629
762
  */
630
- environments?: (string | ((event: EventPayload) => string | Promise<string>))[];
763
+ contexts?: (string | ((event: EventPayload) => string | Promise<string>))[];
631
764
  /** Environment variables. Static object or a function of the normalized event envelope (resolved at orchestrator two-phase eval). */
632
765
  env?: Record<string, string> | ((event: EventPayload) => Record<string, string> | Promise<Record<string, string>>);
633
- /** Concurrency group name. Defaults to the first bound environment's name if not set. String or a function of the normalized event envelope. */
766
+ /** Concurrency group name. Defaults to the first bound context's name if not set. String or a function of the normalized event envelope. */
634
767
  concurrencyGroup?: string | ((event: EventPayload) => string | Promise<string>);
635
768
  /** Runs on cancellation. */
636
769
  onCancel?: HookInput;
@@ -679,8 +812,8 @@ export interface JobOptions {
679
812
  * Private npm registry declaration. Tells the agent to authenticate against
680
813
  * a private package registry before running `npm install`.
681
814
  *
682
- * `tokenSecret` uses qualified `<environment>:<secret-name>` syntax — the
683
- * orchestrator resolves the secret from the named environment's scoped secret
815
+ * `tokenSecret` uses qualified `<context>:<secret-name>` syntax — the
816
+ * orchestrator resolves the secret from the named context's scoped secret
684
817
  * store at dispatch time. Example: `tokenSecret: 'production:NPM_TOKEN'`.
685
818
  */
686
819
  export interface Registry {
@@ -692,8 +825,8 @@ export interface Registry {
692
825
  */
693
826
  readonly scope?: string;
694
827
  /**
695
- * Qualified secret reference of the form `<environment>:<secret-name>`.
696
- * The orchestrator resolves it at dispatch via `secretResolver.resolveForJob(orgId, environment)`.
828
+ * Qualified secret reference of the form `<context>:<secret-name>`.
829
+ * The orchestrator resolves it at dispatch via `secretResolver.resolveForJob(orgId, context)`.
697
830
  */
698
831
  readonly tokenSecret: string;
699
832
  /** Whether to require auth on every request (rendered as `always-auth=true` in `.npmrc`). Defaults to `true`. */
@@ -721,14 +854,14 @@ export interface Workflow {
721
854
  readonly hashFiles?: string[];
722
855
  /**
723
856
  * Private npm registries the agent should authenticate against before `npm install`.
724
- * Each registry's `tokenSecret` uses qualified `<environment>:<secret-name>` syntax.
857
+ * Each registry's `tokenSecret` uses qualified `<context>:<secret-name>` syntax.
725
858
  */
726
859
  readonly registries?: readonly Registry[];
727
860
  /**
728
861
  * Extra secrets to project as environment variables on the install subprocess.
729
862
  * Used together with a customer-committed `.kici/.npmrc` containing `${VAR}` placeholders.
730
- * Each entry uses qualified `<environment>:<secret-name>` syntax; the resolved value is
731
- * exposed to the install subprocess under the `<secret-name>` key (the environment
863
+ * Each entry uses qualified `<context>:<secret-name>` syntax; the resolved value is
864
+ * exposed to the install subprocess under the `<secret-name>` key (the context
732
865
  * prefix is stripped for the env-var name).
733
866
  */
734
867
  readonly installEnv?: readonly string[];
@@ -775,13 +908,13 @@ export interface WorkflowOptions {
775
908
  hashFiles?: string[];
776
909
  /**
777
910
  * Private npm registries the agent should authenticate against before `npm install`.
778
- * Each registry's `tokenSecret` uses qualified `<environment>:<secret-name>` syntax.
911
+ * Each registry's `tokenSecret` uses qualified `<context>:<secret-name>` syntax.
779
912
  */
780
913
  registries?: Registry[];
781
914
  /**
782
915
  * Extra secrets to project as environment variables on the install subprocess.
783
916
  * Used together with a customer-committed `.kici/.npmrc` containing `${VAR}` placeholders.
784
- * Each entry uses qualified `<environment>:<secret-name>` syntax; the resolved value is
917
+ * Each entry uses qualified `<context>:<secret-name>` syntax; the resolved value is
785
918
  * exposed to the install subprocess under the `<secret-name>` key.
786
919
  */
787
920
  installEnv?: string[];
@@ -51,12 +51,20 @@ function validateDag(nodes) {
51
51
  }
52
52
  }
53
53
  if (sortedOrder.length !== nodes.length) {
54
- const cycleNodes = [];
55
- for (const [id, degree] of inDegree) if (degree > 0) cycleNodes.push(id);
54
+ const residual = /* @__PURE__ */ new Set();
55
+ for (const [id, degree] of inDegree) if (degree > 0) residual.add(id);
56
+ let changed = true;
57
+ while (changed) {
58
+ changed = false;
59
+ for (const id of residual) if (!(adjacencyList.get(id) ?? []).some((target) => residual.has(target))) {
60
+ residual.delete(id);
61
+ changed = true;
62
+ }
63
+ }
56
64
  return {
57
65
  valid: false,
58
66
  error: "cycle",
59
- nodesInCycle: cycleNodes
67
+ nodesInCycle: [...residual]
60
68
  };
61
69
  }
62
70
  return {
package/dist/workflow.js CHANGED
@@ -27,7 +27,7 @@ function normalizeTriggers(on) {
27
27
  * });
28
28
  */
29
29
  /**
30
- * Validates a `<environment>:<secret-name>` qualified secret reference.
30
+ * Validates a `<context>:<secret-name>` qualified secret reference.
31
31
  * Both halves must be non-empty and free of additional colons.
32
32
  */
33
33
  function isQualifiedSecretRef(value) {
@@ -47,7 +47,7 @@ function workflow(name, options) {
47
47
  } catch {
48
48
  throw new Error(`workflow('${name}'): registries[${i}].url is not a valid URL: ${reg.url}`);
49
49
  }
50
- if (!isQualifiedSecretRef(reg.tokenSecret)) throw new Error(`workflow('${name}'): registries[${i}].tokenSecret must use qualified <environment>:<secret-name> syntax (got: ${reg.tokenSecret})`);
50
+ if (!isQualifiedSecretRef(reg.tokenSecret)) throw new Error(`workflow('${name}'): registries[${i}].tokenSecret must use qualified <context>:<secret-name> syntax (got: ${reg.tokenSecret})`);
51
51
  if (reg.scope === void 0) defaultCount += 1;
52
52
  else {
53
53
  if (!/^@[a-z0-9][a-z0-9-]*$/i.test(reg.scope)) throw new Error(`workflow('${name}'): registries[${i}].scope must match @<package-scope> (got: ${reg.scope})`);
@@ -58,7 +58,7 @@ function workflow(name, options) {
58
58
  if (defaultCount > 1) throw new Error(`workflow('${name}'): at most one registries entry may omit \`scope\` (default registry); got ${defaultCount}`);
59
59
  }
60
60
  if (options.installEnv) {
61
- for (const [i, ref] of options.installEnv.entries()) if (!isQualifiedSecretRef(ref)) throw new Error(`workflow('${name}'): installEnv[${i}] must use qualified <environment>:<secret-name> syntax (got: ${ref})`);
61
+ for (const [i, ref] of options.installEnv.entries()) if (!isQualifiedSecretRef(ref)) throw new Error(`workflow('${name}'): installEnv[${i}] must use qualified <context>:<secret-name> syntax (got: ${ref})`);
62
62
  }
63
63
  return {
64
64
  _tag: "Workflow",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kici-dev/sdk",
3
- "version": "0.1.26",
3
+ "version": "0.2.0",
4
4
  "description": "TypeScript SDK for defining KiCI workflows. Import into `.kici/workflows/*.ts` to declare workflows, jobs, steps, triggers, rules, and matrix configurations.",
5
5
  "keywords": [
6
6
  "ci",
@@ -43,21 +43,26 @@
43
43
  "types": "./dist/index.d.ts",
44
44
  "import": "./dist/index.js",
45
45
  "default": "./dist/index.js"
46
+ },
47
+ "./testing": {
48
+ "types": "./dist/testing/index.d.ts",
49
+ "import": "./dist/testing/index.js",
50
+ "default": "./dist/testing/index.js"
46
51
  }
47
52
  },
48
53
  "dependencies": {
49
54
  "micromatch": "^4.0.8",
50
55
  "zod": "^4.4.3",
51
56
  "zx": "^8.8.5",
52
- "@kici-dev/core": "0.1.26",
53
- "@kici-dev/engine": "0.1.26"
57
+ "@kici-dev/core": "0.2.0",
58
+ "@kici-dev/engine": "0.2.0"
54
59
  },
55
60
  "devDependencies": {
56
61
  "@types/micromatch": "^4.0.10"
57
62
  },
58
63
  "scripts": {
59
- "build": "node ../../scripts/build-ts.mjs && tsc --emitDeclarationOnly",
60
- "typecheck": "tsc --noEmit",
64
+ "build": "node ../../scripts/build-ts.mjs && tsgo --emitDeclarationOnly",
65
+ "typecheck": "tsgo --noEmit",
61
66
  "test": "vitest run",
62
67
  "test:watch": "vitest"
63
68
  }