claude-code-session-manager 0.105.0 → 0.107.0
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/{AgentLibrary-BDtr4oI-.js → AgentLibrary-Bjeq2-kp.js} +1 -1
- package/dist/assets/{Button-CuXBCQDp.js → Button-CRLhoI2K.js} +1 -1
- package/dist/assets/{DataModel-DmvRipT5.js → DataModel-iPJw5jcG.js} +1 -1
- package/dist/assets/{History-B-v9pwwr.js → History-DfPR3_6N.js} +1 -1
- package/dist/assets/{Hooks-S_0oHHlr.js → Hooks-Bw7H_DmC.js} +1 -1
- package/dist/assets/{Library-BcbGJxQ3.js → Library-rqaJJJ1b.js} +1 -1
- package/dist/assets/{ListDetail-DtqHqJHt.js → ListDetail-qI27OYfy.js} +1 -1
- package/dist/assets/{MarkdownEditor-B56sPp_l.js → MarkdownEditor-BEFuHVNz.js} +1 -1
- package/dist/assets/{McpServers-D5VnmxAn.js → McpServers-Bz9O79rz.js} +1 -1
- package/dist/assets/{Memory-0yGZLyRP.js → Memory-DobxIoZ-.js} +1 -1
- package/dist/assets/{Panel-NoAADJwx.js → Panel-Cyt1wpUf.js} +1 -1
- package/dist/assets/{Permissions-D2Dh-ee8.js → Permissions-ChSNRUJJ.js} +1 -1
- package/dist/assets/{Plugins-kGoUgS9b.js → Plugins-CII221R2.js} +2 -2
- package/dist/assets/{ProvenanceBadge-CHwDFTX7.js → ProvenanceBadge-C_sk5-v3.js} +1 -1
- package/dist/assets/{SaveBar-BtKNMIYK.js → SaveBar-BuYrg5E9.js} +1 -1
- package/dist/assets/Scheduler-eixbMGEk.js +16 -0
- package/dist/assets/{ScopeSwitcher-BusohWZ5.js → ScopeSwitcher-BM0gLPhd.js} +1 -1
- package/dist/assets/Settings-qD-0W-Ut.js +4 -0
- package/dist/assets/{SkillReferenceGraph-BHOdIpsy.js → SkillReferenceGraph-D0Hgdfxq.js} +1 -1
- package/dist/assets/{Skills-DSTLZimI.js → Skills-C3T_2_2o.js} +1 -1
- package/dist/assets/{SystemPrompt-BG7wFWdb.js → SystemPrompt-B-xTy9Qn.js} +1 -1
- package/dist/assets/{TagLibrary-Bx27aE3_.js → TagLibrary-BH67tR5z.js} +1 -1
- package/dist/assets/{TiptapBody-j0O8J7YI.js → TiptapBody-CP_F_mwb.js} +1 -1
- package/dist/assets/{Toggle-DzVZ6rC7.js → Toggle-haRPHS5N.js} +1 -1
- package/dist/assets/{ViewTabs-BizjuynT.js → ViewTabs-BHV3MA5u.js} +1 -1
- package/dist/assets/{index-BLVzrc4k.css → index-tr1w3hHB.css} +1 -1
- package/dist/assets/index-v_3LvhMx.js +3077 -0
- package/dist/assets/{settingsSchema-7MRAxaJk.js → settingsSchema-CE46TA3V.js} +1 -1
- package/dist/assets/useAgentsDirWatch-BZTi2K2e.js +1 -0
- package/dist/assets/{useConfigDirWatch-ot0VYwQC.js → useConfigDirWatch-CjcswsWR.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +2 -1
- package/scripts/scheduler-mcp-server.cjs +58 -2
- package/src/main/build-info.json +4 -4
- package/src/main/chatRunner.cjs +4 -2
- package/src/main/crashDiagnostics.cjs +39 -3
- package/src/main/index.cjs +47 -3
- package/src/main/ipcSchemas.cjs +120 -0
- package/src/main/lib/claudeBin.cjs +22 -8
- package/src/main/lib/cleanEnv.cjs +6 -5
- package/src/main/lib/customerFeedbackAdminRoutes.cjs +94 -0
- package/src/main/lib/customerFeedbackClient.cjs +261 -0
- package/src/main/lib/customerFeedbackInbox.cjs +286 -0
- package/src/main/lib/customerFeedbackIpc.cjs +43 -0
- package/src/main/lib/e2eStateMachine.cjs +39 -0
- package/src/main/lib/insideHome.cjs +1 -1
- package/src/main/lib/mcpToolCatalog.cjs +31 -6
- package/src/main/lib/projectFolder.cjs +89 -0
- package/src/main/lib/usageCircuit.cjs +15 -0
- package/src/main/lib/winUserData.cjs +22 -0
- package/src/main/scheduler.cjs +68 -20
- package/src/main/usage.cjs +10 -2
- package/src/main/webRemote.cjs +1364 -0
- package/src/preload/api.d.ts +132 -1
- package/src/preload/index.cjs +51 -0
- package/dist/assets/Scheduler-B0_kMMDs.js +0 -16
- package/dist/assets/Settings-DuRJntaE.js +0 -3
- package/dist/assets/index-CLIaYljt.js +0 -3073
- package/dist/assets/useAgentsDirWatch-BUwfU2Kq.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{aw as b,ah as n,S as N,aN as O}from"./index-CLIaYljt.js";import{r as K}from"./claude-settings-schema-COigsajx.js";function G({node:e,targetScope:r,onOverride:s,schema:a,rootLabel:m}){const[f,x]=b.useState(""),[u,o]=b.useState(!0),g=[],d=e.kind==="object"&&Object.keys(e.children).length===0,y=((a==null?void 0:a.rootKeys())??[]).length>0;return n.jsxs("div",{className:"h-full flex flex-col",children:[n.jsxs("div",{className:"shrink-0 border-b border-line bg-bg-elev px-3 py-1.5 flex items-center gap-2 text-xs",children:[n.jsxs("span",{className:"text-fg-faint",children:["merged · overrides → ",n.jsx("span",{className:"text-fg-dim",children:N[r].label})]}),y&&n.jsxs("label",{className:"flex items-center gap-1 text-fg-faint cursor-pointer",children:[n.jsx("input",{type:"checkbox",checked:u,onChange:t=>o(t.target.checked),className:"accent-accent"}),"show unset keys"]}),n.jsx("div",{className:"flex-1"}),n.jsx("input",{value:f,onChange:t=>x(t.target.value),placeholder:"filter keys or descriptions…",className:"bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg w-56"})]}),n.jsx("div",{className:"flex-1 overflow-auto px-3 py-2 font-mono text-xs",children:d&&!u?n.jsx("div",{className:"text-fg-faint italic px-2 py-4",children:"no settings resolved — all scope files empty or missing"}):n.jsx(E,{node:e,path:g,label:m??"(root)",targetScope:r,onOverride:s,filter:f,depth:0,schema:a,showGhosts:u,defaultOpen:!0})})]})}function E(e){return e.node.kind==="object"?n.jsx(P,{...e,node:e.node}):e.node.kind==="array"?n.jsx(J,{...e,node:e.node}):n.jsx(R,{...e,node:e.node})}function A(e,r){return r.trim()?e.toLowerCase().includes(r.toLowerCase()):!0}function w(e,r,s){return!!(!s.trim()||e.some(a=>A(a,s))||r!=null&&r.description&&A(r.description,s))}function P({node:e,path:r,label:s,targetScope:a,onOverride:m,filter:f,depth:x,schema:u,showGhosts:o,defaultOpen:g}){const[d,h]=b.useState(g??x<2),y=(u==null?void 0:u.at(r))??null,t=new Set(Object.keys(e.children)),i=o?(y==null?void 0:y.knownChildren)??[]:[],c=Array.from(new Set([...t,...i])).sort(),l=b.useMemo(()=>f.trim()?c.filter(p=>{const v=(u==null?void 0:u.at([...r,p]))??null;return w([...r,p],v,f)?!0:t.has(p)?L(e.children[p],[...r,p],f,u):!1}):c,[c.join(","),f,e,r.join("."),u]);if(f.trim()&&l.length===0&&!w(r,y,f))return null;if(r.length===0)return n.jsx("div",{children:l.map(p=>j(p))});return n.jsxs("div",{children:[n.jsxs("div",{className:"flex items-center gap-1 py-0.5 hover:bg-bg-elev/50 rounded cursor-pointer",style:{paddingLeft:x*14},onClick:()=>h(!d),children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0",children:d?"▾":"▸"}),n.jsx("span",{className:"text-fg",children:s}),n.jsxs("span",{className:"text-fg-faint",children:[" {",t.size,i.length>t.size?`/${new Set([...t,...i]).size}`:"","}"]}),(y==null?void 0:y.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:y.description,children:["— ",y.description]})]}),d&&l.map(p=>j(p))]});function j(p){if(t.has(p))return n.jsx(E,{node:e.children[p],path:[...r,p],label:p,targetScope:a,onOverride:m,filter:f,depth:x+1,schema:u,showGhosts:o},p);const v=(u==null?void 0:u.at([...r,p]))??null;return v?n.jsx(I,{path:[...r,p],label:p,info:v,targetScope:a,onSet:m,depth:x+1,filter:f},p):null}}function L(e,r,s,a){const m=(a==null?void 0:a.at(r))??null;if(w(r,m,s))return!0;if(e.kind!=="object")return!1;for(const f of Object.keys(e.children))if(L(e.children[f],[...r,f],s,a))return!0;return!1}function J({node:e,path:r,label:s,filter:a,depth:m,schema:f}){const[x,u]=b.useState(!0),o=(f==null?void 0:f.at(r))??null;return w(r,o,a)?n.jsxs("div",{children:[n.jsxs("div",{className:"flex items-center gap-1 py-0.5 hover:bg-bg-elev/50 rounded cursor-pointer",style:{paddingLeft:m*14},onClick:()=>u(!x),children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0",children:x?"▾":"▸"}),n.jsx("span",{className:"text-fg",children:s}),n.jsxs("span",{className:"text-fg-faint",children:[" [",e.items.length,"]"]}),n.jsx(S,{tone:"warn",children:"accumulated"}),(o==null?void 0:o.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:o.description,children:["— ",o.description]})]}),x&&e.items.map((g,d)=>n.jsxs("div",{className:"flex items-start gap-2 py-0.5",style:{paddingLeft:(m+1)*14},children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0"}),n.jsxs("span",{className:"text-fg-faint",children:[d,":"]}),n.jsx("span",{className:"text-fg-dim truncate max-w-xl",children:k(g.value)}),n.jsx(C,{scope:g.fromScope})]},d))]}):null}function R({node:e,path:r,label:s,targetScope:a,onOverride:m,filter:f,depth:x,schema:u}){const[o,g]=b.useState(!1),d=(u==null?void 0:u.at(r))??null;if(!w(r,d,f))return null;const h=e.winner!==a,y=e.shadowed.length>0;return n.jsxs("div",{children:[n.jsxs("div",{className:"group flex items-center gap-2 py-0.5 hover:bg-bg-elev/50 rounded",style:{paddingLeft:x*14},children:[n.jsx("span",{className:`w-3 shrink-0 ${y?"text-fg-faint cursor-pointer":""}`,onClick:()=>y&&g(!o),children:y?o?"▾":"▸":""}),n.jsxs("span",{className:"text-fg",children:[s,":"]}),n.jsx("span",{className:"text-fg-dim truncate max-w-md",children:k(e.value)}),n.jsx(C,{scope:e.winner}),(d==null?void 0:d.deprecated)&&n.jsx(S,{tone:"warn",children:"deprecated"}),(d==null?void 0:d.default)!==void 0&&_(e.value,d.default)&&n.jsx(S,{tone:"dim",children:"= default"}),e.typeMismatch&&n.jsx("span",{className:"text-yellow-500/80",title:"shadowed scopes have a different value type",children:"⚠"}),(d==null?void 0:d.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:d.description,children:["— ",d.description]}),n.jsx("div",{className:"flex-1"}),(d==null?void 0:d.enum)&&n.jsx(F,{values:d.enum}),n.jsx("button",{disabled:!h,onClick:()=>m(r,e.value),className:`opacity-0 group-hover:opacity-100 px-1.5 py-0 text-[10px] border rounded transition-opacity ${h?"border-accent/50 text-accent hover:bg-accent hover:text-bg":"border-line text-fg-faint cursor-not-allowed"}`,title:h?`copy into ${N[a].label} draft`:`already winning in ${N[a].label}`,children:"override"})]}),o&&e.shadowed.map((t,i)=>n.jsxs("div",{className:"flex items-center gap-2 py-0.5 text-fg-faint line-through",style:{paddingLeft:(x+1)*14+12},children:[n.jsx("span",{children:"shadowed:"}),n.jsx("span",{className:"truncate max-w-xl",children:k(t.value)}),n.jsx(C,{scope:t.scope,dim:!0})]},i))]})}function I({path:e,label:r,info:s,targetScope:a,onSet:m,depth:f,filter:x}){if(!w(e,s,x))return null;const u=s.default!==void 0?s.default:M(s.type),o=Array.isArray(s.type)?s.type.join("|"):s.type??"?";return n.jsxs("div",{className:"group flex items-center gap-2 py-0.5 hover:bg-bg-elev/50 rounded opacity-60",style:{paddingLeft:f*14},children:[n.jsx("span",{className:"w-3 shrink-0"}),n.jsx("span",{className:"text-fg-dim",children:r}),n.jsx(S,{tone:"dim",children:o}),s.required&&n.jsx(S,{tone:"warn",children:"required"}),s.default!==void 0&&n.jsxs("span",{className:"text-fg-faint",children:["default: ",k(s.default)]}),s.description&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:s.description,children:["— ",s.description]}),n.jsx("div",{className:"flex-1"}),s.enum&&n.jsx(F,{values:s.enum}),n.jsx("button",{onClick:()=>m(e,u),className:"opacity-0 group-hover:opacity-100 px-1.5 py-0 text-[10px] border border-accent/50 rounded text-accent hover:bg-accent hover:text-bg transition-opacity",title:`write to ${N[a].label} draft`,children:"set"})]})}function _(e,r){return e===r?!0:typeof e!=typeof r?!1:e&&r&&typeof e=="object"?JSON.stringify(e)===JSON.stringify(r):!1}function M(e){switch(Array.isArray(e)?e[0]:e){case"boolean":return!1;case"integer":case"number":return 0;case"string":return"";case"array":return[];case"object":return{};case"null":return null;default:return null}}function k(e){if(typeof e=="string")return JSON.stringify(e);if(e===null)return"null";if(typeof e=="object"){const r=JSON.stringify(e);return r.length>100?r.slice(0,100)+"…":r}return String(e)}function C({scope:e,dim:r}){const s=e==="user"?"bg-bg-hi text-fg-dim border-line":e==="project"?"bg-blue-950/30 text-blue-400/80 border-blue-900/40":"bg-purple-950/30 text-purple-400/80 border-purple-900/40";return n.jsx("span",{className:`text-[10px] px-1 py-0 rounded border uppercase tracking-wide shrink-0 ${s} ${r?"opacity-60":""}`,children:N[e].label})}function S({children:e,tone:r="default"}){const s={default:"bg-bg-hi text-fg-dim border-line",warn:"bg-yellow-950/30 text-yellow-500/80 border-yellow-900/40",dim:"bg-bg-elev text-fg-faint border-line"}[r];return n.jsx("span",{className:`text-[10px] px-1 py-0 rounded border uppercase tracking-wide shrink-0 ${s}`,children:e})}function F({values:e}){const r=e.map(s=>String(s)).join(" | ");return n.jsxs("span",{className:"text-[10px] text-fg-faint truncate max-w-xs",title:`allowed: ${r}`,children:["∈ ",r.length>30?r.slice(0,30)+"…":r]})}function V(e,r,s){const a=O(o=>o.files),m=O(o=>o.loadJson),f=O(o=>o.watchFile),x=O(o=>o.unwatchFile),u=b.useMemo(()=>{const o={};for(const g of e.scopes){const d=e.resolve(g,r??"",s);d&&(o[g]=d)}return o},[e,r,s]);return b.useEffect(()=>{const o=Object.values(u).filter(Boolean);return o.forEach(g=>{a[g]||m(g),f(g)}),()=>{o.forEach(g=>x(g))}},[JSON.stringify(u)]),u}function B(e){const r=e??{},s={...r.$defs??{},...r.definitions??{}},a=t=>{if(!t)return{};if(!t.$ref)return x(t);const i=/^#\/(\$defs|definitions)\/(.+)$/.exec(t.$ref);if(!i)return x(t);const c=s[i[2]];return c?a({...c,...u(t)}):x(t)},m=new Set(["__proto__","constructor","prototype"]),f=t=>{if(!t)return{};const i={};for(const[c,l]of Object.entries(t))m.has(c)||(i[c]=l);return i},x=t=>{if(!t.allOf||t.allOf.length===0)return t;const i={...t};delete i.allOf;for(const c of t.allOf){const l=a(c);Object.assign(i,{properties:{...f(i.properties),...f(l.properties)},required:q([...i.required??[],...l.required??[]])}),!i.description&&l.description&&(i.description=l.description),i.default===void 0&&l.default!==void 0&&(i.default=l.default),!i.type&&l.type&&(i.type=l.type)}return i},u=t=>{const{$ref:i,...c}=t;return c},o=(t,i)=>{let c=a(t);for(const l of i){if(!c)return null;if(/^\d+$/.test(l)){const v=Array.isArray(c.items)?c.items[Number(l)]:c.items;c=v?a(v):null;continue}const j=c.properties;if(j&&!m.has(l)&&l in j){c=a(j[l]);continue}const p=c.additionalProperties;if(p&&typeof p=="object"){c=a(p);continue}return null}return c},g=(t,i,c)=>{if(!t)return null;const l={};if(t.description&&(l.description=t.description),t.type&&(l.type=t.type),t.default!==void 0&&(l.default=t.default),t.enum&&(l.enum=t.enum),t.const!==void 0&&(l.const=t.const),t.deprecated&&(l.deprecated=!0),t.examples&&(l.examples=t.examples),t.properties&&(l.knownChildren=Object.keys(t.properties)),c&&(i!=null&&i.includes(c))&&(l.required=!0),t.items&&!Array.isArray(t.items)){const j=a(t.items),p=g(j,void 0,null);p&&(l.itemsInfo=p)}return(t.additionalProperties===!0||typeof t.additionalProperties=="object")&&(l.open=!0),l},d=t=>i=>{if(i.length===0)return g(a(t),void 0,null);const c=o(t,i.slice(0,-1));if(!c)return null;const l=i[i.length-1],j=o(c,[l]);return g(j,c.required,l)},h=t=>{const i=a(t);return Object.keys(i.properties??{})},y=t=>({at:d(t),rootKeys:()=>h(t),rootedAt:i=>{const c=o(t,i);return y(c??{})}});return y(r)}function q(e){return Array.from(new Set(e))}let $=null;function D(){return $||($=B(K)),$}export{G as E,D as s,V as u};
|
|
1
|
+
import{aw as b,ah as n,S as N,aN as O}from"./index-v_3LvhMx.js";import{r as K}from"./claude-settings-schema-COigsajx.js";function G({node:e,targetScope:r,onOverride:s,schema:a,rootLabel:m}){const[f,x]=b.useState(""),[u,o]=b.useState(!0),g=[],d=e.kind==="object"&&Object.keys(e.children).length===0,y=((a==null?void 0:a.rootKeys())??[]).length>0;return n.jsxs("div",{className:"h-full flex flex-col",children:[n.jsxs("div",{className:"shrink-0 border-b border-line bg-bg-elev px-3 py-1.5 flex items-center gap-2 text-xs",children:[n.jsxs("span",{className:"text-fg-faint",children:["merged · overrides → ",n.jsx("span",{className:"text-fg-dim",children:N[r].label})]}),y&&n.jsxs("label",{className:"flex items-center gap-1 text-fg-faint cursor-pointer",children:[n.jsx("input",{type:"checkbox",checked:u,onChange:t=>o(t.target.checked),className:"accent-accent"}),"show unset keys"]}),n.jsx("div",{className:"flex-1"}),n.jsx("input",{value:f,onChange:t=>x(t.target.value),placeholder:"filter keys or descriptions…",className:"bg-bg border border-line rounded px-2 py-0.5 text-xs text-fg w-56"})]}),n.jsx("div",{className:"flex-1 overflow-auto px-3 py-2 font-mono text-xs",children:d&&!u?n.jsx("div",{className:"text-fg-faint italic px-2 py-4",children:"no settings resolved — all scope files empty or missing"}):n.jsx(E,{node:e,path:g,label:m??"(root)",targetScope:r,onOverride:s,filter:f,depth:0,schema:a,showGhosts:u,defaultOpen:!0})})]})}function E(e){return e.node.kind==="object"?n.jsx(P,{...e,node:e.node}):e.node.kind==="array"?n.jsx(J,{...e,node:e.node}):n.jsx(R,{...e,node:e.node})}function A(e,r){return r.trim()?e.toLowerCase().includes(r.toLowerCase()):!0}function w(e,r,s){return!!(!s.trim()||e.some(a=>A(a,s))||r!=null&&r.description&&A(r.description,s))}function P({node:e,path:r,label:s,targetScope:a,onOverride:m,filter:f,depth:x,schema:u,showGhosts:o,defaultOpen:g}){const[d,h]=b.useState(g??x<2),y=(u==null?void 0:u.at(r))??null,t=new Set(Object.keys(e.children)),i=o?(y==null?void 0:y.knownChildren)??[]:[],c=Array.from(new Set([...t,...i])).sort(),l=b.useMemo(()=>f.trim()?c.filter(p=>{const v=(u==null?void 0:u.at([...r,p]))??null;return w([...r,p],v,f)?!0:t.has(p)?L(e.children[p],[...r,p],f,u):!1}):c,[c.join(","),f,e,r.join("."),u]);if(f.trim()&&l.length===0&&!w(r,y,f))return null;if(r.length===0)return n.jsx("div",{children:l.map(p=>j(p))});return n.jsxs("div",{children:[n.jsxs("div",{className:"flex items-center gap-1 py-0.5 hover:bg-bg-elev/50 rounded cursor-pointer",style:{paddingLeft:x*14},onClick:()=>h(!d),children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0",children:d?"▾":"▸"}),n.jsx("span",{className:"text-fg",children:s}),n.jsxs("span",{className:"text-fg-faint",children:[" {",t.size,i.length>t.size?`/${new Set([...t,...i]).size}`:"","}"]}),(y==null?void 0:y.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:y.description,children:["— ",y.description]})]}),d&&l.map(p=>j(p))]});function j(p){if(t.has(p))return n.jsx(E,{node:e.children[p],path:[...r,p],label:p,targetScope:a,onOverride:m,filter:f,depth:x+1,schema:u,showGhosts:o},p);const v=(u==null?void 0:u.at([...r,p]))??null;return v?n.jsx(I,{path:[...r,p],label:p,info:v,targetScope:a,onSet:m,depth:x+1,filter:f},p):null}}function L(e,r,s,a){const m=(a==null?void 0:a.at(r))??null;if(w(r,m,s))return!0;if(e.kind!=="object")return!1;for(const f of Object.keys(e.children))if(L(e.children[f],[...r,f],s,a))return!0;return!1}function J({node:e,path:r,label:s,filter:a,depth:m,schema:f}){const[x,u]=b.useState(!0),o=(f==null?void 0:f.at(r))??null;return w(r,o,a)?n.jsxs("div",{children:[n.jsxs("div",{className:"flex items-center gap-1 py-0.5 hover:bg-bg-elev/50 rounded cursor-pointer",style:{paddingLeft:m*14},onClick:()=>u(!x),children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0",children:x?"▾":"▸"}),n.jsx("span",{className:"text-fg",children:s}),n.jsxs("span",{className:"text-fg-faint",children:[" [",e.items.length,"]"]}),n.jsx(S,{tone:"warn",children:"accumulated"}),(o==null?void 0:o.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:o.description,children:["— ",o.description]})]}),x&&e.items.map((g,d)=>n.jsxs("div",{className:"flex items-start gap-2 py-0.5",style:{paddingLeft:(m+1)*14},children:[n.jsx("span",{className:"text-fg-faint w-3 shrink-0"}),n.jsxs("span",{className:"text-fg-faint",children:[d,":"]}),n.jsx("span",{className:"text-fg-dim truncate max-w-xl",children:k(g.value)}),n.jsx(C,{scope:g.fromScope})]},d))]}):null}function R({node:e,path:r,label:s,targetScope:a,onOverride:m,filter:f,depth:x,schema:u}){const[o,g]=b.useState(!1),d=(u==null?void 0:u.at(r))??null;if(!w(r,d,f))return null;const h=e.winner!==a,y=e.shadowed.length>0;return n.jsxs("div",{children:[n.jsxs("div",{className:"group flex items-center gap-2 py-0.5 hover:bg-bg-elev/50 rounded",style:{paddingLeft:x*14},children:[n.jsx("span",{className:`w-3 shrink-0 ${y?"text-fg-faint cursor-pointer":""}`,onClick:()=>y&&g(!o),children:y?o?"▾":"▸":""}),n.jsxs("span",{className:"text-fg",children:[s,":"]}),n.jsx("span",{className:"text-fg-dim truncate max-w-md",children:k(e.value)}),n.jsx(C,{scope:e.winner}),(d==null?void 0:d.deprecated)&&n.jsx(S,{tone:"warn",children:"deprecated"}),(d==null?void 0:d.default)!==void 0&&_(e.value,d.default)&&n.jsx(S,{tone:"dim",children:"= default"}),e.typeMismatch&&n.jsx("span",{className:"text-yellow-500/80",title:"shadowed scopes have a different value type",children:"⚠"}),(d==null?void 0:d.description)&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:d.description,children:["— ",d.description]}),n.jsx("div",{className:"flex-1"}),(d==null?void 0:d.enum)&&n.jsx(F,{values:d.enum}),n.jsx("button",{disabled:!h,onClick:()=>m(r,e.value),className:`opacity-0 group-hover:opacity-100 px-1.5 py-0 text-[10px] border rounded transition-opacity ${h?"border-accent/50 text-accent hover:bg-accent hover:text-bg":"border-line text-fg-faint cursor-not-allowed"}`,title:h?`copy into ${N[a].label} draft`:`already winning in ${N[a].label}`,children:"override"})]}),o&&e.shadowed.map((t,i)=>n.jsxs("div",{className:"flex items-center gap-2 py-0.5 text-fg-faint line-through",style:{paddingLeft:(x+1)*14+12},children:[n.jsx("span",{children:"shadowed:"}),n.jsx("span",{className:"truncate max-w-xl",children:k(t.value)}),n.jsx(C,{scope:t.scope,dim:!0})]},i))]})}function I({path:e,label:r,info:s,targetScope:a,onSet:m,depth:f,filter:x}){if(!w(e,s,x))return null;const u=s.default!==void 0?s.default:M(s.type),o=Array.isArray(s.type)?s.type.join("|"):s.type??"?";return n.jsxs("div",{className:"group flex items-center gap-2 py-0.5 hover:bg-bg-elev/50 rounded opacity-60",style:{paddingLeft:f*14},children:[n.jsx("span",{className:"w-3 shrink-0"}),n.jsx("span",{className:"text-fg-dim",children:r}),n.jsx(S,{tone:"dim",children:o}),s.required&&n.jsx(S,{tone:"warn",children:"required"}),s.default!==void 0&&n.jsxs("span",{className:"text-fg-faint",children:["default: ",k(s.default)]}),s.description&&n.jsxs("span",{className:"text-fg-faint truncate max-w-md",title:s.description,children:["— ",s.description]}),n.jsx("div",{className:"flex-1"}),s.enum&&n.jsx(F,{values:s.enum}),n.jsx("button",{onClick:()=>m(e,u),className:"opacity-0 group-hover:opacity-100 px-1.5 py-0 text-[10px] border border-accent/50 rounded text-accent hover:bg-accent hover:text-bg transition-opacity",title:`write to ${N[a].label} draft`,children:"set"})]})}function _(e,r){return e===r?!0:typeof e!=typeof r?!1:e&&r&&typeof e=="object"?JSON.stringify(e)===JSON.stringify(r):!1}function M(e){switch(Array.isArray(e)?e[0]:e){case"boolean":return!1;case"integer":case"number":return 0;case"string":return"";case"array":return[];case"object":return{};case"null":return null;default:return null}}function k(e){if(typeof e=="string")return JSON.stringify(e);if(e===null)return"null";if(typeof e=="object"){const r=JSON.stringify(e);return r.length>100?r.slice(0,100)+"…":r}return String(e)}function C({scope:e,dim:r}){const s=e==="user"?"bg-bg-hi text-fg-dim border-line":e==="project"?"bg-blue-950/30 text-blue-400/80 border-blue-900/40":"bg-purple-950/30 text-purple-400/80 border-purple-900/40";return n.jsx("span",{className:`text-[10px] px-1 py-0 rounded border uppercase tracking-wide shrink-0 ${s} ${r?"opacity-60":""}`,children:N[e].label})}function S({children:e,tone:r="default"}){const s={default:"bg-bg-hi text-fg-dim border-line",warn:"bg-yellow-950/30 text-yellow-500/80 border-yellow-900/40",dim:"bg-bg-elev text-fg-faint border-line"}[r];return n.jsx("span",{className:`text-[10px] px-1 py-0 rounded border uppercase tracking-wide shrink-0 ${s}`,children:e})}function F({values:e}){const r=e.map(s=>String(s)).join(" | ");return n.jsxs("span",{className:"text-[10px] text-fg-faint truncate max-w-xs",title:`allowed: ${r}`,children:["∈ ",r.length>30?r.slice(0,30)+"…":r]})}function V(e,r,s){const a=O(o=>o.files),m=O(o=>o.loadJson),f=O(o=>o.watchFile),x=O(o=>o.unwatchFile),u=b.useMemo(()=>{const o={};for(const g of e.scopes){const d=e.resolve(g,r??"",s);d&&(o[g]=d)}return o},[e,r,s]);return b.useEffect(()=>{const o=Object.values(u).filter(Boolean);return o.forEach(g=>{a[g]||m(g),f(g)}),()=>{o.forEach(g=>x(g))}},[JSON.stringify(u)]),u}function B(e){const r=e??{},s={...r.$defs??{},...r.definitions??{}},a=t=>{if(!t)return{};if(!t.$ref)return x(t);const i=/^#\/(\$defs|definitions)\/(.+)$/.exec(t.$ref);if(!i)return x(t);const c=s[i[2]];return c?a({...c,...u(t)}):x(t)},m=new Set(["__proto__","constructor","prototype"]),f=t=>{if(!t)return{};const i={};for(const[c,l]of Object.entries(t))m.has(c)||(i[c]=l);return i},x=t=>{if(!t.allOf||t.allOf.length===0)return t;const i={...t};delete i.allOf;for(const c of t.allOf){const l=a(c);Object.assign(i,{properties:{...f(i.properties),...f(l.properties)},required:q([...i.required??[],...l.required??[]])}),!i.description&&l.description&&(i.description=l.description),i.default===void 0&&l.default!==void 0&&(i.default=l.default),!i.type&&l.type&&(i.type=l.type)}return i},u=t=>{const{$ref:i,...c}=t;return c},o=(t,i)=>{let c=a(t);for(const l of i){if(!c)return null;if(/^\d+$/.test(l)){const v=Array.isArray(c.items)?c.items[Number(l)]:c.items;c=v?a(v):null;continue}const j=c.properties;if(j&&!m.has(l)&&l in j){c=a(j[l]);continue}const p=c.additionalProperties;if(p&&typeof p=="object"){c=a(p);continue}return null}return c},g=(t,i,c)=>{if(!t)return null;const l={};if(t.description&&(l.description=t.description),t.type&&(l.type=t.type),t.default!==void 0&&(l.default=t.default),t.enum&&(l.enum=t.enum),t.const!==void 0&&(l.const=t.const),t.deprecated&&(l.deprecated=!0),t.examples&&(l.examples=t.examples),t.properties&&(l.knownChildren=Object.keys(t.properties)),c&&(i!=null&&i.includes(c))&&(l.required=!0),t.items&&!Array.isArray(t.items)){const j=a(t.items),p=g(j,void 0,null);p&&(l.itemsInfo=p)}return(t.additionalProperties===!0||typeof t.additionalProperties=="object")&&(l.open=!0),l},d=t=>i=>{if(i.length===0)return g(a(t),void 0,null);const c=o(t,i.slice(0,-1));if(!c)return null;const l=i[i.length-1],j=o(c,[l]);return g(j,c.required,l)},h=t=>{const i=a(t);return Object.keys(i.properties??{})},y=t=>({at:d(t),rootKeys:()=>h(t),rootedAt:i=>{const c=o(t,i);return y(c??{})}});return y(r)}function q(e){return Array.from(new Set(e))}let $=null;function D(){return $||($=B(K)),$}export{G as E,D as s,V as u};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{aS as u}from"./index-v_3LvhMx.js";import{u as a}from"./useConfigDirWatch-CjcswsWR.js";function r(e,s){const o=u();a([o?`${o}/.claude/agents`:null,e?`${e}/.claude/agents`:null],s)}export{r as u};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{aw as c}from"./index-
|
|
1
|
+
import{aw as c}from"./index-v_3LvhMx.js";function s(t){const n=t.replace(/\/{2,}/g,"/");return n.length>1?n.replace(/\/+$/,""):n}function u(t,n){const r=c.useRef(n);r.current=n;const e=Array.from(new Set(t.filter(o=>!!o).map(s))),i=e.slice().sort().join("\0");c.useEffect(()=>{if(e.length===0)return;window.api.config.watch(e);const o=window.api.config.onChanged(a=>{e.includes(a.path)&&r.current()});return()=>{o(),window.api.config.unwatch(e)}},[i])}export{u};
|
package/dist/index.html
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
8
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
9
|
<link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;0,6..72,700;1,6..72,400&family=Geist:wght@300;400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
10
|
-
<script type="module" crossorigin src="./assets/index-
|
|
10
|
+
<script type="module" crossorigin src="./assets/index-v_3LvhMx.js"></script>
|
|
11
11
|
<link rel="modulepreload" crossorigin href="./assets/monaco-editor-BsIlgG7c.js">
|
|
12
12
|
<link rel="stylesheet" crossorigin href="./assets/monaco-editor-Cu5D__1H.css">
|
|
13
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
13
|
+
<link rel="stylesheet" crossorigin href="./assets/index-tr1w3hHB.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body class="bg-bg text-fg font-sans antialiased">
|
|
16
16
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-session-manager",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.107.0",
|
|
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",
|
|
@@ -132,6 +132,7 @@
|
|
|
132
132
|
"chokidar": "^4.0.1",
|
|
133
133
|
"electron": "42.11.10",
|
|
134
134
|
"node-pty": "^1.2.0-beta.12",
|
|
135
|
+
"ws": "^8.18.0",
|
|
135
136
|
"zod": "^3.23.8"
|
|
136
137
|
},
|
|
137
138
|
"devDependencies": {
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
*
|
|
17
17
|
* macro_list({ cwd? }) -> GET /admin/macros?cwd=<encoded>
|
|
18
18
|
* macro_save({ cwd?, id?, label, agentName, tag, prompt }) -> POST /admin/macros/save
|
|
19
|
+
* customer_feedback_list({ pull?, includeHidden? }) -> GET /admin/customer-feedback/inbox
|
|
20
|
+
* customer_feedback_set_status({ id, status, note? }) -> POST /admin/customer-feedback/status
|
|
19
21
|
*
|
|
20
22
|
* This is a separate process from the Electron app — it only ever reaches
|
|
21
23
|
* it over the token-authed loopback HTTP API in admin-api.json, never by
|
|
@@ -141,7 +143,12 @@ const TOOLS = [
|
|
|
141
143
|
{
|
|
142
144
|
name: 'scheduler_pause',
|
|
143
145
|
description: descriptionFor('scheduler_pause'),
|
|
144
|
-
inputSchema: {
|
|
146
|
+
inputSchema: {
|
|
147
|
+
type: 'object',
|
|
148
|
+
properties: {
|
|
149
|
+
reason: { type: 'string', description: 'Why you are pausing — shown to the human.' },
|
|
150
|
+
},
|
|
151
|
+
},
|
|
145
152
|
},
|
|
146
153
|
{
|
|
147
154
|
name: 'scheduler_resume',
|
|
@@ -419,6 +426,30 @@ const TOOLS = [
|
|
|
419
426
|
required: ['label', 'agentName', 'tag', 'prompt'],
|
|
420
427
|
},
|
|
421
428
|
},
|
|
429
|
+
{
|
|
430
|
+
name: 'customer_feedback_list',
|
|
431
|
+
description: descriptionFor('customer_feedback_list'),
|
|
432
|
+
inputSchema: {
|
|
433
|
+
type: 'object',
|
|
434
|
+
properties: {
|
|
435
|
+
pull: { type: 'boolean', description: 'Fetch new items from the feedback service before listing. Default true.' },
|
|
436
|
+
includeHidden: { type: 'boolean', description: 'Include moderated-away items. Default false.' },
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
name: 'customer_feedback_set_status',
|
|
442
|
+
description: descriptionFor('customer_feedback_set_status'),
|
|
443
|
+
inputSchema: {
|
|
444
|
+
type: 'object',
|
|
445
|
+
properties: {
|
|
446
|
+
id: { type: 'string', description: 'Feedback item id (from customer_feedback_list).' },
|
|
447
|
+
status: { type: 'string', enum: ['open', 'in_progress', 'resolved', 'wontfix'], description: 'New status.' },
|
|
448
|
+
note: { type: 'string', description: 'Optional short note shown to the customer.' },
|
|
449
|
+
},
|
|
450
|
+
required: ['id', 'status'],
|
|
451
|
+
},
|
|
452
|
+
},
|
|
422
453
|
{
|
|
423
454
|
name: 'session_manager_help',
|
|
424
455
|
description: descriptionFor('session_manager_help'),
|
|
@@ -450,7 +481,14 @@ async function handleCallTool(request) {
|
|
|
450
481
|
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
451
482
|
}
|
|
452
483
|
if (name === 'scheduler_pause' || name === 'scheduler_resume') {
|
|
453
|
-
const
|
|
484
|
+
const body = name === 'scheduler_pause'
|
|
485
|
+
? {
|
|
486
|
+
originClaudeSessionId: process.env.SM_CHAT_SESSION_ID ?? null,
|
|
487
|
+
cwd: process.cwd(),
|
|
488
|
+
reason: args && typeof args.reason === 'string' ? args.reason : undefined,
|
|
489
|
+
}
|
|
490
|
+
: {};
|
|
491
|
+
const result = await adminRequest('POST', `/admin/scheduler/${name === 'scheduler_pause' ? 'pause' : 'resume'}`, body);
|
|
454
492
|
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
455
493
|
}
|
|
456
494
|
if (name === 'scheduler_list_jobs') {
|
|
@@ -662,6 +700,24 @@ async function handleCallTool(request) {
|
|
|
662
700
|
const result = await adminRequest('POST', '/admin/macros/save', payload);
|
|
663
701
|
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
664
702
|
}
|
|
703
|
+
if (name === 'customer_feedback_list') {
|
|
704
|
+
const qs = new URLSearchParams();
|
|
705
|
+
if (!args || args.pull !== false) qs.set('pull', '1');
|
|
706
|
+
if (args && args.includeHidden === true) qs.set('includeHidden', '1');
|
|
707
|
+
const result = await adminRequest('GET', `/admin/customer-feedback/inbox?${qs.toString()}`);
|
|
708
|
+
if (result?.ok === false) return errorResult(result.error ?? 'customer_feedback_list failed');
|
|
709
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
710
|
+
}
|
|
711
|
+
if (name === 'customer_feedback_set_status') {
|
|
712
|
+
if (!args || typeof args.id !== 'string' || !args.id || typeof args.status !== 'string') {
|
|
713
|
+
return errorResult('missing or invalid required argument(s): id, status');
|
|
714
|
+
}
|
|
715
|
+
const payload = { id: args.id, status: args.status };
|
|
716
|
+
if (typeof args.note === 'string' && args.note) payload.note = args.note;
|
|
717
|
+
const result = await adminRequest('POST', '/admin/customer-feedback/status', payload);
|
|
718
|
+
if (result?.ok === false) return errorResult(result.error ?? 'customer_feedback_set_status failed');
|
|
719
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
720
|
+
}
|
|
665
721
|
if (name === 'session_manager_help') {
|
|
666
722
|
const toolName = args && typeof args.tool === 'string' ? args.tool : null;
|
|
667
723
|
const topic = args && typeof args.topic === 'string' ? args.topic : null;
|
package/src/main/build-info.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
3
|
-
"gitSha": "
|
|
4
|
-
"gitShortSha": "
|
|
2
|
+
"version": "0.107.0",
|
|
3
|
+
"gitSha": "a18e184a574f54d301d80aeeb88e477d88741be9",
|
|
4
|
+
"gitShortSha": "a18e184a",
|
|
5
5
|
"gitBranch": "HEAD",
|
|
6
|
-
"builtAt": "2026-10-
|
|
6
|
+
"builtAt": "2026-10-09T18:00:32.692Z",
|
|
7
7
|
"dirty": false
|
|
8
8
|
}
|
package/src/main/chatRunner.cjs
CHANGED
|
@@ -690,9 +690,11 @@ function executeRun({ tabId, sessionId, prompt, cwd, resume, silent, onSilentRes
|
|
|
690
690
|
// real-time NDJSON streaming; stderr piped for error-message capture.
|
|
691
691
|
let thisChild;
|
|
692
692
|
try {
|
|
693
|
-
const
|
|
694
|
-
|
|
693
|
+
const chatArgs = buildArgs(resumeFlag);
|
|
694
|
+
const target = claudeSpawnTarget('chat', sessionId, claudeBin, chatArgs);
|
|
695
|
+
thisChild = spawn(target.command, target.args || chatArgs, {
|
|
695
696
|
...(target.argv0 ? { argv0: target.argv0 } : {}),
|
|
697
|
+
...(target.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}),
|
|
696
698
|
cwd: execCwd,
|
|
697
699
|
env: childEnv,
|
|
698
700
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -56,15 +56,51 @@ function sentinelFile() {
|
|
|
56
56
|
// crashes (NOT OOM — a SIGKILL leaves no dump). Local-only, never uploaded.
|
|
57
57
|
// Must run before the app is ready, so call this at module require time from
|
|
58
58
|
// index.cjs's top level.
|
|
59
|
-
|
|
59
|
+
//
|
|
60
|
+
// On Windows Electron relaunches this exe as the crashpad handler with
|
|
61
|
+
// --database=<crashDumps path>; an empty path makes the handler exit with
|
|
62
|
+
// "--database is required" and the browser process self-terminates before any
|
|
63
|
+
// window opens. So we resolve a non-empty dir first, and skip start() entirely
|
|
64
|
+
// if we cannot — crash reporting must never be able to stop boot.
|
|
65
|
+
// `deps` is injectable for tests; the runtime call signature is unchanged.
|
|
66
|
+
function startCrashReporter(deps = {}) {
|
|
67
|
+
const a = deps.app || app;
|
|
68
|
+
const reporter = deps.crashReporter || crashReporter;
|
|
69
|
+
const fsImpl = deps.fs || fs;
|
|
70
|
+
const log = deps.log || ((msg) => console.warn(msg));
|
|
60
71
|
try {
|
|
61
|
-
|
|
72
|
+
const reasons = [];
|
|
73
|
+
const getDir = (name) => {
|
|
74
|
+
try {
|
|
75
|
+
const d = a.getPath(name);
|
|
76
|
+
if (typeof d === 'string') return d.trim();
|
|
77
|
+
reasons.push(`${name}: non-string ${typeof d}`);
|
|
78
|
+
} catch (err) {
|
|
79
|
+
reasons.push(`${name}: ${err && err.message}`);
|
|
80
|
+
}
|
|
81
|
+
return '';
|
|
82
|
+
};
|
|
83
|
+
let dir = getDir('crashDumps');
|
|
84
|
+
let fellBack = false;
|
|
85
|
+
if (!dir) {
|
|
86
|
+
const userData = getDir('userData');
|
|
87
|
+
if (userData) { dir = path.join(userData, 'Crashpad'); fellBack = true; }
|
|
88
|
+
}
|
|
89
|
+
if (!dir) {
|
|
90
|
+
log('[crash-diag] no crashDumps/userData path; crash reporter skipped' + (reasons.length ? ` (${reasons.join('; ')})` : ''));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
fsImpl.mkdirSync(dir, { recursive: true });
|
|
94
|
+
if (fellBack) a.setPath('crashDumps', dir);
|
|
95
|
+
reporter.start({
|
|
62
96
|
productName: 'claude-code-session-manager',
|
|
63
97
|
companyName: 'session-manager',
|
|
64
98
|
uploadToServer: false,
|
|
65
99
|
compress: true,
|
|
66
100
|
});
|
|
67
|
-
} catch {
|
|
101
|
+
} catch (err) {
|
|
102
|
+
try { (deps.log || ((m) => console.warn(m)))(`[crash-diag] crash reporter skipped: ${err && err.message}`); } catch { /* ignore */ }
|
|
103
|
+
}
|
|
68
104
|
}
|
|
69
105
|
|
|
70
106
|
// Snapshot per-process memory. app.getAppMetrics() returns one entry per
|
package/src/main/index.cjs
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
// Boot diagnostic (SM_BOOT_DIAG=1, set by the packaged smoke): one stderr line that
|
|
2
|
+
// shows whether `electron` resolved to the built-in module or ran in node mode.
|
|
3
|
+
if (process.env.SM_BOOT_DIAG === '1') {
|
|
4
|
+
try {
|
|
5
|
+
const e = require('electron');
|
|
6
|
+
let userData;
|
|
7
|
+
try { userData = String(e.app.getPath('userData')); } catch (err) { userData = `error: ${err && err.message}`; }
|
|
8
|
+
process.stderr.write(`[boot-diag] platform=${process.platform} electron=${process.versions.electron} typeof electron=${typeof e} typeof electron.app=${typeof (e && e.app)} ELECTRON_RUN_AS_NODE=${process.env.ELECTRON_RUN_AS_NODE} execPath=${process.execPath} __dirname=${__dirname} userData=${userData}\n`);
|
|
9
|
+
} catch (err) {
|
|
10
|
+
process.stderr.write(`[boot-diag] failed: ${err && err.message}\n`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
1
13
|
const { PROC_NAMES, setProcessTitle, inhibitHolderShell } = require('./lib/smProcNames.cjs');
|
|
2
14
|
// Self-describing `comm` in System Monitor (pn-03). process.argv is a JS copy
|
|
3
15
|
// made at startup, so the argv-memory overwrite below never affects the
|
|
@@ -10,6 +22,7 @@ const fs = require('node:fs');
|
|
|
10
22
|
const fsp = require('node:fs/promises');
|
|
11
23
|
const os = require('node:os');
|
|
12
24
|
const { schemas, validated } = require('./ipcSchemas.cjs');
|
|
25
|
+
const { createProjectFolder } = require('./lib/projectFolder.cjs');
|
|
13
26
|
const { cleanChildEnv } = require('./lib/cleanEnv.cjs');
|
|
14
27
|
const { applyLoginShellPath } = require('./lib/loginShellPath.cjs');
|
|
15
28
|
const { terminateLosingInstance } = require('./lib/singleInstanceGuard.cjs');
|
|
@@ -26,6 +39,20 @@ const crashDiagnostics = require('./crashDiagnostics.cjs');
|
|
|
26
39
|
// Start the local minidump collector before app-ready (required by Electron).
|
|
27
40
|
// Catches renderer/GPU SIGSEGV/SIGABRT; OOM-kills leave no dump but are caught
|
|
28
41
|
// by the sentinel + render-process-gone hooks in crashDiagnostics.init().
|
|
42
|
+
// ELECTRON_RUN_AS_NODE audit: every src/main site (appRuntime, watchdogHelpers,
|
|
43
|
+
// seedSchedulerMcp, timeoutShim) sets it only in a child's env / launcher text,
|
|
44
|
+
// or deletes it from a copied env — none writes the main process.env, so it
|
|
45
|
+
// cannot leak into Electron's own children (crashpad handler included).
|
|
46
|
+
// Chromium can't resolve known folders under a stripped Windows profile; pin an explicit
|
|
47
|
+
// userData path first (never allowed to stop boot).
|
|
48
|
+
try {
|
|
49
|
+
require('./lib/winUserData.cjs').ensureWinUserDataPath({
|
|
50
|
+
app: require('electron').app, env: process.env, platform: process.platform, fs: require('node:fs'), os: require('node:os'),
|
|
51
|
+
log: (m) => process.stderr.write(`${m}\n`),
|
|
52
|
+
});
|
|
53
|
+
} catch (err) {
|
|
54
|
+
process.stderr.write(`[win-userdata] fallback failed: ${err && err.message}\n`);
|
|
55
|
+
}
|
|
29
56
|
crashDiagnostics.startCrashReporter();
|
|
30
57
|
const voiceHotkey = require('./voiceHotkey.cjs');
|
|
31
58
|
const voiceWizard = require('./voiceWizard.cjs');
|
|
@@ -41,6 +68,7 @@ const chatRunner = require('./chatRunner.cjs');
|
|
|
41
68
|
const promptSessionEvents = require('./promptSessionEvents.cjs');
|
|
42
69
|
const activeIndexMerge = require('./lib/activeIndexMerge.cjs');
|
|
43
70
|
const promptSessionsCreateEpic = require('./lib/promptSessionsCreateEpic.cjs');
|
|
71
|
+
const customerFeedbackIpc = require('./lib/customerFeedbackIpc.cjs');
|
|
44
72
|
const epicWorktreeMint = require('./lib/epicWorktreeMint.cjs');
|
|
45
73
|
const epicWorktreeMerge = require('./lib/epicWorktreeMerge.cjs');
|
|
46
74
|
const epicWorktreeProjectConfig = require('./lib/epicWorktreeProjectConfig.cjs');
|
|
@@ -68,6 +96,7 @@ chatRunner.registerAdminRoute(adminHttp);
|
|
|
68
96
|
// several projects' operations roots open, so it performs the cross-folder
|
|
69
97
|
// write (lib/crossProjectFeedback.cjs).
|
|
70
98
|
crossProjectFeedback.registerAdminRoute(adminHttp);
|
|
99
|
+
require('./lib/customerFeedbackAdminRoutes.cjs').registerAdminRoute(adminHttp);
|
|
71
100
|
const supervisor = require('./supervisor.cjs');
|
|
72
101
|
const watchers = require('./watchers.cjs');
|
|
73
102
|
const queueOps = require('./queueOps.cjs');
|
|
@@ -99,6 +128,7 @@ const git = require('./git.cjs');
|
|
|
99
128
|
const heapSnapshot = require('./heapSnapshot.cjs');
|
|
100
129
|
const filesIpc = require('./files.cjs');
|
|
101
130
|
const { registerDocEditHandlers, attachWindow: attachDocEditWindow } = require('./docEdit.cjs');
|
|
131
|
+
const webRemote = require('./webRemote.cjs');
|
|
102
132
|
const { listExchanges } = require('./exchanges.cjs');
|
|
103
133
|
const { resolveClaudeBin } = require('./lib/claudeBin.cjs');
|
|
104
134
|
const { checkInsideHome, assertInsideHome } = require('./lib/insideHome.cjs');
|
|
@@ -366,6 +396,7 @@ async function performReboot() {
|
|
|
366
396
|
scheduler.attachWindow(mainWindow);
|
|
367
397
|
watchers.attachWindow(mainWindow);
|
|
368
398
|
pluginInstall.attachWindow(mainWindow);
|
|
399
|
+
webRemote.attachWindow(mainWindow);
|
|
369
400
|
chatRunner.attachWindow(mainWindow);
|
|
370
401
|
promptSessionEvents.attachWindow(mainWindow);
|
|
371
402
|
attachDocEditWindow(mainWindow);
|
|
@@ -743,9 +774,9 @@ ipcMain.handle('app:seed-status', () => getSeedStatus());
|
|
|
743
774
|
ipcMain.handle('app:pick-directory', async () => {
|
|
744
775
|
console.log('[main] pick-directory invoked');
|
|
745
776
|
const result = await dialog.showOpenDialog(mainWindow, {
|
|
746
|
-
// createDirectory
|
|
747
|
-
//
|
|
748
|
-
//
|
|
777
|
+
// createDirectory is macOS-only (it adds the picker's "New Folder"
|
|
778
|
+
// button there). Linux/Windows users create folders via the in-app
|
|
779
|
+
// Start new project form (app:create-project-folder, below).
|
|
749
780
|
properties: ['openDirectory', 'createDirectory'],
|
|
750
781
|
defaultPath: os.homedir(),
|
|
751
782
|
title: 'Open a project folder — or create a new one to start a project',
|
|
@@ -755,6 +786,12 @@ ipcMain.handle('app:pick-directory', async () => {
|
|
|
755
786
|
return result.filePaths[0];
|
|
756
787
|
});
|
|
757
788
|
|
|
789
|
+
ipcMain.handle('app:create-project-folder', validated(schemas.appCreateProjectFolder, async (payload) => {
|
|
790
|
+
const result = await createProjectFolder(payload);
|
|
791
|
+
console.log('[main] create-project-folder', result.ok ? 'ok' : result.code, JSON.stringify({ parentDir: payload.parentDir, name: payload.name }));
|
|
792
|
+
return result;
|
|
793
|
+
}));
|
|
794
|
+
|
|
758
795
|
ipcMain.on('app:reboot-app', (_e, opts) => { rebootApp({ force: opts?.force === true }).catch((e) => logReboot(`reboot failed: ${e?.message}`)); });
|
|
759
796
|
|
|
760
797
|
// Image paste — Ctrl+V in the Terminal pane. Reads the OS clipboard via
|
|
@@ -1030,6 +1067,7 @@ pluginInstall.registerPluginInstallHandlers();
|
|
|
1030
1067
|
memoryTool.registerMemoryHandlers();
|
|
1031
1068
|
activeIndexMerge.registerActiveIndexMergeHandlers();
|
|
1032
1069
|
promptSessionsCreateEpic.registerPromptSessionsCreateEpicHandlers();
|
|
1070
|
+
customerFeedbackIpc.registerCustomerFeedbackHandlers();
|
|
1033
1071
|
epicWorktreeMint.registerEpicWorktreeMintHandlers();
|
|
1034
1072
|
epicWorktreeMerge.registerEpicWorktreeMergeHandlers();
|
|
1035
1073
|
epicWorktreeProjectConfig.registerEpicWorktreeProjectConfigHandlers();
|
|
@@ -1059,6 +1097,7 @@ agentMemory.registerAgentMemoryHandlers();
|
|
|
1059
1097
|
git.register(ipcMain);
|
|
1060
1098
|
filesIpc.registerFilesHandlers();
|
|
1061
1099
|
registerDocEditHandlers();
|
|
1100
|
+
webRemote.registerRemoteHandlers();
|
|
1062
1101
|
chatRunner.registerChatHandlers();
|
|
1063
1102
|
|
|
1064
1103
|
// Direct in-process PRD authoring for a queued PromptTicket classified
|
|
@@ -1418,6 +1457,7 @@ app.whenReady().then(async () => {
|
|
|
1418
1457
|
scheduler.attachWindow(mainWindow);
|
|
1419
1458
|
watchers.attachWindow(mainWindow);
|
|
1420
1459
|
pluginInstall.attachWindow(mainWindow);
|
|
1460
|
+
webRemote.attachWindow(mainWindow);
|
|
1421
1461
|
chatRunner.attachWindow(mainWindow);
|
|
1422
1462
|
promptSessionEvents.attachWindow(mainWindow);
|
|
1423
1463
|
attachDocEditWindow(mainWindow);
|
|
@@ -1448,6 +1488,9 @@ app.whenReady().then(async () => {
|
|
|
1448
1488
|
seedDevPlugin({ logger: console, writeLog: logs.writeLine }).catch((e) => {
|
|
1449
1489
|
logs.writeLine({ scope: 'seed-dev-plugin', level: 'error', message: 'seed failed', meta: { error: e?.message } });
|
|
1450
1490
|
});
|
|
1491
|
+
webRemote.init().catch((e) => {
|
|
1492
|
+
logs.writeLine({ scope: 'webRemote', level: 'error', message: 'init failed', meta: { error: e?.message } });
|
|
1493
|
+
});
|
|
1451
1494
|
// First-boot default: seed the bundled Architect + Dev Lead Agent personas
|
|
1452
1495
|
// into ~/.claude/agents/ so the Agent Library isn't empty on a fresh
|
|
1453
1496
|
// install. One-shot + idempotent; never overwrites an existing persona;
|
|
@@ -1558,6 +1601,7 @@ let teardownDone = false;
|
|
|
1558
1601
|
function runShutdownCleanup() {
|
|
1559
1602
|
if (teardownDone) return; // idempotent — will-quit may still fire after an app.exit path
|
|
1560
1603
|
teardownDone = true;
|
|
1604
|
+
webRemote.destroy();
|
|
1561
1605
|
// Mark a clean exit so the next boot can distinguish a graceful quit from an
|
|
1562
1606
|
// OOM-kill / native crash (which leaves the sentinel `open`).
|
|
1563
1607
|
crashDiagnostics.markCleanShutdown();
|
package/src/main/ipcSchemas.cjs
CHANGED
|
@@ -30,6 +30,17 @@ const ptyTabId = z.object({ tabId: z.string().min(1).max(128) });
|
|
|
30
30
|
// have a live PTY? Bounded so the renderer can't ask about unbounded lists.
|
|
31
31
|
const ptyAlive = z.object({ tabIds: z.array(z.string().min(1).max(128)).max(500) });
|
|
32
32
|
|
|
33
|
+
// v2 mobile: subscribe to a session's live state + summary. cwd is needed to
|
|
34
|
+
// locate the transcript JSONL (transcriptPath); validated against home-dir boundary
|
|
35
|
+
// in webRemote before any fs access.
|
|
36
|
+
const sessionSubscribe = z.object({
|
|
37
|
+
// tabId becomes a transcript FILENAME (`<tabId>.jsonl`) — restrict to a
|
|
38
|
+
// session-id charset (no '/', no '.', so it can't traverse out of the project
|
|
39
|
+
// transcript dir). sessionId is a UUID, which satisfies this.
|
|
40
|
+
tabId: z.string().min(1).max(128).regex(/^[A-Za-z0-9][A-Za-z0-9_-]*$/),
|
|
41
|
+
cwd: z.string().min(1).max(4096),
|
|
42
|
+
});
|
|
43
|
+
|
|
33
44
|
// 64 KiB cap per pty:write — typewriter input is bounded; a renderer firing
|
|
34
45
|
// megabytes per call is either a bug or an attack. Block it at the boundary.
|
|
35
46
|
const PTY_WRITE_MAX_BYTES = 64 * 1024;
|
|
@@ -687,6 +698,13 @@ const delegationReadinessCwd = z.object({
|
|
|
687
698
|
cwd: z.string().min(1).max(4096),
|
|
688
699
|
}).strict();
|
|
689
700
|
|
|
701
|
+
// app:create-project-folder — name semantics (separators, '..') are checked in
|
|
702
|
+
// lib/projectFolder.cjs, not here.
|
|
703
|
+
const appCreateProjectFolder = z.object({
|
|
704
|
+
parentDir: z.string().min(1).max(4096),
|
|
705
|
+
name: z.string().min(1).max(255),
|
|
706
|
+
}).strict();
|
|
707
|
+
|
|
690
708
|
// ──────────────────────────────────────────── Agent persona body (New Epic AIM)
|
|
691
709
|
const agentsGetPersonaBody = z.object({
|
|
692
710
|
cwd: z.string().min(1).max(4096),
|
|
@@ -925,6 +943,23 @@ const chatExternalSend = z.object({
|
|
|
925
943
|
),
|
|
926
944
|
});
|
|
927
945
|
|
|
946
|
+
// ──────────────────────────────────────────── Web Remote
|
|
947
|
+
// OTP is 8 uppercase alphanumeric chars (case-insensitive entry, normalised to upper in handler).
|
|
948
|
+
const WEB_REMOTE_OTP_RE = /^[A-Z0-9]{8}$/i;
|
|
949
|
+
const DEVICE_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
950
|
+
|
|
951
|
+
const webRemotePair = z.object({
|
|
952
|
+
otp: z.string().regex(WEB_REMOTE_OTP_RE),
|
|
953
|
+
}).strict();
|
|
954
|
+
|
|
955
|
+
const webRemoteRevokeDevice = z.object({
|
|
956
|
+
deviceId: z.string().regex(DEVICE_ID_RE),
|
|
957
|
+
}).strict();
|
|
958
|
+
|
|
959
|
+
const webRemoteAuditTail = z.object({
|
|
960
|
+
lines: z.number().int().min(1).max(500).optional(),
|
|
961
|
+
}).strict();
|
|
962
|
+
|
|
928
963
|
// ──────────────────────────────────────────── History
|
|
929
964
|
const DATE_YYYY_MM_DD = /^\d{4}-\d{2}-\d{2}$/;
|
|
930
965
|
|
|
@@ -1051,6 +1086,20 @@ const telemetrySetConfig = z.object({
|
|
|
1051
1086
|
schemaVersion: z.literal(1),
|
|
1052
1087
|
});
|
|
1053
1088
|
|
|
1089
|
+
const customerFeedbackSubmit = z.object({
|
|
1090
|
+
title: z.string().max(120),
|
|
1091
|
+
body: z.string().max(4000),
|
|
1092
|
+
tag: z.enum(['bug', 'feature', 'discussion']),
|
|
1093
|
+
});
|
|
1094
|
+
const customerFeedbackMarkSeen = z.object({ ids: z.array(z.string().max(200)).max(500).optional() });
|
|
1095
|
+
const customerFeedbackInboxList = z.object({ includeHidden: z.boolean().optional() });
|
|
1096
|
+
const customerFeedbackInboxSetStatus = z.object({
|
|
1097
|
+
id: z.string().max(200),
|
|
1098
|
+
status: z.enum(['open', 'in_progress', 'resolved', 'wontfix']),
|
|
1099
|
+
note: z.string().max(500).optional(),
|
|
1100
|
+
});
|
|
1101
|
+
const customerFeedbackInboxLinkEpic = z.object({ id: z.string().max(200), epicId: z.string().max(200) });
|
|
1102
|
+
|
|
1054
1103
|
/**
|
|
1055
1104
|
* Wrap an IPC handler with schema validation. Returns a new handler that
|
|
1056
1105
|
* parses the payload before calling the original. On invalid payload throws
|
|
@@ -1072,13 +1121,78 @@ function validated(schema, handler) {
|
|
|
1072
1121
|
};
|
|
1073
1122
|
}
|
|
1074
1123
|
|
|
1124
|
+
// ──────────────────────────────────────────── Web Remote command allowlist
|
|
1125
|
+
// Commands are split into three tiers:
|
|
1126
|
+
// READ_COMMANDS — return data; allowed when remoteEnabled=true.
|
|
1127
|
+
// SAS_GATED_READS — return sensitive user data (sessions, PRDs, logs,
|
|
1128
|
+
// transcript summaries); additionally require
|
|
1129
|
+
// _e2eAuthenticated=true (SAS confirmed by user).
|
|
1130
|
+
// A compromised relay cannot exfiltrate this data from
|
|
1131
|
+
// a session that has not been SAS-confirmed.
|
|
1132
|
+
// MUTATE_COMMANDS — write files, spawn processes, or mutate persisted
|
|
1133
|
+
// state; gated behind remoteControlEnabled=true AND
|
|
1134
|
+
// _e2eAuthenticated=true.
|
|
1135
|
+
// ALLOWED_COMMANDS is the union, kept for existing import compatibility.
|
|
1136
|
+
//
|
|
1137
|
+
// Ungated READ_COMMANDS (justify each):
|
|
1138
|
+
// cmd:app:version — exposes only the app semver string; no user data.
|
|
1139
|
+
// cmd:session:unsubscribe — teardown lifecycle; returns nothing sensitive.
|
|
1140
|
+
const READ_COMMANDS = new Set([
|
|
1141
|
+
'cmd:app:version',
|
|
1142
|
+
// v2 mobile: unsubscribe is a teardown lifecycle call with no data payload.
|
|
1143
|
+
'cmd:session:unsubscribe',
|
|
1144
|
+
]);
|
|
1145
|
+
|
|
1146
|
+
// Sensitive reads — return user data; require SAS confirmation same as MUTATE.
|
|
1147
|
+
const SAS_GATED_READS = new Set([
|
|
1148
|
+
'cmd:sessions:load',
|
|
1149
|
+
'cmd:history:aggregate',
|
|
1150
|
+
// subscribe initiates a live stream of session state/summary — sensitive.
|
|
1151
|
+
'cmd:session:subscribe',
|
|
1152
|
+
// NOTE: cmd:exchanges:list is intentionally NOT allowlisted — webRemote.cjs has
|
|
1153
|
+
// no dispatch handler for it, so an allowlist entry would only fail closed with
|
|
1154
|
+
// an opaque reject. Re-add here together with the handler when remote exchanges
|
|
1155
|
+
// are wired, so the allowlist always mirrors an actual capability.
|
|
1156
|
+
//
|
|
1157
|
+
// Scheduler/Epics commands (cmd:schedule:*) were removed here deliberately
|
|
1158
|
+
// (core scheduler/Epics redesign) — Remote no longer reaches into scheduler
|
|
1159
|
+
// internals at all; a future Remote rebuild will talk to Scheduler/Epics
|
|
1160
|
+
// through whatever higher-level surface that redesign lands on, not this
|
|
1161
|
+
// per-command allowlist.
|
|
1162
|
+
]);
|
|
1163
|
+
|
|
1164
|
+
const MUTATE_COMMANDS = new Set([
|
|
1165
|
+
'cmd:sessions:save',
|
|
1166
|
+
'cmd:pty:spawn',
|
|
1167
|
+
'cmd:pty:write',
|
|
1168
|
+
// pty:kill terminates a live session; pty:resize drives the geometry of the
|
|
1169
|
+
// user's interactive PTY — both write live process state, so they are gated
|
|
1170
|
+
// behind remoteControlEnabled + SAS like every other mutation. A read-only
|
|
1171
|
+
// mobile mirror has no business killing or resizing the desktop's session.
|
|
1172
|
+
'cmd:pty:kill',
|
|
1173
|
+
'cmd:pty:resize',
|
|
1174
|
+
// Pushes a prompt into an open tab's chat queue — a real mutation of that
|
|
1175
|
+
// tab's session, same tier as pty:write.
|
|
1176
|
+
'cmd:chat:send',
|
|
1177
|
+
]);
|
|
1178
|
+
|
|
1179
|
+
const ALLOWED_COMMANDS = new Set([...READ_COMMANDS, ...SAS_GATED_READS, ...MUTATE_COMMANDS]);
|
|
1180
|
+
|
|
1075
1181
|
module.exports = {
|
|
1076
1182
|
// Centralized slug regex — used by scheduler.cjs and queueOps.cjs for
|
|
1077
1183
|
// direct test()/match() containment checks alongside the zod parses.
|
|
1078
1184
|
SCHEDULE_SLUG_RE,
|
|
1079
1185
|
SCHEDULE_RUN_ID_RE,
|
|
1080
1186
|
PRD_CREATE_SLUG_RE,
|
|
1187
|
+
READ_COMMANDS,
|
|
1188
|
+
SAS_GATED_READS,
|
|
1189
|
+
MUTATE_COMMANDS,
|
|
1190
|
+
ALLOWED_COMMANDS,
|
|
1081
1191
|
schemas: {
|
|
1192
|
+
webRemotePair,
|
|
1193
|
+
webRemoteRevokeDevice,
|
|
1194
|
+
webRemoteAuditTail,
|
|
1195
|
+
sessionSubscribe,
|
|
1082
1196
|
ptySpawn,
|
|
1083
1197
|
ptyTabId,
|
|
1084
1198
|
ptyAlive,
|
|
@@ -1179,7 +1293,13 @@ module.exports = {
|
|
|
1179
1293
|
agentsResolveEpicModel,
|
|
1180
1294
|
agentsResolveEpicEffort,
|
|
1181
1295
|
delegationReadinessCwd,
|
|
1296
|
+
appCreateProjectFolder,
|
|
1182
1297
|
telemetrySetConfig,
|
|
1298
|
+
customerFeedbackSubmit,
|
|
1299
|
+
customerFeedbackMarkSeen,
|
|
1300
|
+
customerFeedbackInboxList,
|
|
1301
|
+
customerFeedbackInboxSetStatus,
|
|
1302
|
+
customerFeedbackInboxLinkEpic,
|
|
1183
1303
|
},
|
|
1184
1304
|
validated,
|
|
1185
1305
|
};
|