fullcourtdefense-cli 1.21.4 → 1.21.6
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/commands/desktopChatGuard.js +73 -28
- package/dist/version.json +1 -1
- package/package.json +1 -1
|
@@ -279,19 +279,36 @@ function ensureWatcherScript() {
|
|
|
279
279
|
fs.writeFileSync(WATCHER_PS1_PATH, desktopChatWatcherScript(), { encoding: 'utf8' });
|
|
280
280
|
return WATCHER_PS1_PATH;
|
|
281
281
|
}
|
|
282
|
-
function relaunchThrottleOk() {
|
|
282
|
+
function relaunchThrottleOk(currentClaudeCreated) {
|
|
283
283
|
try {
|
|
284
|
-
const
|
|
285
|
-
|
|
284
|
+
const marker = JSON.parse(fs.readFileSync(A11Y_RELAUNCH_MARKER, 'utf8'));
|
|
285
|
+
const last = marker.lastAttemptAt || 0;
|
|
286
|
+
const sinceLast = Date.now() - last;
|
|
287
|
+
// Hard floor: never two relaunches within 15 minutes regardless of
|
|
288
|
+
// instance tracking — bounds the worst case if a relaunched Claude drops
|
|
289
|
+
// our flag (e.g. MSIX self-restart) so we can't kill it in a loop.
|
|
290
|
+
if (sinceLast < 15 * 60_000)
|
|
291
|
+
return false;
|
|
292
|
+
// Claude was restarted (update, crash, user quit/reopen) since our last
|
|
293
|
+
// relaunch attempt — the old throttle must not leave the composer blind.
|
|
294
|
+
// Legacy markers without claudeCreated also take this path once, then
|
|
295
|
+
// start tracking the instance.
|
|
296
|
+
if (currentClaudeCreated && marker.claudeCreated !== currentClaudeCreated) {
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
return sinceLast > A11Y_RELAUNCH_THROTTLE_MS;
|
|
286
300
|
}
|
|
287
301
|
catch {
|
|
288
302
|
return true;
|
|
289
303
|
}
|
|
290
304
|
}
|
|
291
|
-
function stampRelaunch() {
|
|
305
|
+
function stampRelaunch(claudeCreated) {
|
|
292
306
|
try {
|
|
293
307
|
fs.mkdirSync(path.dirname(A11Y_RELAUNCH_MARKER), { recursive: true });
|
|
294
|
-
fs.writeFileSync(A11Y_RELAUNCH_MARKER, JSON.stringify({
|
|
308
|
+
fs.writeFileSync(A11Y_RELAUNCH_MARKER, JSON.stringify({
|
|
309
|
+
lastAttemptAt: Date.now(),
|
|
310
|
+
...(claudeCreated ? { claudeCreated } : {}),
|
|
311
|
+
}), { encoding: 'utf8', mode: 0o600 });
|
|
295
312
|
}
|
|
296
313
|
catch { /* best-effort */ }
|
|
297
314
|
}
|
|
@@ -319,50 +336,78 @@ function ensureAccessibilityScript(allowRelaunch) {
|
|
|
319
336
|
// The browser (main) process is the claude.exe with no --type= child switch.
|
|
320
337
|
"$main = $procs | Where-Object { $_.CommandLine -and ($_.CommandLine -notmatch '--type=') } | Select-Object -First 1",
|
|
321
338
|
"if (-not $main) { Write-Output 'STATE:not-running'; exit 0 }",
|
|
322
|
-
"
|
|
323
|
-
|
|
339
|
+
"$created = $main.CreationDate",
|
|
340
|
+
"if ($main.CommandLine -match '--force-renderer-accessibility') { Write-Output ('STATE:has-flag:' + $created); exit 0 }",
|
|
341
|
+
allowRelaunch ? '' : "Write-Output ('STATE:needs-flag:' + $created); exit 0",
|
|
324
342
|
"$exe = $main.ExecutablePath",
|
|
325
343
|
"if (-not $exe -or -not (Test-Path $exe)) { Write-Output 'STATE:error:no-exe'; exit 0 }",
|
|
326
344
|
'try {',
|
|
327
345
|
" $procs | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }",
|
|
328
346
|
' Start-Sleep -Milliseconds 1500',
|
|
329
347
|
" Start-Process -FilePath $exe -ArgumentList '--force-renderer-accessibility'",
|
|
330
|
-
" Write-Output 'STATE:relaunched'",
|
|
348
|
+
" Write-Output ('STATE:relaunched:' + $created)",
|
|
331
349
|
"} catch { Write-Output ('STATE:error:' + $_.Exception.Message) }",
|
|
332
350
|
].filter(Boolean).join('\n');
|
|
333
351
|
}
|
|
352
|
+
function parseAccessibilityState(out) {
|
|
353
|
+
const match = out.match(/STATE:([^:\r\n]+)(?::(.+))?/);
|
|
354
|
+
if (!match)
|
|
355
|
+
return { state: '' };
|
|
356
|
+
return { state: match[1].trim(), claudeCreated: match[2]?.trim() };
|
|
357
|
+
}
|
|
334
358
|
/**
|
|
335
359
|
* Ensure Claude Desktop is running with renderer accessibility so the composer
|
|
336
360
|
* is readable. Non-blocking best-effort; throttled to at most one relaunch per
|
|
337
|
-
* A11Y_RELAUNCH_THROTTLE_MS
|
|
361
|
+
* A11Y_RELAUNCH_THROTTLE_MS for the SAME Claude process instance. A fresh
|
|
362
|
+
* Claude restart (update, MSI, user reopen) bypasses the throttle.
|
|
338
363
|
*/
|
|
339
364
|
function ensureClaudeForceAccessibility(log) {
|
|
340
365
|
if (process.platform !== 'win32')
|
|
341
366
|
return;
|
|
342
|
-
|
|
343
|
-
|
|
367
|
+
// Probe first without committing to relaunch so we can compare process age
|
|
368
|
+
// against the throttle marker before deciding.
|
|
369
|
+
const probe = (0, child_process_1.spawn)('powershell', [
|
|
344
370
|
'-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-WindowStyle', 'Hidden',
|
|
345
|
-
'-Command', ensureAccessibilityScript(
|
|
371
|
+
'-Command', ensureAccessibilityScript(false),
|
|
346
372
|
], { windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
347
|
-
let
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const state
|
|
352
|
-
if (state === '
|
|
353
|
-
|
|
354
|
-
|
|
373
|
+
let probeOut = '';
|
|
374
|
+
probe.stdout?.on('data', (d) => { probeOut += d.toString(); });
|
|
375
|
+
probe.on('error', () => { });
|
|
376
|
+
probe.on('exit', () => {
|
|
377
|
+
const { state, claudeCreated } = parseAccessibilityState(probeOut);
|
|
378
|
+
if (!state || state === 'not-running' || state.startsWith('has-flag'))
|
|
379
|
+
return;
|
|
380
|
+
if (!state.startsWith('needs-flag')) {
|
|
381
|
+
if (state.startsWith('error'))
|
|
382
|
+
log(`Claude Desktop guard: accessibility probe failed (${state}).`);
|
|
383
|
+
return;
|
|
355
384
|
}
|
|
356
|
-
|
|
385
|
+
const allow = relaunchThrottleOk(claudeCreated);
|
|
386
|
+
if (!allow) {
|
|
357
387
|
log('Claude Desktop guard: Claude is running without accessibility; relaunch throttled — composer stays unreadable until the next allowed relaunch.');
|
|
388
|
+
return;
|
|
358
389
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
}
|
|
390
|
+
const child = (0, child_process_1.spawn)('powershell', [
|
|
391
|
+
'-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-WindowStyle', 'Hidden',
|
|
392
|
+
'-Command', ensureAccessibilityScript(true),
|
|
393
|
+
], { windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
394
|
+
let out = '';
|
|
395
|
+
child.stdout?.on('data', (d) => { out += d.toString(); });
|
|
396
|
+
child.on('error', () => { });
|
|
397
|
+
child.on('exit', () => {
|
|
398
|
+
const result = parseAccessibilityState(out);
|
|
399
|
+
if (result.state === 'relaunched') {
|
|
400
|
+
stampRelaunch(result.claudeCreated || claudeCreated);
|
|
401
|
+
log('Claude Desktop guard: relaunched Claude with --force-renderer-accessibility so the chat composer is readable.');
|
|
402
|
+
}
|
|
403
|
+
else if (result.state.startsWith('error')) {
|
|
404
|
+
// An error can surface AFTER we already force-stopped Claude (Start-Process
|
|
405
|
+
// threw). Stamp the throttle regardless so a half-failed restart can never
|
|
406
|
+
// loop the kill every 5 minutes — wait the full window before retrying.
|
|
407
|
+
stampRelaunch(result.claudeCreated || claudeCreated);
|
|
408
|
+
log(`Claude Desktop guard: could not enable accessibility (${result.state}); backing off before retry.`);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
366
411
|
});
|
|
367
412
|
}
|
|
368
413
|
/**
|
package/dist/version.json
CHANGED