@farmslot/recipe-runner 0.23.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 (212) hide show
  1. package/CHANGELOG.md +255 -0
  2. package/LICENSE +21 -0
  3. package/README.md +274 -0
  4. package/bin/farmslot-visual-review.mjs +152 -0
  5. package/dist/adapters/app-lifecycle.d.ts +41 -0
  6. package/dist/adapters/app-lifecycle.d.ts.map +1 -0
  7. package/dist/adapters/app-lifecycle.js +224 -0
  8. package/dist/adapters/app-lifecycle.js.map +1 -0
  9. package/dist/adapters/core.d.ts +5 -0
  10. package/dist/adapters/core.d.ts.map +1 -0
  11. package/dist/adapters/core.js +433 -0
  12. package/dist/adapters/core.js.map +1 -0
  13. package/dist/adapters/gesture.d.ts +12 -0
  14. package/dist/adapters/gesture.d.ts.map +1 -0
  15. package/dist/adapters/gesture.js +76 -0
  16. package/dist/adapters/gesture.js.map +1 -0
  17. package/dist/adapters/ui.d.ts +33 -0
  18. package/dist/adapters/ui.d.ts.map +1 -0
  19. package/dist/adapters/ui.js +130 -0
  20. package/dist/adapters/ui.js.map +1 -0
  21. package/dist/cli/error-output.d.ts +6 -0
  22. package/dist/cli/error-output.d.ts.map +1 -0
  23. package/dist/cli/error-output.js +19 -0
  24. package/dist/cli/error-output.js.map +1 -0
  25. package/dist/cli/index.d.ts +12 -0
  26. package/dist/cli/index.d.ts.map +1 -0
  27. package/dist/cli/index.js +29 -0
  28. package/dist/cli/index.js.map +1 -0
  29. package/dist/cli/run-command.d.ts +8 -0
  30. package/dist/cli/run-command.d.ts.map +1 -0
  31. package/dist/cli/run-command.js +253 -0
  32. package/dist/cli/run-command.js.map +1 -0
  33. package/dist/cli/support.d.ts +31 -0
  34. package/dist/cli/support.d.ts.map +1 -0
  35. package/dist/cli/support.js +297 -0
  36. package/dist/cli/support.js.map +1 -0
  37. package/dist/cli/validate-command.d.ts +4 -0
  38. package/dist/cli/validate-command.d.ts.map +1 -0
  39. package/dist/cli/validate-command.js +52 -0
  40. package/dist/cli/validate-command.js.map +1 -0
  41. package/dist/core/compose.d.ts +23 -0
  42. package/dist/core/compose.d.ts.map +1 -0
  43. package/dist/core/compose.js +104 -0
  44. package/dist/core/compose.js.map +1 -0
  45. package/dist/core/execution.d.ts +44 -0
  46. package/dist/core/execution.d.ts.map +1 -0
  47. package/dist/core/execution.js +304 -0
  48. package/dist/core/execution.js.map +1 -0
  49. package/dist/core/failure.d.ts +23 -0
  50. package/dist/core/failure.d.ts.map +1 -0
  51. package/dist/core/failure.js +43 -0
  52. package/dist/core/failure.js.map +1 -0
  53. package/dist/core/graph.d.ts +12 -0
  54. package/dist/core/graph.d.ts.map +1 -0
  55. package/dist/core/graph.js +65 -0
  56. package/dist/core/graph.js.map +1 -0
  57. package/dist/core/hud.d.ts +11 -0
  58. package/dist/core/hud.d.ts.map +1 -0
  59. package/dist/core/hud.js +120 -0
  60. package/dist/core/hud.js.map +1 -0
  61. package/dist/core/invocation.d.ts +13 -0
  62. package/dist/core/invocation.d.ts.map +1 -0
  63. package/dist/core/invocation.js +68 -0
  64. package/dist/core/invocation.js.map +1 -0
  65. package/dist/core/json.d.ts +9 -0
  66. package/dist/core/json.d.ts.map +1 -0
  67. package/dist/core/json.js +74 -0
  68. package/dist/core/json.js.map +1 -0
  69. package/dist/core/library-manifest.d.ts +53 -0
  70. package/dist/core/library-manifest.d.ts.map +1 -0
  71. package/dist/core/library-manifest.js +263 -0
  72. package/dist/core/library-manifest.js.map +1 -0
  73. package/dist/core/library.d.ts +80 -0
  74. package/dist/core/library.d.ts.map +1 -0
  75. package/dist/core/library.js +321 -0
  76. package/dist/core/library.js.map +1 -0
  77. package/dist/core/parameters.d.ts +5 -0
  78. package/dist/core/parameters.d.ts.map +1 -0
  79. package/dist/core/parameters.js +57 -0
  80. package/dist/core/parameters.js.map +1 -0
  81. package/dist/core/passive-observations.d.ts +25 -0
  82. package/dist/core/passive-observations.d.ts.map +1 -0
  83. package/dist/core/passive-observations.js +62 -0
  84. package/dist/core/passive-observations.js.map +1 -0
  85. package/dist/core/path.d.ts +7 -0
  86. package/dist/core/path.d.ts.map +1 -0
  87. package/dist/core/path.js +147 -0
  88. package/dist/core/path.js.map +1 -0
  89. package/dist/core/recording-cleanup.d.ts +10 -0
  90. package/dist/core/recording-cleanup.d.ts.map +1 -0
  91. package/dist/core/recording-cleanup.js +32 -0
  92. package/dist/core/recording-cleanup.js.map +1 -0
  93. package/dist/core/resolution-error.d.ts +7 -0
  94. package/dist/core/resolution-error.d.ts.map +1 -0
  95. package/dist/core/resolution-error.js +9 -0
  96. package/dist/core/resolution-error.js.map +1 -0
  97. package/dist/core/runner.d.ts +4 -0
  98. package/dist/core/runner.d.ts.map +1 -0
  99. package/dist/core/runner.js +738 -0
  100. package/dist/core/runner.js.map +1 -0
  101. package/dist/core/scroll-to.d.ts +111 -0
  102. package/dist/core/scroll-to.d.ts.map +1 -0
  103. package/dist/core/scroll-to.js +353 -0
  104. package/dist/core/scroll-to.js.map +1 -0
  105. package/dist/core/suite.d.ts +25 -0
  106. package/dist/core/suite.d.ts.map +1 -0
  107. package/dist/core/suite.js +76 -0
  108. package/dist/core/suite.js.map +1 -0
  109. package/dist/core/trace.d.ts +5 -0
  110. package/dist/core/trace.d.ts.map +1 -0
  111. package/dist/core/trace.js +25 -0
  112. package/dist/core/trace.js.map +1 -0
  113. package/dist/core/trust-error.d.ts +9 -0
  114. package/dist/core/trust-error.d.ts.map +1 -0
  115. package/dist/core/trust-error.js +18 -0
  116. package/dist/core/trust-error.js.map +1 -0
  117. package/dist/core/trust-input.d.ts +20 -0
  118. package/dist/core/trust-input.d.ts.map +1 -0
  119. package/dist/core/trust-input.js +68 -0
  120. package/dist/core/trust-input.js.map +1 -0
  121. package/dist/core/trust.d.ts +28 -0
  122. package/dist/core/trust.d.ts.map +1 -0
  123. package/dist/core/trust.js +284 -0
  124. package/dist/core/trust.js.map +1 -0
  125. package/dist/core/types.d.ts +319 -0
  126. package/dist/core/types.d.ts.map +1 -0
  127. package/dist/core/types.js +2 -0
  128. package/dist/core/types.js.map +1 -0
  129. package/dist/core/validation.d.ts +4 -0
  130. package/dist/core/validation.d.ts.map +1 -0
  131. package/dist/core/validation.js +18 -0
  132. package/dist/core/validation.js.map +1 -0
  133. package/dist/index.d.ts +32 -0
  134. package/dist/index.d.ts.map +1 -0
  135. package/dist/index.js +25 -0
  136. package/dist/index.js.map +1 -0
  137. package/dist/node/writers.d.ts +27 -0
  138. package/dist/node/writers.d.ts.map +1 -0
  139. package/dist/node/writers.js +99 -0
  140. package/dist/node/writers.js.map +1 -0
  141. package/dist/recording/android-mirror.d.ts +10 -0
  142. package/dist/recording/android-mirror.d.ts.map +1 -0
  143. package/dist/recording/android-mirror.js +180 -0
  144. package/dist/recording/android-mirror.js.map +1 -0
  145. package/dist/recording/capture-helper-timing.d.ts +4 -0
  146. package/dist/recording/capture-helper-timing.d.ts.map +1 -0
  147. package/dist/recording/capture-helper-timing.js +47 -0
  148. package/dist/recording/capture-helper-timing.js.map +1 -0
  149. package/dist/recording/capture-helper.d.ts +10 -0
  150. package/dist/recording/capture-helper.d.ts.map +1 -0
  151. package/dist/recording/capture-helper.js +335 -0
  152. package/dist/recording/capture-helper.js.map +1 -0
  153. package/dist/recording/cdp-video-recorder.d.ts +9 -0
  154. package/dist/recording/cdp-video-recorder.d.ts.map +1 -0
  155. package/dist/recording/cdp-video-recorder.js +319 -0
  156. package/dist/recording/cdp-video-recorder.js.map +1 -0
  157. package/dist/recording/frame-sampler.d.ts +19 -0
  158. package/dist/recording/frame-sampler.d.ts.map +1 -0
  159. package/dist/recording/frame-sampler.js +29 -0
  160. package/dist/recording/frame-sampler.js.map +1 -0
  161. package/dist/recording/timeline.d.ts +15 -0
  162. package/dist/recording/timeline.d.ts.map +1 -0
  163. package/dist/recording/timeline.js +88 -0
  164. package/dist/recording/timeline.js.map +1 -0
  165. package/dist/runtime/browser-extension.d.ts +17 -0
  166. package/dist/runtime/browser-extension.d.ts.map +1 -0
  167. package/dist/runtime/browser-extension.js +42 -0
  168. package/dist/runtime/browser-extension.js.map +1 -0
  169. package/dist/runtime/cdp.d.ts +138 -0
  170. package/dist/runtime/cdp.d.ts.map +1 -0
  171. package/dist/runtime/cdp.js +1429 -0
  172. package/dist/runtime/cdp.js.map +1 -0
  173. package/dist/runtime/decision-types.d.ts +23 -0
  174. package/dist/runtime/decision-types.d.ts.map +1 -0
  175. package/dist/runtime/decision-types.js +2 -0
  176. package/dist/runtime/decision-types.js.map +1 -0
  177. package/dist/runtime/deps-readiness.d.ts +21 -0
  178. package/dist/runtime/deps-readiness.d.ts.map +1 -0
  179. package/dist/runtime/deps-readiness.js +141 -0
  180. package/dist/runtime/deps-readiness.js.map +1 -0
  181. package/dist/runtime/log-analysis.d.ts +48 -0
  182. package/dist/runtime/log-analysis.d.ts.map +1 -0
  183. package/dist/runtime/log-analysis.js +128 -0
  184. package/dist/runtime/log-analysis.js.map +1 -0
  185. package/dist/runtime/metro-probe.d.ts +5 -0
  186. package/dist/runtime/metro-probe.d.ts.map +1 -0
  187. package/dist/runtime/metro-probe.js +24 -0
  188. package/dist/runtime/metro-probe.js.map +1 -0
  189. package/dist/runtime/operation.d.ts +32 -0
  190. package/dist/runtime/operation.d.ts.map +1 -0
  191. package/dist/runtime/operation.js +184 -0
  192. package/dist/runtime/operation.js.map +1 -0
  193. package/dist/runtime/orchestrate-up.d.ts +22 -0
  194. package/dist/runtime/orchestrate-up.d.ts.map +1 -0
  195. package/dist/runtime/orchestrate-up.js +35 -0
  196. package/dist/runtime/orchestrate-up.js.map +1 -0
  197. package/dist/runtime/react-native-bridge.d.ts +17 -0
  198. package/dist/runtime/react-native-bridge.d.ts.map +1 -0
  199. package/dist/runtime/react-native-bridge.js +57 -0
  200. package/dist/runtime/react-native-bridge.js.map +1 -0
  201. package/dist/version.d.ts +2 -0
  202. package/dist/version.d.ts.map +1 -0
  203. package/dist/version.js +8 -0
  204. package/dist/version.js.map +1 -0
  205. package/package.json +163 -0
  206. package/test/fixtures/scroll-to-visible/README.md +39 -0
  207. package/visual-review/feedback-draft.mjs +82 -0
  208. package/visual-review/index.d.mts +41 -0
  209. package/visual-review/index.mjs +4 -0
  210. package/visual-review/recipe-board.mjs +142 -0
  211. package/visual-review/review-board.mjs +981 -0
  212. package/visual-review/server.mjs +69 -0
@@ -0,0 +1,184 @@
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
+ ensureRuntimeDirectory(path.dirname(logPath));
37
+ fs.writeFileSync(logPath, '', { flag: 'wx', mode: 0o600 });
38
+ fs.chmodSync(logPath, 0o600);
39
+ const identity = processIdentity(process.pid);
40
+ if (!identity)
41
+ throw new Error('Cannot identify the operation owner process');
42
+ this.value = {
43
+ schemaVersion: 1,
44
+ id,
45
+ ...(options.parentId ? { parentId: options.parentId } : {}),
46
+ command,
47
+ target,
48
+ pid: process.pid,
49
+ processStartedAt: identity,
50
+ startedAt: now,
51
+ updatedAt: now,
52
+ status: 'running',
53
+ logPath,
54
+ };
55
+ for (const file of this.files)
56
+ ensureRuntimeDirectory(path.dirname(file));
57
+ this.flush();
58
+ this.timer = setInterval(() => this.observe(() => this.flush()), 5000);
59
+ this.timer.unref();
60
+ }
61
+ stage(stage) {
62
+ if (stage === this.value.stage)
63
+ return;
64
+ this.value.stage = stage;
65
+ this.value.stageStartedAt = new Date().toISOString();
66
+ this.observe(() => this.flush());
67
+ }
68
+ output(chunk) {
69
+ if (chunk.length === 0)
70
+ return;
71
+ this.observe(() => {
72
+ fs.appendFileSync(this.value.logPath, chunk);
73
+ this.value.lastOutputAt = new Date().toISOString();
74
+ if (Date.now() - this.lastFlush >= 1000)
75
+ this.flush();
76
+ });
77
+ }
78
+ finish(exitCode) {
79
+ clearInterval(this.timer);
80
+ this.value.exitCode = exitCode;
81
+ this.value.status = exitCode === 0 ? 'pass' : 'fail';
82
+ this.value.finishedAt = new Date().toISOString();
83
+ this.observe(() => this.flush());
84
+ }
85
+ observe(write) {
86
+ if (this.disabled)
87
+ return;
88
+ try {
89
+ write();
90
+ }
91
+ catch (error) {
92
+ // Observation is optional. Stop it explicitly without terminating the
93
+ // command or its children from an event callback. Old records go stale.
94
+ this.disabled = true;
95
+ clearInterval(this.timer);
96
+ process.stderr.write(`Operation observation disabled for ${this.value.id}: ${error.message}\n`);
97
+ }
98
+ }
99
+ flush() {
100
+ this.lastFlush = Date.now();
101
+ this.value.updatedAt = new Date(this.lastFlush).toISOString();
102
+ for (const file of this.files) {
103
+ const temporary = `${file}.${process.pid}.tmp`;
104
+ fs.writeFileSync(temporary, `${JSON.stringify(this.value)}\n`, { mode: 0o600 });
105
+ fs.chmodSync(temporary, 0o600);
106
+ fs.renameSync(temporary, file);
107
+ }
108
+ if (this.notification) {
109
+ const temporary = `${this.notification}.${this.value.id}.tmp`;
110
+ fs.writeFileSync(temporary, JSON.stringify({ updatedAt: this.value.updatedAt }), {
111
+ mode: 0o600,
112
+ });
113
+ fs.chmodSync(temporary, 0o600);
114
+ fs.renameSync(temporary, this.notification);
115
+ }
116
+ }
117
+ }
118
+ // New observation directories must remain traversable even under a restrictive umask.
119
+ export function ensureRuntimeDirectory(directory) {
120
+ if (fs.existsSync(directory))
121
+ return;
122
+ ensureRuntimeDirectory(path.dirname(directory));
123
+ try {
124
+ fs.mkdirSync(directory, { mode: 0o700 });
125
+ }
126
+ catch (error) {
127
+ // Another invocation can create the shared directory first.
128
+ if (error.code === 'EEXIST')
129
+ return;
130
+ throw error;
131
+ }
132
+ fs.chmodSync(directory, 0o700);
133
+ }
134
+ export function readOperations(directory, onInvalid) {
135
+ if (!fs.existsSync(directory))
136
+ return [];
137
+ return fs
138
+ .readdirSync(directory)
139
+ .filter((name) => /^[a-f0-9-]{36}\.json$/.test(name))
140
+ .flatMap((name) => {
141
+ try {
142
+ const value = JSON.parse(fs.readFileSync(path.join(directory, name), 'utf8'));
143
+ validateOperationRecord(value, name.slice(0, -5));
144
+ return [value];
145
+ }
146
+ catch (error) {
147
+ // Another invocation may prune a completed record between list and read.
148
+ if (error.code === 'ENOENT')
149
+ return [];
150
+ if (!onInvalid)
151
+ throw error;
152
+ onInvalid(error);
153
+ return [];
154
+ }
155
+ })
156
+ .sort((a, b) => a.startedAt.localeCompare(b.startedAt));
157
+ }
158
+ /** Keep captured text bounded without copying the whole tail for every chunk. */
159
+ export class OperationOutputTail {
160
+ constructor(limit = 64 * 1024 * 1024) {
161
+ this.limit = limit;
162
+ this.chunks = [];
163
+ this.length = 0;
164
+ }
165
+ append(chunk) {
166
+ if (!chunk)
167
+ return;
168
+ this.chunks.push(chunk);
169
+ this.length += chunk.length;
170
+ while (this.length > this.limit) {
171
+ const first = this.chunks[0];
172
+ const remove = Math.min(first.length, this.length - this.limit);
173
+ if (remove === first.length)
174
+ this.chunks.shift();
175
+ else
176
+ this.chunks[0] = first.slice(remove);
177
+ this.length -= remove;
178
+ }
179
+ }
180
+ toString() {
181
+ return this.chunks.join('');
182
+ }
183
+ }
184
+ //# sourceMappingURL=operation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operation.js","sourceRoot":"","sources":["../../src/runtime/operation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { RuntimeDecisionReport, RuntimeReadinessDecision } from './decision-types.js';
2
+ export interface OrchestrateRuntimeUpOptions<T extends RuntimeDecisionReport = RuntimeDecisionReport> {
3
+ decide: () => Promise<T>;
4
+ onInstall: (report: T) => Promise<void>;
5
+ onLaunch: (report: T) => Promise<void>;
6
+ onReady: (report: T) => Promise<void>;
7
+ onBlocked?: (report: T) => Promise<void>;
8
+ /** Shared flag — set true after first install attempt to prevent loops. */
9
+ installAttempted?: {
10
+ value: boolean;
11
+ };
12
+ }
13
+ export interface OrchestrateRuntimeUpResult<T extends RuntimeDecisionReport = RuntimeDecisionReport> {
14
+ report: T;
15
+ exitDecision: RuntimeReadinessDecision;
16
+ }
17
+ /**
18
+ * Generic install → relaunch decision loop for product runners.
19
+ * Hosts supply shell/platform actions via onInstall/onLaunch/onReady callbacks.
20
+ */
21
+ export declare function orchestrateRuntimeUp<T extends RuntimeDecisionReport>(options: OrchestrateRuntimeUpOptions<T>): Promise<OrchestrateRuntimeUpResult<T>>;
22
+ //# sourceMappingURL=orchestrate-up.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrate-up.d.ts","sourceRoot":"","sources":["../../src/runtime/orchestrate-up.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE3F,MAAM,WAAW,2BAA2B,CAC1C,CAAC,SAAS,qBAAqB,GAAG,qBAAqB;IAEvD,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC;IACzB,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;CACvC;AAED,MAAM,WAAW,0BAA0B,CACzC,CAAC,SAAS,qBAAqB,GAAG,qBAAqB;IAEvD,MAAM,EAAE,CAAC,CAAC;IACV,YAAY,EAAE,wBAAwB,CAAC;CACxC;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,SAAS,qBAAqB,EACxE,OAAO,EAAE,2BAA2B,CAAC,CAAC,CAAC,GACtC,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC,CA4BxC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Generic install → relaunch decision loop for product runners.
3
+ * Hosts supply shell/platform actions via onInstall/onLaunch/onReady callbacks.
4
+ */
5
+ export async function orchestrateRuntimeUp(options) {
6
+ const installFlag = options.installAttempted ?? { value: false };
7
+ const report = await options.decide();
8
+ switch (report.decision) {
9
+ case 'install': {
10
+ if (installFlag.value) {
11
+ throw new Error(`install did not resolve runtime (${report.reasonCode})`);
12
+ }
13
+ installFlag.value = true;
14
+ await options.onInstall(report);
15
+ return orchestrateRuntimeUp({ ...options, installAttempted: installFlag });
16
+ }
17
+ case 'launch':
18
+ case 'relaunch':
19
+ case 'build':
20
+ await options.onLaunch(report);
21
+ return { report, exitDecision: report.decision };
22
+ case 'ready':
23
+ await options.onReady(report);
24
+ return { report, exitDecision: 'ready' };
25
+ case 'blocked':
26
+ if (options.onBlocked)
27
+ await options.onBlocked(report);
28
+ return { report, exitDecision: 'blocked' };
29
+ default:
30
+ if (options.onBlocked)
31
+ await options.onBlocked(report);
32
+ return { report, exitDecision: report.decision };
33
+ }
34
+ }
35
+ //# sourceMappingURL=orchestrate-up.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrate-up.js","sourceRoot":"","sources":["../../src/runtime/orchestrate-up.ts"],"names":[],"mappings":"AAqBA;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,OAAuC;IAEvC,MAAM,WAAW,GAAG,OAAO,CAAC,gBAAgB,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IACjE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;IAEtC,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,IAAI,WAAW,CAAC,KAAK,EAAE,CAAC;gBACtB,MAAM,IAAI,KAAK,CAAC,oCAAoC,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC;YAC5E,CAAC;YACD,WAAW,CAAC,KAAK,GAAG,IAAI,CAAC;YACzB,MAAM,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YAChC,OAAO,oBAAoB,CAAC,EAAE,GAAG,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,KAAK,QAAQ,CAAC;QACd,KAAK,UAAU,CAAC;QAChB,KAAK,OAAO;YACV,MAAM,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC/B,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACnD,KAAK,OAAO;YACV,MAAM,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC9B,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;QAC3C,KAAK,SAAS;YACZ,IAAI,OAAO,CAAC,SAAS;gBAAE,MAAM,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACvD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;QAC7C;YACE,IAAI,OAAO,CAAC,SAAS;gBAAE,MAAM,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;YACvD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;IACrD,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { UiActionTransport } from '../adapters/ui.js';
2
+ import type { ActionExecutionContext } from '../core/types.js';
3
+ export type ReactNativeBridgeCommandName = 'navigate' | 'press' | 'keyPress' | 'setInput' | 'scroll' | 'waitFor' | 'screenshot' | 'status' | 'lifecycle' | 'hud' | 'trace' | 'observeUi';
4
+ export interface ReactNativeBridgeCommand {
5
+ command: ReactNativeBridgeCommandName;
6
+ nodeId: string;
7
+ payload: Record<string, unknown>;
8
+ }
9
+ export interface ReactNativeBridge {
10
+ send(command: ReactNativeBridgeCommand, context: ActionExecutionContext): Promise<unknown>;
11
+ }
12
+ export interface CreateReactNativeBridgeUiTransportOptions {
13
+ bridge: ReactNativeBridge;
14
+ }
15
+ export declare function createReactNativeBridgeUiTransport(options: CreateReactNativeBridgeUiTransportOptions): UiActionTransport;
16
+ export declare const createReactNativeCdpBridgeUiTransport: typeof createReactNativeBridgeUiTransport;
17
+ //# sourceMappingURL=react-native-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-native-bridge.d.ts","sourceRoot":"","sources":["../../src/runtime/react-native-bridge.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,sBAAsB,EAA2B,MAAM,kBAAkB,CAAC;AAExF,MAAM,MAAM,4BAA4B,GACpC,UAAU,GACV,OAAO,GACP,UAAU,GACV,UAAU,GACV,QAAQ,GACR,SAAS,GACT,YAAY,GACZ,QAAQ,GACR,WAAW,GACX,KAAK,GACL,OAAO,GACP,WAAW,CAAC;AAEhB,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,4BAA4B,CAAC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,OAAO,EAAE,wBAAwB,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5F;AAED,MAAM,WAAW,yCAAyC;IACxD,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAgBD,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,yCAAyC,GACjD,iBAAiB,CAwBnB;AAED,eAAO,MAAM,qCAAqC,2CAAqC,CAAC"}
@@ -0,0 +1,57 @@
1
+ const RN_COMMANDS = {
2
+ 'ui.navigate': 'navigate',
3
+ 'ui.press': 'press',
4
+ 'ui.key_press': 'keyPress',
5
+ 'ui.set_input': 'setInput',
6
+ 'ui.scroll': 'scroll',
7
+ 'ui.wait_for': 'waitFor',
8
+ 'ui.screenshot': 'screenshot',
9
+ 'app.status': 'status',
10
+ 'app.lifecycle': 'lifecycle',
11
+ 'app.hud': 'hud',
12
+ 'app.trace': 'trace',
13
+ };
14
+ export function createReactNativeBridgeUiTransport(options) {
15
+ return {
16
+ async execute(action, node, context) {
17
+ const { action: _action, ...payload } = node;
18
+ const command = RN_COMMANDS[action];
19
+ if (!command) {
20
+ throw new Error(`${action} is not supported by the React Native bridge. Next: use the native Agent Device adapter for ${action}.`);
21
+ }
22
+ return options.bridge.send({ command, nodeId: context.nodeId, payload }, context);
23
+ },
24
+ async observe(refs, node, context) {
25
+ const result = await options.bridge.send({
26
+ command: 'observeUi',
27
+ nodeId: context.nodeId,
28
+ payload: { refs, node },
29
+ }, context);
30
+ return normalizeBridgeObservationResult(result, refs);
31
+ },
32
+ };
33
+ }
34
+ export const createReactNativeCdpBridgeUiTransport = createReactNativeBridgeUiTransport;
35
+ function normalizeBridgeObservationResult(result, refs) {
36
+ if (!result || typeof result !== 'object' || Array.isArray(result)) {
37
+ return {
38
+ warnings: refs.map((ref) => ({ ref, message: 'Bridge observeUi returned no object.' })),
39
+ };
40
+ }
41
+ const record = result;
42
+ if (record.ok === false) {
43
+ const message = typeof record.error === 'string' ? record.error : 'Bridge observeUi failed.';
44
+ return { warnings: refs.map((ref) => ({ ref, message })) };
45
+ }
46
+ const observations = record.observations;
47
+ const warnings = record.warnings;
48
+ return {
49
+ ...(observations && typeof observations === 'object' && !Array.isArray(observations)
50
+ ? { observations: observations }
51
+ : {}),
52
+ ...(Array.isArray(warnings)
53
+ ? { warnings: warnings }
54
+ : {}),
55
+ };
56
+ }
57
+ //# sourceMappingURL=react-native-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-native-bridge.js","sourceRoot":"","sources":["../../src/runtime/react-native-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const RECIPE_RUNNER_VERSION: string;
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,qBAAqB,QAAsB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { createRequire } from 'node:module';
2
+ const require = createRequire(import.meta.url);
3
+ const packageJson = require('../package.json');
4
+ if (typeof packageJson.version !== 'string' || !packageJson.version) {
5
+ throw new Error('@farmslot/recipe-runner package version is missing.');
6
+ }
7
+ export const RECIPE_RUNNER_VERSION = packageJson.version;
8
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAA0B,CAAC;AAExE,IAAI,OAAO,WAAW,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;IACpE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;AACzE,CAAC;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG,WAAW,CAAC,OAAO,CAAC"}
package/package.json ADDED
@@ -0,0 +1,163 @@
1
+ {
2
+ "name": "@farmslot/recipe-runner",
3
+ "version": "0.23.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "farmslot-visual-review": "./bin/farmslot-visual-review.mjs"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./cli": {
17
+ "types": "./dist/cli/index.d.ts",
18
+ "import": "./dist/cli/index.js",
19
+ "default": "./dist/cli/index.js"
20
+ },
21
+ "./cli/support": {
22
+ "types": "./dist/cli/support.d.ts",
23
+ "import": "./dist/cli/support.js",
24
+ "default": "./dist/cli/support.js"
25
+ },
26
+ "./runner": {
27
+ "types": "./dist/core/runner.d.ts",
28
+ "import": "./dist/core/runner.js",
29
+ "default": "./dist/core/runner.js"
30
+ },
31
+ "./types": {
32
+ "types": "./dist/core/types.d.ts",
33
+ "import": "./dist/core/types.js",
34
+ "default": "./dist/core/types.js"
35
+ },
36
+ "./writers": {
37
+ "types": "./dist/node/writers.d.ts",
38
+ "import": "./dist/node/writers.js",
39
+ "default": "./dist/node/writers.js"
40
+ },
41
+ "./adapters/core": {
42
+ "types": "./dist/adapters/core.d.ts",
43
+ "import": "./dist/adapters/core.js",
44
+ "default": "./dist/adapters/core.js"
45
+ },
46
+ "./adapters/app-lifecycle": {
47
+ "types": "./dist/adapters/app-lifecycle.d.ts",
48
+ "import": "./dist/adapters/app-lifecycle.js",
49
+ "default": "./dist/adapters/app-lifecycle.js"
50
+ },
51
+ "./adapters/ui": {
52
+ "types": "./dist/adapters/ui.d.ts",
53
+ "import": "./dist/adapters/ui.js",
54
+ "default": "./dist/adapters/ui.js"
55
+ },
56
+ "./runtime/browser-extension": {
57
+ "types": "./dist/runtime/browser-extension.d.ts",
58
+ "import": "./dist/runtime/browser-extension.js",
59
+ "default": "./dist/runtime/browser-extension.js"
60
+ },
61
+ "./runtime/cdp": {
62
+ "types": "./dist/runtime/cdp.d.ts",
63
+ "import": "./dist/runtime/cdp.js",
64
+ "default": "./dist/runtime/cdp.js"
65
+ },
66
+ "./runtime/react-native-bridge": {
67
+ "types": "./dist/runtime/react-native-bridge.d.ts",
68
+ "import": "./dist/runtime/react-native-bridge.js",
69
+ "default": "./dist/runtime/react-native-bridge.js"
70
+ },
71
+ "./runtime/deps-readiness": {
72
+ "types": "./dist/runtime/deps-readiness.d.ts",
73
+ "import": "./dist/runtime/deps-readiness.js",
74
+ "default": "./dist/runtime/deps-readiness.js"
75
+ },
76
+ "./runtime/log-analysis": {
77
+ "types": "./dist/runtime/log-analysis.d.ts",
78
+ "import": "./dist/runtime/log-analysis.js",
79
+ "default": "./dist/runtime/log-analysis.js"
80
+ },
81
+ "./runtime/metro-probe": {
82
+ "types": "./dist/runtime/metro-probe.d.ts",
83
+ "import": "./dist/runtime/metro-probe.js",
84
+ "default": "./dist/runtime/metro-probe.js"
85
+ },
86
+ "./runtime/decision-types": {
87
+ "types": "./dist/runtime/decision-types.d.ts",
88
+ "import": "./dist/runtime/decision-types.js",
89
+ "default": "./dist/runtime/decision-types.js"
90
+ },
91
+ "./runtime/orchestrate-up": {
92
+ "types": "./dist/runtime/orchestrate-up.d.ts",
93
+ "import": "./dist/runtime/orchestrate-up.js",
94
+ "default": "./dist/runtime/orchestrate-up.js"
95
+ },
96
+ "./visual-review": {
97
+ "types": "./visual-review/index.d.mts",
98
+ "import": "./visual-review/index.mjs",
99
+ "default": "./visual-review/index.mjs"
100
+ },
101
+ "./runtime/operation": {
102
+ "types": "./dist/runtime/operation.d.ts",
103
+ "import": "./dist/runtime/operation.js",
104
+ "default": "./dist/runtime/operation.js"
105
+ }
106
+ },
107
+ "scripts": {
108
+ "typecheck": "tsc --noEmit --project tsconfig.json",
109
+ "test": "node ../../scripts/quality/run-tsx-tests.mjs --cwd . --tsconfig tsconfig.json test src/core/observations.test.ts && node --test test/visual-review-tool.test.mjs",
110
+ "format": "prettier --write --ignore-path ../../.prettierignore .",
111
+ "format:check": "prettier --check --ignore-path ../../.prettierignore .",
112
+ "lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
113
+ "lint:fix": "eslint \"src/**/*.ts\" \"test/**/*.ts\" --fix",
114
+ "quality": "prettier --check --ignore-path ../../.prettierignore . && eslint \"src/**/*.ts\" \"test/**/*.ts\" && tsc --noEmit --project tsconfig.json && yarn build && yarn test",
115
+ "build": "yarn workspace @farmslot/protocol build && rm -rf dist && tsc --project tsconfig.build.json",
116
+ "prepack": "cd ../.. && node scripts/quality/check-farmslot-package-readiness.mjs --require-changelog --packages @farmslot/recipe-runner && yarn workspace @farmslot/recipe-runner build",
117
+ "prepublishOnly": "node ../../scripts/quality/check-farmslot-package-readiness.mjs --publish --packages @farmslot/recipe-runner"
118
+ },
119
+ "dependencies": {
120
+ "@farmslot/protocol": "0.34.0",
121
+ "commander": "^12.0.0",
122
+ "semver": "^7.7.3",
123
+ "ws": "^8.18.0"
124
+ },
125
+ "devDependencies": {
126
+ "@types/node": "^22.0.0",
127
+ "@types/ws": "^8.5.12",
128
+ "eslint": "^10.3.0",
129
+ "prettier": "^3.8.3",
130
+ "tsx": "^4.19.0",
131
+ "typescript": "^5.6.0"
132
+ },
133
+ "repository": {
134
+ "type": "git",
135
+ "url": "git+https://github.com/deeeed/farmslot.git",
136
+ "directory": "packages/recipe-runner"
137
+ },
138
+ "bugs": {
139
+ "url": "https://github.com/deeeed/farmslot/issues"
140
+ },
141
+ "license": "MIT",
142
+ "homepage": "https://farmslot.io/docs/architecture/recipe-runner",
143
+ "keywords": [
144
+ "farmslot",
145
+ "recipe-runner",
146
+ "validation",
147
+ "agentic-engineering"
148
+ ],
149
+ "files": [
150
+ "README.md",
151
+ "LICENSE",
152
+ "bin/**/*.mjs",
153
+ "visual-review/**/*.mjs",
154
+ "visual-review/**/*.d.mts",
155
+ "dist/**/*.js",
156
+ "dist/**/*.d.ts",
157
+ "dist/**/*.d.ts.map",
158
+ "dist/**/*.js.map"
159
+ ],
160
+ "publishConfig": {
161
+ "access": "public"
162
+ }
163
+ }
@@ -0,0 +1,39 @@
1
+ # scroll-to-visible conformance
2
+
3
+ Two layers exercise the `ui.scroll_to` contract.
4
+
5
+ ## Provider-neutral suite
6
+
7
+ `test/scroll-to-visible.test.ts` runs recipes through `createRecipeRunner` and
8
+ `createStandardUiAdapters` against a fake native-style provider: one scroll surface in
9
+ window coordinates, one device lock, and a retained session:
10
+
11
+ | Case | Expectation |
12
+ | -------------------------------------------- | ------------------------------------------------------------------------- |
13
+ | Target already in the safe viewport | `alreadyVisible: true`, no `scrollTo` call, settlement `skipped` |
14
+ | Far target | one absolute `scrollTo`, `before`/`after` bounds, settled, `finalVisible` |
15
+ | Flattened Text target + measurable anchor | passes via `visibility_anchor_test_id`; target still asserted present |
16
+ | Anchor present, target missing | `SCROLL_TARGET_MISSING`; the anchor never stands in for the target |
17
+ | Row under the HUD | `safeViewport` excludes the HUD; the row ends above it |
18
+ | Layout that never stops changing | `SCROLL_SETTLEMENT_TIMEOUT` with the last geometry |
19
+ | Layout that shifts once, then settles | passes with the settled bounds |
20
+ | Three nodes on one retained session | same `sessionId`, one connection, no self-lock |
21
+ | Provider that keeps its lock | next node fails `SCROLL_SESSION_CONFLICT` |
22
+ | Surface missing / box-less target, no anchor | `SCROLL_SURFACE_MISSING` / `SCROLL_TARGET_NOT_MEASURABLE` |
23
+ | Content too short to reach the target | `SCROLL_TARGET_NOT_VISIBLE`; `verify_visible: false` records it instead |
24
+
25
+ Every failure is `cause_class: harness` with `error_code` and the observed geometry in
26
+ `error_details`. Absolute versus relative `ui.scroll` movement is covered in
27
+ `test/recipe-runner.test.ts`.
28
+
29
+ ## Real browser page
30
+
31
+ `page.html` is the same scenario set on a real DOM: a `scroll-surface` container, a far
32
+ `history-proof` row, a `display: contents` `flat-text` span inside the measurable
33
+ `flat-anchor` row, and a `jitter-target` below a block that grows on every animation
34
+ frame when the URL has `?jitter=1`. Serve it from the Command Center Vite origin so the
35
+ CDP runner can attach to it:
36
+
37
+ ```
38
+ <ui_url>/@fs<repo>/packages/recipe-runner/test/fixtures/scroll-to-visible/page.html
39
+ ```