@wukazis/euphony 0.1.46 → 0.1.47

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/index.html CHANGED
@@ -13,7 +13,7 @@
13
13
  <title>Euphony: Visualize Chat Data in Your Browser</title>
14
14
 
15
15
  <link rel="stylesheet" href="/global.css" />
16
- <script type="module" crossorigin src="/assets/main-DS3CAMrw.js"></script>
16
+ <script type="module" crossorigin src="/assets/main-BMLAkPsC.js"></script>
17
17
  </head>
18
18
 
19
19
  <body>
@@ -24,7 +24,7 @@ declare enum DataType {
24
24
  CODEX_USAGE = "codex-usage",
25
25
  JSON = "json"
26
26
  }
27
- type MenuItems = 'Load without cache' | 'Load Codex sessions' | 'Usage' | 'Load from clipboard' | 'Load local file' | 'Editor mode' | 'Leave editor mode' | 'Filter data' | 'Preferences' | 'Code';
27
+ type MenuItems = 'Load without cache' | 'Load Codex sessions' | 'Load Claude sessions' | 'Usage' | 'Load from clipboard' | 'Load local file' | 'Editor mode' | 'Leave editor mode' | 'Filter data' | 'Preferences' | 'Code';
28
28
  type CodexUsageRange = '7d' | '30d' | '1y';
29
29
  type ConversationViewerElement = EuphonyConversation | EuphonyCodex;
30
30
  export declare const isCodexUsageRange: (value: string) => value is CodexUsageRange;
@@ -122,6 +122,7 @@ export declare class EuphonyApp extends LitElement {
122
122
  selectAllButtonClicked(): void;
123
123
  updatePageNumber(newPageNumber: number, scrollToTop: boolean): Promise<void>;
124
124
  loadCodexSessionsClicked(): Promise<void>;
125
+ loadClaudeSessionsClicked(): Promise<void>;
125
126
  loadCodexUsageClicked(): Promise<void>;
126
127
  openCodexSessionSummary(summary: CodexSessionSummary): Promise<void>;
127
128
  pageClicked(e: CustomEvent<number>): void;
@@ -1,6 +1,6 @@
1
- import{v as t,i as e,n as o,r as n,e as s,a,b as i,o as r,w as l}from"../../chunks/third-party.js";import{R as p}from"../../chunks/harmony-types.js";import"../../chunks/conversation.js";import{c as d}from"../../chunks/css/codex.js";import"../message-code/message-code.js";import"../message-developer-content/message-developer-content.js";import"../message-system-content/message-system-content.js";import"../message-text/message-text.js";import"../message-unsupported/message-unsupported.js";import"../../chunks/utils.js";const u={"purple-700":"hsl(282, 67.88%, 37.84%)","blue-700":"hsl(209, 78.72%, 46.08%)","green-700":"hsl(122, 43.43%, 38.82%)","gray-700":"hsl(0, 0.0%, 38.04%)"},c={colors:u},m=new Set(["session_meta","response_item","event_msg","turn_context","compacted"]),y=new Set(["message","function_call","function_call_output","custom_tool_call","custom_tool_call_output","reasoning"]),h=new Set(["user_message","agent_message","agent_reasoning","context_compacted","turn_aborted","token_count"]),g=t=>"object"==typeof t&&null!==t,b=t=>{try{return JSON.parse(t)}catch{return null}},f=t=>{const e=[];for(const o of t){if("string"==typeof o){const t=b(o);g(t)&&e.push(t);continue}g(o)&&e.push(o)}return e},v=t=>{if("string"!=typeof t.type)return!1;if(m.has(t.type)){if("response_item"===t.type){const e=t.payload&&"string"==typeof t.payload.type?t.payload.type:null;return!e||y.has(e)}if("event_msg"===t.type){const e=t.payload&&"string"==typeof t.payload.type?t.payload.type:null;return!e||h.has(e)}return!0}return t.type.startsWith("response_")||t.type.startsWith("event_")},_=t=>{if(!Array.isArray(t)||0===t.length)return!1;const e=f(t).filter(v);return!(0===e.length||e.length/t.length<.6)&&(!!e.some(t=>"session_meta"===t.type)||e.filter(t=>m.has(t.type??"")).length/e.length>=.6)},$=t=>{if(!t)return null;const e=Date.parse(t);return Number.isNaN(e)?null:e/1e3},w=t=>{try{const e=JSON.parse(JSON.stringify(t));return e.payload&&"encrypted_content"in e.payload&&(e.payload.encrypted_content="[omitted]"),e}catch{return t}},x=t=>JSON.stringify(t,null,2),S=t=>"string"==typeof t||"number"==typeof t||"boolean"==typeof t?String(t):null,B=t=>{if("string"==typeof t)return t;if(Array.isArray(t))return t.map(t=>B(t)).filter(t=>""!==t).join("\n");if(g(t))for(const e of["text","content","value"])if(e in t)return B(t[e]);return""},C=t=>g(t)&&"text"in t?B(t.text).trim():B(t).trim(),M=t=>{const e=b(t);return null===e?t:x(e)},k=t=>{if(g(t)&&"string"==typeof t.cmd)return t.cmd;if("string"!=typeof t)return null;const e=b(t);return null===e?t:g(e)&&"string"==typeof e.cmd?e.cmd:null},A=t=>{switch(t){case"assistant":return p.Assistant;case"user":return p.User;case"system":default:return p.System;case"tool":return p.Tool;case"developer":return p.Developer}},T=({id:t,role:e,text:o,timestamp:n,metadata:s,name:a,recipient:i,channel:r})=>({id:t,role:e,name:a,content:[{text:o}],create_time:n??void 0,metadata:s,recipient:i,channel:r}),j=({id:t,instructions:e,timestamp:o,metadata:n,name:s,recipient:a,channel:i})=>({id:t,role:p.Developer,name:s,content:[{instructions:e}],create_time:o??void 0,metadata:n,recipient:a,channel:i}),D=({id:t,role:e,code:o,language:n,timestamp:s,metadata:a,name:i,recipient:r,channel:l})=>({id:t,role:e,name:i,content:[{content_type:"code",text:o,language:n}],create_time:s??void 0,metadata:a,recipient:r,channel:l}),N=t=>{if(!_(t))return null;const e=f(t).filter(v),o=[],n=new Map,s=e.some(t=>"response_item"===t.type&&g(t.payload)&&"message"===t.payload.type&&"user"===t.payload.role),a=e.some(t=>"response_item"===t.type&&g(t.payload)&&"message"===t.payload.type&&"assistant"===t.payload.role),i=e.some(t=>"response_item"===t.type&&g(t.payload)&&"reasoning"===t.payload.type);let r=0,l=0,d=0,u=0;const m=e.find(t=>"session_meta"===t.type),y=m?.payload??{},h=e.find(t=>"turn_context"===t.type)?.payload??{},b=y.id??`codex-session-${Date.now()}`,B="string"==typeof h.model?h.model:null,N="string"==typeof y.cli_version?y.cli_version:null,W="string"==typeof y.timestamp?y.timestamp:null,L=C(y.base_instructions),R=["Codex session"],E=S(y.id);E&&R.push(`id: ${E}`),W&&R.push(`started: ${W}`);const O=S(y.cwd);O&&R.push(`cwd: ${O}`);const I=S(y.originator);if(I&&R.push(`originator: ${I}`),N&&R.push(`cli_version: ${N}`),B&&R.push(`model: ${B}`),y.git&&g(y.git)){const t=y.git,e=S(t.branch)??"unknown",o=S(t.commit_hash)??"unknown";("unknown"!==e||"unknown"!==o)&&R.push(`git: ${e}@${o}`)}if(R.length>1){const t=m??{type:"session_meta",payload:y};o.push(T({id:`${b}-summary`,role:p.System,text:R.join("\n"),timestamp:$(m?.timestamp),metadata:{codex_event_type:"session_meta",codex_event:w(t)},name:"codex"})),r+=1}L&&(o.push(j({id:`${b}-base-instructions`,instructions:L,timestamp:$(m?.timestamp),metadata:{codex_event_type:"session_meta",codex_source:"session_meta.base_instructions.text",codex_event:w(m??{type:"session_meta",payload:y})}})),r+=1);for(const[t,c]of e.entries()){const e=c.type??"unknown",m=c.payload??{},y=$(c.timestamp),h={codex_event_type:e,codex_payload_type:m.type??null,codex_event:w(c),codex_function_name:"string"==typeof m.name?m.name:null};if("response_item"===e){const e=m.type??"unknown";if("message"===e){const e=(Array.isArray(m.content)?m.content:[]).map(t=>"string"==typeof t.text?t.text:"string"==typeof t.type?`[${t.type}]`:"").filter(Boolean);o.push(T({id:`${b}-message-${t}`,role:A(m.role),text:e.length>0?e.join("\n"):"[empty message]",timestamp:y,metadata:h})),r+=1;continue}if("reasoning"===e){const e=(Array.isArray(m.summary)?m.summary:[]).map(t=>"string"==typeof t.text?t.text:"").filter(Boolean).join("\n");e&&(o.push(T({id:`${b}-reasoning-${t}`,role:p.Assistant,text:e,timestamp:y,metadata:h,channel:"analysis"})),u+=1);continue}if("function_call"===e){const e=m.name??"tool",s=m.call_id??`${b}-call-${t}`;n.set(s,e);const a="exec_command"===e?k(m.arguments):null;null!==a?o.push(D({id:`${b}-call-${s}`,role:p.Tool,code:a,language:"bash",timestamp:y,metadata:h,name:e,recipient:e,channel:"call"})):o.push(D({id:`${b}-call-${s}`,role:p.Tool,code:x(m),language:"json",timestamp:y,metadata:h,name:e,recipient:e,channel:"call"})),l+=1;continue}if("custom_tool_call"===e){const s=m.name??"tool",a=m.call_id??`${b}-call-${t}`;if(n.set(a,s),"string"==typeof m.input&&m.input.includes("\n")){o.push(D({id:`${b}-call-${a}`,role:p.Tool,code:`tool: ${s}\ncall_id: ${a}\n\n${m.input}`,language:"text",timestamp:y,metadata:h,name:s,recipient:s,channel:"call"})),l+=1;continue}const i={type:e,name:s,call_id:a};void 0!==m.input&&(i.input=m.input),m.status&&(i.status=m.status),o.push(D({id:`${b}-call-${a}`,role:p.Tool,code:x(i),language:"json",timestamp:y,metadata:h,name:s,recipient:s,channel:"call"})),l+=1;continue}if("function_call_output"===e||"custom_tool_call_output"===e){const e=m.call_id??`${b}-call-${t}`,s=m.name??n.get(e)??"tool",a="string"==typeof m.output?M(m.output):void 0!==m.output?x(m.output):"[empty output]";o.push(D({id:`${b}-output-${e}`,role:p.Tool,code:a,language:"text",timestamp:y,metadata:h,name:s,recipient:s,channel:"output"})),d+=1;continue}o.push(D({id:`${b}-response-${t}`,role:p.System,code:x({type:e,...m}),language:"json",timestamp:y,metadata:h,name:"codex",channel:"response"}));continue}if("event_msg"===e){const e=m.type??"unknown";if(!s&&"user_message"===e){o.push(T({id:`${b}-user-${t}`,role:p.User,text:"string"==typeof m.message?m.message:"[empty message]",timestamp:y,metadata:h})),r+=1;continue}if(!a&&"agent_message"===e){o.push(T({id:`${b}-assistant-${t}`,role:p.Assistant,text:"string"==typeof m.message?m.message:"[empty message]",timestamp:y,metadata:h})),r+=1;continue}if(!i&&"agent_reasoning"===e){o.push(T({id:`${b}-reasoning-${t}`,role:p.Assistant,text:"string"==typeof m.text?m.text:"[empty reasoning]",timestamp:y,metadata:h,channel:"analysis"})),u+=1;continue}"context_compacted"===e&&(o.push(T({id:`${b}-compacted-${t}`,role:p.System,text:"Context compacted",timestamp:y,metadata:h,name:"codex"})),r+=1)}}const P=e.length>0?$(e[0].timestamp):null,J=[];return b&&J.push(["Session",b.slice(0,8),b,c.colors["blue-700"]]),B&&J.push(["Model",B,"From turn_context",c.colors["purple-700"]]),N&&J.push(["CLI",N,"Codex CLI version",c.colors["gray-700"]]),J.push(["Events",String(e.length),"JSONL event count",c.colors["green-700"]]),{conversation:{id:b,create_time:P??Date.now()/1e3,messages:o,metadata:{codex_session_meta:y,codex_turn_context:h,codex_event_counts:{events:e.length,messages:r,tool_calls:l,tool_outputs:d,reasoning:u},"euphony-custom-labels":J}},customLabels:J}};var W=Object.defineProperty,L=Object.getOwnPropertyDescriptor,R=(t,e,o,n)=>{for(var s,a=n>1?void 0:n?L(e,o):e,i=t.length-1;i>=0;i--)(s=t[i])&&(a=(n?s(e,o,a):s(a))||a);return n&&a&&W(e,o,a),a};let E=class extends a{constructor(){super(...arguments),this.sessionString="",this.sessionData=null,this.sharingURL=null,this.conversationLabel="Session",this.conversationMaxWidth=null,this.conversationStyle="",this.previewMode=!1,this.shouldRenderMarkdown=!1,this.isShowingMetadata=!1,this.focusModeAuthor=[],this.focusModeRecipient=[],this.focusModeContentType=[],this.disableMarkdownButton=!1,this.disableTranslationButton=!1,this.disableShareButton=!1,this.disableMetadataButton=!1,this.disableMessageMetadata=!1,this.disableConversationName=!1,this.disablePreferenceButton=!1,this.disableImagePreviewWindow=!1,this.disableTokenWindow=!1,this.disableEditingModeSaveButton=!1,this.disableConversationIDCopyButton=!1,this.disableDownloadConvoButtonTooltip="",this.disableCopyConvoButtonTooltip="",this.theme="light",this.conversation=null,this.parseError=null}parseSessionString(t){const e=t.split("\n").map(t=>t.trim()).filter(t=>""!==t),o=[];for(const t of e)try{o.push(JSON.parse(t))}catch{}return o}refreshConversationFromSession(){const t=Array.isArray(this.sessionData)&&this.sessionData.length>0?this.sessionData??[]:""!==this.sessionString?this.parseSessionString(this.sessionString):[],e=N(t);if(!e)return this.conversation=null,void(this.parseError=0===t.length?"No Codex session data found.":"Unsupported or malformed Codex session JSONL.");this.conversation=e.conversation,this.parseError=null}willUpdate(t){(t.has("sessionString")||t.has("sessionData"))&&this.refreshConversationFromSession()}render(){if(!this.conversation)return i`
1
+ import{v as t,i as e,n,r as o,e as s,a,b as i,o as r,w as l}from"../../chunks/third-party.js";import{R as p}from"../../chunks/harmony-types.js";import"../../chunks/conversation.js";import{c as u}from"../../chunks/css/codex.js";import"../message-code/message-code.js";import"../message-developer-content/message-developer-content.js";import"../message-system-content/message-system-content.js";import"../message-text/message-text.js";import"../message-unsupported/message-unsupported.js";import"../../chunks/utils.js";const c={"purple-700":"hsl(282, 67.88%, 37.84%)","blue-700":"hsl(209, 78.72%, 46.08%)","teal-700":"hsl(173, 100.0%, 23.73%)","green-700":"hsl(122, 43.43%, 38.82%)","orange-700":"hsl(30, 100.0%, 48.04%)","gray-700":"hsl(0, 0.0%, 38.04%)"},d={colors:c},m=new Set(["session_meta","response_item","event_msg","turn_context","compacted"]),y=new Set(["message","function_call","function_call_output","custom_tool_call","custom_tool_call_output","reasoning"]),h=new Set(["user_message","agent_message","agent_reasoning","context_compacted","turn_aborted","token_count"]),g=new Set(["user","assistant","system","summary","permission-mode","file-history-snapshot","last-prompt","ai-title"]),f=t=>"object"==typeof t&&null!==t,_=t=>{try{return JSON.parse(t)}catch{return null}},b=t=>{const e=[];for(const n of t){if("string"==typeof n){const t=_(n);f(t)&&e.push(t);continue}f(n)&&e.push(n)}return e},v=t=>{const e=[];for(const n of t){if("string"==typeof n){const t=_(n);f(t)&&e.push(t);continue}f(n)&&e.push(n)}return e},$=t=>{if("string"!=typeof t.type)return!1;if(m.has(t.type)){if("response_item"===t.type){const e=t.payload&&"string"==typeof t.payload.type?t.payload.type:null;return!e||y.has(e)}if("event_msg"===t.type){const e=t.payload&&"string"==typeof t.payload.type?t.payload.type:null;return!e||h.has(e)}return!0}return t.type.startsWith("response_")||t.type.startsWith("event_")},w=t=>!("string"!=typeof t.type||!g.has(t.type))&&("user"===t.type||"assistant"===t.type?f(t.message):"string"==typeof t.sessionId||f(t.snapshot)),x=t=>{if(!Array.isArray(t)||0===t.length)return!1;const e=b(t).filter($);return!(0===e.length||e.length/t.length<.6)&&(!!e.some(t=>"session_meta"===t.type)||e.filter(t=>m.has(t.type??"")).length/e.length>=.6)},S=t=>{if(!Array.isArray(t)||0===t.length)return!1;const e=v(t).filter(w);return 0!==e.length&&0!==e.filter(t=>"user"===t.type||"assistant"===t.type).length&&e.length/t.length>=.4},k=t=>x(t)||S(t),B=t=>{if(!t)return null;const e=Date.parse(t);return Number.isNaN(e)?null:e/1e3},C=t=>{try{const e=JSON.parse(JSON.stringify(t));return e.payload&&"encrypted_content"in e.payload&&(e.payload.encrypted_content="[omitted]"),e}catch{return t}},M=(t,e=2e3)=>t.length<=e?t:`${t.slice(0,e).trimEnd()}... [truncated]`,A=t=>{try{const e=JSON.parse(JSON.stringify(t)),n=e.message?.content;if("string"==typeof n)e.message&&(e.message.content=M(n));else if(Array.isArray(n)){if(!e.message)return e;e.message.content=n.map(t=>{if(!f(t))return t;const e={...t};"string"==typeof e.signature&&(e.signature="[omitted]");for(const t of["text","thinking","content"])"string"==typeof e[t]&&(e[t]=M(e[t]));return e})}return e}catch{return t}},T=t=>JSON.stringify(t,null,2),j=t=>"string"==typeof t||"number"==typeof t||"boolean"==typeof t?String(t):null,N=t=>{if("string"==typeof t)return t;if(Array.isArray(t))return t.map(t=>N(t)).filter(t=>""!==t).join("\n");if(f(t))for(const e of["text","content","value"])if(e in t)return N(t[e]);return""},D=t=>{if("string"==typeof t)return t;if(Array.isArray(t))return t.map(t=>D(t)).filter(t=>""!==t).join("\n");if(f(t))for(const e of["text","thinking","content"])if(e in t)return D(t[e]);return""},L=t=>f(t)&&"text"in t?N(t.text).trim():N(t).trim(),I=t=>{const e=_(t);return null===e?t:T(e)},W=t=>{if(f(t)&&"string"==typeof t.cmd)return t.cmd;if("string"!=typeof t)return null;const e=_(t);return null===e?t:f(e)&&"string"==typeof e.cmd?e.cmd:null},O=t=>f(t)&&"string"==typeof t.command?t.command:null,E=t=>{switch(t){case"assistant":return p.Assistant;case"user":return p.User;case"system":default:return p.System;case"tool":return p.Tool;case"developer":return p.Developer}},R=({id:t,role:e,text:n,timestamp:o,metadata:s,name:a,recipient:i,channel:r})=>({id:t,role:e,name:a,content:[{text:n}],create_time:o??void 0,metadata:s,recipient:i,channel:r}),J=({id:t,instructions:e,timestamp:n,metadata:o,name:s,recipient:a,channel:i})=>({id:t,role:p.Developer,name:s,content:[{instructions:e}],create_time:n??void 0,metadata:o,recipient:a,channel:i}),P=({id:t,role:e,code:n,language:o,timestamp:s,metadata:a,name:i,recipient:r,channel:l})=>({id:t,role:e,name:i,content:[{content_type:"code",text:n,language:o}],create_time:s??void 0,metadata:a,recipient:r,channel:l}),U=t=>{if(!x(t))return null;const e=b(t).filter($),n=[],o=new Map,s=e.some(t=>"response_item"===t.type&&f(t.payload)&&"message"===t.payload.type&&"user"===t.payload.role),a=e.some(t=>"response_item"===t.type&&f(t.payload)&&"message"===t.payload.type&&"assistant"===t.payload.role),i=e.some(t=>"response_item"===t.type&&f(t.payload)&&"reasoning"===t.payload.type);let r=0,l=0,u=0,c=0;const m=e.find(t=>"session_meta"===t.type),y=m?.payload??{},h=e.find(t=>"turn_context"===t.type)?.payload??{},g=y.id??`codex-session-${Date.now()}`,_="string"==typeof h.model?h.model:null,v="string"==typeof y.cli_version?y.cli_version:null,w="string"==typeof y.timestamp?y.timestamp:null,S=L(y.base_instructions),k=["Codex session"],M=j(y.id);M&&k.push(`id: ${M}`),w&&k.push(`started: ${w}`);const A=j(y.cwd);A&&k.push(`cwd: ${A}`);const N=j(y.originator);if(N&&k.push(`originator: ${N}`),v&&k.push(`cli_version: ${v}`),_&&k.push(`model: ${_}`),y.git&&f(y.git)){const t=y.git,e=j(t.branch)??"unknown",n=j(t.commit_hash)??"unknown";("unknown"!==e||"unknown"!==n)&&k.push(`git: ${e}@${n}`)}if(k.length>1){const t=m??{type:"session_meta",payload:y};n.push(R({id:`${g}-summary`,role:p.System,text:k.join("\n"),timestamp:B(m?.timestamp),metadata:{codex_event_type:"session_meta",codex_event:C(t)},name:"codex"})),r+=1}S&&(n.push(J({id:`${g}-base-instructions`,instructions:S,timestamp:B(m?.timestamp),metadata:{codex_event_type:"session_meta",codex_source:"session_meta.base_instructions.text",codex_event:C(m??{type:"session_meta",payload:y})}})),r+=1);for(const[t,d]of e.entries()){const e=d.type??"unknown",m=d.payload??{},y=B(d.timestamp),h={codex_event_type:e,codex_payload_type:m.type??null,codex_event:C(d),codex_function_name:"string"==typeof m.name?m.name:null};if("response_item"===e){const e=m.type??"unknown";if("message"===e){const e=(Array.isArray(m.content)?m.content:[]).map(t=>"string"==typeof t.text?t.text:"string"==typeof t.type?`[${t.type}]`:"").filter(Boolean);n.push(R({id:`${g}-message-${t}`,role:E(m.role),text:e.length>0?e.join("\n"):"[empty message]",timestamp:y,metadata:h})),r+=1;continue}if("reasoning"===e){const e=(Array.isArray(m.summary)?m.summary:[]).map(t=>"string"==typeof t.text?t.text:"").filter(Boolean).join("\n");e&&(n.push(R({id:`${g}-reasoning-${t}`,role:p.Assistant,text:e,timestamp:y,metadata:h,channel:"analysis"})),c+=1);continue}if("function_call"===e){const e=m.name??"tool",s=m.call_id??`${g}-call-${t}`;o.set(s,e);const a="exec_command"===e?W(m.arguments):null;null!==a?n.push(P({id:`${g}-call-${s}`,role:p.Tool,code:a,language:"bash",timestamp:y,metadata:h,name:e,recipient:e,channel:"call"})):n.push(P({id:`${g}-call-${s}`,role:p.Tool,code:T(m),language:"json",timestamp:y,metadata:h,name:e,recipient:e,channel:"call"})),l+=1;continue}if("custom_tool_call"===e){const s=m.name??"tool",a=m.call_id??`${g}-call-${t}`;if(o.set(a,s),"string"==typeof m.input&&m.input.includes("\n")){n.push(P({id:`${g}-call-${a}`,role:p.Tool,code:`tool: ${s}\ncall_id: ${a}\n\n${m.input}`,language:"text",timestamp:y,metadata:h,name:s,recipient:s,channel:"call"})),l+=1;continue}const i={type:e,name:s,call_id:a};void 0!==m.input&&(i.input=m.input),m.status&&(i.status=m.status),n.push(P({id:`${g}-call-${a}`,role:p.Tool,code:T(i),language:"json",timestamp:y,metadata:h,name:s,recipient:s,channel:"call"})),l+=1;continue}if("function_call_output"===e||"custom_tool_call_output"===e){const e=m.call_id??`${g}-call-${t}`,s=m.name??o.get(e)??"tool",a="string"==typeof m.output?I(m.output):void 0!==m.output?T(m.output):"[empty output]";n.push(P({id:`${g}-output-${e}`,role:p.Tool,code:a,language:"text",timestamp:y,metadata:h,name:s,recipient:s,channel:"output"})),u+=1;continue}n.push(P({id:`${g}-response-${t}`,role:p.System,code:T({type:e,...m}),language:"json",timestamp:y,metadata:h,name:"codex",channel:"response"}));continue}if("event_msg"===e){const e=m.type??"unknown";if(!s&&"user_message"===e){n.push(R({id:`${g}-user-${t}`,role:p.User,text:"string"==typeof m.message?m.message:"[empty message]",timestamp:y,metadata:h})),r+=1;continue}if(!a&&"agent_message"===e){n.push(R({id:`${g}-assistant-${t}`,role:p.Assistant,text:"string"==typeof m.message?m.message:"[empty message]",timestamp:y,metadata:h})),r+=1;continue}if(!i&&"agent_reasoning"===e){n.push(R({id:`${g}-reasoning-${t}`,role:p.Assistant,text:"string"==typeof m.text?m.text:"[empty reasoning]",timestamp:y,metadata:h,channel:"analysis"})),c+=1;continue}"context_compacted"===e&&(n.push(R({id:`${g}-compacted-${t}`,role:p.System,text:"Context compacted",timestamp:y,metadata:h,name:"codex"})),r+=1)}}const D=e.length>0?B(e[0].timestamp):null,O=[];return g&&O.push(["Session",g.slice(0,8),g,d.colors["blue-700"]]),_&&O.push(["Model",_,"From turn_context",d.colors["purple-700"]]),v&&O.push(["CLI",v,"Codex CLI version",d.colors["gray-700"]]),O.push(["Events",String(e.length),"JSONL event count",d.colors["green-700"]]),{conversation:{id:g,create_time:D??Date.now()/1e3,messages:n,metadata:{codex_session_meta:y,codex_turn_context:h,codex_event_counts:{events:e.length,messages:r,tool_calls:l,tool_outputs:u,reasoning:c},"euphony-custom-labels":O}},customLabels:O}},F=t=>{if(!S(t))return null;const e=v(t).filter(w),n=[],o=new Map,s=new Set;let a=0,i=0,r=0,l=0;const u=e.find(t=>"string"==typeof t.sessionId)?.sessionId??`claude-session-${Date.now()}`,c=e.length>0?B(e[0].timestamp):null,m=e.find(t=>"user"===t.type||"assistant"===t.type),y=e.find(t=>"string"==typeof t.timestamp)?.timestamp??(f(m?.snapshot)&&"string"==typeof m?.snapshot.timestamp?m.snapshot.timestamp:null),h=e.map(t=>f(t.message)&&"string"==typeof t.message.model?t.message.model:null).find(t=>null!==t),g=e.find(t=>"string"==typeof t.version)?.version,_=e.find(t=>"string"==typeof t.cwd)?.cwd,b=e.find(t=>"string"==typeof t.entrypoint)?.entrypoint,$=e.find(t=>"string"==typeof t.gitBranch)?.gitBranch,x=e.find(t=>"ai-title"===t.type&&"string"==typeof t.aiTitle)?.aiTitle,k={input_tokens:0,cached_input_tokens:0,output_tokens:0,reasoning_output_tokens:0,total_tokens:0};for(const t of e){if("assistant"!==t.type||!f(t.message))continue;const e=t.message.usage;if(!f(e))continue;const n=j(t.message.id)??j(t.requestId)??j(t.uuid);if(n&&s.has(n))continue;n&&s.add(n);const o=Number(e.input_tokens)||0,a=Number(e.cache_creation_input_tokens)||0,i=Number(e.cache_read_input_tokens)||0,r=Number(e.output_tokens)||0;k.input_tokens+=o,k.cached_input_tokens+=a+i,k.output_tokens+=r,k.total_tokens+=o+a+i+r}const C=["Claude session"];C.push(`id: ${u}`),y&&C.push(`started: ${y}`),_&&C.push(`cwd: ${_}`),b&&C.push(`entrypoint: ${b}`),g&&C.push(`version: ${g}`),h&&C.push(`model: ${h}`),$&&C.push(`git: ${$}`),x&&C.push(`title: ${x}`),n.push(R({id:`${u}-summary`,role:p.System,text:C.join("\n"),timestamp:B(y??void 0),metadata:{claude_session_id:u,claude_source:"summary"},name:"claude"})),a+=1;for(const[t,s]of e.entries()){if("user"!==s.type&&"assistant"!==s.type||s.isMeta||!f(s.message))continue;const e=s.message,c="string"==typeof e.role?E(e.role):E(s.type),d=B(s.timestamp),m=j(s.uuid)??`${u}-${t}`,y={claude_event_type:s.type,claude_uuid:s.uuid??null,claude_request_id:s.requestId??null,claude_session_id:s.sessionId??u,claude_event:A(s)},h=e.content,g="string"==typeof h?[{type:"text",text:h}]:Array.isArray(h)?h.filter(f):[];for(const[t,e]of g.entries()){const s=e.type??"unknown",u=j(e.id)??`${m}-${t}`;if("text"===s){const o="string"==typeof e.text?e.text:"";if(0===o.trim().length)continue;n.push(R({id:`${m}-text-${t}`,role:c,text:o,timestamp:d,metadata:y})),a+=1;continue}if("thinking"===s){const o="string"==typeof e.thinking?e.thinking:"";if(0===o.trim().length)continue;n.push(R({id:`${m}-thinking-${t}`,role:p.Assistant,text:o,timestamp:d,metadata:y,channel:"analysis"})),l+=1;continue}if("tool_use"===s){const a="string"==typeof e.name?e.name:"tool";o.set(u,a);const r="Bash"===a?O(e.input):null,l=r??T({type:s,name:a,id:u,input:e.input});n.push(P({id:`${m}-tool-call-${t}`,role:p.Tool,code:l,language:null===r?"json":"bash",timestamp:d,metadata:{...y,claude_tool_name:a,claude_tool_id:u},name:a,recipient:a,channel:"call"})),i+=1;continue}if("tool_result"===s){const s="string"==typeof e.tool_use_id?e.tool_use_id:`${m}-${t}`,a=o.get(s)??"tool_result",i=D(e.content);n.push(P({id:`${m}-tool-output-${t}`,role:p.Tool,code:i||"[empty output]",language:"text",timestamp:d,metadata:{...y,claude_tool_name:a,claude_tool_id:s,claude_tool_error:!0===e.is_error},name:a,recipient:a,channel:"output"})),r+=1;continue}n.push(P({id:`${m}-content-${t}`,role:c===p.Assistant?p.Assistant:p.System,code:T(e),language:"json",timestamp:d,metadata:y,name:"claude"})),a+=1}}const M=[];return M.push(["Session",u.slice(0,8),u,d.colors["orange-700"]]),h&&M.push(["Model",h,"Claude model",d.colors["purple-700"]]),g&&M.push(["CLI",g,"Claude Code version",d.colors["gray-700"]]),M.push(["Events",String(e.length),"JSONL event count",d.colors["green-700"]]),k.total_tokens>0&&M.push(["Tokens",String(k.total_tokens),"Summed unique Claude assistant usage",d.colors["teal-700"]]),{conversation:{id:u,create_time:c??Date.now()/1e3,messages:n,metadata:{claude_session:{id:u,cwd:_??null,entrypoint:b??null,version:g??null,model:h??null,title:x??null},claude_event_counts:{events:e.length,messages:a,tool_calls:i,tool_outputs:r,reasoning:l},claude_token_usage:k.total_tokens>0?k:null,"euphony-custom-labels":M}},customLabels:M}},q=t=>U(t)??F(t);var z=Object.defineProperty,G=Object.getOwnPropertyDescriptor,H=(t,e,n,o)=>{for(var s,a=o>1?void 0:o?G(e,n):e,i=t.length-1;i>=0;i--)(s=t[i])&&(a=(o?s(e,n,a):s(a))||a);return o&&a&&z(e,n,a),a};let K=class extends a{constructor(){super(...arguments),this.sessionString="",this.sessionData=null,this.sharingURL=null,this.conversationLabel="Session",this.conversationMaxWidth=null,this.conversationStyle="",this.previewMode=!1,this.shouldRenderMarkdown=!1,this.isShowingMetadata=!1,this.focusModeAuthor=[],this.focusModeRecipient=[],this.focusModeContentType=[],this.disableMarkdownButton=!1,this.disableTranslationButton=!1,this.disableShareButton=!1,this.disableMetadataButton=!1,this.disableMessageMetadata=!1,this.disableConversationName=!1,this.disablePreferenceButton=!1,this.disableImagePreviewWindow=!1,this.disableTokenWindow=!1,this.disableEditingModeSaveButton=!1,this.disableConversationIDCopyButton=!1,this.disableDownloadConvoButtonTooltip="",this.disableCopyConvoButtonTooltip="",this.theme="light",this.conversation=null,this.parseError=null}parseSessionString(t){const e=t.split("\n").map(t=>t.trim()).filter(t=>""!==t),n=[];for(const t of e)try{n.push(JSON.parse(t))}catch{}return n}refreshConversationFromSession(){const t=Array.isArray(this.sessionData)&&this.sessionData.length>0?this.sessionData??[]:""!==this.sessionString?this.parseSessionString(this.sessionString):[],e=q(t);if(!e)return this.conversation=null,void(this.parseError=0===t.length?"No local agent session data found.":"Unsupported or malformed local agent session JSONL.");this.conversation=e.conversation,this.parseError=null}willUpdate(t){(t.has("sessionString")||t.has("sessionData"))&&this.refreshConversationFromSession()}render(){if(!this.conversation)return i`
2
2
  <div class="empty-state">
3
- ${this.parseError??"No Codex session to display."}
3
+ ${this.parseError??"No local agent session to display."}
4
4
  </div>
5
5
  `;const t=this.previewMode&&0===this.focusModeAuthor.length?["user","assistant"]:this.focusModeAuthor;return i`
6
6
  <div class="codex-wrapper">
@@ -31,6 +31,6 @@ import{v as t,i as e,n as o,r as n,e as s,a,b as i,o as r,w as l}from"../../chun
31
31
  style=${this.conversationStyle}
32
32
  ></euphony-conversation>
33
33
  </div>
34
- `}preferenceWindowMessageLabelChanged(t){this.conversationComponent?.preferenceWindowMessageLabelChanged(t)}preferenceWindowFocusModeSettingsChanged(t){this.conversationComponent?.preferenceWindowFocusModeSettingsChanged(t)}expandBlockContents(){this.conversationComponent?.expandBlockContents()}collapseBlockContents(){this.conversationComponent?.collapseBlockContents()}translationButtonClicked(){this.conversationComponent?.translationButtonClicked()}};E.styles=[e`
35
- ${t(d)}
36
- `],R([o({type:String,attribute:"session-string"})],E.prototype,"sessionString",2),R([o({attribute:!1})],E.prototype,"sessionData",2),R([o({type:String,attribute:"sharing-url"})],E.prototype,"sharingURL",2),R([o({type:String,attribute:"conversation-label"})],E.prototype,"conversationLabel",2),R([o({type:String,attribute:"conversation-max-width"})],E.prototype,"conversationMaxWidth",2),R([o({type:String,attribute:"conversation-style"})],E.prototype,"conversationStyle",2),R([o({type:Boolean,attribute:"preview-mode"})],E.prototype,"previewMode",2),R([o({type:Boolean,attribute:"should-render-markdown"})],E.prototype,"shouldRenderMarkdown",2),R([o({type:Boolean,attribute:"is-showing-metadata"})],E.prototype,"isShowingMetadata",2),R([o({type:Array,attribute:"focus-mode-author"})],E.prototype,"focusModeAuthor",2),R([o({type:Array,attribute:"focus-mode-recipient"})],E.prototype,"focusModeRecipient",2),R([o({type:Array,attribute:"focus-mode-content-type"})],E.prototype,"focusModeContentType",2),R([o({type:Boolean,attribute:"disable-markdown-button"})],E.prototype,"disableMarkdownButton",2),R([o({type:Boolean,attribute:"disable-translation-button"})],E.prototype,"disableTranslationButton",2),R([o({type:Boolean,attribute:"disable-share-button"})],E.prototype,"disableShareButton",2),R([o({type:Boolean,attribute:"disable-metadata-button"})],E.prototype,"disableMetadataButton",2),R([o({type:Boolean,attribute:"disable-message-metadata"})],E.prototype,"disableMessageMetadata",2),R([o({type:Boolean,attribute:"disable-conversation-name"})],E.prototype,"disableConversationName",2),R([o({type:Boolean,attribute:"disable-preference-button"})],E.prototype,"disablePreferenceButton",2),R([o({type:Boolean,attribute:"disable-image-preview-window"})],E.prototype,"disableImagePreviewWindow",2),R([o({type:Boolean,attribute:"disable-token-window"})],E.prototype,"disableTokenWindow",2),R([o({type:Boolean,attribute:"disable-editing-mode-save-button"})],E.prototype,"disableEditingModeSaveButton",2),R([o({type:Boolean,attribute:"disable-conversation-id-copy-button"})],E.prototype,"disableConversationIDCopyButton",2),R([o({type:String,attribute:"disable-download-convo-button-tooltip"})],E.prototype,"disableDownloadConvoButtonTooltip",2),R([o({type:String,attribute:"disable-copy-convo-button-tooltip"})],E.prototype,"disableCopyConvoButtonTooltip",2),R([o({type:String,attribute:"theme"})],E.prototype,"theme",2),R([n()],E.prototype,"conversation",2),R([n()],E.prototype,"parseError",2),R([s("euphony-conversation")],E.prototype,"conversationComponent",2),E=R([l("euphony-codex")],E);export{E as EuphonyCodex,_ as i,N as p};
34
+ `}preferenceWindowMessageLabelChanged(t){this.conversationComponent?.preferenceWindowMessageLabelChanged(t)}preferenceWindowFocusModeSettingsChanged(t){this.conversationComponent?.preferenceWindowFocusModeSettingsChanged(t)}expandBlockContents(){this.conversationComponent?.expandBlockContents()}collapseBlockContents(){this.conversationComponent?.collapseBlockContents()}translationButtonClicked(){this.conversationComponent?.translationButtonClicked()}};K.styles=[e`
35
+ ${t(u)}
36
+ `],H([n({type:String,attribute:"session-string"})],K.prototype,"sessionString",2),H([n({attribute:!1})],K.prototype,"sessionData",2),H([n({type:String,attribute:"sharing-url"})],K.prototype,"sharingURL",2),H([n({type:String,attribute:"conversation-label"})],K.prototype,"conversationLabel",2),H([n({type:String,attribute:"conversation-max-width"})],K.prototype,"conversationMaxWidth",2),H([n({type:String,attribute:"conversation-style"})],K.prototype,"conversationStyle",2),H([n({type:Boolean,attribute:"preview-mode"})],K.prototype,"previewMode",2),H([n({type:Boolean,attribute:"should-render-markdown"})],K.prototype,"shouldRenderMarkdown",2),H([n({type:Boolean,attribute:"is-showing-metadata"})],K.prototype,"isShowingMetadata",2),H([n({type:Array,attribute:"focus-mode-author"})],K.prototype,"focusModeAuthor",2),H([n({type:Array,attribute:"focus-mode-recipient"})],K.prototype,"focusModeRecipient",2),H([n({type:Array,attribute:"focus-mode-content-type"})],K.prototype,"focusModeContentType",2),H([n({type:Boolean,attribute:"disable-markdown-button"})],K.prototype,"disableMarkdownButton",2),H([n({type:Boolean,attribute:"disable-translation-button"})],K.prototype,"disableTranslationButton",2),H([n({type:Boolean,attribute:"disable-share-button"})],K.prototype,"disableShareButton",2),H([n({type:Boolean,attribute:"disable-metadata-button"})],K.prototype,"disableMetadataButton",2),H([n({type:Boolean,attribute:"disable-message-metadata"})],K.prototype,"disableMessageMetadata",2),H([n({type:Boolean,attribute:"disable-conversation-name"})],K.prototype,"disableConversationName",2),H([n({type:Boolean,attribute:"disable-preference-button"})],K.prototype,"disablePreferenceButton",2),H([n({type:Boolean,attribute:"disable-image-preview-window"})],K.prototype,"disableImagePreviewWindow",2),H([n({type:Boolean,attribute:"disable-token-window"})],K.prototype,"disableTokenWindow",2),H([n({type:Boolean,attribute:"disable-editing-mode-save-button"})],K.prototype,"disableEditingModeSaveButton",2),H([n({type:Boolean,attribute:"disable-conversation-id-copy-button"})],K.prototype,"disableConversationIDCopyButton",2),H([n({type:String,attribute:"disable-download-convo-button-tooltip"})],K.prototype,"disableDownloadConvoButtonTooltip",2),H([n({type:String,attribute:"disable-copy-convo-button-tooltip"})],K.prototype,"disableCopyConvoButtonTooltip",2),H([n({type:String,attribute:"theme"})],K.prototype,"theme",2),H([o()],K.prototype,"conversation",2),H([o()],K.prototype,"parseError",2),H([s("euphony-conversation")],K.prototype,"conversationComponent",2),K=H([l("euphony-codex")],K);export{K as EuphonyCodex,S as a,x as b,F as c,U as d,k as i,q as p};
package/lib/euphony.js CHANGED
@@ -1 +1 @@
1
- import{EuphonyCodex as s,i as e,p as a}from"./components/codex/codex.js";import{E as o,p as t}from"./chunks/conversation.js";import{EuphonyMessageCode as n}from"./components/message-code/message-code.js";import{EuphonyMessageDeveloperContent as p}from"./components/message-developer-content/message-developer-content.js";import{EuphonyMessageSystemContent as r}from"./components/message-system-content/message-system-content.js";import{EuphonyMessageText as m}from"./components/message-text/message-text.js";import{EuphonyMessageUnsupported as g}from"./components/message-unsupported/message-unsupported.js";import{E as u,b as i,R as d,a as y,g as c,t as C}from"./chunks/harmony-types.js";import{E,f as h,h as l,i as M,j as x,a as S,c as T,g as f,e as j,d as v,s as k,b,u as O}from"./chunks/utils.js";export{u as EUPHONY_MESSAGE_CONTENT_TYPES,s as EuphonyCodex,o as EuphonyConversation,E as EuphonyLitElementWithBlockContents,n as EuphonyMessageCode,p as EuphonyMessageDeveloperContent,r as EuphonyMessageSystemContent,m as EuphonyMessageText,g as EuphonyMessageUnsupported,i as RealAuthor,d as Role,h as arrayToTable,l as blobToBase64,M as createBase64DataURL,x as digestMessage,y as getContentFromContentOrString,c as getContentTypeFromContent,S as getCustomLabelsFromMagicMetadata,T as getDeferredPromise,f as getMarkdownTemplate,e as isCodexSessionJSONL,a as parseCodexSession,t as parseConversationJSONString,j as sharedCollapseBlockContents,v as sharedExpandBlockContents,k as styleToString,C as tryGetContentTypeFromContent,b as updateFloatPosition,O as updatePopperOverlay};
1
+ import{EuphonyCodex as s,i as e,a,b as o,p as t,c as n,d as p}from"./components/codex/codex.js";import{E as r,p as m}from"./chunks/conversation.js";import{EuphonyMessageCode as g}from"./components/message-code/message-code.js";import{EuphonyMessageDeveloperContent as i}from"./components/message-developer-content/message-developer-content.js";import{EuphonyMessageSystemContent as u}from"./components/message-system-content/message-system-content.js";import{EuphonyMessageText as d}from"./components/message-text/message-text.js";import{EuphonyMessageUnsupported as y}from"./components/message-unsupported/message-unsupported.js";import{E as C,b as c,R as E,a as h,g as l,t as S}from"./chunks/harmony-types.js";import{E as M,f as x,h as T,i as f,j,a as v,c as O,g as k,e as N,d as b,s as L,b as B,u as F}from"./chunks/utils.js";export{C as EUPHONY_MESSAGE_CONTENT_TYPES,s as EuphonyCodex,r as EuphonyConversation,M as EuphonyLitElementWithBlockContents,g as EuphonyMessageCode,i as EuphonyMessageDeveloperContent,u as EuphonyMessageSystemContent,d as EuphonyMessageText,y as EuphonyMessageUnsupported,c as RealAuthor,E as Role,x as arrayToTable,T as blobToBase64,f as createBase64DataURL,j as digestMessage,h as getContentFromContentOrString,l as getContentTypeFromContent,v as getCustomLabelsFromMagicMetadata,O as getDeferredPromise,k as getMarkdownTemplate,e as isAgentSessionJSONL,a as isClaudeSessionJSONL,o as isCodexSessionJSONL,t as parseAgentSession,n as parseClaudeSession,p as parseCodexSession,m as parseConversationJSONString,N as sharedCollapseBlockContents,b as sharedExpandBlockContents,L as styleToString,S as tryGetContentTypeFromContent,B as updateFloatPosition,F as updatePopperOverlay};
@@ -4,4 +4,8 @@ export interface CodexSessionParseResult {
4
4
  customLabels: string[][];
5
5
  }
6
6
  export declare const isCodexSessionJSONL: (raw: unknown[]) => boolean;
7
+ export declare const isClaudeSessionJSONL: (raw: unknown[]) => boolean;
8
+ export declare const isAgentSessionJSONL: (raw: unknown[]) => boolean;
7
9
  export declare const parseCodexSession: (raw: unknown[]) => CodexSessionParseResult | null;
10
+ export declare const parseClaudeSession: (raw: unknown[]) => CodexSessionParseResult | null;
11
+ export declare const parseAgentSession: (raw: unknown[]) => CodexSessionParseResult | null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wukazis/euphony",
3
3
  "private": false,
4
- "version": "0.1.46",
4
+ "version": "0.1.47",
5
5
  "type": "module",
6
6
  "types": "./lib/euphony.d.ts",
7
7
  "exports": {
@@ -436,9 +436,14 @@ const DIST_DIR = resolve(PACKAGE_ROOT, "dist");
436
436
  const CODEX_SESSIONS_URL = "codex:sessions";
437
437
  const CODEX_USAGE_URL = "codex:usage";
438
438
  const CODEX_SESSION_URL_PREFIX = "codex:session:";
439
+ const CLAUDE_SESSIONS_URL = "claude:sessions";
440
+ const CLAUDE_SESSION_URL_PREFIX = "claude:session:";
439
441
  const CODEX_SESSIONS_DIR = resolve(
440
442
  process.env.CODEX_SESSIONS_DIR ?? join(process.env.HOME ?? process.cwd(), ".codex", "sessions")
441
443
  );
444
+ const CLAUDE_PROJECTS_DIR = resolve(
445
+ process.env.CLAUDE_PROJECTS_DIR ?? join(process.env.HOME ?? process.cwd(), ".claude", "projects")
446
+ );
442
447
  const MAX_PUBLIC_JSON_BYTES = 25 * 1024 * 1024;
443
448
  const MAX_CODEX_SESSION_BYTES = 25 * 1024 * 1024;
444
449
  const MAX_CODEX_SEARCH_PREVIEW_BYTES = 256 * 1024;
@@ -511,6 +516,21 @@ const parseJsonl = (text) => {
511
516
  }
512
517
  return events;
513
518
  };
519
+ const parseJsonlTolerant = (text) => {
520
+ const events = [];
521
+ for (const line of text.split(/\r?\n/)) {
522
+ const stripped = line.trim();
523
+ if (stripped.length === 0) {
524
+ continue;
525
+ }
526
+ try {
527
+ events.push(JSON.parse(stripped));
528
+ } catch {
529
+ continue;
530
+ }
531
+ }
532
+ return events;
533
+ };
514
534
  const parseJsonOrJsonl = (text) => {
515
535
  const stripped = text.trim();
516
536
  if (stripped.length === 0) {
@@ -533,6 +553,9 @@ const loadJsonlText = async (path) => {
533
553
  const loadJsonlEvents = async (path) => {
534
554
  return parseJsonl(await loadJsonlText(path));
535
555
  };
556
+ const loadJsonlEventsTolerant = async (path) => {
557
+ return parseJsonlTolerant(await loadJsonlText(path));
558
+ };
536
559
  const resolveCodexSessionPath = (sessionRef) => {
537
560
  const relativePath = decodeURIComponent(sessionRef);
538
561
  const candidate = resolve(CODEX_SESSIONS_DIR, relativePath);
@@ -541,6 +564,14 @@ const resolveCodexSessionPath = (sessionRef) => {
541
564
  }
542
565
  return candidate;
543
566
  };
567
+ const resolveClaudeSessionPath = (sessionRef) => {
568
+ const relativePath = decodeURIComponent(sessionRef);
569
+ const candidate = resolve(CLAUDE_PROJECTS_DIR, relativePath);
570
+ if (!isInsideDirectory(CLAUDE_PROJECTS_DIR, candidate) || extname(candidate) !== ".jsonl") {
571
+ throw new HttpError(404, "Claude session not found");
572
+ }
573
+ return candidate;
574
+ };
544
575
  const parseTimestamp = (timestamp) => {
545
576
  if (typeof timestamp !== "string" || timestamp.length === 0) {
546
577
  return null;
@@ -620,6 +651,51 @@ const extractCodexUsageSnapshotFromEvents = (events) => {
620
651
  }
621
652
  return snapshot;
622
653
  };
654
+ const extractClaudeUsageSnapshotFromEvents = (events) => {
655
+ const usage = emptyCodexTokenUsage();
656
+ const seenUsageIds = /* @__PURE__ */ new Set();
657
+ let timestamp = null;
658
+ for (const event of events) {
659
+ if (!event || typeof event !== "object") {
660
+ continue;
661
+ }
662
+ const record = event;
663
+ if (record.type !== "assistant") {
664
+ continue;
665
+ }
666
+ const message = record.message;
667
+ if (!message || typeof message !== "object") {
668
+ continue;
669
+ }
670
+ const messageRecord = message;
671
+ const rawUsage = messageRecord.usage;
672
+ if (!rawUsage || typeof rawUsage !== "object") {
673
+ continue;
674
+ }
675
+ const usageId = typeof messageRecord.id === "string" ? messageRecord.id : typeof record.requestId === "string" ? record.requestId : typeof record.uuid === "string" ? record.uuid : null;
676
+ if (usageId && seenUsageIds.has(usageId)) {
677
+ continue;
678
+ }
679
+ if (usageId) {
680
+ seenUsageIds.add(usageId);
681
+ }
682
+ const usageRecord = rawUsage;
683
+ const inputTokens = numberFromUsageField(usageRecord.input_tokens);
684
+ const cacheCreationTokens = numberFromUsageField(
685
+ usageRecord.cache_creation_input_tokens
686
+ );
687
+ const cacheReadTokens = numberFromUsageField(
688
+ usageRecord.cache_read_input_tokens
689
+ );
690
+ const outputTokens = numberFromUsageField(usageRecord.output_tokens);
691
+ usage.input_tokens += inputTokens;
692
+ usage.cached_input_tokens += cacheCreationTokens + cacheReadTokens;
693
+ usage.output_tokens += outputTokens;
694
+ usage.total_tokens += inputTokens + cacheCreationTokens + cacheReadTokens + outputTokens;
695
+ timestamp = parseTimestamp(record.timestamp) ?? timestamp;
696
+ }
697
+ return usage.total_tokens > 0 ? { usage, timestamp } : null;
698
+ };
623
699
  const extractTextFromContent = (content) => {
624
700
  if (typeof content === "string") {
625
701
  const stripped = content.trim();
@@ -655,6 +731,12 @@ const isDisplayableFirstMessage = (text) => {
655
731
  "<collaboration_mode>",
656
732
  "<model_switch>",
657
733
  "<environment_context>",
734
+ "<local-command-caveat>",
735
+ "<local-command-stdout>",
736
+ "<local-command-stderr>",
737
+ "<command-name>",
738
+ "<command-message>",
739
+ "<command-args>",
658
740
  "# AGENTS.md instructions"
659
741
  ].some((prefix) => stripped.startsWith(prefix));
660
742
  };
@@ -665,6 +747,7 @@ const cleanSummaryText = (text, maxLength = 240) => {
665
747
  return cleaned.length <= maxLength ? cleaned : `${cleaned.slice(0, maxLength - 3).trimEnd()}...`;
666
748
  };
667
749
  const relativeCodexPath = (path) => resolve(path).slice(resolve(CODEX_SESSIONS_DIR).length + 1).split(sep).join("/");
750
+ const relativeClaudePath = (path) => resolve(path).slice(resolve(CLAUDE_PROJECTS_DIR).length + 1).split(sep).join("/");
668
751
  const normalizeCodexSearchQuery = (query) => query.trim().toLowerCase().split(/\s+/).filter(Boolean);
669
752
  const extractDisplayableUserMessage = (record) => {
670
753
  const payload = record.payload;
@@ -680,6 +763,21 @@ const extractDisplayableUserMessage = (record) => {
680
763
  }
681
764
  return candidate.length > 0 && isDisplayableFirstMessage(candidate) ? candidate : "";
682
765
  };
766
+ const extractClaudeDisplayableUserMessage = (record) => {
767
+ if (record.type !== "user" || record.isMeta === true) {
768
+ return "";
769
+ }
770
+ const message = record.message;
771
+ if (!message || typeof message !== "object") {
772
+ return "";
773
+ }
774
+ const messageRecord = message;
775
+ if (messageRecord.role !== "user") {
776
+ return "";
777
+ }
778
+ const candidate = extractTextFromContent(messageRecord.content) ?? "";
779
+ return candidate.length > 0 && isDisplayableFirstMessage(candidate) ? candidate : "";
780
+ };
683
781
  const readCodexSessionFirstMessage = async (path) => {
684
782
  const fileStat = await stat(path);
685
783
  const previewByteLength = Math.min(
@@ -727,6 +825,53 @@ const readCodexSessionFirstMessage = async (path) => {
727
825
  }
728
826
  return "";
729
827
  };
828
+ const readClaudeSessionFirstMessage = async (path) => {
829
+ const fileStat = await stat(path);
830
+ const previewByteLength = Math.min(
831
+ fileStat.size,
832
+ MAX_CODEX_SEARCH_PREVIEW_BYTES
833
+ );
834
+ const fileHandle = await open(path, "r");
835
+ let text = "";
836
+ try {
837
+ const buffer = Buffer.alloc(previewByteLength);
838
+ const { bytesRead } = await fileHandle.read(
839
+ buffer,
840
+ 0,
841
+ previewByteLength,
842
+ 0
843
+ );
844
+ text = stripBom(buffer.subarray(0, bytesRead).toString("utf8"));
845
+ } finally {
846
+ await fileHandle.close();
847
+ }
848
+ const lines = text.split(/\r?\n/);
849
+ if (fileStat.size > previewByteLength) {
850
+ lines.pop();
851
+ }
852
+ for (const line of lines) {
853
+ const stripped = line.trim();
854
+ if (stripped.length === 0) {
855
+ continue;
856
+ }
857
+ let event;
858
+ try {
859
+ event = JSON.parse(stripped);
860
+ } catch {
861
+ continue;
862
+ }
863
+ if (!event || typeof event !== "object") {
864
+ continue;
865
+ }
866
+ const candidate = extractClaudeDisplayableUserMessage(
867
+ event
868
+ );
869
+ if (candidate.length > 0) {
870
+ return cleanSummaryText(candidate, 1e3);
871
+ }
872
+ }
873
+ return "";
874
+ };
730
875
  const readCodexUsageSnapshot = async (path) => {
731
876
  const fileStat = await stat(path);
732
877
  if (fileStat.size === 0) {
@@ -792,6 +937,18 @@ const codexSessionMatchesSearch = async (path, searchTerms) => {
792
937
  ${await readCodexSessionFirstMessage(path)}`.toLowerCase();
793
938
  return searchTerms.every((term) => searchableText.includes(term));
794
939
  };
940
+ const claudeSessionMatchesSearch = async (path, searchTerms) => {
941
+ if (searchTerms.length === 0) {
942
+ return true;
943
+ }
944
+ const relativePath = relativeClaudePath(path).toLowerCase();
945
+ if (searchTerms.every((term) => relativePath.includes(term))) {
946
+ return true;
947
+ }
948
+ const searchableText = `${relativePath}
949
+ ${await readClaudeSessionFirstMessage(path)}`.toLowerCase();
950
+ return searchTerms.every((term) => searchableText.includes(term));
951
+ };
795
952
  const summarizeCodexSession = async (path) => {
796
953
  const events = await loadJsonlEvents(path);
797
954
  const relativePath = relativeCodexPath(path);
@@ -849,6 +1006,56 @@ const summarizeCodexSession = async (path) => {
849
1006
  usage_timestamp: usageSnapshot?.timestamp?.toISOString() ?? null
850
1007
  };
851
1008
  };
1009
+ const summarizeClaudeSession = async (path) => {
1010
+ const events = await loadJsonlEventsTolerant(path);
1011
+ const relativePath = relativeClaudePath(path);
1012
+ const encodedPath = encodeURIComponent(relativePath);
1013
+ let sessionTimestamp = null;
1014
+ let firstMessage = "";
1015
+ let aiTitle = "";
1016
+ const usageSnapshot = extractClaudeUsageSnapshotFromEvents(events);
1017
+ for (const event of events) {
1018
+ if (!event || typeof event !== "object") {
1019
+ continue;
1020
+ }
1021
+ const record = event;
1022
+ const eventTimestamp = parseTimestamp(record.timestamp) ?? (record.snapshot && typeof record.snapshot === "object" ? parseTimestamp(record.snapshot.timestamp) : null);
1023
+ if (sessionTimestamp === null && eventTimestamp !== null) {
1024
+ sessionTimestamp = eventTimestamp;
1025
+ }
1026
+ if (record.type === "ai-title" && typeof record.aiTitle === "string" && aiTitle.length === 0) {
1027
+ aiTitle = cleanSummaryText(record.aiTitle);
1028
+ }
1029
+ if (firstMessage.length > 0) {
1030
+ continue;
1031
+ }
1032
+ const candidate = extractClaudeDisplayableUserMessage(record);
1033
+ if (candidate.length > 0 && isDisplayableFirstMessage(candidate)) {
1034
+ firstMessage = cleanSummaryText(candidate);
1035
+ }
1036
+ }
1037
+ if (firstMessage.length === 0) {
1038
+ firstMessage = aiTitle || "(no user message)";
1039
+ }
1040
+ if (sessionTimestamp === null) {
1041
+ const fileStat = await stat(path);
1042
+ sessionTimestamp = new Date(fileStat.mtimeMs);
1043
+ }
1044
+ const ageSeconds = Math.max(
1045
+ 0,
1046
+ Math.floor((Date.now() - sessionTimestamp.getTime()) / 1e3)
1047
+ );
1048
+ return {
1049
+ path: relativePath,
1050
+ load_url: `${CLAUDE_SESSION_URL_PREFIX}${encodedPath}`,
1051
+ first_message: firstMessage,
1052
+ timestamp: sessionTimestamp.toISOString(),
1053
+ age_seconds: ageSeconds,
1054
+ event_count: events.length,
1055
+ token_usage: usageSnapshot?.usage ?? null,
1056
+ usage_timestamp: usageSnapshot?.timestamp?.toISOString() ?? null
1057
+ };
1058
+ };
852
1059
  const findJsonlFiles = async (root) => {
853
1060
  const files = [];
854
1061
  const walk = async (directory) => {
@@ -933,6 +1140,58 @@ const getCodexSessions = async (offset, limit, searchQuery = "") => {
933
1140
  resolvedURL: CODEX_SESSIONS_URL
934
1141
  };
935
1142
  };
1143
+ const getClaudeSessions = async (offset, limit, searchQuery = "") => {
1144
+ const searchTerms = normalizeCodexSearchQuery(searchQuery);
1145
+ try {
1146
+ const rootStat = await stat(CLAUDE_PROJECTS_DIR);
1147
+ if (!rootStat.isDirectory()) {
1148
+ throw new Error("Not a directory");
1149
+ }
1150
+ } catch {
1151
+ return {
1152
+ data: [],
1153
+ offset,
1154
+ limit,
1155
+ total: 0,
1156
+ isFiltered: searchTerms.length > 0,
1157
+ matchedCount: 0,
1158
+ resolvedURL: CLAUDE_SESSIONS_URL
1159
+ };
1160
+ }
1161
+ const files = await findJsonlFiles(CLAUDE_PROJECTS_DIR);
1162
+ const matchedFiles = [];
1163
+ if (searchTerms.length === 0) {
1164
+ matchedFiles.push(...files);
1165
+ } else {
1166
+ for (const path of files) {
1167
+ try {
1168
+ if (await claudeSessionMatchesSearch(path, searchTerms)) {
1169
+ matchedFiles.push(path);
1170
+ }
1171
+ } catch (error) {
1172
+ console.error(`Failed to search Claude session file: ${path}`, error);
1173
+ }
1174
+ }
1175
+ }
1176
+ const pageFiles = matchedFiles.slice(offset, offset + limit);
1177
+ const summaries = [];
1178
+ for (const path of pageFiles) {
1179
+ try {
1180
+ summaries.push(await summarizeClaudeSession(path));
1181
+ } catch (error) {
1182
+ console.error(`Failed to load Claude session file: ${path}`, error);
1183
+ }
1184
+ }
1185
+ return {
1186
+ data: summaries,
1187
+ offset,
1188
+ limit,
1189
+ total: files.length,
1190
+ isFiltered: searchTerms.length > 0,
1191
+ matchedCount: matchedFiles.length,
1192
+ resolvedURL: CLAUDE_SESSIONS_URL
1193
+ };
1194
+ };
936
1195
  const buildCodexUsageDays = async (range) => {
937
1196
  try {
938
1197
  const rootStat = await stat(CODEX_SESSIONS_DIR);
@@ -1034,6 +1293,29 @@ const getCodexSession = async (sessionRef, offset, limit) => {
1034
1293
  resolvedURL
1035
1294
  };
1036
1295
  };
1296
+ const getClaudeSession = async (sessionRef, offset, limit) => {
1297
+ const path = resolveClaudeSessionPath(sessionRef);
1298
+ try {
1299
+ const fileStat = await stat(path);
1300
+ if (!fileStat.isFile()) {
1301
+ throw new Error("Not a file");
1302
+ }
1303
+ } catch (error) {
1304
+ throw new HttpError(404, "Claude session not found");
1305
+ }
1306
+ const events = await loadJsonlEventsTolerant(path);
1307
+ const relativePath = relativeClaudePath(path);
1308
+ const resolvedURL = `${CLAUDE_SESSION_URL_PREFIX}${encodeURIComponent(relativePath)}`;
1309
+ return {
1310
+ data: events.slice(offset, offset + limit),
1311
+ offset,
1312
+ limit,
1313
+ total: events.length,
1314
+ isFiltered: false,
1315
+ matchedCount: events.length,
1316
+ resolvedURL
1317
+ };
1318
+ };
1037
1319
  const readRemoteResponseBody = async (response) => {
1038
1320
  const chunks = [];
1039
1321
  let totalBytes = 0;
@@ -1475,6 +1757,15 @@ const handleBlobJsonl = async (req, res, url) => {
1475
1757
  );
1476
1758
  return;
1477
1759
  }
1760
+ if (blobURL === CLAUDE_SESSIONS_URL) {
1761
+ await sendJson(
1762
+ req,
1763
+ res,
1764
+ 200,
1765
+ await getClaudeSessions(offset, limit, codexSearchQuery)
1766
+ );
1767
+ return;
1768
+ }
1478
1769
  if (blobURL === CODEX_USAGE_URL) {
1479
1770
  await sendJson(
1480
1771
  req,
@@ -1497,6 +1788,19 @@ const handleBlobJsonl = async (req, res, url) => {
1497
1788
  );
1498
1789
  return;
1499
1790
  }
1791
+ if (blobURL.startsWith(CLAUDE_SESSION_URL_PREFIX)) {
1792
+ await sendJson(
1793
+ req,
1794
+ res,
1795
+ 200,
1796
+ await getClaudeSession(
1797
+ blobURL.slice(CLAUDE_SESSION_URL_PREFIX.length),
1798
+ offset,
1799
+ limit
1800
+ )
1801
+ );
1802
+ return;
1803
+ }
1500
1804
  await sendJson(
1501
1805
  req,
1502
1806
  res,
@@ -1596,6 +1900,9 @@ const startServer = (options = {}) => {
1596
1900
  console.log(
1597
1901
  `Local Codex sessions: http://${host}:${port}/?path=codex%3Asessions`
1598
1902
  );
1903
+ console.log(
1904
+ `Local Claude sessions: http://${host}:${port}/?path=claude%3Asessions`
1905
+ );
1599
1906
  console.log(`Codex usage: http://${host}:${port}/?path=codex%3Ausage`);
1600
1907
  });
1601
1908
  return server;
@@ -1,2 +0,0 @@
1
- (function(){"use strict";const l=new Set(["session_meta","response_item","event_msg","turn_context","compacted"]),u=new Set(["message","function_call","function_call_output","custom_tool_call","custom_tool_call_output","reasoning"]),p=new Set(["user_message","agent_message","agent_reasoning","context_compacted","turn_aborted","token_count"]),f=e=>typeof e=="object"&&e!==null,y=e=>{try{return JSON.parse(e)}catch{return null}},d=e=>{const t=[];for(const s of e){if(typeof s=="string"){const r=y(s);f(r)&&t.push(r);continue}f(s)&&t.push(s)}return t},m=e=>{if(typeof e.type!="string")return!1;if(l.has(e.type)){if(e.type==="response_item"){const t=e.payload&&typeof e.payload.type=="string"?e.payload.type:null;return t?u.has(t):!0}if(e.type==="event_msg"){const t=e.payload&&typeof e.payload.type=="string"?e.payload.type:null;return t?p.has(t):!0}return!0}return e.type.startsWith("response_")||e.type.startsWith("event_")},g=e=>{if(!Array.isArray(e)||e.length===0)return!1;const t=d(e).filter(m);return t.length===0||t.length/e.length<.6?!1:t.some(r=>r.type==="session_meta")?!0:t.filter(r=>l.has(r.type??"")).length/t.length>=.6},c=e=>typeof e!="object"||e===null?!1:"messages"in e&&Array.isArray(e.messages),_=e=>{let t=null;if(e.length>0&&typeof e[0]=="object"&&!c(e[0])&&(t=e),e.length>0&&typeof e[0]=="string"){let s=!1;try{const r=JSON.parse(e[0]);c(r)&&(s=!0)}catch{s=!0}if(!s){t=[];for(const r of e){const o=JSON.parse(r);t.push(o)}}}if(t!==null){let s=null,r=!1;for(const o in t[0])if(typeof t[0][o]=="string")try{const n=JSON.parse(t[0][o]);if(c(n)){s=o,r=!0;break}}catch{continue}else if(c(t[0][o])){s=o;break}if(s!==null){const o=[];for(const n of t){const a=r?JSON.parse(n[s]):n[s];a.metadata??={};for(const i in n)i!==s&&(a.metadata[`euphonyTransformed-${i}`]=n[i]);o.push(a)}return o}}return null},h=e=>{const t=[];for(const[s,r]of e.entries())if(typeof r=="string"){const o=JSON.parse(r);let n=r;o.conversation_id!==void 0&&o.id===void 0&&(o.id=o.conversation_id,n=JSON.stringify(o)),e[s]=n,t.push(Array.isArray(o.messages))}else{const o=r;o.conversation_id!==void 0&&o.id===void 0&&(o.id=o.conversation_id),e[s]=o,t.push(Array.isArray(o.messages))}return t.every(Boolean)},S=e=>{const t=[];try{const s=JSON.parse(e);return t.push(s),t}catch{for(const r of e.split(`
2
- `))try{t.push(JSON.parse(r))}catch{}}return t},N=e=>{let t=S(e);if(t.length===0)throw new Error("Failed to read any JSON or JSONL data.");if(g(t))return{dataType:"codex",codexSessionData:t};const s=_(t);if(s&&(t=s),!h(t))return{dataType:"json",jsonData:t};const r=[];for(const o of t)typeof o=="string"?r.push(JSON.parse(o)):r.push(o);return{dataType:"conversation",conversationData:r}};self.onmessage=async e=>{if(e.data.command!=="startParseData"){console.error("Worker: unknown message",e.data.command);return}const{requestID:t,sourceName:s,sourceText:r,sourceFile:o}=e.data.payload;try{const n=r??await o?.text();if(n===void 0)throw new Error("No source text or file was provided.");const a=N(n),i={command:"finishParseData",payload:{requestID:t,sourceName:s,...a}};postMessage(i)}catch(n){const a={command:"error",payload:{requestID:t,sourceName:s,message:n instanceof Error?n.message:String(n)}};postMessage(a)}}})();