clawmoney 0.17.19 → 0.17.20

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.
@@ -173,21 +173,100 @@ function runCli(command, prompt, timeoutMs, orderId) {
173
173
  });
174
174
  let stdout = "";
175
175
  let stderr = "";
176
+ let earlyResolved = false;
177
+ const tryEarlyExit = () => {
178
+ if (earlyResolved)
179
+ return;
180
+ // Only attempt early exit for codex — others either don't stream
181
+ // events with this shape, or already finish promptly after their
182
+ // last output. Codex on xhigh/high reasoning_effort wastes 60–90s
183
+ // doing "final reflection" reasoning even after the agent_message
184
+ // is already emitted with the image_path. Once we see that
185
+ // message in the JSONL stream the deliverable is fully on disk
186
+ // and we can ship the order — kill the child to avoid burning
187
+ // buyer wall-time on reasoning we won't read.
188
+ if (command !== "codex")
189
+ return;
190
+ if (!hasCodexDeliverable(stdout))
191
+ return;
192
+ earlyResolved = true;
193
+ // SIGTERM is enough; codex closes its writer and we'll get a
194
+ // 'close' event shortly. Don't await — let the resolve below
195
+ // return the snapshot we already have.
196
+ try {
197
+ child.kill("SIGTERM");
198
+ }
199
+ catch {
200
+ // pid may already be gone
201
+ }
202
+ resolve({ stdout, stderr, exitCode: 0 });
203
+ };
176
204
  child.stdout.on("data", (chunk) => {
177
205
  stdout += chunk.toString();
206
+ tryEarlyExit();
178
207
  });
179
208
  child.stderr.on("data", (chunk) => {
180
209
  stderr += chunk.toString();
181
210
  });
182
211
  child.on("close", (code) => {
212
+ if (earlyResolved)
213
+ return;
183
214
  resolve({ stdout, stderr, exitCode: code });
184
215
  });
185
216
  child.on("error", (err) => {
217
+ if (earlyResolved)
218
+ return;
186
219
  stderr += err.message;
187
220
  resolve({ stdout, stderr, exitCode: null });
188
221
  });
189
222
  });
190
223
  }
224
+ /**
225
+ * Detect whether codex's JSONL stream already contains a final
226
+ * agent_message event whose text parses to JSON with an `image_path`.
227
+ * That's the signal that the image is on disk and the agent has
228
+ * acknowledged it — everything codex does after this point is its
229
+ * own final reasoning loop, which the buyer never sees.
230
+ *
231
+ * Scans newest-to-oldest so we exit as soon as we find the marker.
232
+ */
233
+ function hasCodexDeliverable(streamSoFar) {
234
+ const lines = streamSoFar.split("\n");
235
+ for (let i = lines.length - 1; i >= 0; i--) {
236
+ const line = lines[i].trim();
237
+ if (!line.startsWith("{"))
238
+ continue;
239
+ let event;
240
+ try {
241
+ event = JSON.parse(line);
242
+ }
243
+ catch {
244
+ continue;
245
+ }
246
+ if (event.type !== "item.completed")
247
+ continue;
248
+ const item = event.item;
249
+ if (item?.type !== "agent_message")
250
+ continue;
251
+ const text = item.text;
252
+ if (typeof text !== "string")
253
+ continue;
254
+ // Cheap pre-check before JSON.parse: must mention image_path.
255
+ if (!text.includes("image_path"))
256
+ continue;
257
+ try {
258
+ const parsed = JSON.parse(text);
259
+ const path = parsed.image_path;
260
+ if (typeof path === "string" && path.length > 0)
261
+ return true;
262
+ }
263
+ catch {
264
+ // text wasn't JSON; agent might be talking about image_path
265
+ // in prose. Keep looking — don't early-exit on prose.
266
+ }
267
+ }
268
+ return false;
269
+ }
191
270
  // ── JSON parser ──
192
271
  function parseJsonOutput(raw) {
193
272
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawmoney",
3
- "version": "0.17.19",
3
+ "version": "0.17.20",
4
4
  "description": "ClawMoney CLI -- Earn rewards with your AI agent",
5
5
  "type": "module",
6
6
  "bin": {