@masslessai/push-todo 3.8.2 → 3.8.3
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.
- package/lib/daemon.js +54 -0
- package/package.json +1 -1
package/lib/daemon.js
CHANGED
|
@@ -1775,6 +1775,53 @@ async function pollAndExecute() {
|
|
|
1775
1775
|
updateStatusFile();
|
|
1776
1776
|
}
|
|
1777
1777
|
|
|
1778
|
+
/**
|
|
1779
|
+
* Recover tasks orphaned by a previous daemon instance.
|
|
1780
|
+
* When the daemon restarts, tasks claimed by this machine may be stuck in 'running'
|
|
1781
|
+
* with no process working on them. Reset them to 'queued' so the normal poll cycle
|
|
1782
|
+
* picks them up and autoHeal detects prior work.
|
|
1783
|
+
*/
|
|
1784
|
+
async function recoverOrphanedTasks() {
|
|
1785
|
+
const suffix = getWorktreeSuffix();
|
|
1786
|
+
if (!suffix) return;
|
|
1787
|
+
|
|
1788
|
+
try {
|
|
1789
|
+
const params = new URLSearchParams();
|
|
1790
|
+
params.set('execution_status', 'running');
|
|
1791
|
+
|
|
1792
|
+
const response = await apiRequest(`synced-todos?${params}`);
|
|
1793
|
+
if (!response.ok) return;
|
|
1794
|
+
|
|
1795
|
+
const data = await response.json();
|
|
1796
|
+
const tasks = data.todos || [];
|
|
1797
|
+
|
|
1798
|
+
// Filter to tasks owned by THIS machine (branch contains our suffix)
|
|
1799
|
+
const orphaned = tasks.filter(t => {
|
|
1800
|
+
const branch = t.executionBranch || t.execution_branch || '';
|
|
1801
|
+
return branch.endsWith(suffix);
|
|
1802
|
+
});
|
|
1803
|
+
|
|
1804
|
+
if (orphaned.length === 0) return;
|
|
1805
|
+
|
|
1806
|
+
log(`Recovering ${orphaned.length} orphaned task(s) from previous daemon instance`);
|
|
1807
|
+
|
|
1808
|
+
for (const task of orphaned) {
|
|
1809
|
+
const dn = task.displayNumber || task.display_number;
|
|
1810
|
+
log(`Task #${dn}: resetting from 'running' to 'queued' (orphaned by restart)`);
|
|
1811
|
+
await updateTaskStatus(dn, 'queued', {
|
|
1812
|
+
event: {
|
|
1813
|
+
type: 'requeued',
|
|
1814
|
+
timestamp: new Date().toISOString(),
|
|
1815
|
+
machineName: getMachineName() || undefined,
|
|
1816
|
+
summary: 'Daemon restarted — re-queuing for auto-heal'
|
|
1817
|
+
}
|
|
1818
|
+
});
|
|
1819
|
+
}
|
|
1820
|
+
} catch (error) {
|
|
1821
|
+
log(`Orphaned task recovery failed (non-fatal): ${error.message}`);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1778
1825
|
async function mainLoop() {
|
|
1779
1826
|
daemonStartTime = new Date().toISOString();
|
|
1780
1827
|
|
|
@@ -1815,6 +1862,13 @@ async function mainLoop() {
|
|
|
1815
1862
|
writeFileSync(VERSION_FILE, getVersion());
|
|
1816
1863
|
} catch {}
|
|
1817
1864
|
|
|
1865
|
+
// Recover orphaned tasks from previous daemon instance
|
|
1866
|
+
// When the daemon restarts (self-update, crash, reboot), tasks may be stuck
|
|
1867
|
+
// in 'running' with no process actually working on them. Reset them to 'queued'
|
|
1868
|
+
// so the normal poll cycle picks them up and autoHeal handles the rest.
|
|
1869
|
+
// See: docs/20260211_auto_complete_failure_investigation.md
|
|
1870
|
+
await recoverOrphanedTasks();
|
|
1871
|
+
|
|
1818
1872
|
// Initial status
|
|
1819
1873
|
updateStatusFile();
|
|
1820
1874
|
|