transitions-refine 0.3.13 → 0.3.14
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.
|
@@ -54,6 +54,19 @@ The relay reports the agent as "available" for ~120s after your last poll, so
|
|
|
54
54
|
short pauses keep you live. A successful job always resets idle, so an active
|
|
55
55
|
session never backs off.
|
|
56
56
|
|
|
57
|
+
0. **Announce yourself once, before the first poll.** The relay keeps a *sticky*
|
|
58
|
+
Stop latch: after a panel **Stop** (or the idle auto-stop) it answers every
|
|
59
|
+
`GET /jobs/next` with `{"stop": true}` until a new agent explicitly resumes —
|
|
60
|
+
so a stopped session can't silently come back. Clear the latch a single time
|
|
61
|
+
at startup, then begin polling:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
curl -s -X POST http://localhost:7331/poller/start
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Do **not** call this again mid-loop (it would defeat a user's Stop). Only on a
|
|
68
|
+
fresh `/refine live`.
|
|
69
|
+
|
|
57
70
|
1. **Claim the next job (long-poll).** This call blocks up to ~25s, then returns.
|
|
58
71
|
|
|
59
72
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "transitions-refine",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.14",
|
|
4
4
|
"description": "Live, agent-driven Refine panel for CSS/Motion transitions — injects a timeline + Refine UI and runs transitions.dev suggestions via your coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -446,6 +446,10 @@ async function pollOnce() {
|
|
|
446
446
|
|
|
447
447
|
async function loop() {
|
|
448
448
|
log(`live refine loop → ${RELAY} (workspace: ${WORKSPACE})`);
|
|
449
|
+
// Announce ourselves so the relay clears any prior Stop latch — otherwise a
|
|
450
|
+
// relay that's still "stopped" from a previous panel Stop would answer our
|
|
451
|
+
// first poll with {stop:true} and we'd exit immediately.
|
|
452
|
+
try { await post("/poller/start", {}); } catch {}
|
|
449
453
|
// eslint-disable-next-line no-constant-condition
|
|
450
454
|
while (true) {
|
|
451
455
|
try {
|
package/server/relay.mjs
CHANGED
|
@@ -91,13 +91,6 @@ const PENDING_TIMEOUT_MS = Number(process.env.REFINE_PENDING_TIMEOUT_MS) || 1200
|
|
|
91
91
|
// to stop (it returns {stop:true} from /jobs/next). 0 disables. Only the chat
|
|
92
92
|
// loop is affected — a wired REFINE_AGENT_CMD never polls /jobs/next.
|
|
93
93
|
const POLLER_IDLE_STOP_MS = Number(process.env.REFINE_POLLER_IDLE_STOP_MS) || 600000;
|
|
94
|
-
// After a Stop (manual or idle), hold the poller "stopped" for this long: every
|
|
95
|
-
// GET /jobs/next keeps getting {stop:true} and /health reports the poller
|
|
96
|
-
// inactive. Without it, a single straggler poll fired right after the stop
|
|
97
|
-
// (an agent mid-backoff, or one that batches a poll before exiting) re-registers
|
|
98
|
-
// as live and the panel flips "Live" back on a few seconds later. A genuine
|
|
99
|
-
// re-run of `/refine live` after the window resumes normally.
|
|
100
|
-
const STOP_GRACE_MS = Number(process.env.REFINE_STOP_GRACE_MS) || 5000;
|
|
101
94
|
|
|
102
95
|
/** @type {Map<string, Job>} */
|
|
103
96
|
const jobs = new Map();
|
|
@@ -106,10 +99,14 @@ const now = () => Date.now();
|
|
|
106
99
|
// When did a `/refine live` agent last poll? Used to know if LLM mode can be
|
|
107
100
|
// served by a live editor agent (vs. needing REFINE_AGENT_CMD).
|
|
108
101
|
let lastPollAt = 0;
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
102
|
+
// Stop latch. Set by POST /poller/stop (and idle auto-stop); cleared ONLY by an
|
|
103
|
+
// explicit POST /poller/start (a fresh `/refine live` / agent loop announcing
|
|
104
|
+
// itself). While latched the poller reports inactive and every GET /jobs/next
|
|
105
|
+
// answers {stop:true}, so a loop that ignores the stop — or keeps polling /
|
|
106
|
+
// re-polls — can never silently revive the session. This is what makes the
|
|
107
|
+
// panel's Stop button stick instead of flipping "Live" back on seconds later.
|
|
108
|
+
let pollerStopped = false;
|
|
109
|
+
const pollerActive = () => !pollerStopped && now() - lastPollAt < POLLER_TTL_MS;
|
|
113
110
|
const llmAvailable = () => Boolean(AGENT_CMD) || pollerActive();
|
|
114
111
|
|
|
115
112
|
// Stop signal for the in-chat `/refine live` loop. Set by POST /poller/stop
|
|
@@ -663,32 +660,31 @@ const server = createServer(async (req, res) => {
|
|
|
663
660
|
|
|
664
661
|
// GET /jobs/next — long-poll claimed by a `/refine live` agent (LLM jobs).
|
|
665
662
|
if (method === "GET" && path === "/jobs/next") {
|
|
666
|
-
//
|
|
667
|
-
// exit and don't
|
|
668
|
-
//
|
|
669
|
-
|
|
670
|
-
if (now() < stoppedUntil && !nextPendingLlm()) {
|
|
663
|
+
// Stopped latch: a Stop was issued and not yet resumed. Keep telling every
|
|
664
|
+
// poller to exit and report inactive (don't touch lastPollAt) until a fresh
|
|
665
|
+
// POST /poller/start. A pending job still wins so we never drop real work.
|
|
666
|
+
if (pollerStopped && !nextPendingLlm()) {
|
|
671
667
|
return send(res, 200, { stop: true });
|
|
672
668
|
}
|
|
673
669
|
lastPollAt = now();
|
|
674
670
|
if (!lastJobAt) lastJobAt = now(); // seed idle window on the loop's first poll
|
|
675
|
-
//
|
|
676
|
-
// A pending job always wins so we never drop real work
|
|
671
|
+
// Idle auto-stop (or a leftover stopRequested) → latch stopped + tell the
|
|
672
|
+
// loop to exit. A pending job always wins so we never drop real work.
|
|
677
673
|
if ((stopRequested || (POLLER_IDLE_STOP_MS && now() - lastJobAt >= POLLER_IDLE_STOP_MS)) && !nextPendingLlm()) {
|
|
678
674
|
stopRequested = false;
|
|
675
|
+
pollerStopped = true; // latch until an explicit /poller/start
|
|
679
676
|
lastJobAt = 0;
|
|
680
677
|
lastPollAt = 0; // loop is exiting → report it inactive immediately on /health
|
|
681
|
-
stoppedUntil = now() + STOP_GRACE_MS; // hold the stop so re-polls can't revive it
|
|
682
678
|
return send(res, 200, { stop: true });
|
|
683
679
|
}
|
|
684
680
|
const deadline = now() + LONGPOLL_MS;
|
|
685
681
|
const attempt = () => {
|
|
686
682
|
if (res.writableEnded) return;
|
|
687
|
-
if (
|
|
683
|
+
if (stopRequested && !nextPendingLlm()) {
|
|
688
684
|
stopRequested = false;
|
|
685
|
+
pollerStopped = true; // latch until an explicit /poller/start
|
|
689
686
|
lastJobAt = 0;
|
|
690
687
|
lastPollAt = 0; // loop is exiting → report it inactive immediately on /health
|
|
691
|
-
stoppedUntil = now() + STOP_GRACE_MS; // hold the stop so re-polls can't revive it
|
|
692
688
|
return send(res, 200, { stop: true });
|
|
693
689
|
}
|
|
694
690
|
const job = nextPendingLlm();
|
|
@@ -709,11 +705,23 @@ const server = createServer(async (req, res) => {
|
|
|
709
705
|
if (method === "POST" && path === "/poller/stop") {
|
|
710
706
|
const wasActive = now() - lastPollAt < POLLER_TTL_MS;
|
|
711
707
|
stopRequested = true;
|
|
708
|
+
pollerStopped = true; // latch: stays stopped until POST /poller/start
|
|
712
709
|
lastPollAt = 0; // report inactive immediately; a straggler poll won't revive it
|
|
713
|
-
stoppedUntil = now() + STOP_GRACE_MS; // hold the stop through the grace window
|
|
714
710
|
return send(res, 200, { ok: true, stopping: wasActive });
|
|
715
711
|
}
|
|
716
712
|
|
|
713
|
+
// POST /poller/start — a fresh `/refine live` agent (or loop) announces itself
|
|
714
|
+
// and clears the Stop latch so its polls count as live again. Without this an
|
|
715
|
+
// explicit re-run couldn't resume, since a latched relay tells every poll to
|
|
716
|
+
// stop. Call it once at loop startup, before the first GET /jobs/next.
|
|
717
|
+
if (method === "POST" && path === "/poller/start") {
|
|
718
|
+
pollerStopped = false;
|
|
719
|
+
stopRequested = false;
|
|
720
|
+
lastJobAt = now();
|
|
721
|
+
lastPollAt = now();
|
|
722
|
+
return send(res, 200, { ok: true });
|
|
723
|
+
}
|
|
724
|
+
|
|
717
725
|
const m = path.match(/^\/jobs\/([^/]+)(?:\/(status|result|error))?$/);
|
|
718
726
|
if (m) {
|
|
719
727
|
const job = jobs.get(m[1]);
|