← 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
M data/local-fleet.jsonM server.js
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 →