@kici-dev/sdk 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.
- package/dist/api-types.d.ts +28 -0
- package/dist/api-types.js +3 -1
- package/dist/approval.d.ts +3 -8
- package/dist/approval.js +9 -0
- package/dist/artifacts-types.d.ts +45 -0
- package/dist/artifacts-types.js +3 -0
- package/dist/context.d.ts +65 -15
- package/dist/events/define-event.d.ts +11 -3
- package/dist/events/define-event.js +14 -4
- package/dist/events/emit-typing.test-d.d.ts +2 -0
- package/dist/events/emit-typing.test-d.js +36 -0
- package/dist/events/event-payloads.d.ts +4 -0
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.js +2 -2
- package/dist/fleet/agent-version-converge.d.ts +23 -0
- package/dist/fleet/agent-version-converge.js +58 -0
- package/dist/index.d.ts +9 -3
- package/dist/index.js +6 -2
- package/dist/job-outputs.test-d.d.ts +2 -0
- package/dist/job-outputs.test-d.js +164 -0
- package/dist/job.d.ts +71 -9
- package/dist/job.js +14 -0
- package/dist/matrix/expand.d.ts +1 -1
- package/dist/matrix/expand.js +2 -2
- package/dist/needs-context.d.ts +13 -6
- package/dist/outputs.d.ts +19 -4
- package/dist/outputs.js +21 -7
- package/dist/parallel.d.ts +1 -1
- package/dist/parallel.js +1 -1
- package/dist/provenance-types.d.ts +3 -3
- package/dist/rules/context.d.ts +33 -0
- package/dist/rules/context.js +51 -0
- package/dist/rules/evaluator.d.ts +10 -0
- package/dist/rules/evaluator.js +9 -1
- package/dist/rules/index.d.ts +2 -0
- package/dist/rules/index.js +2 -1
- package/dist/rules/types.d.ts +11 -1
- package/dist/step.d.ts +6 -6
- package/dist/step.js +6 -3
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +3 -0
- package/dist/testing/step-context.d.ts +56 -0
- package/dist/testing/step-context.js +259 -0
- package/dist/triggers/index.d.ts +2 -1
- package/dist/triggers/index.js +2 -1
- package/dist/triggers/types.d.ts +23 -1
- package/dist/triggers/workflows-failed-batch.d.ts +20 -0
- package/dist/triggers/workflows-failed-batch.js +25 -0
- package/dist/types.d.ts +142 -9
- package/dist/validation/dag.js +11 -3
- package/package.json +10 -5
- 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
|
-
/**
|
|
49
|
-
|
|
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
|
-
/**
|
|
428
|
-
|
|
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:
|
|
546
|
+
readonly name: TName;
|
|
431
547
|
/** Single-agent targeting. Mutually exclusive with `runsOnAll`. */
|
|
432
548
|
readonly runsOn?: RunsOn;
|
|
433
549
|
/**
|
|
@@ -471,6 +587,8 @@ 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;
|
|
590
|
+
/** Per-job sandbox escape hatch (container jobs only); granted within the operator allow-list. */
|
|
591
|
+
readonly sandbox?: SandboxOptions;
|
|
474
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). */
|
|
475
593
|
readonly context?: string | ((event: EventPayload) => string | Promise<string>);
|
|
476
594
|
/**
|
|
@@ -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
|
|
534
|
-
|
|
535
|
-
|
|
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,6 +746,12 @@ export interface JobOptions {
|
|
|
619
746
|
* When set, all steps run inside the container.
|
|
620
747
|
*/
|
|
621
748
|
container?: string | ContainerConfig;
|
|
749
|
+
/**
|
|
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;
|
|
622
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). */
|
|
623
756
|
context?: string | ((event: EventPayload) => string | Promise<string>);
|
|
624
757
|
/**
|
package/dist/validation/dag.js
CHANGED
|
@@ -51,12 +51,20 @@ function validateDag(nodes) {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
if (sortedOrder.length !== nodes.length) {
|
|
54
|
-
const
|
|
55
|
-
for (const [id, degree] of inDegree) if (degree > 0)
|
|
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:
|
|
67
|
+
nodesInCycle: [...residual]
|
|
60
68
|
};
|
|
61
69
|
}
|
|
62
70
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kici-dev/sdk",
|
|
3
|
-
"version": "0.
|
|
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/
|
|
53
|
-
"@kici-dev/
|
|
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 &&
|
|
60
|
-
"typecheck": "
|
|
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
|
}
|