claude-code-session-manager 0.10.0 → 0.10.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-DWlBzlpW.js → cssMode-AMMSQWRU.js} +1 -1
- package/dist/assets/{freemarker2-Cgg83m-Z.js → freemarker2-B7HmOKy0.js} +1 -1
- package/dist/assets/{handlebars-C4r4LOI9.js → handlebars-DMhLYNJA.js} +1 -1
- package/dist/assets/{html-DaxRI5sW.js → html-DIfRfeTv.js} +1 -1
- package/dist/assets/{htmlMode-Bu_8jtXo.js → htmlMode-DBqG7xl_.js} +1 -1
- package/dist/assets/{index-C_tgFedf.js → index-BqL_4JKo.js} +600 -600
- package/dist/assets/{index-Dj3Db4OA.css → index-CxncC9a0.css} +1 -1
- package/dist/assets/{javascript-D5Ztx-Ej.js → javascript-Bsn1K6_V.js} +1 -1
- package/dist/assets/{jsonMode-tfsgezVc.js → jsonMode-DVLHW2S3.js} +1 -1
- package/dist/assets/{liquid-F2cD9OL0.js → liquid-9zGHPTSW.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-Bz_Eih8F.js → lspLanguageFeatures-BaqyiCFJ.js} +1 -1
- package/dist/assets/{mdx-BPlD1clX.js → mdx-DhU2NoOP.js} +1 -1
- package/dist/assets/{python-B4gUOWNI.js → python-WMVORwGT.js} +1 -1
- package/dist/assets/{razor-B6pMxVp1.js → razor-C13iUMo9.js} +1 -1
- package/dist/assets/{tsMode-C9nq6cHi.js → tsMode-B7lSY5y7.js} +1 -1
- package/dist/assets/{typescript-Do5Vtwxu.js → typescript-BbjzsO4g.js} +1 -1
- package/dist/assets/{xml-C0mTbVRp.js → xml-BH6qG4pe.js} +1 -1
- package/dist/assets/{yaml-D3sePJfA.js → yaml-B4r3z4qP.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +2 -1
- package/src/main/index.cjs +101 -0
- package/src/main/scheduler.cjs +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as e}from"./index-
|
|
1
|
+
import{l as e}from"./index-BqL_4JKo.js";const n={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],onEnterRules:[{beforeText:/^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,afterText:/^\s*\*\/$/,action:{indentAction:e.IndentAction.IndentOutdent,appendText:" * "}},{beforeText:/^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,action:{indentAction:e.IndentAction.None,appendText:" * "}},{beforeText:/^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,action:{indentAction:e.IndentAction.None,appendText:"* "}},{beforeText:/^(\t|(\ \ ))*\ \*\/\s*$/,action:{indentAction:e.IndentAction.None,removeText:1}}],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"',notIn:["string"]},{open:"'",close:"'",notIn:["string","comment"]},{open:"`",close:"`",notIn:["string","comment"]},{open:"/**",close:" */",notIn:["string"]}],folding:{markers:{start:new RegExp("^\\s*//\\s*#?region\\b"),end:new RegExp("^\\s*//\\s*#?endregion\\b")}}},o={defaultToken:"invalid",tokenPostfix:".ts",keywords:["abstract","any","as","asserts","bigint","boolean","break","case","catch","class","continue","const","constructor","debugger","declare","default","delete","do","else","enum","export","extends","false","finally","for","from","function","get","if","implements","import","in","infer","instanceof","interface","is","keyof","let","module","namespace","never","new","null","number","object","out","package","private","protected","public","override","readonly","require","global","return","satisfies","set","static","string","super","switch","symbol","this","throw","true","try","type","typeof","undefined","unique","unknown","var","void","while","with","yield","async","await","of"],operators:["<=",">=","==","!=","===","!==","=>","+","-","**","*","/","%","++","--","<<","</",">>",">>>","&","|","^","!","~","&&","||","??","?",":","=","+=","-=","*=","**=","/=","%=","<<=",">>=",">>>=","&=","|=","^=","@"],symbols:/[=><!~?:&|+\-*\/\^%]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,digits:/\d+(_+\d+)*/,octaldigits:/[0-7]+(_+[0-7]+)*/,binarydigits:/[0-1]+(_+[0-1]+)*/,hexdigits:/[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,regexpctl:/[(){}\[\]\$\^|\-*+?\.]/,regexpesc:/\\(?:[bBdDfnrstvwWn0\\\/]|@regexpctl|c[A-Z]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})/,tokenizer:{root:[[/[{}]/,"delimiter.bracket"],{include:"common"}],common:[[/#?[a-z_$][\w$]*/,{cases:{"@keywords":"keyword","@default":"identifier"}}],[/[A-Z][\w\$]*/,"type.identifier"],{include:"@whitespace"},[/\/(?=([^\\\/]|\\.)+\/([dgimsuy]*)(\s*)(\.|;|,|\)|\]|\}|$))/,{token:"regexp",bracket:"@open",next:"@regexp"}],[/[()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/!(?=([^=]|$))/,"delimiter"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/(@digits)[eE]([\-+]?(@digits))?/,"number.float"],[/(@digits)\.(@digits)([eE][\-+]?(@digits))?/,"number.float"],[/0[xX](@hexdigits)n?/,"number.hex"],[/0[oO]?(@octaldigits)n?/,"number.octal"],[/0[bB](@binarydigits)n?/,"number.binary"],[/(@digits)n?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string_double"],[/'/,"string","@string_single"],[/`/,"string","@string_backtick"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@jsdoc"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],jsdoc:[[/[^\/*]+/,"comment.doc"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],regexp:[[/(\{)(\d+(?:,\d*)?)(\})/,["regexp.escape.control","regexp.escape.control","regexp.escape.control"]],[/(\[)(\^?)(?=(?:[^\]\\\/]|\\.)+)/,["regexp.escape.control",{token:"regexp.escape.control",next:"@regexrange"}]],[/(\()(\?:|\?=|\?!)/,["regexp.escape.control","regexp.escape.control"]],[/[()]/,"regexp.escape.control"],[/@regexpctl/,"regexp.escape.control"],[/[^\\\/]/,"regexp"],[/@regexpesc/,"regexp.escape"],[/\\\./,"regexp.invalid"],[/(\/)([dgimsuy]*)/,[{token:"regexp",bracket:"@close",next:"@pop"},"keyword.other"]]],regexrange:[[/-/,"regexp.escape.control"],[/\^/,"regexp.invalid"],[/@regexpesc/,"regexp.escape"],[/[^\]]/,"regexp"],[/\]/,{token:"regexp.escape.control",next:"@pop",bracket:"@close"}]],string_double:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],string_single:[[/[^\\']+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/'/,"string","@pop"]],string_backtick:[[/\$\{/,{token:"delimiter.bracket",next:"@bracketCounting"}],[/[^\\`$]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/`/,"string","@pop"]],bracketCounting:[[/\{/,"delimiter.bracket","@bracketCounting"],[/\}/,"delimiter.bracket","@pop"],{include:"common"}]}};export{n as conf,o as language};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as e}from"./index-
|
|
1
|
+
import{l as e}from"./index-BqL_4JKo.js";const n={comments:{blockComment:["<!--","-->"]},brackets:[["<",">"]],autoClosingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],surroundingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],onEnterRules:[{beforeText:new RegExp("<([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$","i"),afterText:/^<\/([_:\w][_:\w-.\d]*)\s*>$/i,action:{indentAction:e.IndentAction.IndentOutdent}},{beforeText:new RegExp("<(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$","i"),action:{indentAction:e.IndentAction.Indent}}]},o={defaultToken:"",tokenPostfix:".xml",ignoreCase:!0,qualifiedName:/(?:[\w\.\-]+:)?[\w\.\-]+/,tokenizer:{root:[[/[^<&]+/,""],{include:"@whitespace"},[/(<)(@qualifiedName)/,[{token:"delimiter"},{token:"tag",next:"@tag"}]],[/(<\/)(@qualifiedName)(\s*)(>)/,[{token:"delimiter"},{token:"tag"},"",{token:"delimiter"}]],[/(<\?)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/(<\!)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/<\!\[CDATA\[/,{token:"delimiter.cdata",next:"@cdata"}],[/&\w+;/,"string.escape"]],cdata:[[/[^\]]+/,""],[/\]\]>/,{token:"delimiter.cdata",next:"@pop"}],[/\]/,""]],tag:[[/[ \t\r\n]+/,""],[/(@qualifiedName)(\s*=\s*)("[^"]*"|'[^']*')/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">?\/]*|'[^'>?\/]*)(?=[\?\/]\>)/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">]*|'[^'>]*)/,["attribute.name","","attribute.value"]],[/@qualifiedName/,"attribute.name"],[/\?>/,{token:"delimiter",next:"@pop"}],[/(\/)(>)/,[{token:"tag"},{token:"delimiter",next:"@pop"}]],[/>/,{token:"delimiter",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,""],[/<!--/,{token:"comment",next:"@comment"}]],comment:[[/[^<\-]+/,"comment.content"],[/-->/,{token:"comment",next:"@pop"}],[/<!--/,"comment.content.invalid"],[/[<\-]/,"comment.content"]]}};export{n as conf,o as language};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as e}from"./index-
|
|
1
|
+
import{l as e}from"./index-BqL_4JKo.js";const t={comments:{lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],folding:{offSide:!0},onEnterRules:[{beforeText:/:\s*$/,action:{indentAction:e.IndentAction.Indent}}]},o={tokenPostfix:".yaml",brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.square",open:"[",close:"]"}],keywords:["true","True","TRUE","false","False","FALSE","null","Null","Null","~"],numberInteger:/(?:0|[+-]?[0-9]+)/,numberFloat:/(?:0|[+-]?[0-9]+)(?:\.[0-9]+)?(?:e[-+][1-9][0-9]*)?/,numberOctal:/0o[0-7]+/,numberHex:/0x[0-9a-fA-F]+/,numberInfinity:/[+-]?\.(?:inf|Inf|INF)/,numberNaN:/\.(?:nan|Nan|NAN)/,numberDate:/\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?/,escapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,tokenizer:{root:[{include:"@whitespace"},{include:"@comment"},[/%[^ ]+.*$/,"meta.directive"],[/---/,"operators.directivesEnd"],[/\.{3}/,"operators.documentEnd"],[/[-?:](?= )/,"operators"],{include:"@anchor"},{include:"@tagHandle"},{include:"@flowCollections"},{include:"@blockStyle"},[/@numberInteger(?![ \t]*\S+)/,"number"],[/@numberFloat(?![ \t]*\S+)/,"number.float"],[/@numberOctal(?![ \t]*\S+)/,"number.octal"],[/@numberHex(?![ \t]*\S+)/,"number.hex"],[/@numberInfinity(?![ \t]*\S+)/,"number.infinity"],[/@numberNaN(?![ \t]*\S+)/,"number.nan"],[/@numberDate(?![ \t]*\S+)/,"number.date"],[/(".*?"|'.*?'|[^#'"]*?)([ \t]*)(:)( |$)/,["type","white","operators","white"]],{include:"@flowScalars"},[/.+?(?=(\s+#|$))/,{cases:{"@keywords":"keyword","@default":"string"}}]],object:[{include:"@whitespace"},{include:"@comment"},[/\}/,"@brackets","@pop"],[/,/,"delimiter.comma"],[/:(?= )/,"operators"],[/(?:".*?"|'.*?'|[^,\{\[]+?)(?=: )/,"type"],{include:"@flowCollections"},{include:"@flowScalars"},{include:"@tagHandle"},{include:"@anchor"},{include:"@flowNumber"},[/[^\},]+/,{cases:{"@keywords":"keyword","@default":"string"}}]],array:[{include:"@whitespace"},{include:"@comment"},[/\]/,"@brackets","@pop"],[/,/,"delimiter.comma"],{include:"@flowCollections"},{include:"@flowScalars"},{include:"@tagHandle"},{include:"@anchor"},{include:"@flowNumber"},[/[^\],]+/,{cases:{"@keywords":"keyword","@default":"string"}}]],multiString:[[/^( +).+$/,"string","@multiStringContinued.$1"]],multiStringContinued:[[/^( *).+$/,{cases:{"$1==$S2":"string","@default":{token:"@rematch",next:"@popall"}}}]],whitespace:[[/[ \t\r\n]+/,"white"]],comment:[[/#.*$/,"comment"]],flowCollections:[[/\[/,"@brackets","@array"],[/\{/,"@brackets","@object"]],flowScalars:[[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/'[^']*'/,"string"],[/"/,"string","@doubleQuotedString"]],doubleQuotedString:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],blockStyle:[[/[>|][0-9]*[+-]?$/,"operators","@multiString"]],flowNumber:[[/@numberInteger(?=[ \t]*[,\]\}])/,"number"],[/@numberFloat(?=[ \t]*[,\]\}])/,"number.float"],[/@numberOctal(?=[ \t]*[,\]\}])/,"number.octal"],[/@numberHex(?=[ \t]*[,\]\}])/,"number.hex"],[/@numberInfinity(?=[ \t]*[,\]\}])/,"number.infinity"],[/@numberNaN(?=[ \t]*[,\]\}])/,"number.nan"],[/@numberDate(?=[ \t]*[,\]\}])/,"number.date"]],tagHandle:[[/\![^ ]*/,"tag"]],anchor:[[/[&*][^ ]+/,"namespace"]]}};export{t as conf,o as language};
|
package/dist/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Claude Session Manager</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-BqL_4JKo.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CxncC9a0.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body class="bg-bg text-fg font-mono antialiased">
|
|
11
11
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-session-manager",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Local cockpit for the Claude Code CLI — multi-tab terminal, full config surface, scheduler, voice dictation, and live observability.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main/index.cjs",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"prepublishOnly": "vite build",
|
|
29
29
|
"test:unit": "vitest run",
|
|
30
30
|
"test:e2e": "xvfb-run -a playwright test",
|
|
31
|
+
"smoke:darwin": "playwright test tests/smoke/darwin-boot.spec.ts",
|
|
31
32
|
"test:e2e:mic": "xvfb-run -a playwright test e2e/mic.spec.mjs",
|
|
32
33
|
"test:e2e:gen-fixture": "espeak-ng -w /tmp/sm-raw.wav -s 140 'testing one two three four five' && ffmpeg -y -i /tmp/sm-raw.wav -ar 48000 -ac 2 -sample_fmt s16 e2e/fixtures/speech.wav",
|
|
33
34
|
"refresh:vad-assets": "cp node_modules/@ricky0123/vad-web/dist/silero_vad_v5.onnx node_modules/@ricky0123/vad-web/dist/vad.worklet.bundle.min.js src/renderer/public/vad/ && cp node_modules/onnxruntime-web/dist/ort-wasm-simd-threaded.wasm node_modules/onnxruntime-web/dist/ort-wasm-simd-threaded.mjs node_modules/onnxruntime-web/dist/ort-wasm-simd-threaded.jsep.wasm node_modules/onnxruntime-web/dist/ort-wasm-simd-threaded.jsep.mjs src/renderer/public/vad/"
|
package/src/main/index.cjs
CHANGED
|
@@ -24,10 +24,32 @@ const otel = require('./otel.cjs');
|
|
|
24
24
|
const otelSettings = require('./otelSettings.cjs');
|
|
25
25
|
const { registerHistoryAggregatorHandlers } = require('./historyAggregator.cjs');
|
|
26
26
|
const memoryTool = require('./memoryTool.cjs');
|
|
27
|
+
const { resolveClaudeBin } = require('./lib/claudeBin.cjs');
|
|
28
|
+
const { assertCwdInsideHome } = require('./lib/insideHome.cjs');
|
|
27
29
|
|
|
28
30
|
let mainWindow = null;
|
|
29
31
|
let rebooting = false;
|
|
30
32
|
|
|
33
|
+
// Boot diagnostics — populated at app.whenReady so the renderer can poll their
|
|
34
|
+
// state via IPC and surface toasts on the failure paths. The first-paint
|
|
35
|
+
// deadline timer reads these into the boot log if ready-to-show never fires.
|
|
36
|
+
let bootClaudeBin = { resolved: 'claude', foundOnDisk: false };
|
|
37
|
+
let bootHomeSelfCheck = { ok: true };
|
|
38
|
+
const bootRecentIpcInvocations = [];
|
|
39
|
+
let firstPaintTimer = null;
|
|
40
|
+
|
|
41
|
+
// Wrap ipcMain.handle once to track which channels the renderer actually
|
|
42
|
+
// invokes — the boot log dumps the last 5 so a hang is attributable to a
|
|
43
|
+
// specific handler.
|
|
44
|
+
const originalIpcHandle = ipcMain.handle.bind(ipcMain);
|
|
45
|
+
ipcMain.handle = function trackedHandle(channel, listener) {
|
|
46
|
+
return originalIpcHandle(channel, (...args) => {
|
|
47
|
+
bootRecentIpcInvocations.push({ channel, at: new Date().toISOString() });
|
|
48
|
+
if (bootRecentIpcInvocations.length > 5) bootRecentIpcInvocations.shift();
|
|
49
|
+
return listener(...args);
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
31
53
|
const REBOOT_LOG = path.join(os.homedir(), '.claude', 'session-manager-reboot.log');
|
|
32
54
|
|
|
33
55
|
function logReboot(line) {
|
|
@@ -39,6 +61,52 @@ function logReboot(line) {
|
|
|
39
61
|
} catch { /* best-effort */ }
|
|
40
62
|
}
|
|
41
63
|
|
|
64
|
+
// Writes a diagnostic dump when the renderer fails to fire ready-to-show
|
|
65
|
+
// within the boot deadline. Sync I/O is fine — this is the failure path and
|
|
66
|
+
// the user is already staring at a blank window.
|
|
67
|
+
function writeFirstPaintFailureLog() {
|
|
68
|
+
try {
|
|
69
|
+
const logDir = path.join(os.homedir(), '.claude', 'session-manager', 'logs');
|
|
70
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
71
|
+
|
|
72
|
+
const ymd = new Date().toISOString().slice(0, 10);
|
|
73
|
+
const logPath = path.join(logDir, `boot-${ymd}.log`);
|
|
74
|
+
|
|
75
|
+
const homeCheck = assertCwdInsideHome(os.homedir());
|
|
76
|
+
const lines = [
|
|
77
|
+
`=== first-paint deadline exceeded @ ${new Date().toISOString()} ===`,
|
|
78
|
+
`process.versions: ${JSON.stringify(process.versions)}`,
|
|
79
|
+
`process.platform: ${process.platform}`,
|
|
80
|
+
`process.arch: ${process.arch}`,
|
|
81
|
+
`os.homedir(): ${os.homedir()}`,
|
|
82
|
+
`claudeBin: ${JSON.stringify(bootClaudeBin)}`,
|
|
83
|
+
`homeSelfCheck: ${JSON.stringify(homeCheck)}`,
|
|
84
|
+
`recentIpcInvocations: ${JSON.stringify(bootRecentIpcInvocations)}`,
|
|
85
|
+
'RENDERER DID NOT FIRE ready-to-show WITHIN 10s — likely renderer JS error or main-process IPC hang.',
|
|
86
|
+
'',
|
|
87
|
+
];
|
|
88
|
+
fs.appendFileSync(logPath, lines.join('\n'), { mode: 0o600 });
|
|
89
|
+
|
|
90
|
+
// Keep last 3 boot-*.log files; unlink older ones.
|
|
91
|
+
try {
|
|
92
|
+
const entries = fs.readdirSync(logDir)
|
|
93
|
+
.filter((f) => /^boot-\d{4}-\d{2}-\d{2}\.log$/.test(f))
|
|
94
|
+
.map((f) => {
|
|
95
|
+
const full = path.join(logDir, f);
|
|
96
|
+
let mtimeMs = 0;
|
|
97
|
+
try { mtimeMs = fs.statSync(full).mtimeMs; } catch { /* */ }
|
|
98
|
+
return { full, mtimeMs };
|
|
99
|
+
})
|
|
100
|
+
.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
101
|
+
for (const e of entries.slice(3)) {
|
|
102
|
+
try { fs.unlinkSync(e.full); } catch { /* */ }
|
|
103
|
+
}
|
|
104
|
+
} catch { /* */ }
|
|
105
|
+
} catch (err) {
|
|
106
|
+
console.error('[firstPaint] failed to write boot log:', err?.message);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
42
110
|
function resolveNpx() {
|
|
43
111
|
const isWin = process.platform === 'win32';
|
|
44
112
|
try {
|
|
@@ -161,7 +229,14 @@ function createWindow() {
|
|
|
161
229
|
},
|
|
162
230
|
});
|
|
163
231
|
|
|
232
|
+
// Boot-time detection 3: if ready-to-show never fires (blank window from
|
|
233
|
+
// renderer JS error or main-process IPC hang), write a diagnostic dump so
|
|
234
|
+
// the user has a postmortem instead of just an empty window.
|
|
235
|
+
if (firstPaintTimer) clearTimeout(firstPaintTimer);
|
|
236
|
+
firstPaintTimer = setTimeout(() => { writeFirstPaintFailureLog(); }, 10_000);
|
|
237
|
+
|
|
164
238
|
mainWindow.once('ready-to-show', () => {
|
|
239
|
+
if (firstPaintTimer) { clearTimeout(firstPaintTimer); firstPaintTimer = null; }
|
|
165
240
|
mainWindow.maximize();
|
|
166
241
|
mainWindow.show();
|
|
167
242
|
});
|
|
@@ -183,6 +258,7 @@ function createWindow() {
|
|
|
183
258
|
}
|
|
184
259
|
|
|
185
260
|
mainWindow.on('closed', () => {
|
|
261
|
+
if (firstPaintTimer) { clearTimeout(firstPaintTimer); firstPaintTimer = null; }
|
|
186
262
|
mainWindow = null;
|
|
187
263
|
});
|
|
188
264
|
}
|
|
@@ -201,6 +277,11 @@ ipcMain.handle('app:is-e2e', () => process.env.SM_E2E === '1');
|
|
|
201
277
|
|
|
202
278
|
ipcMain.handle('app:engage-rules-path', () => process.env.SESSION_MANAGER_ENGAGE_RULES || null);
|
|
203
279
|
|
|
280
|
+
// Boot diagnostics — renderer polls these to surface toasts when `claude` isn't
|
|
281
|
+
// on disk or the home self-check failed (e.g. macOS /Users symlink mismatch).
|
|
282
|
+
ipcMain.handle('app:claude-bin-status', () => bootClaudeBin);
|
|
283
|
+
ipcMain.handle('app:home-self-check', () => bootHomeSelfCheck);
|
|
284
|
+
|
|
204
285
|
ipcMain.handle('app:pick-directory', async () => {
|
|
205
286
|
console.log('[main] pick-directory invoked');
|
|
206
287
|
const result = await dialog.showOpenDialog(mainWindow, {
|
|
@@ -550,6 +631,26 @@ app.whenReady().then(async () => {
|
|
|
550
631
|
logs.pruneOld();
|
|
551
632
|
logs.writeLine({ scope: 'main', level: 'info', message: 'app start', meta: { version: app.getVersion(), platform: process.platform } });
|
|
552
633
|
|
|
634
|
+
// Boot-time detection 1: surface `claude` binary resolution so a missing
|
|
635
|
+
// install becomes visible to the renderer instead of failing silently on
|
|
636
|
+
// first spawn attempt.
|
|
637
|
+
const claudeResolved = resolveClaudeBin();
|
|
638
|
+
const claudeFoundOnDisk = claudeResolved !== 'claude';
|
|
639
|
+
bootClaudeBin = { resolved: claudeResolved, foundOnDisk: claudeFoundOnDisk };
|
|
640
|
+
if (claudeFoundOnDisk) {
|
|
641
|
+
console.log(`[claudeBin] resolved=${claudeResolved}`);
|
|
642
|
+
} else {
|
|
643
|
+
console.warn('[claudeBin] FALLBACK no candidate found; spawn will rely on PATH');
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Boot-time detection 2: symlinked /Users on macOS can make os.homedir()
|
|
647
|
+
// realpath to a path outside itself, which breaks every cwd containment
|
|
648
|
+
// check downstream. Surface here rather than failing on first session spawn.
|
|
649
|
+
bootHomeSelfCheck = assertCwdInsideHome(os.homedir());
|
|
650
|
+
if (!bootHomeSelfCheck.ok) {
|
|
651
|
+
console.error(`[insideHome] SELF-CHECK FAILED: ${bootHomeSelfCheck.error}; sessions will not be able to spawn`);
|
|
652
|
+
}
|
|
653
|
+
|
|
553
654
|
process.on('uncaughtException', (err) => {
|
|
554
655
|
logs.writeLine({ scope: 'main', level: 'error', message: 'uncaughtException', meta: { error: err?.message, stack: err?.stack } });
|
|
555
656
|
});
|
package/src/main/scheduler.cjs
CHANGED
|
@@ -1385,16 +1385,16 @@ function registerScheduleHandlers() {
|
|
|
1385
1385
|
return { ok: true };
|
|
1386
1386
|
});
|
|
1387
1387
|
|
|
1388
|
-
// Archive
|
|
1389
|
-
//
|
|
1390
|
-
// prds-archived/<ISO>/ so the user can recover them.
|
|
1391
|
-
// enforced — only files inside PRDS_DIR are moved.
|
|
1388
|
+
// Archive every non-running PRD and drop its entry from queue.json.
|
|
1389
|
+
// Running entries are kept (would orphan an in-flight job). PRD files are
|
|
1390
|
+
// moved (not deleted) to prds-archived/<ISO>/ so the user can recover them.
|
|
1391
|
+
// Path containment is enforced — only files inside PRDS_DIR are moved.
|
|
1392
1392
|
ipcMain.handle('schedule:clear-queue', async () => {
|
|
1393
1393
|
ensureDirs();
|
|
1394
1394
|
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
1395
1395
|
const archiveDir = path.join(PRDS_ARCHIVE_DIR, ts);
|
|
1396
1396
|
const state = await readQueue();
|
|
1397
|
-
const victims = state.jobs.filter((j) => j.status
|
|
1397
|
+
const victims = state.jobs.filter((j) => j.status !== 'running');
|
|
1398
1398
|
if (victims.length === 0) {
|
|
1399
1399
|
return { ok: true, archived: 0, archivedTo: null };
|
|
1400
1400
|
}
|