[object Object]

← back to AbramsEgo

fix: cron cards derive model from invoked script (was 0/114 populated)

f2100b79f8808f26280c0c93e818ac39c0292863 · 2026-07-01 14:31:22 -0700 · Steve

Files touched

Diff

commit f2100b79f8808f26280c0c93e818ac39c0292863
Author: Steve <steve@designerwallcoverings.com>
Date:   Wed Jul 1 14:31:22 2026 -0700

    fix: cron cards derive model from invoked script (was 0/114 populated)
---
 data/local-fleet.json |  2 +-
 server.js             | 29 +++++++++++++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/data/local-fleet.json b/data/local-fleet.json
index 1c7789f..cd9cab1 100644
--- a/data/local-fleet.json
+++ b/data/local-fleet.json
@@ -1 +1 @@
-{"fetchedAt":"2026-07-01T21:27:40.536Z","procs":[{"name":"pm2-logrotate","status":"online","cpu":0,"memMB":45,"restarts":0,"uptimeMs":1262951,"startedAt":"2026-07-01T21:06:37.585Z"},{"name":"abramsego","status":"online","cpu":0,"memMB":63,"restarts":3,"uptimeMs":273161,"startedAt":"2026-07-01T21:23:07.375Z"},{"name":"api-token-dashboard","status":"online","cpu":0,"memMB":56,"restarts":2,"uptimeMs":331864,"startedAt":"2026-07-01T21:22:08.672Z"}]}
\ No newline at end of file
+{"fetchedAt":"2026-07-01T21:30:56.755Z","procs":[{"name":"pm2-logrotate","status":"online","cpu":0,"memMB":45,"restarts":0,"uptimeMs":1459170,"startedAt":"2026-07-01T21:06:37.585Z"},{"name":"abramsego","status":"online","cpu":0,"memMB":76,"restarts":5,"uptimeMs":552,"startedAt":"2026-07-01T21:30:56.203Z"},{"name":"api-token-dashboard","status":"online","cpu":0,"memMB":59,"restarts":2,"uptimeMs":528083,"startedAt":"2026-07-01T21:22:08.672Z"}]}
\ No newline at end of file
diff --git a/server.js b/server.js
index 645c07e..bffdc0b 100644
--- a/server.js
+++ b/server.js
@@ -259,6 +259,35 @@ function readCronPlistMeta(filePath) {
   const mTok = xml.match(/\bclaude-(?:opus|sonnet|haiku|fable)[\w.-]*/i);
   if (mArg) meta.model = mArg[1].trim();
   else if (mTok) meta.model = mTok[0];
+  // plists almost never name a model — it lives one hop away, in the script
+  // the job invokes. Resolve that script and scan it with the same regexes.
+  if (!meta.model) {
+    try {
+      const argsBlock = xml.match(/<key>ProgramArguments<\/key>\s*<array>([\s\S]*?)<\/array>/i);
+      const args = argsBlock ? [...argsBlock[1].matchAll(/<string>([^<]*)<\/string>/g)].map((m) => m[1]) : [];
+      let script = args.find((a) => a.startsWith('/') && /\.(sh|js|cjs|mjs|py)$/.test(a));
+      if (!script) {
+        // relative script + WorkingDirectory (e.g. `node server.js` with a cwd)
+        const wd = (xml.match(/<key>WorkingDirectory<\/key>\s*<string>([^<]+)<\/string>/i) || [])[1];
+        const rel = args.find((a) => !a.startsWith('-') && !a.startsWith('/') && /\.(sh|js|cjs|mjs|py)$/.test(a));
+        if (wd && rel) script = path.join(wd, rel);
+      }
+      if (!script && args.length) {
+        const last = args[args.length - 1];
+        if (last.startsWith('/') && fs.existsSync(last) && fs.statSync(last).isFile()) script = last;
+      }
+      if (script && fs.existsSync(script)) {
+        const st = fs.statSync(script);
+        if (st.isFile() && st.size < 512 * 1024) {
+          const src = fs.readFileSync(script, 'utf8');
+          const sArg = src.match(/--model[= ]["']?([\w.-]+)/);
+          const sTok = src.match(/\bclaude-(?:opus|sonnet|haiku|fable)[\w.-]*/i);
+          if (sArg) meta.model = sArg[1];
+          else if (sTok) meta.model = sTok[0];
+        }
+      }
+    } catch (e) {}
+  }
   return meta;
 }
 

← ec4ed7b chore: replenish audit (fable) — all 8 SPEC panels live + ha  ·  back to AbramsEgo  ·  auto-save: 2026-07-01T14:36:50 (3 files) — build-queue/tasks 3fa9c02 →