@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/README.md +19 -1
- package/dist/assets/local-data-worker-DTkq_bcN.js +2 -0
- package/dist/assets/{main-DS3CAMrw.js → main-BMLAkPsC.js} +419 -417
- package/dist/index.html +1 -1
- package/lib/components/app/app.d.ts +2 -1
- package/lib/components/codex/codex.js +5 -5
- package/lib/euphony.js +1 -1
- package/lib/utils/codex-session.d.ts +4 -0
- package/package.json +1 -1
- package/server-dist/node-main.js +307 -0
- package/dist/assets/local-data-worker-CHLGzNeW.js +0 -2
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-
|
|
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
|
|
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()}};
|
|
35
|
-
${t(
|
|
36
|
-
`],
|
|
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
|
|
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
package/server-dist/node-main.js
CHANGED
|
@@ -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)}}})();
|