@farmslot/recipe-harness 0.20.0 → 0.21.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/CHANGELOG.md CHANGED
@@ -6,6 +6,10 @@ All notable changes to `@farmslot/recipe-harness` are tracked here.
6
6
 
7
7
  - Active-development baseline; add user-facing changes here before release or package publication.
8
8
 
9
+ ## 0.21.0 - 2026-09-29
10
+
11
+ - Retain invocation-scoped command stages, timings and logs for live operation observers.
12
+
9
13
  ## 0.20.0 - 2026-09-28
10
14
 
11
15
  - Prefer capture-helper's native frame and snapshot timing, wait for its first recorded frame, and retain trace-linked action markers; preserve variable frame timing for browser recording fallback.
@@ -0,0 +1,31 @@
1
+ import { type TaskOperation } from '@farmslot/protocol';
2
+ export declare function processIdentity(pid: number): string | undefined;
3
+ export declare class OperationRecord {
4
+ readonly file: string;
5
+ readonly value: TaskOperation;
6
+ private readonly files;
7
+ private readonly timer;
8
+ private lastFlush;
9
+ private disabled;
10
+ private readonly notification?;
11
+ constructor(directory: string, command: string, target: string, options?: {
12
+ parentId?: string;
13
+ mirrorDirectory?: string;
14
+ });
15
+ stage(stage: string): void;
16
+ output(chunk: string | Uint8Array): void;
17
+ finish(exitCode: number): void;
18
+ private observe;
19
+ private flush;
20
+ }
21
+ export declare function readOperations(directory: string, onInvalid?: (error: Error) => void): TaskOperation[];
22
+ /** Keep captured text bounded without copying the whole tail for every chunk. */
23
+ export declare class OperationOutputTail {
24
+ private limit;
25
+ private chunks;
26
+ private length;
27
+ constructor(limit?: number);
28
+ append(chunk: string): void;
29
+ toString(): string;
30
+ }
31
+ //# sourceMappingURL=operation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation.d.ts","sourceRoot":"","sources":["../../src/runtime/operation.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,aAAa,EAA2B,MAAM,oBAAoB,CAAC;AAEjF,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW/D;AAED,qBAAa,eAAe;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAW;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiC;IACvD,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;gBAGrC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAO;IAoC/D,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAO1B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IASxC,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQ9B,OAAO,CAAC,OAAO;IAef,OAAO,CAAC,KAAK;CAgBd;AAED,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GACjC,aAAa,EAAE,CAqBjB;AAED,iFAAiF;AACjF,qBAAa,mBAAmB;IAGlB,OAAO,CAAC,KAAK;IAFzB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,MAAM,CAAK;gBACC,KAAK,SAAmB;IAC5C,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAY3B,QAAQ,IAAI,MAAM;CAGnB"}
@@ -0,0 +1,165 @@
1
+ // Invocation records keep concurrent command observations independent of locks.
2
+ import { execFileSync } from 'node:child_process';
3
+ import { randomUUID } from 'node:crypto';
4
+ import fs from 'node:fs';
5
+ import path from 'node:path';
6
+ import { validateOperationRecord } from '@farmslot/protocol';
7
+ export function processIdentity(pid) {
8
+ try {
9
+ const value = execFileSync('ps', ['-p', String(pid), '-o', 'lstart='], {
10
+ encoding: 'utf8',
11
+ stdio: ['ignore', 'pipe', 'pipe'],
12
+ }).trim();
13
+ return value || undefined;
14
+ }
15
+ catch (error) {
16
+ if (error.status === 1)
17
+ return undefined;
18
+ throw error;
19
+ }
20
+ }
21
+ export class OperationRecord {
22
+ constructor(directory, command, target, options = {}) {
23
+ this.lastFlush = 0;
24
+ this.disabled = false;
25
+ const id = randomUUID();
26
+ const now = new Date().toISOString();
27
+ this.notification = options.mirrorDirectory
28
+ ? path.join(path.dirname(options.mirrorDirectory), 'operations-updated.json')
29
+ : undefined;
30
+ this.file = path.join(directory, `${id}.json`);
31
+ this.files = [
32
+ this.file,
33
+ ...(options.mirrorDirectory ? [path.join(options.mirrorDirectory, `${id}.json`)] : []),
34
+ ];
35
+ const logPath = path.join(options.mirrorDirectory ?? directory, `${id}.log`);
36
+ fs.mkdirSync(path.dirname(logPath), { recursive: true });
37
+ fs.writeFileSync(logPath, '', { flag: 'wx', mode: 0o600 });
38
+ const identity = processIdentity(process.pid);
39
+ if (!identity)
40
+ throw new Error('Cannot identify the operation owner process');
41
+ this.value = {
42
+ schemaVersion: 1,
43
+ id,
44
+ ...(options.parentId ? { parentId: options.parentId } : {}),
45
+ command,
46
+ target,
47
+ pid: process.pid,
48
+ processStartedAt: identity,
49
+ startedAt: now,
50
+ updatedAt: now,
51
+ status: 'running',
52
+ logPath,
53
+ };
54
+ for (const file of this.files)
55
+ fs.mkdirSync(path.dirname(file), { recursive: true });
56
+ this.flush();
57
+ this.timer = setInterval(() => this.observe(() => this.flush()), 5000);
58
+ this.timer.unref();
59
+ }
60
+ stage(stage) {
61
+ if (stage === this.value.stage)
62
+ return;
63
+ this.value.stage = stage;
64
+ this.value.stageStartedAt = new Date().toISOString();
65
+ this.observe(() => this.flush());
66
+ }
67
+ output(chunk) {
68
+ if (chunk.length === 0)
69
+ return;
70
+ this.observe(() => {
71
+ fs.appendFileSync(this.value.logPath, chunk);
72
+ this.value.lastOutputAt = new Date().toISOString();
73
+ if (Date.now() - this.lastFlush >= 1000)
74
+ this.flush();
75
+ });
76
+ }
77
+ finish(exitCode) {
78
+ clearInterval(this.timer);
79
+ this.value.exitCode = exitCode;
80
+ this.value.status = exitCode === 0 ? 'pass' : 'fail';
81
+ this.value.finishedAt = new Date().toISOString();
82
+ this.observe(() => this.flush());
83
+ }
84
+ observe(write) {
85
+ if (this.disabled)
86
+ return;
87
+ try {
88
+ write();
89
+ }
90
+ catch (error) {
91
+ // Observation is optional. Stop it explicitly without terminating the
92
+ // command or its children from an event callback. Old records go stale.
93
+ this.disabled = true;
94
+ clearInterval(this.timer);
95
+ process.stderr.write(`Operation observation disabled for ${this.value.id}: ${error.message}\n`);
96
+ }
97
+ }
98
+ flush() {
99
+ this.lastFlush = Date.now();
100
+ this.value.updatedAt = new Date(this.lastFlush).toISOString();
101
+ for (const file of this.files) {
102
+ const temporary = `${file}.${process.pid}.tmp`;
103
+ fs.writeFileSync(temporary, `${JSON.stringify(this.value)}\n`, { mode: 0o600 });
104
+ fs.renameSync(temporary, file);
105
+ }
106
+ if (this.notification) {
107
+ const temporary = `${this.notification}.${this.value.id}.tmp`;
108
+ fs.writeFileSync(temporary, JSON.stringify({ updatedAt: this.value.updatedAt }), {
109
+ mode: 0o600,
110
+ });
111
+ fs.renameSync(temporary, this.notification);
112
+ }
113
+ }
114
+ }
115
+ export function readOperations(directory, onInvalid) {
116
+ if (!fs.existsSync(directory))
117
+ return [];
118
+ return fs
119
+ .readdirSync(directory)
120
+ .filter((name) => /^[a-f0-9-]{36}\.json$/.test(name))
121
+ .flatMap((name) => {
122
+ try {
123
+ const value = JSON.parse(fs.readFileSync(path.join(directory, name), 'utf8'));
124
+ validateOperationRecord(value, name.slice(0, -5));
125
+ return [value];
126
+ }
127
+ catch (error) {
128
+ // Another invocation may prune a completed record between list and read.
129
+ if (error.code === 'ENOENT')
130
+ return [];
131
+ if (!onInvalid)
132
+ throw error;
133
+ onInvalid(error);
134
+ return [];
135
+ }
136
+ })
137
+ .sort((a, b) => a.startedAt.localeCompare(b.startedAt));
138
+ }
139
+ /** Keep captured text bounded without copying the whole tail for every chunk. */
140
+ export class OperationOutputTail {
141
+ constructor(limit = 64 * 1024 * 1024) {
142
+ this.limit = limit;
143
+ this.chunks = [];
144
+ this.length = 0;
145
+ }
146
+ append(chunk) {
147
+ if (!chunk)
148
+ return;
149
+ this.chunks.push(chunk);
150
+ this.length += chunk.length;
151
+ while (this.length > this.limit) {
152
+ const first = this.chunks[0];
153
+ const remove = Math.min(first.length, this.length - this.limit);
154
+ if (remove === first.length)
155
+ this.chunks.shift();
156
+ else
157
+ this.chunks[0] = first.slice(remove);
158
+ this.length -= remove;
159
+ }
160
+ }
161
+ toString() {
162
+ return this.chunks.join('');
163
+ }
164
+ }
165
+ //# sourceMappingURL=operation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation.js","sourceRoot":"","sources":["../../src/runtime/operation.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAsB,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAEjF,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE;YACrE,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,KAAK,IAAI,SAAS,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA6B,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QAClE,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,OAAO,eAAe;IAS1B,YACE,SAAiB,EACjB,OAAe,EACf,MAAc,EACd,UAA2D,EAAE;QARvD,cAAS,GAAG,CAAC,CAAC;QACd,aAAQ,GAAG,KAAK,CAAC;QASvB,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,eAAe;YACzC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,yBAAyB,CAAC;YAC7E,CAAC,CAAC,SAAS,CAAC;QACd,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK,GAAG;YACX,IAAI,CAAC,IAAI;YACT,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;SACvF,CAAC;QACF,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,IAAI,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;QAC7E,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QAC9E,IAAI,CAAC,KAAK,GAAG;YACX,aAAa,EAAE,CAAC;YAChB,EAAE;YACF,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3D,OAAO;YACP,MAAM;YACN,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,gBAAgB,EAAE,QAAQ;YAC1B,SAAS,EAAE,GAAG;YACd,SAAS,EAAE,GAAG;YACd,MAAM,EAAE,SAAS;YACjB,OAAO;SACR,CAAC;QACF,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK;YAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrF,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,IAAI,CAAC,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;QACvE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,KAAa;QACjB,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK;YAAE,OAAO;QACvC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QACzB,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrD,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,CAAC,KAA0B;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC/B,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;YAChB,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAC7C,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACnD,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI;gBAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QACxD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,QAAgB;QACrB,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;QACrD,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACjD,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACnC,CAAC;IAEO,OAAO,CAAC,KAAiB;QAC/B,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC;YACH,KAAK,EAAE,CAAC;QACV,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,sEAAsE;YACtE,wEAAwE;YACxE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACrB,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,sCAAsC,IAAI,CAAC,KAAK,CAAC,EAAE,KAAM,KAAe,CAAC,OAAO,IAAI,CACrF,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,KAAK;QACX,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9D,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,MAAM,SAAS,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;YAC/C,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAChF,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,MAAM,SAAS,GAAG,GAAG,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAC9D,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,EAAE;gBAC/E,IAAI,EAAE,KAAK;aACZ,CAAC,CAAC;YACH,EAAE,CAAC,UAAU,CAAC,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;CACF;AAED,MAAM,UAAU,cAAc,CAC5B,SAAiB,EACjB,SAAkC;IAElC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,EAAE,CAAC;IACzC,OAAO,EAAE;SACN,WAAW,CAAC,SAAS,CAAC;SACtB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SACpD,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAChB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CACtB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CACnC,CAAC;YACnB,uBAAuB,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAClD,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,yEAAyE;YACzE,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,EAAE,CAAC;YAClE,IAAI,CAAC,SAAS;gBAAE,MAAM,KAAK,CAAC;YAC5B,SAAS,CAAC,KAAc,CAAC,CAAC;YAC1B,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,iFAAiF;AACjF,MAAM,OAAO,mBAAmB;IAG9B,YAAoB,QAAQ,EAAE,GAAG,IAAI,GAAG,IAAI;QAAxB,UAAK,GAAL,KAAK,CAAmB;QAFpC,WAAM,GAAa,EAAE,CAAC;QACtB,WAAM,GAAG,CAAC,CAAC;IAC4B,CAAC;IAChD,MAAM,CAAC,KAAa;QAClB,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxB,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC;QAC5B,OAAO,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;YAChE,IAAI,MAAM,KAAK,KAAK,CAAC,MAAM;gBAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;;gBAC5C,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC1C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC;QACxB,CAAC;IACH,CAAC;IACD,QAAQ;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC9B,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farmslot/recipe-harness",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -98,6 +98,11 @@
98
98
  "types": "./visual-review/index.d.mts",
99
99
  "import": "./visual-review/index.mjs",
100
100
  "default": "./visual-review/index.mjs"
101
+ },
102
+ "./runtime/operation": {
103
+ "types": "./dist/runtime/operation.d.ts",
104
+ "import": "./dist/runtime/operation.js",
105
+ "default": "./dist/runtime/operation.js"
101
106
  }
102
107
  },
103
108
  "scripts": {
@@ -113,7 +118,7 @@
113
118
  "prepublishOnly": "node ../../scripts/quality/check-farmslot-package-readiness.mjs --publish --packages @farmslot/recipe-harness"
114
119
  },
115
120
  "dependencies": {
116
- "@farmslot/protocol": "0.32.0",
121
+ "@farmslot/protocol": "0.33.0",
117
122
  "commander": "^12.0.0",
118
123
  "semver": "^7.7.3",
119
124
  "ws": "^8.18.0"