claude-code-session-manager 0.6.0 → 0.7.1
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/dist/assets/{cssMode-lflrC2i6.js → cssMode-n6P73NOL.js} +1 -1
- package/dist/assets/{editor.main-CSEn01y_.js → editor.main-C5N-IAZE.js} +3 -3
- package/dist/assets/{freemarker2-C6z0bRyX.js → freemarker2-pYaw8aW7.js} +1 -1
- package/dist/assets/{handlebars-B8qhzkfD.js → handlebars-6lzqG1Qt.js} +1 -1
- package/dist/assets/{html-ClohgtU2.js → html-BSwRVs7K.js} +1 -1
- package/dist/assets/{htmlMode-Dz83xKTf.js → htmlMode-SxYq5ncR.js} +1 -1
- package/dist/assets/{index-L3iJj9Hb.js → index-B9O0rahO.js} +256 -256
- package/dist/assets/{javascript-7TdbROJ_.js → javascript-DTG_L--r.js} +1 -1
- package/dist/assets/{jsonMode-JC4N0hXM.js → jsonMode-BnjJ_O8o.js} +1 -1
- package/dist/assets/{liquid-DZ-smecj.js → liquid-Bim-6sAj.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-C1GF1Vk4.js → lspLanguageFeatures-Bb0xKMlG.js} +1 -1
- package/dist/assets/{mdx-COF8YTAc.js → mdx-D76-Dd3O.js} +1 -1
- package/dist/assets/{python-Bt-i4SS8.js → python-CZXdAfWd.js} +1 -1
- package/dist/assets/{razor-CzPEv6v-.js → razor-DJhAKh2n.js} +1 -1
- package/dist/assets/{tsMode-DSY_MbAd.js → tsMode-CbK0-ZcV.js} +1 -1
- package/dist/assets/{typescript-fdsVN98s.js → typescript-DSTpxnxU.js} +1 -1
- package/dist/assets/{xml-De0u_YGE.js → xml-qdPlplP1.js} +1 -1
- package/dist/assets/{yaml-DfkoGAru.js → yaml-DFiEYpry.js} +1 -1
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/main/index.cjs +33 -4
- package/src/main/scheduler.cjs +35 -34
- package/src/main/voiceSettings.cjs +1 -10
- package/src/preload/api.d.ts +0 -2
package/src/main/scheduler.cjs
CHANGED
|
@@ -220,23 +220,22 @@ function reconcile(state) {
|
|
|
220
220
|
|
|
221
221
|
// ---------- next-reset detection ----------
|
|
222
222
|
|
|
223
|
-
let cachedNextReset =
|
|
223
|
+
let cachedNextReset = null;
|
|
224
224
|
let cachedUtilization = null; // five_hour utilization %, 0–100, or null if unknown
|
|
225
225
|
|
|
226
226
|
async function refreshNextReset() {
|
|
227
227
|
try {
|
|
228
228
|
const r = await billing.fetchUsage();
|
|
229
|
-
|
|
230
|
-
cachedNextReset = { at, fetchedAt: Date.now() };
|
|
229
|
+
cachedNextReset = r?.usage?.five_hour?.resets_at ?? null;
|
|
231
230
|
cachedUtilization = r?.usage?.five_hour?.utilization ?? cachedUtilization;
|
|
232
|
-
return
|
|
231
|
+
return cachedNextReset;
|
|
233
232
|
} catch {
|
|
234
|
-
return cachedNextReset
|
|
233
|
+
return cachedNextReset;
|
|
235
234
|
}
|
|
236
235
|
}
|
|
237
236
|
|
|
238
237
|
function getNextResetCached() {
|
|
239
|
-
return cachedNextReset
|
|
238
|
+
return cachedNextReset;
|
|
240
239
|
}
|
|
241
240
|
|
|
242
241
|
// ---------- timer ----------
|
|
@@ -245,6 +244,8 @@ let mainWindow = null;
|
|
|
245
244
|
let fireTimer = null;
|
|
246
245
|
let resumeTimer = null;
|
|
247
246
|
let pollTimer = null;
|
|
247
|
+
let rescheduleInterval = null;
|
|
248
|
+
let initialPollTimeout = null;
|
|
248
249
|
let isExecuting = false;
|
|
249
250
|
let cancelToken = { cancelled: false };
|
|
250
251
|
let claudeBinPathCached = null;
|
|
@@ -315,7 +316,6 @@ async function rescheduleTimer() {
|
|
|
315
316
|
function setPaused(reason, resumeAtIso) {
|
|
316
317
|
const s = readQueue();
|
|
317
318
|
if (s.paused && s.paused.reason === reason) {
|
|
318
|
-
// already paused for this reason; just refresh resumeAt if newer
|
|
319
319
|
if (resumeAtIso) s.paused.resumeAt = resumeAtIso;
|
|
320
320
|
} else {
|
|
321
321
|
s.paused = { reason, since: new Date().toISOString(), resumeAt: resumeAtIso || null };
|
|
@@ -324,19 +324,18 @@ function setPaused(reason, resumeAtIso) {
|
|
|
324
324
|
broadcast();
|
|
325
325
|
cancelToken.cancelled = true;
|
|
326
326
|
if (resumeTimer) { clearTimeout(resumeTimer); resumeTimer = null; }
|
|
327
|
-
if (resumeAtIso)
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
runDueJobs().catch(() => {});
|
|
334
|
-
}, delay);
|
|
335
|
-
console.log(`[scheduler] paused (${reason}); auto-resume in ${Math.round(delay/1000)}s`);
|
|
336
|
-
} else {
|
|
337
|
-
console.warn(`[scheduler] paused (${reason}); resumeAt too far in future for setTimeout (${delay}ms)`);
|
|
338
|
-
}
|
|
327
|
+
if (!resumeAtIso) return;
|
|
328
|
+
// Resume 30s after the reset to give the auth/billing endpoint time to flip.
|
|
329
|
+
const delay = Math.max(30_000, new Date(resumeAtIso).getTime() - Date.now() + 30_000);
|
|
330
|
+
if (delay > 0x7fffffff) {
|
|
331
|
+
console.warn(`[scheduler] paused (${reason}); resumeAt too far for setTimeout (${delay}ms)`);
|
|
332
|
+
return;
|
|
339
333
|
}
|
|
334
|
+
resumeTimer = setTimeout(() => {
|
|
335
|
+
clearPause('resume-timer');
|
|
336
|
+
runDueJobs().catch(() => {});
|
|
337
|
+
}, delay);
|
|
338
|
+
console.log(`[scheduler] paused (${reason}); auto-resume in ${Math.round(delay/1000)}s`);
|
|
340
339
|
}
|
|
341
340
|
|
|
342
341
|
function clearPause(source) {
|
|
@@ -350,6 +349,16 @@ function clearPause(source) {
|
|
|
350
349
|
}
|
|
351
350
|
}
|
|
352
351
|
|
|
352
|
+
/** Mutate a job in place to "pending" with cleared run metadata. */
|
|
353
|
+
function resetJobFields(job, errorMsg) {
|
|
354
|
+
job.status = 'pending';
|
|
355
|
+
job.runId = null;
|
|
356
|
+
job.startedAt = null;
|
|
357
|
+
job.finishedAt = null;
|
|
358
|
+
job.exitCode = null;
|
|
359
|
+
job.error = errorMsg ?? null;
|
|
360
|
+
}
|
|
361
|
+
|
|
353
362
|
/** Scan the tail of a job's log for the canonical rate-limit signal. We look
|
|
354
363
|
* at the last 16 KB — final result event always lands at the end. */
|
|
355
364
|
function detectRateLimitInLog(logPath) {
|
|
@@ -514,12 +523,7 @@ async function runDueJobs() {
|
|
|
514
523
|
if (i2 >= 0) {
|
|
515
524
|
const treatAsPending = res.rateLimited || (sn.paused && sn.paused.reason === 'rate_limit');
|
|
516
525
|
if (treatAsPending) {
|
|
517
|
-
sn.jobs[i2].
|
|
518
|
-
sn.jobs[i2].runId = null;
|
|
519
|
-
sn.jobs[i2].startedAt = null;
|
|
520
|
-
sn.jobs[i2].finishedAt = null;
|
|
521
|
-
sn.jobs[i2].exitCode = null;
|
|
522
|
-
sn.jobs[i2].error = res.rateLimited ? 'paused: rate limit' : 'paused: queue halted';
|
|
526
|
+
resetJobFields(sn.jobs[i2], res.rateLimited ? 'paused: rate limit' : 'paused: queue halted');
|
|
523
527
|
} else {
|
|
524
528
|
sn.jobs[i2].status = res.exitCode === 0 ? 'completed' : 'failed';
|
|
525
529
|
sn.jobs[i2].finishedAt = new Date().toISOString();
|
|
@@ -632,12 +636,7 @@ function registerScheduleHandlers() {
|
|
|
632
636
|
const state = readQueue();
|
|
633
637
|
const idx = state.jobs.findIndex((j) => j.slug === slug);
|
|
634
638
|
if (idx < 0) return { ok: false, error: 'not found' };
|
|
635
|
-
state.jobs[idx]
|
|
636
|
-
state.jobs[idx].runId = null;
|
|
637
|
-
state.jobs[idx].startedAt = null;
|
|
638
|
-
state.jobs[idx].finishedAt = null;
|
|
639
|
-
state.jobs[idx].exitCode = null;
|
|
640
|
-
state.jobs[idx].error = null;
|
|
639
|
+
resetJobFields(state.jobs[idx]);
|
|
641
640
|
writeQueue(state);
|
|
642
641
|
broadcast();
|
|
643
642
|
return { ok: true };
|
|
@@ -704,14 +703,16 @@ async function init() {
|
|
|
704
703
|
|
|
705
704
|
await rescheduleTimer();
|
|
706
705
|
// Refresh next-reset every 10 minutes — billing window can shift if usage
|
|
707
|
-
// resets early or the auth token rotates.
|
|
708
|
-
|
|
706
|
+
// resets early or the auth token rotates. Tracked so re-init doesn't leak.
|
|
707
|
+
if (rescheduleInterval) clearInterval(rescheduleInterval);
|
|
708
|
+
rescheduleInterval = setInterval(() => { rescheduleTimer().catch(() => {}); }, 10 * 60_000);
|
|
709
709
|
// when-available poll loop. Tick every 2 minutes; the function itself is
|
|
710
710
|
// a no-op when policy != 'when-available' or queue is empty/paused.
|
|
711
711
|
if (pollTimer) clearInterval(pollTimer);
|
|
712
712
|
pollTimer = setInterval(() => { pollWhenAvailable().catch(() => {}); }, 2 * 60_000);
|
|
713
713
|
// First tick fires after a short delay so billing is warmed up.
|
|
714
|
-
|
|
714
|
+
if (initialPollTimeout) clearTimeout(initialPollTimeout);
|
|
715
|
+
initialPollTimeout = setTimeout(() => { pollWhenAvailable().catch(() => {}); }, 15_000);
|
|
715
716
|
}
|
|
716
717
|
|
|
717
718
|
module.exports = { registerScheduleHandlers, attachWindow, init, ROOT, PRDS_DIR };
|
|
@@ -260,7 +260,6 @@ async function saveWizard(state) {
|
|
|
260
260
|
const DEFAULT_TURN_DETECTOR = Object.freeze({
|
|
261
261
|
enabled: false,
|
|
262
262
|
mode: 'off', // 'audio' | 'text' | 'off'
|
|
263
|
-
dictationMode: false,
|
|
264
263
|
schemaVersion: TURN_DETECTOR_SCHEMA,
|
|
265
264
|
});
|
|
266
265
|
|
|
@@ -270,21 +269,13 @@ function isValidTurnDetectorState(t) {
|
|
|
270
269
|
if (!t || typeof t !== 'object') return false;
|
|
271
270
|
if (typeof t.enabled !== 'boolean') return false;
|
|
272
271
|
if (typeof t.mode !== 'string' || !TURN_DETECTOR_MODES.has(t.mode)) return false;
|
|
273
|
-
if (typeof t.dictationMode !== 'boolean') return false;
|
|
274
272
|
return true;
|
|
275
273
|
}
|
|
276
274
|
|
|
277
275
|
function normalizeTurnDetector(t) {
|
|
278
276
|
const mode = TURN_DETECTOR_MODES.has(t.mode) ? t.mode : 'off';
|
|
279
|
-
// Cross-field invariant: `enabled` and `mode === 'off'` are inconsistent.
|
|
280
|
-
// We normalize so callers can be sloppy: if mode is 'off' then enabled=false.
|
|
281
277
|
const enabled = mode === 'off' ? false : !!t.enabled;
|
|
282
|
-
return {
|
|
283
|
-
enabled,
|
|
284
|
-
mode,
|
|
285
|
-
dictationMode: !!t.dictationMode,
|
|
286
|
-
schemaVersion: TURN_DETECTOR_SCHEMA,
|
|
287
|
-
};
|
|
278
|
+
return { enabled, mode, schemaVersion: TURN_DETECTOR_SCHEMA };
|
|
288
279
|
}
|
|
289
280
|
|
|
290
281
|
/**
|
package/src/preload/api.d.ts
CHANGED
|
@@ -169,8 +169,6 @@ export interface VoiceTurnDetectorState {
|
|
|
169
169
|
enabled: boolean;
|
|
170
170
|
/** 'audio' = smart-turn-v3 (planned); 'text' = rejected on license; 'off' = pure-VAD (default). */
|
|
171
171
|
mode: 'audio' | 'text' | 'off';
|
|
172
|
-
/** When true, bypass the model and use pure-VAD endpointing (out-of-distribution code). */
|
|
173
|
-
dictationMode: boolean;
|
|
174
172
|
schemaVersion: 1;
|
|
175
173
|
}
|
|
176
174
|
|