@qaick/executor 0.1.0-alpha.2 → 0.1.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.
@@ -0,0 +1,11 @@
1
+ import type { ExecutableInput } from '@qaick/contract';
2
+ /** Shared invocation semantics. Never coerce supplied arguments or rewrite declarations. */
3
+ export declare function resolveDeclaredInputs(declarations?: readonly ExecutableInput[], arguments_?: Record<string, unknown>): {
4
+ inputs: {
5
+ [x: string]: unknown;
6
+ };
7
+ issues: {
8
+ code: string;
9
+ message: string;
10
+ }[];
11
+ };
@@ -0,0 +1,32 @@
1
+ /** Shared invocation semantics. Never coerce supplied arguments or rewrite declarations. */
2
+ export function resolveDeclaredInputs(declarations = [], arguments_ = {}) {
3
+ const inputs = { ...arguments_ };
4
+ const issues = [];
5
+ if (!Array.isArray(declarations))
6
+ return { inputs, issues: [{ code: 'INVALID_INPUT', message: 'Declared inputs must be an array.' }] };
7
+ const seen = new Set();
8
+ for (const declaration of declarations) {
9
+ if (!declaration || typeof declaration.name !== 'string' || !declaration.name || seen.has(declaration.name) || !['string', 'number', 'boolean', 'json'].includes(declaration.type) || typeof declaration.required !== 'boolean') {
10
+ issues.push({ code: 'INVALID_INPUT', message: 'Invalid or duplicate declared input.' });
11
+ continue;
12
+ }
13
+ seen.add(declaration.name);
14
+ const supplied = Object.hasOwn(arguments_, declaration.name);
15
+ const value = supplied ? arguments_[declaration.name] : declaration.defaultValue;
16
+ if (value === undefined) {
17
+ delete inputs[declaration.name];
18
+ if (declaration.required)
19
+ issues.push({ code: 'MISSING_INPUT', message: `Required input ${declaration.name} is missing.` });
20
+ continue;
21
+ }
22
+ const validType = declaration.type === 'number' ? typeof value === 'number' && Number.isFinite(value)
23
+ : declaration.type === 'json' ? typeof value === 'object' && value !== null
24
+ : typeof value === declaration.type;
25
+ if (!validType || (declaration.required && value === '')) {
26
+ issues.push({ code: 'INVALID_INPUT', message: `Input ${declaration.name}: expected ${declaration.type}${declaration.required ? ' (required)' : ''}.` });
27
+ continue;
28
+ }
29
+ inputs[declaration.name] = value;
30
+ }
31
+ return { inputs, issues };
32
+ }
package/dist/executor.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { resolveDeclaredInputs } from './declared-inputs.js';
1
2
  const MAX_TIMEOUT_MS = 300_000;
2
3
  export function createExecutor(configuration = {}) {
3
4
  return new DefaultExecutor(configuration);
@@ -131,11 +132,18 @@ export class DefaultExecutor {
131
132
  const definition = findDefinition(run.request.executionPackage.steps, stepId, invocationPath);
132
133
  if (!located || !definition?.children?.length || definition.control || definition.kind !== 'operation')
133
134
  return;
134
- const nested = await this.executeFlow(run, definition.children, located.inputs, [...(located.invocationPath ?? []).slice(0, -1), definition.id], new Date().toISOString());
135
+ const arguments_ = resolveDeclaredInputs(definition.inputDeclarations, located.inputs);
136
+ if (arguments_.issues.length)
137
+ return;
138
+ const childScope = { ...run.request.inputs, ...arguments_.inputs };
139
+ const nested = await this.executeFlow(run, definition.children, childScope, [...(located.invocationPath ?? []).slice(0, -1), definition.id], new Date().toISOString());
140
+ const outputs = definition.outputBindings && nested.state === 'completed'
141
+ ? { ...definition.staticOutputs, ...resolveBindings(definition.outputBindings, childScope, new Map(nested.steps.map(child => [child.id, child.outputs]))) }
142
+ : nested.steps.at(-1)?.outputs ?? {};
135
143
  const previous = located.attempts ?? [];
136
- located.attempts = [...previous, { attempt: previous.length + 1, kind: 'interactive', state: nested.state === 'completed' ? 'finished' : 'failed', startedAt: nested.startedAt, finishedAt: nested.finishedAt, inputs: located.inputs, outputs: nested.steps.at(-1)?.outputs ?? {}, durationMs: nested.steps.reduce((sum, child) => sum + child.durationMs, 0), children: nested.steps, ...(nested.error ? { error: nested.error } : {}) }];
144
+ located.attempts = [...previous, { attempt: previous.length + 1, kind: 'interactive', state: nested.state === 'completed' ? 'finished' : 'failed', startedAt: nested.startedAt, finishedAt: nested.finishedAt, inputs: located.inputs, outputs, durationMs: nested.steps.reduce((sum, child) => sum + child.durationMs, 0), children: nested.steps, ...(nested.error ? { error: nested.error } : {}) }];
137
145
  located.children = nested.steps;
138
- located.outputs = nested.steps.at(-1)?.outputs ?? {};
146
+ located.outputs = outputs;
139
147
  located.state = nested.state === 'completed' ? 'finished' : nested.state === 'cancelled' ? 'cancelled' : 'failed';
140
148
  located.terminalReason = nested.terminalReason ?? (located.state === 'finished' ? 'completed' : 'failed');
141
149
  located.error = nested.error;
@@ -180,7 +188,9 @@ export class DefaultExecutor {
180
188
  timeout = setTimeout(() => { run.timedOut = true; run.abort.abort(); }, timeoutMs);
181
189
  this.emit(run, 'run_started', 'created', 'Execution accepted.');
182
190
  this.emit(run, 'preflight_started', 'preflighting', 'Validating execution package.');
183
- const issues = preflight(run.request.executionPackage, run.request.inputs, run.request.runtimeValues ?? {});
191
+ const arguments_ = resolveDeclaredInputs(run.request.executionPackage.inputDeclarations, run.request.inputs);
192
+ run.request = { ...run.request, inputs: arguments_.inputs };
193
+ const issues = [...arguments_.issues, ...preflight(run.request.executionPackage, arguments_.inputs, run.request.runtimeValues ?? {})];
184
194
  if (issues.length > 0 || run.request.options?.dryRun) {
185
195
  const result = this.baseResult(run, startedAt, issues.length > 0 ? 'failed' : 'completed', undefined, issues);
186
196
  if (timeout)
@@ -211,7 +221,11 @@ export class DefaultExecutor {
211
221
  for (const flow of pkg.flows ?? []) {
212
222
  if (run.cancelled || run.abort.signal.aborted)
213
223
  throw new Error('Execution cancelled.');
214
- const child = await this.executeFlow(run, flow.steps, inputs, [flow.id], startedAt, { kind: 'flow', id: flow.id, name: flow.name });
224
+ const arguments_ = resolveDeclaredInputs(flow.inputDeclarations, inputs);
225
+ const issues = [...arguments_.issues, ...preflight({ ...pkg, target: { kind: 'flow', id: flow.id, name: flow.name }, requiredInputs: flow.requiredInputs }, arguments_.inputs, run.request.runtimeValues ?? {})];
226
+ if (issues.length)
227
+ return this.baseResult(run, startedAt, 'failed', undefined, issues, children);
228
+ const child = await this.executeFlow(run, flow.steps, arguments_.inputs, [flow.id], startedAt, { kind: 'flow', id: flow.id, name: flow.name });
215
229
  children.push(child);
216
230
  if (child.state !== 'completed')
217
231
  return this.baseResult(run, startedAt, child.state, child.error, undefined, children);
@@ -231,7 +245,7 @@ export class DefaultExecutor {
231
245
  const results = [];
232
246
  let state = 'completed';
233
247
  let error;
234
- for (const step of orderSteps(steps)) {
248
+ for (const step of run.request.executionPackage.ordering === 'routine' ? steps : orderSteps(steps)) {
235
249
  if (run.cancelled || signal.aborted)
236
250
  throw new Error('Execution cancelled.');
237
251
  await this.boundary(run, step, [...path, step.id], signal);
@@ -269,8 +283,17 @@ export class DefaultExecutor {
269
283
  return { ...base, ...controlResult, inputs, durationMs: Date.now() - startedMs };
270
284
  }
271
285
  if (step.kind === 'operation' && step.children) {
272
- const nested = await this.executeFlow(run, step.children, { ...scope, ...inputs }, [...path, step.id], new Date().toISOString(), run.request.executionPackage.target, signal);
273
- const outputs = nested.steps.at(-1)?.outputs ?? {};
286
+ const arguments_ = resolveDeclaredInputs(step.inputDeclarations, inputs);
287
+ if (arguments_.issues.length)
288
+ throw failure('BINDING_RESOLUTION_ERROR', arguments_.issues.map(issue => issue.message).join(' '));
289
+ const childScope = { ...scope, ...arguments_.inputs };
290
+ for (const declaration of step.inputDeclarations ?? [])
291
+ if (!Object.hasOwn(arguments_.inputs, declaration.name))
292
+ delete childScope[declaration.name];
293
+ const nested = await this.executeFlow(run, step.children, childScope, [...path, step.id], new Date().toISOString(), run.request.executionPackage.target, signal);
294
+ const outputs = step.outputBindings && nested.state === 'completed'
295
+ ? { ...step.staticOutputs, ...resolveBindings(step.outputBindings, childScope, new Map(nested.steps.map(child => [child.id, child.outputs]))) }
296
+ : nested.steps.at(-1)?.outputs ?? {};
274
297
  const result = { ...base, state: nested.state === 'completed' ? 'finished' : nested.state, inputs, outputs, durationMs: Date.now() - startedMs, children: nested.steps, terminalReason: nested.terminalReason ?? (nested.state === 'completed' ? 'completed' : 'failed'), error: nested.error };
275
298
  this.emit(run, 'step_finished', result.state, `${step.name} ${result.state}.`, step);
276
299
  return result;
@@ -297,9 +320,12 @@ export class DefaultExecutor {
297
320
  if (control.condition !== undefined && !evaluateCondition(control.condition, { ...scope, ...inputs }, outputs))
298
321
  return { state: 'skipped', outputs: {}, terminalReason: 'condition-skipped' };
299
322
  if (control.type === 'repeat') {
300
- const total = control.iterations ?? 1;
323
+ const total = run.request.executionPackage.ordering === 'routine' ? Math.max(1, Math.min(control.iterations ?? (control.durationMs !== undefined ? 32 : 1), 32)) : control.iterations ?? 1;
324
+ const loopStarted = Date.now();
301
325
  const children = [];
302
326
  for (let current = 1; current <= total; current++) {
327
+ if (run.request.executionPackage.ordering === 'routine' && control.durationMs !== undefined && Date.now() - loopStarted >= control.durationMs)
328
+ break;
303
329
  const child = await this.executeFlow(run, step.children ?? [], { ...scope, ...inputs }, [...path, step.id, String(current)], new Date().toISOString(), run.request.executionPackage.target, signal);
304
330
  children.push(...child.steps.map(item => ({ ...item, iteration: { loopId: step.id, current, total } })));
305
331
  if (child.state !== 'completed' && control.onError !== 'continue')
@@ -309,6 +335,18 @@ export class DefaultExecutor {
309
335
  }
310
336
  return { state: 'finished', outputs: children.at(-1)?.outputs ?? {}, children, terminalReason: 'completed' };
311
337
  }
338
+ if (control.type === 'branch' && run.request.executionPackage.ordering === 'routine') {
339
+ const children = [];
340
+ for (const branch of control.branches ?? []) {
341
+ if (!evaluateCondition(branch.condition, { ...scope, ...inputs }, outputs))
342
+ continue;
343
+ const nested = await this.executeFlow(run, branch.children, { ...scope, ...inputs }, [...path, step.id, branch.id], new Date().toISOString(), run.request.executionPackage.target, signal);
344
+ children.push(...nested.steps);
345
+ if (nested.state !== 'completed' && control.onError !== 'continue')
346
+ return { state: nested.state, outputs: nested.steps.at(-1)?.outputs ?? {}, children, terminalReason: nested.terminalReason, error: nested.error };
347
+ }
348
+ return { state: children.length ? 'finished' : 'skipped', outputs: children.at(-1)?.outputs ?? {}, children, terminalReason: children.length ? 'completed' : 'branch-skipped' };
349
+ }
312
350
  if (control.type === 'branch') {
313
351
  const branch = control.branches?.find(candidate => evaluateCondition(candidate.condition, { ...scope, ...inputs }, outputs));
314
352
  if (!branch)
@@ -318,13 +356,18 @@ export class DefaultExecutor {
318
356
  }
319
357
  if (control.type === 'parallel') {
320
358
  const branches = control.branches ?? [];
359
+ const join = run.request.executionPackage.ordering === 'routine' ? 'all' : control.join;
321
360
  const selected = branches.filter(branch => evaluateCondition(branch.condition, { ...scope, ...inputs }, outputs));
322
361
  const controllers = selected.map(() => new AbortController());
362
+ const abortBranches = () => controllers.forEach(controller => controller.abort());
363
+ signal.addEventListener('abort', abortBranches, { once: true });
364
+ if (signal.aborted)
365
+ abortBranches();
323
366
  let winnerIndex;
324
367
  const branchRuns = await Promise.all(selected.map(async (branch, index) => {
325
368
  try {
326
369
  const result = await this.executeFlow(run, branch.children, { ...scope, ...inputs }, [...path, step.id, branch.id], new Date().toISOString(), run.request.executionPackage.target, controllers[index].signal);
327
- if (control.join === 'first' && result.state === 'completed' && winnerIndex === undefined) {
370
+ if (join === 'first' && result.state === 'completed' && winnerIndex === undefined) {
328
371
  winnerIndex = index;
329
372
  controllers.forEach((controller, candidate) => { if (candidate !== index)
330
373
  controller.abort(); });
@@ -335,12 +378,13 @@ export class DefaultExecutor {
335
378
  return { branch, result: this.baseResult(run, new Date().toISOString(), controllers[index].signal.aborted ? 'cancelled' : 'failed', this.failure(cause, run, controllers[index].signal)) };
336
379
  }
337
380
  }));
381
+ signal.removeEventListener('abort', abortBranches);
338
382
  const nested = branchRuns.map(item => item.result);
339
383
  const children = nested.flatMap(item => item.steps);
340
- const considered = control.join === 'first' && winnerIndex !== undefined ? [branchRuns[winnerIndex]] : branchRuns;
384
+ const considered = join === 'first' && winnerIndex !== undefined ? [branchRuns[winnerIndex]] : branchRuns;
341
385
  const failed = considered.find(item => item.result.state !== 'completed');
342
386
  const branchOutputs = considered.map(({ branch, result }) => ({ id: branch.id, outputs: result.steps.at(-1)?.outputs ?? {} }));
343
- const mergedOutputs = mergeBranchOutputs(branchOutputs, control.mergeBindings, control.merge ?? (control.join === 'first' ? 'first' : 'collect'));
387
+ const mergedOutputs = mergeBranchOutputs(branchOutputs, control.mergeBindings, control.merge ?? (join === 'first' ? 'first' : 'collect'));
344
388
  return { state: failed ? invocationState(failed.result.state) : 'finished', outputs: mergedOutputs, children, terminalReason: failed?.result.terminalReason ?? 'parallel-joined', error: failed?.result.error };
345
389
  }
346
390
  const nested = await this.executeFlow(run, step.children ?? [], { ...scope, ...inputs }, [...path, step.id], new Date().toISOString(), run.request.executionPackage.target, signal);
@@ -434,8 +478,9 @@ function preflight(pkg, inputs, runtimeValues) {
434
478
  const issues = [];
435
479
  if (!pkg.packageId || !pkg.target?.id)
436
480
  issues.push({ code: 'INVALID_PACKAGE', message: 'Execution package identity is incomplete.' });
481
+ const flowInputNames = new Set(pkg.target.kind === 'project' ? (pkg.flows ?? []).flatMap(flow => Array.isArray(flow.inputDeclarations) ? flow.inputDeclarations.flatMap(input => typeof input?.name === 'string' ? [input.name] : []) : []) : []);
437
482
  for (const name of pkg.requiredInputs ?? [])
438
- if (inputs[name] === undefined)
483
+ if (inputs[name] === undefined && !flowInputNames.has(name))
439
484
  issues.push({ code: 'MISSING_INPUT', message: `Required input '${name}' is missing.` });
440
485
  for (const name of pkg.runtimeRequirements ?? [])
441
486
  if (runtimeValues[name] === undefined)
package/dist/index.d.ts CHANGED
@@ -2,3 +2,4 @@ export * from './types.js';
2
2
  export * from './executor.js';
3
3
  export * from './in-memory-action-executor.js';
4
4
  export * from './migrated-executor.js';
5
+ export { resolveDeclaredInputs } from './declared-inputs.js';
package/dist/index.js CHANGED
@@ -2,3 +2,4 @@ export * from './types.js';
2
2
  export * from './executor.js';
3
3
  export * from './in-memory-action-executor.js';
4
4
  export * from './migrated-executor.js';
5
+ export { resolveDeclaredInputs } from './declared-inputs.js';
@@ -6,6 +6,8 @@ export declare function createMigratedExecutor(configuration?: ExecutorConfigura
6
6
  * remains the migrated Runner implementation.
7
7
  */
8
8
  export declare class MigratedExecutor implements Executor {
9
+ private readonly routineRuns;
10
+ private readonly routine;
9
11
  private readonly adapters;
10
12
  private readonly engine;
11
13
  constructor(configuration?: ExecutorConfiguration);
@@ -1,3 +1,4 @@
1
+ import { createExecutor } from './executor.js';
1
2
  import { MigratedRunnerEngine } from './migrated-runner-engine.js';
2
3
  import { MigratedProjectRunnerEngine } from './migrated-project-engine.js';
3
4
  export function createMigratedExecutor(configuration = {}) {
@@ -9,20 +10,30 @@ export function createMigratedExecutor(configuration = {}) {
9
10
  * remains the migrated Runner implementation.
10
11
  */
11
12
  export class MigratedExecutor {
13
+ routineRuns = new Set();
14
+ routine;
12
15
  adapters;
13
16
  engine;
14
17
  constructor(configuration = {}) {
18
+ this.routine = createExecutor(configuration);
15
19
  this.adapters = (configuration.actionExecutors ?? []).map(toActionAdapter);
16
20
  this.engine = new MigratedProjectRunnerEngine(new MigratedRunnerEngine(this.adapters));
17
21
  }
18
- registerActionExecutor(executor) { this.adapters.push(toActionAdapter(executor)); }
22
+ registerActionExecutor(executor) { this.routine.registerActionExecutor(executor); this.adapters.push(toActionAdapter(executor)); }
19
23
  async execute(request) {
24
+ if (request.executionPackage.ordering === 'routine') {
25
+ this.routineRuns.add(request.requestId);
26
+ return this.routine.execute(request);
27
+ }
20
28
  const runnerRequest = request;
21
29
  await this.engine.submit(runnerRequest);
22
30
  return { requestId: request.requestId, result: this.result(request.requestId) };
23
31
  }
24
- async cancel(requestId) { await this.engine.cancel(requestId); }
32
+ async cancel(requestId) { if (this.routineRuns.has(requestId))
33
+ return this.routine.cancel(requestId); await this.engine.cancel(requestId); }
25
34
  async control(requestId, command) {
35
+ if (this.routineRuns.has(requestId))
36
+ return this.routine.control(requestId, command);
26
37
  if (command.type === 'pause')
27
38
  return this.engine.pauseExecution(requestId);
28
39
  if (command.type === 'resume')
@@ -34,12 +45,18 @@ export class MigratedExecutor {
34
45
  if (command.type === 'stop-after')
35
46
  return;
36
47
  }
37
- controlState(requestId) { return this.engine.controlState(requestId); }
38
- async *events(requestId) { yield* this.engine.events(requestId); }
39
- async result(requestId) { return this.engine.result(requestId); }
40
- async retryAction(requestId, stepId, invocationPath) { await this.engine.retryAction(requestId, stepId, invocationPath); }
41
- async retryOperation(requestId, stepId, invocationPath) { await this.engine.retryOperation(requestId, stepId, invocationPath); }
48
+ controlState(requestId) { return this.routineRuns.has(requestId) ? this.routine.controlState(requestId) : this.engine.controlState(requestId); }
49
+ async *events(requestId) { yield* (this.routineRuns.has(requestId) ? this.routine.events(requestId) : this.engine.events(requestId)); }
50
+ async result(requestId) { return (this.routineRuns.has(requestId) ? this.routine.result(requestId) : this.engine.result(requestId)); }
51
+ async retryAction(requestId, stepId, invocationPath) { await (this.routineRuns.has(requestId) ? this.routine.retryAction(requestId, stepId, invocationPath) : this.engine.retryAction(requestId, stepId, invocationPath)); }
52
+ async retryOperation(requestId, stepId, invocationPath) { await (this.routineRuns.has(requestId) ? this.routine.retryOperation(requestId, stepId, invocationPath) : this.engine.retryOperation(requestId, stepId, invocationPath)); }
42
53
  async runFromHere(requestId, stepId, invocationPath) {
54
+ if (this.routineRuns.has(requestId)) {
55
+ const targeted = await this.routine.runFromHere(requestId, stepId, invocationPath);
56
+ if (targeted)
57
+ this.routineRuns.add(targeted.requestId);
58
+ return targeted;
59
+ }
43
60
  const accepted = await this.engine.runFromHere(requestId, stepId, invocationPath);
44
61
  return accepted ? { requestId: accepted.requestId, result: this.result(accepted.requestId) } : undefined;
45
62
  }
@@ -90,7 +90,7 @@ export class MigratedProjectRunnerEngine {
90
90
  }
91
91
  const childRequestId = `${request.requestId}:${flow.id}`;
92
92
  run.activeRequestId = childRequestId;
93
- const childPackage = { ...request.executionPackage, target: { kind: 'flow', id: flow.id, name: flow.name }, steps: flow.steps, flows: undefined, requiredInputs: flow.requiredInputs };
93
+ const childPackage = { ...request.executionPackage, target: { kind: 'flow', id: flow.id, name: flow.name }, steps: flow.steps, flows: undefined, requiredInputs: flow.requiredInputs, inputDeclarations: flow.inputDeclarations };
94
94
  await this.flowEngine.submit({ requestId: childRequestId, executionPackage: childPackage, inputs, environment: request.environment, runtimeValues: request.runtimeValues, options: request.options, debugSession: request.debugSession });
95
95
  for await (const event of this.flowEngine.events(childRequestId))
96
96
  this.emit(run, event.type, event.state, `[${flow.name}] ${event.message}`, event.stepId ? { stepId: event.stepId, stepName: event.stepName } : undefined, event.parallel, event.control);
@@ -1,3 +1,4 @@
1
+ import { resolveDeclaredInputs } from './declared-inputs.js';
1
2
  import { InMemoryActionAdapter } from './migrated-adapter.js';
2
3
  import { hashExecutionPackage } from './migrated-artifact-hash.js';
3
4
  import { RUNNER_PACKAGE_SCHEMA_VERSION } from './migrated-types.js';
@@ -119,7 +120,7 @@ export class MigratedRunnerEngine {
119
120
  return new Promise(resolve => { const previous = run.resultResolve; run.resultResolve = result => { previous(result); resolve(result); }; });
120
121
  }
121
122
  async execute(run, resultPromise) {
122
- const { request } = run;
123
+ let { request } = run;
123
124
  const startedAt = new Date().toISOString();
124
125
  run.controlState = 'running';
125
126
  this.emit(run, 'run_started', 'created', 'Run accepted.');
@@ -133,7 +134,10 @@ export class MigratedRunnerEngine {
133
134
  await resultPromise;
134
135
  return;
135
136
  }
136
- const validationIssues = this.validateAll(request.executionPackage, request.inputs, request.runtimeValues ?? {});
137
+ const arguments_ = resolveDeclaredInputs(request.executionPackage.inputDeclarations, request.inputs);
138
+ run.request = { ...request, inputs: arguments_.inputs };
139
+ request = run.request;
140
+ const validationIssues = [...arguments_.issues, ...this.validateAll(request.executionPackage, arguments_.inputs, request.runtimeValues ?? {})];
137
141
  const validationWarnings = this.validateBrowserWarnings(request.executionPackage);
138
142
  if (request.options?.dryRun) {
139
143
  const dryRunResult = { requestId: request.requestId, state: validationIssues.length ? 'failed' : 'completed', target: request.executionPackage.target, environment: request.environment, startedAt, finishedAt: new Date().toISOString(), steps: [], terminalReason: validationIssues.length ? 'failed' : 'completed', issues: validationIssues, warnings: validationWarnings, ...(validationIssues.length ? { error: { code: 'DRY_RUN_ISSUES', message: `${validationIssues.length} issue${validationIssues.length === 1 ? '' : 's'} found during dry run.` } } : {}) };
@@ -1,7 +1,7 @@
1
1
  /** Compatibility import for Runner modules. The canonical contract is the published @qaick/contract package. */
2
2
  export * from '@qaick/contract';
3
3
  /** The exact published contract package consumed by this Runner release. */
4
- export declare const RUNNER_CONTRACT_VERSION: "0.2.10";
4
+ export declare const RUNNER_CONTRACT_VERSION: "0.2.12";
5
5
  export type ExecutionControlState = 'idle' | 'running' | 'pause-requested' | 'paused' | 'resuming' | 'stopped' | 'failed' | 'completed';
6
6
  export interface RunnerDebugSession {
7
7
  breakpoints: string[];
@@ -1,4 +1,4 @@
1
1
  /** Compatibility import for Runner modules. The canonical contract is the published @qaick/contract package. */
2
2
  export * from '@qaick/contract';
3
3
  /** The exact published contract package consumed by this Runner release. */
4
- export const RUNNER_CONTRACT_VERSION = '0.2.10';
4
+ export const RUNNER_CONTRACT_VERSION = '0.2.12';
package/package.json CHANGED
@@ -1,10 +1,18 @@
1
1
  {
2
2
  "name": "@qaick/executor",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0",
4
4
  "description": "Host-neutral QAick action execution primitives.",
5
- "files": ["dist", "README.md"],
6
- "engines": { "node": ">=20" },
7
- "publishConfig": { "access": "public", "tag": "alpha" },
5
+ "files": [
6
+ "dist",
7
+ "README.md"
8
+ ],
9
+ "engines": {
10
+ "node": ">=20"
11
+ },
12
+ "publishConfig": {
13
+ "access": "public",
14
+ "tag": "latest"
15
+ },
8
16
  "repository": {
9
17
  "type": "git",
10
18
  "url": "git+https://github.com/vladmus/QAICK_Executor.git"
@@ -26,7 +34,7 @@
26
34
  "prepublishOnly": "npm run typecheck && npm test && npm run build"
27
35
  },
28
36
  "dependencies": {
29
- "@qaick/contract": "0.2.11"
37
+ "@qaick/contract": "0.2.13"
30
38
  },
31
39
  "devDependencies": {
32
40
  "@types/node": "^24.13.3",