← back to Ticket System
Sync YOLO cycles into the ticket ledger
7ebd636ec83a65ccc1c4f7a75d35dc2df4171151 · 2026-08-30 23:07:41 -0700 · Steve Abrams
Files touched
M .gitignoreA com.steve.yolo-ticket-sync.plistA scripts/yolo-ticket-sync.mjs
Diff
commit 7ebd636ec83a65ccc1c4f7a75d35dc2df4171151
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sun Aug 30 23:07:41 2026 -0700
Sync YOLO cycles into the ticket ledger
---
.gitignore | 3 ++
com.steve.yolo-ticket-sync.plist | 28 +++++++++++++++
scripts/yolo-ticket-sync.mjs | 75 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 106 insertions(+)
diff --git a/.gitignore b/.gitignore
index 1924158c..3608ca56 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,7 @@
node_modules/
+data/yolo-ticket-sync.json
+yolo-ticket-sync.out.log
+yolo-ticket-sync.err.log
.env*
tmp/
*.log
diff --git a/com.steve.yolo-ticket-sync.plist b/com.steve.yolo-ticket-sync.plist
new file mode 100644
index 00000000..1facc787
--- /dev/null
+++ b/com.steve.yolo-ticket-sync.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.steve.yolo-ticket-sync</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/opt/homebrew/bin/node</string>
+ <string>/Users/macstudio3/Projects/ticket-system/scripts/yolo-ticket-sync.mjs</string>
+ </array>
+ <key>RunAtLoad</key>
+ <true/>
+ <key>StartInterval</key>
+ <integer>120</integer>
+ <key>EnvironmentVariables</key>
+ <dict>
+ <key>HOME</key>
+ <string>/Users/macstudio3</string>
+ <key>PATH</key>
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
+ </dict>
+ <key>StandardOutPath</key>
+ <string>/Users/macstudio3/Projects/ticket-system/yolo-ticket-sync.out.log</string>
+ <key>StandardErrorPath</key>
+ <string>/Users/macstudio3/Projects/ticket-system/yolo-ticket-sync.err.log</string>
+</dict>
+</plist>
diff --git a/scripts/yolo-ticket-sync.mjs b/scripts/yolo-ticket-sync.mjs
new file mode 100644
index 00000000..eaa37869
--- /dev/null
+++ b/scripts/yolo-ticket-sync.mjs
@@ -0,0 +1,75 @@
+#!/usr/bin/env node
+
+import { execFileSync } from 'node:child_process';
+import fs from 'node:fs';
+import path from 'node:path';
+
+const TICKET = 'TK-10986';
+const ROOT = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..');
+const STATE_FILE = path.join(ROOT, 'data', 'yolo-ticket-sync.json');
+const TK = '/opt/homebrew/bin/tk';
+const SSH = '/usr/bin/ssh';
+const HOST = 'root@45.61.58.125';
+const REMOTE_STATUS = `YOLO_PASS=$(pm2 jlist | jq -r '.[]|select(.name=="yolo-agent").pm2_env.YOLO_AUTH_PASS'); curl -fsS -u "admin:$YOLO_PASS" http://127.0.0.1:9670/api/status`;
+const REMOTE_START = `YOLO_PASS=$(pm2 jlist | jq -r '.[]|select(.name=="yolo-agent").pm2_env.YOLO_AUTH_PASS'); curl -fsS -u "admin:$YOLO_PASS" -X POST http://127.0.0.1:9670/api/start`;
+
+function ssh(command) {
+ return execFileSync(SSH, ['-o', 'BatchMode=yes', '-o', 'ConnectTimeout=10', HOST, command], {
+ encoding: 'utf8', timeout: 20_000, maxBuffer: 4 * 1024 * 1024,
+ });
+}
+
+function tk(kind, text) {
+ execFileSync(TK, [kind, TICKET, text, '-a', 'yolo-ticket-sync'], {
+ encoding: 'utf8', timeout: 10_000,
+ });
+}
+
+function loadState() {
+ try { return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8')); }
+ catch { return { lastTaskId: null, lastRunning: null, lastError: null }; }
+}
+
+function saveState(state) {
+ fs.mkdirSync(path.dirname(STATE_FILE), { recursive: true });
+ fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2) + '\n');
+}
+
+const state = loadState();
+
+try {
+ const status = JSON.parse(ssh(REMOTE_STATUS));
+ const result = status.lastResult || null;
+
+ if (status.running === false) {
+ const response = JSON.parse(ssh(REMOTE_START));
+ tk('challenge', `YOLO loop was stopped; monitor issued the existing /api/start recovery (${response.msg || 'started'}). No email; $0 spend.`);
+ }
+
+ if (result?.taskId && result.taskId !== state.lastTaskId) {
+ const summary = String(result.stdout || '').replace(/\s+/g, ' ').trim().slice(0, 700);
+ const railProof = String(result.stderr || '').includes('No Shopify write')
+ ? ' Hard-rail attestation present.' : '';
+ const text = `YOLO cycle #${result.taskId} ${result.status} in ${result.duration || 'unknown duration'}. ${summary || 'No summary returned.'}${railProof} Loop running=${status.running}; mode=${status.mode}; cooldown=${status.cooldownSec}s. No email notification path; spend ceiling <=$5.`;
+ tk(result.status === 'success' ? 'win' : 'challenge', text);
+ state.lastTaskId = result.taskId;
+ }
+
+ if (state.lastRunning !== status.running) {
+ tk('note', `YOLO runtime state changed to running=${status.running}, paused=${status.paused}, mode=${status.mode}, currentTask=${status.currentTask || 'none'}.`);
+ state.lastRunning = status.running;
+ }
+
+ state.lastError = null;
+ state.checkedAt = new Date().toISOString();
+ saveState(state);
+} catch (error) {
+ const message = String(error?.message || error).replace(/\s+/g, ' ').slice(0, 500);
+ if (message !== state.lastError) {
+ try { tk('challenge', `YOLO ticket-sync monitor could not verify the remote loop: ${message}`); } catch {}
+ state.lastError = message;
+ }
+ state.checkedAt = new Date().toISOString();
+ saveState(state);
+ process.exitCode = 1;
+}
← 5ef46940 use owner-safe PM2 CLI wrapper
·
back to Ticket System
·
record Kamatera PM2 fracture prevention rollout 32dadef2 →