purplemux 0.2.3 → 0.2.5
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +84 -79
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/routes-manifest.json +47 -7
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-qfa-x._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__00nrs0h._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__014kwh_._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__02v.lnb._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__03x8rds._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__04cr_m8._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06j59n1._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__07gfnob._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__07xijrd._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__08tdlcm._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__09_6_.~._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__09gkm_6._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_uldiw._.js +15 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0a4t.z5._.js +52 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ar~lbk._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0bo-su1._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0c-bb5d._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0dvelfj._.js +52 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e.7isl._.js +5 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0gcec.8._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0i1m1v9._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ihlt6s._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0kd7wei._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0mky_tj._.js +35 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oiiu_o._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0oqgn2h._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qvunc5._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0r77-u1._.js +52 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rhb4gk._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0vi8p6v._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w0wz~a._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0w3m-oz._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0zh8xur._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0~iu3jq._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__11xw16l._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__12nr~pb._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_0v81st0._.js → _0.pa6m1._.js} +1 -1
- package/.next/standalone/.next/server/chunks/_05ib7yu._.js +3 -0
- package/.next/standalone/.next/server/chunks/{_0ll-5~7._.js → _09y8.gr._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{_0.5a4bu._.js → _0mwq11h._.js} +1 -1
- package/.next/standalone/.next/server/chunks/_0y4b49w._.js +3 -0
- package/.next/standalone/.next/server/chunks/_0zcb2f0._.js +3 -0
- package/.next/standalone/.next/server/chunks/{node_modules_ws_124xx2_._.js → _106a~ya._.js} +2 -2
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_pages-api_00-t8sq.js +1 -1
- package/.next/standalone/.next/server/chunks/src_lib_00fhuqb._.js +3 -0
- package/.next/standalone/.next/server/chunks/src_lib_0dvlmi4._.js +15 -0
- package/.next/standalone/.next/server/chunks/src_lib_0ojl2hg._.js +52 -0
- package/.next/standalone/.next/server/chunks/src_lib_layout-store_ts_0fanx7c._.js +1 -1
- package/.next/standalone/.next/server/chunks/src_lib_status-manager_ts_003hlhy._.js +1 -13
- package/.next/standalone/.next/server/chunks/src_lib_workspace-store_ts_0vqt073._.js +1 -40
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0ap_bxo._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0t9ru13._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0-9ezhb._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_101yu0w._.js → _0-kyl7i._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_003~2_g._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_00zsk6a._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_017c8be._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_01eupc5._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_05bq96_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0c1yivm._.js → _070zc_o._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_07jud1p._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0_sz4y0._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0_uqm-4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0ftinmo._.js → _0ed.wmt._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0egedv6._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0-ug5tr._.js → _0fota2a._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0h4kenr._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0ihbr8k._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0lpqhot._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0mltzed._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0nbe6mv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0pg-q18._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0pp3e5j._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0ttiybw._.js +18 -0
- package/.next/standalone/.next/server/chunks/ssr/_0w.1ts8._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0w6jc9p._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_0xp3-y6._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0y7o.rg._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0ytb_c0._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0ozy~xe._.js → _0~hz1fq._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_1289s74._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_02jzd5g._.js → _13gee5r._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/messages_de_terminal_json_[json]_cjs_05jirtw._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_de_webBrowser_json_[json]_cjs_0wfz-ul._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_en_terminal_json_[json]_cjs_0~ls3g-._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_en_webBrowser_json_[json]_cjs_0oirdko._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_es_terminal_json_[json]_cjs_04g2b1_._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_es_webBrowser_json_[json]_cjs_0w_no_c._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_fr_terminal_json_[json]_cjs_0uhakya._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_fr_webBrowser_json_[json]_cjs_00tbjsf._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_ja_terminal_json_[json]_cjs_0776z4f._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_ja_webBrowser_json_[json]_cjs_0tp7~.s._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_ko_terminal_json_[json]_cjs_06611.3._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_ko_webBrowser_json_[json]_cjs_0ibtysu._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_pt-BR_terminal_json_[json]_cjs_034yyg9._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_pt-BR_webBrowser_json_[json]_cjs_0eu9fbw._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_ru_terminal_json_[json]_cjs_0emk8~n._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_ru_webBrowser_json_[json]_cjs_113-x8n._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_tr_terminal_json_[json]_cjs_0xpix8g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_tr_webBrowser_json_[json]_cjs_12ziott._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_zh-CN_terminal_json_[json]_cjs_12vwl8a._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_zh-CN_webBrowser_json_[json]_cjs_0e5lp-q._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_zh-TW_terminal_json_[json]_cjs_0spg5z5._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/messages_zh-TW_webBrowser_json_[json]_cjs_0u_cpuv._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/node_modules_@base-ui_030gj7p._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_@base-ui_04tdvwp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_@base-ui_0bdox2f._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{src_components_12mrirb._.js → src_components_0_nzv1r._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{src_components_0_aiven._.js → src_components_0bqqfju._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{src_components_0geizrr._.js → src_components_0rqxlj-._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{src_components_11_1i~k._.js → src_components_0s~.n3e._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_components_features_mobile_mobile-terminal-page_tsx_02chzch._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_components_features_workspace_terminal-page_tsx_0wuq-jl._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_lib_message-namespaces_ts_0wjpvu5._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_lib_workspace-store_ts_00x-rec._.js +10 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +84 -79
- package/.next/standalone/.next/server/pages/_app/build-manifest.json +4 -4
- package/.next/standalone/.next/server/pages/_app/client-build-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/_app.js +2 -2
- package/.next/standalone/.next/server/pages/_app.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/auth/login.js +2 -2
- package/.next/standalone/.next/server/pages/api/auth/login.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/auth/preflight.js +2 -2
- package/.next/standalone/.next/server/pages/api/auth/preflight.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/auth/setup.js +2 -2
- package/.next/standalone/.next/server/pages/api/auth/setup.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/check-claude.js +5 -1
- package/.next/standalone/.next/server/pages/api/check-claude.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/console/pages-manifest.json +3 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/console.js +8 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/console.js.map +5 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/console.js.nft.json +1 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/eval/pages-manifest.json +3 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/eval.js +8 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/eval.js.map +5 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/eval.js.nft.json +1 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/network/pages-manifest.json +3 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/network.js +8 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/network.js.map +5 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/network.js.nft.json +1 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/screenshot/pages-manifest.json +3 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/screenshot.js +8 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/screenshot.js.map +5 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/screenshot.js.nft.json +1 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/url/pages-manifest.json +3 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/url.js +8 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/url.js.map +5 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/browser/url.js.nft.json +1 -0
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/result.js +3 -3
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/result.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/send.js +3 -3
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/send.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/status.js +3 -3
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId]/status.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId].js +3 -3
- package/.next/standalone/.next/server/pages/api/cli/tabs/[tabId].js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/cli/tabs.js +3 -3
- package/.next/standalone/.next/server/pages/api/cli/tabs.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/cli/workspaces.js +2 -2
- package/.next/standalone/.next/server/pages/api/cli/workspaces.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/config.js +2 -2
- package/.next/standalone/.next/server/pages/api/config.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/layout/diff.js +1 -1
- package/.next/standalone/.next/server/pages/api/layout/diff.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/layout/pane/[paneId]/tabs/[tabId]/move.js +2 -2
- package/.next/standalone/.next/server/pages/api/layout/pane/[paneId]/tabs/[tabId]/move.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/layout/pane/[paneId]/tabs/[tabId].js +5 -5
- package/.next/standalone/.next/server/pages/api/layout/pane/[paneId]/tabs/[tabId].js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/layout/pane/[paneId]/tabs/order.js +2 -2
- package/.next/standalone/.next/server/pages/api/layout/pane/[paneId]/tabs/order.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/layout/pane/[paneId]/tabs.js +5 -5
- package/.next/standalone/.next/server/pages/api/layout/pane/[paneId]/tabs.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/layout/pane/[paneId].js +2 -2
- package/.next/standalone/.next/server/pages/api/layout/pane/[paneId].js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/layout/pane.js +2 -2
- package/.next/standalone/.next/server/pages/api/layout/pane.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/layout.js +2 -2
- package/.next/standalone/.next/server/pages/api/layout.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/message-history.js +2 -2
- package/.next/standalone/.next/server/pages/api/message-history.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/preflight/runtime.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/stats/daily-report/cache.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/stats/daily-report/generate.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/stats/daily-report/list.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/stats/projects.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/stats/sessions.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/stats/uptime.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/status/hook.js +6 -6
- package/.next/standalone/.next/server/pages/api/status/hook.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/status/statusline.js +6 -6
- package/.next/standalone/.next/server/pages/api/status/statusline.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/timeline/sessions.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/tmux/permission-options.js +3 -2
- package/.next/standalone/.next/server/pages/api/tmux/permission-options.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/tmux/plan-options.js +3 -2
- package/.next/standalone/.next/server/pages/api/tmux/plan-options.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/tmux/recover-unknown.js +5 -5
- package/.next/standalone/.next/server/pages/api/tmux/recover-unknown.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/tmux/reset.js +5 -5
- package/.next/standalone/.next/server/pages/api/tmux/reset.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/workspace/[workspaceId].js +2 -2
- package/.next/standalone/.next/server/pages/api/workspace/[workspaceId].js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/workspace/active.js +2 -2
- package/.next/standalone/.next/server/pages/api/workspace/active.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/workspace/group/[groupId].js +2 -2
- package/.next/standalone/.next/server/pages/api/workspace/group/[groupId].js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/workspace/group/reorder.js +2 -2
- package/.next/standalone/.next/server/pages/api/workspace/group/reorder.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/workspace/group.js +2 -2
- package/.next/standalone/.next/server/pages/api/workspace/group.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/workspace/reorder.js +2 -2
- package/.next/standalone/.next/server/pages/api/workspace/reorder.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/workspace/validate.js +2 -2
- package/.next/standalone/.next/server/pages/api/workspace/validate.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/api/workspace.js +5 -5
- package/.next/standalone/.next/server/pages/api/workspace.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/de/404.html +1 -1
- package/.next/standalone/.next/server/pages/de/500.html +1 -1
- package/.next/standalone/.next/server/pages/en/404.html +1 -1
- package/.next/standalone/.next/server/pages/en/500.html +1 -1
- package/.next/standalone/.next/server/pages/es/404.html +1 -1
- package/.next/standalone/.next/server/pages/es/500.html +1 -1
- package/.next/standalone/.next/server/pages/fr/404.html +1 -1
- package/.next/standalone/.next/server/pages/fr/500.html +1 -1
- package/.next/standalone/.next/server/pages/index/build-manifest.json +16 -15
- package/.next/standalone/.next/server/pages/index/client-build-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/index/react-loadable-manifest.json +9 -9
- package/.next/standalone/.next/server/pages/index.js +13 -12
- package/.next/standalone/.next/server/pages/index.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/ja/404.html +1 -1
- package/.next/standalone/.next/server/pages/ja/500.html +1 -1
- package/.next/standalone/.next/server/pages/ko/404.html +1 -1
- package/.next/standalone/.next/server/pages/ko/500.html +1 -1
- package/.next/standalone/.next/server/pages/login/build-manifest.json +9 -8
- package/.next/standalone/.next/server/pages/login/client-build-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/login.js +8 -7
- package/.next/standalone/.next/server/pages/login.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/pt-BR/404.html +1 -1
- package/.next/standalone/.next/server/pages/pt-BR/500.html +1 -1
- package/.next/standalone/.next/server/pages/reports/build-manifest.json +14 -13
- package/.next/standalone/.next/server/pages/reports/client-build-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/reports/react-loadable-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/reports.js +15 -14
- package/.next/standalone/.next/server/pages/reports.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/reset.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/ru/404.html +1 -1
- package/.next/standalone/.next/server/pages/ru/500.html +1 -1
- package/.next/standalone/.next/server/pages/stats/build-manifest.json +15 -14
- package/.next/standalone/.next/server/pages/stats/client-build-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/stats/react-loadable-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/stats.js +13 -12
- package/.next/standalone/.next/server/pages/stats.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/tools-required/build-manifest.json +6 -6
- package/.next/standalone/.next/server/pages/tools-required/client-build-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/tools-required.js +4 -3
- package/.next/standalone/.next/server/pages/tools-required.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/tr/404.html +1 -1
- package/.next/standalone/.next/server/pages/tr/500.html +1 -1
- package/.next/standalone/.next/server/pages/webview/build-manifest.json +17 -16
- package/.next/standalone/.next/server/pages/webview/client-build-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/webview/react-loadable-manifest.json +1 -1
- package/.next/standalone/.next/server/pages/webview.js +13 -12
- package/.next/standalone/.next/server/pages/webview.js.nft.json +1 -1
- package/.next/standalone/.next/server/pages/zh-CN/404.html +1 -1
- package/.next/standalone/.next/server/pages/zh-CN/500.html +1 -1
- package/.next/standalone/.next/server/pages/zh-TW/404.html +1 -1
- package/.next/standalone/.next/server/pages/zh-TW/500.html +1 -1
- package/.next/standalone/.next/server/pages-manifest.json +5 -0
- package/.next/standalone/.next/static/chunks/{11tnyk0iystp1.js → 0--r5y9cw3.0z.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0w1ldes9u.mhe.js → 0-8v00lvs9_s0.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0oy5nvsojxb2i.js → 0-rskn1p_p.qb.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0h7n~i8gk~-a2.js → 0.4j~sc70rsrv.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0g0xm7shj062y.js → 001_h.~thz3j3.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0ynitrrzdxcnh.js → 00gxls80r--_0.js} +9 -8
- package/.next/standalone/.next/static/chunks/{121i8u8byuotx.js → 01rmp.m_ls7z_.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0312riii_sl_1.js → 02o84r01jsc.z.js} +1 -1
- package/.next/standalone/.next/static/chunks/039-4cgt.dvjg.js +1 -0
- package/.next/standalone/.next/static/chunks/03t01d94eyxh2.js +1 -0
- package/.next/standalone/.next/static/chunks/04hn532e1vcts.js +1 -0
- package/.next/standalone/.next/static/chunks/05o56eq7.f3q2.js +1 -0
- package/.next/standalone/.next/static/chunks/{0x.dxrfqc.ix9.js → 0628quqp4ggh9.js} +1 -1
- package/.next/standalone/.next/static/chunks/06ak74s111wx-.js +1 -0
- package/.next/standalone/.next/static/chunks/07yop4v9xcwb9.js +1 -0
- package/.next/standalone/.next/static/chunks/08d6vee5l~1wn.js +1 -0
- package/.next/standalone/.next/static/chunks/09o2m0ingey8v.js +1 -0
- package/.next/standalone/.next/static/chunks/{0d5b6a8r1tk95.js → 09s7ub8rgh~u-.js} +1 -1
- package/.next/standalone/.next/static/chunks/{02su2lz3i24nj.js → 09zxrli-btome.js} +1 -1
- package/.next/standalone/.next/static/chunks/0_7~rgbkmfo_p.js +1 -0
- package/.next/standalone/.next/static/chunks/0a1xt.enr50l5.js +1 -0
- package/.next/standalone/.next/static/chunks/{0-p.9vdmq-_co.js → 0am7rh7xdv5lw.js} +4 -4
- package/.next/standalone/.next/static/chunks/{13u3mwvwu3kc..js → 0apqp49tgoz~z.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0_67ozmvnvz16.js → 0bx27stclo8g5.js} +1 -1
- package/.next/standalone/.next/static/chunks/0ddh4qmi2awdq.js +1 -0
- package/.next/standalone/.next/static/chunks/0dwxj97xm70gv.js +1 -0
- package/.next/standalone/.next/static/chunks/0f3q6qhpk.i_r.js +1 -0
- package/.next/standalone/.next/static/chunks/{07.5h~7~q9~1e.js → 0f_7vuqno92ye.js} +1 -1
- package/.next/standalone/.next/static/chunks/0fsldybjx443~.js +1 -0
- package/.next/standalone/.next/static/chunks/0gfhd.gsbt9qg.js +1 -0
- package/.next/standalone/.next/static/chunks/0hv4~cszgs3im.js +1 -0
- package/.next/standalone/.next/static/chunks/0ic.mcu6-9din.js +1 -0
- package/.next/standalone/.next/static/chunks/0k4b.~5c4nvtm.js +1 -0
- package/.next/standalone/.next/static/chunks/0kqxfunh4731g.js +1 -0
- package/.next/standalone/.next/static/chunks/{0fn24h74ta78c.js → 0k~crb4s4z7gy.js} +1 -1
- package/.next/standalone/.next/static/chunks/0lrjdru2bjp.x.js +1 -0
- package/.next/standalone/.next/static/chunks/0lujhtd04orw5.js +1 -0
- package/.next/standalone/.next/static/chunks/0mwkajcworx_-.js +1 -0
- package/.next/standalone/.next/static/chunks/0nqofghl.17w_.js +1 -0
- package/.next/standalone/.next/static/chunks/0o1lsz827g-q_.js +1 -0
- package/.next/standalone/.next/static/chunks/0qqsez8ir1.fs.js +1 -0
- package/.next/standalone/.next/static/chunks/{0_htal_j04mkg.js → 0r3lm4_qr3y.a.js} +1 -1
- package/.next/standalone/.next/static/chunks/{06vxx-4w.uvj0.js → 0rahh2v7~f4tj.js} +17 -16
- package/.next/standalone/.next/static/chunks/{0pxbg4s8a4y54.js → 0rb41e1r1urhx.js} +1 -1
- package/.next/standalone/.next/static/chunks/0uii4rziiew3_.js +1 -0
- package/.next/standalone/.next/static/chunks/0vtkq~yk_1~gv.js +1 -0
- package/.next/standalone/.next/static/chunks/0vx2hhzc46h_7.js +1 -0
- package/.next/standalone/.next/static/chunks/0x_.wshvak6zp.js +1 -0
- package/.next/standalone/.next/static/chunks/0xa1.t1vj1mw0.js +1 -0
- package/.next/standalone/.next/static/chunks/0xpr6vw-p8oxk.js +1 -0
- package/.next/standalone/.next/static/chunks/{0uzs3jw4-93ly.js → 0zih6-g65txpb.js} +1 -1
- package/.next/standalone/.next/static/chunks/0zn1jt__-t51p.js +1 -0
- package/.next/standalone/.next/static/chunks/0zrhxc96zwjuv.js +1 -0
- package/.next/standalone/.next/static/chunks/0z~yy1lqrvp_a.js +1 -0
- package/.next/standalone/.next/static/chunks/0~cgr_3dpx0vl.js +1 -0
- package/.next/standalone/.next/static/chunks/{0te1jnz5.rt.r.js → 0~g0t2q6o3r-7.js} +6 -6
- package/.next/standalone/.next/static/chunks/0~vfb0ylk_9au.js +16 -0
- package/.next/standalone/.next/static/chunks/109f1pvy3e_vd.js +1 -0
- package/.next/standalone/.next/static/chunks/10em078j6cul4.js +1 -0
- package/.next/standalone/.next/static/chunks/{0a9t5laucshuf.js → 10todxw2604hk.js} +1 -1
- package/.next/standalone/.next/static/chunks/{05zne4hn1v65k.js → 10uz6x-h~dv0..js} +14 -13
- package/.next/standalone/.next/static/chunks/{0~nexgc77r_hk.js → 13q-ol3bkz8ft.js} +16 -15
- package/.next/standalone/.next/static/chunks/14if5vtky.3sn.js +1 -0
- package/.next/standalone/.next/static/chunks/{017n78o_7xb4_.js → 15hd85wggjanq.js} +1 -1
- package/.next/standalone/.next/static/chunks/15i_n03eg5uyp.js +1 -0
- package/.next/standalone/.next/static/chunks/15sblmrroyskk.js +1 -0
- package/.next/standalone/.next/static/chunks/1756nfyulu.9h.css +1 -0
- package/.next/standalone/.next/static/chunks/{0j.sfmmqj2xel.js → 17rh_.7tkk2m0.js} +15 -14
- package/.next/standalone/.next/static/chunks/turbopack-02..gqxjw9fev.js +1 -0
- package/.next/standalone/.next/static/chunks/turbopack-05luogg2yney~.js +1 -0
- package/.next/standalone/.next/static/chunks/turbopack-092b2t67-z3ds.js +1 -0
- package/.next/standalone/.next/static/chunks/turbopack-0eue2xixdnbd..js +1 -0
- package/.next/standalone/.next/static/chunks/{turbopack-15bv~a4hbvh29.js → turbopack-0i6l53-j6t6zg.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-0d-xk62f1t05v.js → turbopack-0ser1ty-sh2jk.js} +1 -1
- package/.next/standalone/.next/static/chunks/{turbopack-0zcblahe.hdqd.js → turbopack-0zouy.~ram.7g.js} +1 -1
- package/.next/standalone/.next/static/{X7BRO7ORR4bKFt1LXzGU8 → onsm_2jNZvyggHqM9yibn}/_buildManifest.js +11 -6
- package/.next/standalone/messages/de/terminal.json +4 -0
- package/.next/standalone/messages/de/webBrowser.json +5 -1
- package/.next/standalone/messages/en/terminal.json +4 -0
- package/.next/standalone/messages/en/webBrowser.json +5 -1
- package/.next/standalone/messages/es/terminal.json +4 -0
- package/.next/standalone/messages/es/webBrowser.json +5 -1
- package/.next/standalone/messages/fr/terminal.json +4 -0
- package/.next/standalone/messages/fr/webBrowser.json +5 -1
- package/.next/standalone/messages/ja/terminal.json +4 -0
- package/.next/standalone/messages/ja/webBrowser.json +5 -1
- package/.next/standalone/messages/ko/terminal.json +4 -0
- package/.next/standalone/messages/ko/webBrowser.json +5 -1
- package/.next/standalone/messages/pt-BR/terminal.json +4 -0
- package/.next/standalone/messages/pt-BR/webBrowser.json +5 -1
- package/.next/standalone/messages/ru/terminal.json +4 -0
- package/.next/standalone/messages/ru/webBrowser.json +5 -1
- package/.next/standalone/messages/tr/terminal.json +4 -0
- package/.next/standalone/messages/tr/webBrowser.json +5 -1
- package/.next/standalone/messages/zh-CN/terminal.json +4 -0
- package/.next/standalone/messages/zh-CN/webBrowser.json +5 -1
- package/.next/standalone/messages/zh-TW/terminal.json +4 -0
- package/.next/standalone/messages/zh-TW/webBrowser.json +5 -1
- package/.next/standalone/package.json +5 -2
- package/.next/standalone/server.js +1 -1
- package/.next/standalone/src/config/tmux.conf +17 -7
- package/.next/standalone/src/lib/providers/claude/index.ts +44 -0
- package/.next/standalone/src/pages/api/layout/pane/[paneId]/tabs/index.ts +13 -6
- package/README.de.md +5 -4
- package/README.es.md +5 -4
- package/README.fr.md +5 -4
- package/README.ja.md +5 -4
- package/README.ko.md +5 -4
- package/README.md +5 -4
- package/README.pt-BR.md +5 -4
- package/README.ru.md +5 -4
- package/README.tr.md +5 -4
- package/README.zh-CN.md +5 -4
- package/README.zh-TW.md +5 -4
- package/bin/cli.js +95 -0
- package/dist/server.js +974 -852
- package/package.json +5 -2
- package/src/config/tmux.conf +17 -7
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__01.~37j._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__016s4a9._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__02.9a5j._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__03l15.f._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__05ij_ys._.js +0 -42
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__06h3gzy._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_fu3k.._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0_imm_0._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0abp0lo._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0auutl5._.js +0 -5
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0d-inw8._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0g8mjyh._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0lk7ru8._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0l~_wd0._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ovn6yp._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0un_g-o._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__12ksl6n._.js +0 -42
- package/.next/standalone/.next/server/chunks/_03-duro._.js +0 -3
- package/.next/standalone/.next/server/chunks/_07097f~._.js +0 -3
- package/.next/standalone/.next/server/chunks/_0z7d6.z._.js +0 -3
- package/.next/standalone/.next/server/chunks/src_lib_0p.xsqi._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0da5cj~._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__12b2-y_._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_016o9_-._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_04iqb-o._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_04q3_kj._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0a_u5qm._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0d58w1~._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0nib_x8._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0nuztht._.js +0 -18
- package/.next/standalone/.next/server/chunks/ssr/_0oca87b._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0ovjjn5._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0pk4~_0._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0qt-m3l._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0sm8slq._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_10s2f96._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_13c-b~p._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_13lrp9_._.js +0 -3
- package/.next/standalone/.next/static/chunks/0-7en.d6a3jki.js +0 -1
- package/.next/standalone/.next/static/chunks/0-pm5wxsyxs3e.js +0 -1
- package/.next/standalone/.next/static/chunks/01ul8lmr04ls3.js +0 -1
- package/.next/standalone/.next/static/chunks/05b7zy0n0vhms.js +0 -1
- package/.next/standalone/.next/static/chunks/05kshd9vdrl4o.js +0 -1
- package/.next/standalone/.next/static/chunks/05w~qu2zpvh2f.js +0 -1
- package/.next/standalone/.next/static/chunks/08f708~_7uy_i.js +0 -1
- package/.next/standalone/.next/static/chunks/0af.3khnmptnl.js +0 -1
- package/.next/standalone/.next/static/chunks/0cvx059r5hgj1.js +0 -1
- package/.next/standalone/.next/static/chunks/0dp178b-0_ltl.js +0 -1
- package/.next/standalone/.next/static/chunks/0ebjicjua7yl-.js +0 -1
- package/.next/standalone/.next/static/chunks/0gn-f6_m696k0.js +0 -1
- package/.next/standalone/.next/static/chunks/0kaufevqdhsld.js +0 -1
- package/.next/standalone/.next/static/chunks/0n2k1al4sr_qq.css +0 -1
- package/.next/standalone/.next/static/chunks/0n4fv8.loh_0x.js +0 -1
- package/.next/standalone/.next/static/chunks/0nn618je7_7ec.js +0 -16
- package/.next/standalone/.next/static/chunks/0pv9~yath4mjh.js +0 -1
- package/.next/standalone/.next/static/chunks/0q0yh17_ncipj.js +0 -1
- package/.next/standalone/.next/static/chunks/0qkrdmhiv-0d3.js +0 -1
- package/.next/standalone/.next/static/chunks/0r9z55-r31en8.js +0 -1
- package/.next/standalone/.next/static/chunks/0ra09kjja5rf..js +0 -1
- package/.next/standalone/.next/static/chunks/0sc7tkg1gojr..js +0 -1
- package/.next/standalone/.next/static/chunks/0u.snf3d~5nvq.js +0 -1
- package/.next/standalone/.next/static/chunks/0u9yrtrs1bktp.js +0 -1
- package/.next/standalone/.next/static/chunks/0v.l~.~v98c2c.js +0 -1
- package/.next/standalone/.next/static/chunks/0wu~4k-l8otuy.js +0 -1
- package/.next/standalone/.next/static/chunks/0wvu-lowt9uem.js +0 -1
- package/.next/standalone/.next/static/chunks/0x7cvsf..ezd..js +0 -1
- package/.next/standalone/.next/static/chunks/0yb~gw6ukzdmo.js +0 -1
- package/.next/standalone/.next/static/chunks/0yg3hj1g~k1ip.js +0 -1
- package/.next/standalone/.next/static/chunks/0z_~g~mb.kjs1.js +0 -1
- package/.next/standalone/.next/static/chunks/0~c6h8j7ut2ca.js +0 -1
- package/.next/standalone/.next/static/chunks/0~lnp45fxxtgz.js +0 -1
- package/.next/standalone/.next/static/chunks/0~ts_4hi7.u_0.js +0 -1
- package/.next/standalone/.next/static/chunks/13_l03e3sab-g.js +0 -1
- package/.next/standalone/.next/static/chunks/156~ejmpjxu8e.js +0 -1
- package/.next/standalone/.next/static/chunks/15m4y6otktj8z.js +0 -1
- package/.next/standalone/.next/static/chunks/16bag6z7n~ffw.js +0 -1
- package/.next/standalone/.next/static/chunks/17ebdp-_o5o30.js +0 -1
- package/.next/standalone/.next/static/chunks/turbopack-0nn9kx.dkgwqs.js +0 -1
- package/.next/standalone/.next/static/chunks/turbopack-0rb3q18bhb2hu.js +0 -1
- package/.next/standalone/.next/static/chunks/turbopack-0~wmco6ifn20m.js +0 -1
- package/.next/standalone/.next/static/chunks/turbopack-1469mxaruw82d.js +0 -1
- /package/.next/standalone/.next/static/{X7BRO7ORR4bKFt1LXzGU8 → onsm_2jNZvyggHqM9yibn}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{X7BRO7ORR4bKFt1LXzGU8 → onsm_2jNZvyggHqM9yibn}/_ssgManifest.js +0 -0
package/dist/server.js
CHANGED
|
@@ -3847,7 +3847,7 @@ var init_platform = __esm({
|
|
|
3847
3847
|
});
|
|
3848
3848
|
|
|
3849
3849
|
// src/lib/tmux.ts
|
|
3850
|
-
var import_child_process, import_promises, import_util, import_path2, log, execFile, TMUX_SOCKET, TMUX_CONFIG_PATH, CMD_TIMEOUT, listSessions, createSession, sleep, killSession, hasSession, cleanDeadSessions, scanSessions, readShellCwd, getSessionCwd, getSessionPanePid, applyConfig, defaultSessionName, workspaceSessionName, getPaneCurrentCommand, getAllPanesInfo, SAFE_SHELLS, checkTerminalProcess, getPaneTitle, exitCopyMode, sendKeys, capturePaneContent, capturePaneContentWithHistory, getChildPidsOf, getDescendantPids, getListeningPortsLinux, getListeningPorts;
|
|
3850
|
+
var import_child_process, import_promises, import_util, import_path2, log, execFile, TMUX_SOCKET, TMUX_CONFIG_PATH, CMD_TIMEOUT, listSessions, createSession, sleep, killSession, resolveExistingDir, hasSession, cleanDeadSessions, scanSessions, readShellCwd, getSessionCwd, getSessionPanePid, applyConfig, defaultSessionName, workspaceSessionName, getPaneCurrentCommand, getAllPanesInfo, SAFE_SHELLS, checkTerminalProcess, getPaneTitle, exitCopyMode, sendKeys, capturePaneContent, capturePaneContentWithHistory, getChildPidsOf, getDescendantPids, getListeningPortsLinux, getListeningPorts;
|
|
3851
3851
|
var init_tmux = __esm({
|
|
3852
3852
|
"src/lib/tmux.ts"() {
|
|
3853
3853
|
"use strict";
|
|
@@ -3957,6 +3957,21 @@ var init_tmux = __esm({
|
|
|
3957
3957
|
}
|
|
3958
3958
|
log.warn(`tmux session still alive after kill: ${name}`);
|
|
3959
3959
|
};
|
|
3960
|
+
resolveExistingDir = async (preferred) => {
|
|
3961
|
+
const fallback = PRISTINE_ENV.HOME || "/";
|
|
3962
|
+
if (!preferred) return fallback;
|
|
3963
|
+
let candidate = preferred;
|
|
3964
|
+
while (true) {
|
|
3965
|
+
try {
|
|
3966
|
+
const stat = await import_promises.default.stat(candidate);
|
|
3967
|
+
if (stat.isDirectory()) return candidate;
|
|
3968
|
+
} catch {
|
|
3969
|
+
}
|
|
3970
|
+
const parent = import_path2.default.dirname(candidate);
|
|
3971
|
+
if (parent === candidate) return fallback;
|
|
3972
|
+
candidate = parent;
|
|
3973
|
+
}
|
|
3974
|
+
};
|
|
3960
3975
|
hasSession = async (name) => {
|
|
3961
3976
|
try {
|
|
3962
3977
|
await execFile(
|
|
@@ -4219,13 +4234,13 @@ var init_tmux = __esm({
|
|
|
4219
4234
|
});
|
|
4220
4235
|
|
|
4221
4236
|
// src/lib/sync-server.ts
|
|
4222
|
-
var
|
|
4237
|
+
var log2, g2, clients, handleSyncConnection, BACKPRESSURE_LIMIT, broadcastSync, gracefulSyncShutdown;
|
|
4223
4238
|
var init_sync_server = __esm({
|
|
4224
4239
|
"src/lib/sync-server.ts"() {
|
|
4225
4240
|
"use strict";
|
|
4226
4241
|
init_wrapper();
|
|
4227
4242
|
init_logger();
|
|
4228
|
-
|
|
4243
|
+
log2 = createLogger("sync");
|
|
4229
4244
|
g2 = globalThis;
|
|
4230
4245
|
if (!g2.__ptSyncClients) g2.__ptSyncClients = /* @__PURE__ */ new Set();
|
|
4231
4246
|
clients = g2.__ptSyncClients;
|
|
@@ -4233,7 +4248,7 @@ var init_sync_server = __esm({
|
|
|
4233
4248
|
clients.add(ws);
|
|
4234
4249
|
ws.on("close", () => clients.delete(ws));
|
|
4235
4250
|
ws.on("error", (err) => {
|
|
4236
|
-
|
|
4251
|
+
log2.error(`websocket error: ${err.message}`);
|
|
4237
4252
|
clients.delete(ws);
|
|
4238
4253
|
});
|
|
4239
4254
|
};
|
|
@@ -4271,35 +4286,35 @@ var init_layout_tree = __esm({
|
|
|
4271
4286
|
});
|
|
4272
4287
|
|
|
4273
4288
|
// src/lib/layout-store.ts
|
|
4274
|
-
var
|
|
4289
|
+
var import_promises2, import_path3, import_os2, log3, BASE_DIR, g3, setLayoutReconciler, withLock, generatePaneId, generateTabId, resolveLayoutDir, resolveLayoutFile, createDefaultTab, createDefaultPaneNode, readLayoutFile, extractWsIdFromPath, writeLayoutFile, crossCheckLayout, createDefaultLayout, reconcileTabCwd, parseSessionName, mutateTab, updateTabAgentSessionId, updateTabAgentSummary, updateTabClaudeSummary, updateTabLastUserMessage, updateTabCliStatus;
|
|
4275
4290
|
var init_layout_store = __esm({
|
|
4276
4291
|
"src/lib/layout-store.ts"() {
|
|
4277
4292
|
"use strict";
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4293
|
+
import_promises2 = __toESM(require("fs/promises"));
|
|
4294
|
+
import_path3 = __toESM(require("path"));
|
|
4295
|
+
import_os2 = __toESM(require("os"));
|
|
4281
4296
|
init_nanoid();
|
|
4282
4297
|
init_tmux();
|
|
4283
4298
|
init_sync_server();
|
|
4284
4299
|
init_logger();
|
|
4285
4300
|
init_layout_tree();
|
|
4286
4301
|
init_layout_tree();
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
if (!
|
|
4291
|
-
if (!
|
|
4292
|
-
if (
|
|
4302
|
+
log3 = createLogger("layout");
|
|
4303
|
+
BASE_DIR = import_path3.default.join(import_os2.default.homedir(), ".purplemux");
|
|
4304
|
+
g3 = globalThis;
|
|
4305
|
+
if (!g3.__ptLayoutLock) g3.__ptLayoutLock = Promise.resolve();
|
|
4306
|
+
if (!g3.__ptLayoutContentCache) g3.__ptLayoutContentCache = /* @__PURE__ */ new Map();
|
|
4307
|
+
if (g3.__ptLayoutReconciler === void 0) g3.__ptLayoutReconciler = null;
|
|
4293
4308
|
setLayoutReconciler = (reconciler) => {
|
|
4294
|
-
|
|
4309
|
+
g3.__ptLayoutReconciler = reconciler;
|
|
4295
4310
|
};
|
|
4296
|
-
|
|
4311
|
+
withLock = async (fn) => {
|
|
4297
4312
|
let release;
|
|
4298
4313
|
const next2 = new Promise((r) => {
|
|
4299
4314
|
release = r;
|
|
4300
4315
|
});
|
|
4301
|
-
const prev =
|
|
4302
|
-
|
|
4316
|
+
const prev = g3.__ptLayoutLock;
|
|
4317
|
+
g3.__ptLayoutLock = next2;
|
|
4303
4318
|
await prev;
|
|
4304
4319
|
try {
|
|
4305
4320
|
return await fn();
|
|
@@ -4309,8 +4324,8 @@ var init_layout_store = __esm({
|
|
|
4309
4324
|
};
|
|
4310
4325
|
generatePaneId = () => `pane-${nanoid(6)}`;
|
|
4311
4326
|
generateTabId = () => `tab-${nanoid(6)}`;
|
|
4312
|
-
resolveLayoutDir = (wsId) =>
|
|
4313
|
-
resolveLayoutFile = (wsId) =>
|
|
4327
|
+
resolveLayoutDir = (wsId) => import_path3.default.join(BASE_DIR, "workspaces", wsId);
|
|
4328
|
+
resolveLayoutFile = (wsId) => import_path3.default.join(resolveLayoutDir(wsId), "layout.json");
|
|
4314
4329
|
createDefaultTab = (wsId, paneId, order = 0, cwd) => {
|
|
4315
4330
|
const tabId = generateTabId();
|
|
4316
4331
|
const tab = {
|
|
@@ -4333,16 +4348,16 @@ var init_layout_store = __esm({
|
|
|
4333
4348
|
readLayoutFile = async (filePath) => {
|
|
4334
4349
|
let raw;
|
|
4335
4350
|
try {
|
|
4336
|
-
raw = await
|
|
4351
|
+
raw = await import_promises2.default.readFile(filePath, "utf-8");
|
|
4337
4352
|
} catch {
|
|
4338
4353
|
return null;
|
|
4339
4354
|
}
|
|
4340
4355
|
try {
|
|
4341
4356
|
return JSON.parse(raw);
|
|
4342
4357
|
} catch {
|
|
4343
|
-
|
|
4358
|
+
log3.warn(`${filePath} parse failed`);
|
|
4344
4359
|
try {
|
|
4345
|
-
await
|
|
4360
|
+
await import_promises2.default.copyFile(filePath, filePath.replace(/\.json$/, ".json.bak"));
|
|
4346
4361
|
} catch {
|
|
4347
4362
|
}
|
|
4348
4363
|
return null;
|
|
@@ -4354,22 +4369,22 @@ var init_layout_store = __esm({
|
|
|
4354
4369
|
};
|
|
4355
4370
|
writeLayoutFile = async (data, filePath) => {
|
|
4356
4371
|
const contentKey = JSON.stringify({ root: data.root, activePaneId: data.activePaneId });
|
|
4357
|
-
const cache2 =
|
|
4372
|
+
const cache2 = g3.__ptLayoutContentCache;
|
|
4358
4373
|
if (cache2.get(filePath) === contentKey) return;
|
|
4359
|
-
await
|
|
4374
|
+
await import_promises2.default.mkdir(import_path3.default.dirname(filePath), { recursive: true });
|
|
4360
4375
|
const tmpFile = filePath + ".tmp";
|
|
4361
4376
|
try {
|
|
4362
|
-
await
|
|
4363
|
-
await
|
|
4377
|
+
await import_promises2.default.writeFile(tmpFile, JSON.stringify(data, null, 2), { mode: 384 });
|
|
4378
|
+
await import_promises2.default.rename(tmpFile, filePath);
|
|
4364
4379
|
} catch (err) {
|
|
4365
|
-
await
|
|
4380
|
+
await import_promises2.default.unlink(tmpFile).catch(() => {
|
|
4366
4381
|
});
|
|
4367
4382
|
throw err;
|
|
4368
4383
|
}
|
|
4369
4384
|
cache2.set(filePath, contentKey);
|
|
4370
4385
|
const wsId = extractWsIdFromPath(filePath);
|
|
4371
4386
|
if (wsId) {
|
|
4372
|
-
const reconciler =
|
|
4387
|
+
const reconciler = g3.__ptLayoutReconciler;
|
|
4373
4388
|
if (reconciler) {
|
|
4374
4389
|
const validTabIds = collectAllTabs(data.root).map((t) => t.id);
|
|
4375
4390
|
reconciler.reconcileWorkspaceTabs(wsId, validTabIds);
|
|
@@ -4388,7 +4403,7 @@ var init_layout_store = __esm({
|
|
|
4388
4403
|
layoutSessions.add(tab.sessionName);
|
|
4389
4404
|
if (!tmuxSet.has(tab.sessionName) && tab.panelType === "claude-code") {
|
|
4390
4405
|
const cwd = tab.cwd || defaultCwd;
|
|
4391
|
-
|
|
4406
|
+
log3.debug(`crossCheck: Claude tab session recreated: ${tab.sessionName} (cwd: ${cwd})`);
|
|
4392
4407
|
await createSession(tab.sessionName, 80, 24, cwd);
|
|
4393
4408
|
changed = true;
|
|
4394
4409
|
}
|
|
@@ -4433,75 +4448,69 @@ var init_layout_store = __esm({
|
|
|
4433
4448
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4434
4449
|
};
|
|
4435
4450
|
};
|
|
4451
|
+
reconcileTabCwd = async (sessionName) => withLock(async () => {
|
|
4452
|
+
const parsed = parseSessionName(sessionName);
|
|
4453
|
+
if (!parsed) return;
|
|
4454
|
+
const filePath = resolveLayoutFile(parsed.wsId);
|
|
4455
|
+
const layout = await readLayoutFile(filePath);
|
|
4456
|
+
if (!layout) return;
|
|
4457
|
+
const pane = collectPanes(layout.root).find((p) => p.id === parsed.paneId);
|
|
4458
|
+
if (!pane) return;
|
|
4459
|
+
const tab = pane.tabs.find((t) => t.id === parsed.tabId);
|
|
4460
|
+
if (!tab || !tab.cwd) return;
|
|
4461
|
+
const effectiveCwd = await resolveExistingDir(tab.cwd);
|
|
4462
|
+
if (effectiveCwd === tab.cwd) return;
|
|
4463
|
+
log3.warn(`tab cwd missing, clearing: ${sessionName} (was ${tab.cwd})`);
|
|
4464
|
+
tab.cwd = effectiveCwd;
|
|
4465
|
+
delete tab.lastCommand;
|
|
4466
|
+
layout.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4467
|
+
await writeLayoutFile(layout, filePath);
|
|
4468
|
+
});
|
|
4436
4469
|
parseSessionName = (sessionName) => {
|
|
4437
4470
|
const match = sessionName.match(/^pt-(ws-.*?)-(pane-.*?)-(tab-.+)$/);
|
|
4438
4471
|
if (!match) return null;
|
|
4439
4472
|
return { wsId: match[1], paneId: match[2], tabId: match[3] };
|
|
4440
4473
|
};
|
|
4441
|
-
|
|
4442
|
-
const parsed = parseSessionName(sessionName);
|
|
4443
|
-
if (!parsed) return;
|
|
4444
|
-
await withLock2(async () => {
|
|
4445
|
-
const filePath = resolveLayoutFile(parsed.wsId);
|
|
4446
|
-
const layout = await readLayoutFile(filePath);
|
|
4447
|
-
if (!layout) return;
|
|
4448
|
-
const allTabs = collectAllTabs(layout.root);
|
|
4449
|
-
const tab = allTabs.find((t) => t.sessionName === sessionName);
|
|
4450
|
-
if (!tab) return;
|
|
4451
|
-
tab.claudeSessionId = claudeSessionId;
|
|
4452
|
-
layout.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4453
|
-
await writeLayoutFile(layout, filePath);
|
|
4454
|
-
});
|
|
4455
|
-
};
|
|
4456
|
-
updateTabClaudeSummary = async (sessionName, claudeSummary) => {
|
|
4457
|
-
const parsed = parseSessionName(sessionName);
|
|
4458
|
-
if (!parsed) return;
|
|
4459
|
-
await withLock2(async () => {
|
|
4460
|
-
const filePath = resolveLayoutFile(parsed.wsId);
|
|
4461
|
-
const layout = await readLayoutFile(filePath);
|
|
4462
|
-
if (!layout) return;
|
|
4463
|
-
const allTabs = collectAllTabs(layout.root);
|
|
4464
|
-
const tab = allTabs.find((t) => t.sessionName === sessionName);
|
|
4465
|
-
if (!tab) return;
|
|
4466
|
-
if (tab.claudeSummary === claudeSummary) return;
|
|
4467
|
-
tab.claudeSummary = claudeSummary;
|
|
4468
|
-
layout.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4469
|
-
await writeLayoutFile(layout, filePath);
|
|
4470
|
-
});
|
|
4471
|
-
};
|
|
4472
|
-
updateTabLastUserMessage = async (sessionName, lastUserMessage) => {
|
|
4473
|
-
const parsed = parseSessionName(sessionName);
|
|
4474
|
-
if (!parsed) return;
|
|
4475
|
-
await withLock2(async () => {
|
|
4476
|
-
const filePath = resolveLayoutFile(parsed.wsId);
|
|
4477
|
-
const layout = await readLayoutFile(filePath);
|
|
4478
|
-
if (!layout) return;
|
|
4479
|
-
const allTabs = collectAllTabs(layout.root);
|
|
4480
|
-
const tab = allTabs.find((t) => t.sessionName === sessionName);
|
|
4481
|
-
if (!tab) return;
|
|
4482
|
-
if (tab.lastUserMessage === lastUserMessage) return;
|
|
4483
|
-
tab.lastUserMessage = lastUserMessage;
|
|
4484
|
-
layout.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4485
|
-
await writeLayoutFile(layout, filePath);
|
|
4486
|
-
});
|
|
4487
|
-
};
|
|
4488
|
-
updateTabCliStatus = async (sessionName, cliState, dismissedAt) => {
|
|
4474
|
+
mutateTab = async (sessionName, mutator) => {
|
|
4489
4475
|
const parsed = parseSessionName(sessionName);
|
|
4490
4476
|
if (!parsed) return;
|
|
4491
|
-
await
|
|
4477
|
+
await withLock(async () => {
|
|
4492
4478
|
const filePath = resolveLayoutFile(parsed.wsId);
|
|
4493
4479
|
const layout = await readLayoutFile(filePath);
|
|
4494
4480
|
if (!layout) return;
|
|
4495
|
-
const
|
|
4496
|
-
const tab = allTabs.find((t) => t.sessionName === sessionName);
|
|
4481
|
+
const tab = collectAllTabs(layout.root).find((t) => t.sessionName === sessionName);
|
|
4497
4482
|
if (!tab) return;
|
|
4498
|
-
if (tab
|
|
4499
|
-
tab.cliState = cliState;
|
|
4500
|
-
tab.dismissedAt = dismissedAt;
|
|
4483
|
+
if (!mutator(tab)) return;
|
|
4501
4484
|
layout.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4502
4485
|
await writeLayoutFile(layout, filePath);
|
|
4503
4486
|
});
|
|
4504
4487
|
};
|
|
4488
|
+
updateTabAgentSessionId = (sessionName, provider, sessionId) => mutateTab(sessionName, (tab) => {
|
|
4489
|
+
if (provider.readSessionId(tab) === sessionId) return false;
|
|
4490
|
+
provider.writeSessionId(tab, sessionId);
|
|
4491
|
+
return true;
|
|
4492
|
+
});
|
|
4493
|
+
updateTabAgentSummary = (sessionName, provider, summary) => mutateTab(sessionName, (tab) => {
|
|
4494
|
+
if (provider.readSummary(tab) === summary) return false;
|
|
4495
|
+
provider.writeSummary(tab, summary);
|
|
4496
|
+
return true;
|
|
4497
|
+
});
|
|
4498
|
+
updateTabClaudeSummary = (sessionName, claudeSummary) => mutateTab(sessionName, (tab) => {
|
|
4499
|
+
if (tab.claudeSummary === claudeSummary) return false;
|
|
4500
|
+
tab.claudeSummary = claudeSummary;
|
|
4501
|
+
return true;
|
|
4502
|
+
});
|
|
4503
|
+
updateTabLastUserMessage = (sessionName, lastUserMessage) => mutateTab(sessionName, (tab) => {
|
|
4504
|
+
if (tab.lastUserMessage === lastUserMessage) return false;
|
|
4505
|
+
tab.lastUserMessage = lastUserMessage;
|
|
4506
|
+
return true;
|
|
4507
|
+
});
|
|
4508
|
+
updateTabCliStatus = (sessionName, cliState, dismissedAt) => mutateTab(sessionName, (tab) => {
|
|
4509
|
+
if (tab.cliState === cliState && tab.dismissedAt === dismissedAt) return false;
|
|
4510
|
+
tab.cliState = cliState;
|
|
4511
|
+
tab.dismissedAt = dismissedAt;
|
|
4512
|
+
return true;
|
|
4513
|
+
});
|
|
4505
4514
|
}
|
|
4506
4515
|
});
|
|
4507
4516
|
|
|
@@ -4556,6 +4565,16 @@ purplemux api-guide
|
|
|
4556
4565
|
(long-running builds, different project context, parallel exploration).
|
|
4557
4566
|
- Poll \`status\` and read \`result\` to verify delegated work.
|
|
4558
4567
|
- Prefer small, scoped tabs over cramming everything into one session.
|
|
4568
|
+
|
|
4569
|
+
### Tab type notes
|
|
4570
|
+
|
|
4571
|
+
- **\`web-browser\` tabs**: Electron webviews, not tmux. The \`alive\` field in
|
|
4572
|
+
\`tab list\` / \`tab status\` is always \`false\` for these \u2014 that is the normal
|
|
4573
|
+
value, not a sign the tab is dead. Do not gate actions on \`alive\`. Use the
|
|
4574
|
+
browser-specific HTTP endpoints (\`/browser/url\`, \`/browser/screenshot\`, \u2026;
|
|
4575
|
+
see \`purplemux api-guide\`) directly.
|
|
4576
|
+
- **\`terminal\` / \`claude-code\` tabs**: run inside tmux, so \`alive\` is a valid
|
|
4577
|
+
liveness signal.
|
|
4559
4578
|
`;
|
|
4560
4579
|
};
|
|
4561
4580
|
writeClaudePromptFile = async (ws) => {
|
|
@@ -4583,7 +4602,7 @@ var init_workspace_order = __esm({
|
|
|
4583
4602
|
"src/lib/workspace-order.ts"() {
|
|
4584
4603
|
"use strict";
|
|
4585
4604
|
getVisuallyOrderedWorkspaces = (workspaces, groups) => {
|
|
4586
|
-
const validGroupIds = new Set(groups.map((
|
|
4605
|
+
const validGroupIds = new Set(groups.map((g11) => g11.id));
|
|
4587
4606
|
const byGroup = /* @__PURE__ */ new Map();
|
|
4588
4607
|
const ungrouped = [];
|
|
4589
4608
|
for (const ws of workspaces) {
|
|
@@ -4597,8 +4616,8 @@ var init_workspace_order = __esm({
|
|
|
4597
4616
|
}
|
|
4598
4617
|
}
|
|
4599
4618
|
const ordered = [];
|
|
4600
|
-
for (const
|
|
4601
|
-
const list = byGroup.get(
|
|
4619
|
+
for (const g11 of groups) {
|
|
4620
|
+
const list = byGroup.get(g11.id);
|
|
4602
4621
|
if (list) ordered.push(...list);
|
|
4603
4622
|
}
|
|
4604
4623
|
ordered.push(...ungrouped);
|
|
@@ -4662,10 +4681,10 @@ var init_workspace_store = __esm({
|
|
|
4662
4681
|
delete legacy.order;
|
|
4663
4682
|
}
|
|
4664
4683
|
if (!Array.isArray(data.groups)) data.groups = [];
|
|
4665
|
-
for (const
|
|
4666
|
-
delete
|
|
4684
|
+
for (const g11 of data.groups) {
|
|
4685
|
+
delete g11.order;
|
|
4667
4686
|
}
|
|
4668
|
-
const validGroupIds = new Set(data.groups.map((
|
|
4687
|
+
const validGroupIds = new Set(data.groups.map((g11) => g11.id));
|
|
4669
4688
|
for (const ws of data.workspaces) {
|
|
4670
4689
|
if (ws.groupId && !validGroupIds.has(ws.groupId)) {
|
|
4671
4690
|
ws.groupId = null;
|
|
@@ -6942,11 +6961,11 @@ var require_bn = __commonJS({
|
|
|
6942
6961
|
var B = new BN(0);
|
|
6943
6962
|
var C = new BN(0);
|
|
6944
6963
|
var D = new BN(1);
|
|
6945
|
-
var
|
|
6964
|
+
var g11 = 0;
|
|
6946
6965
|
while (x.isEven() && y.isEven()) {
|
|
6947
6966
|
x.iushrn(1);
|
|
6948
6967
|
y.iushrn(1);
|
|
6949
|
-
++
|
|
6968
|
+
++g11;
|
|
6950
6969
|
}
|
|
6951
6970
|
var yp = y.clone();
|
|
6952
6971
|
var xp = x.clone();
|
|
@@ -6988,7 +7007,7 @@ var require_bn = __commonJS({
|
|
|
6988
7007
|
return {
|
|
6989
7008
|
a: C,
|
|
6990
7009
|
b: D,
|
|
6991
|
-
gcd: y.iushln(
|
|
7010
|
+
gcd: y.iushln(g11)
|
|
6992
7011
|
};
|
|
6993
7012
|
};
|
|
6994
7013
|
BN.prototype._invmp = function _invmp(p) {
|
|
@@ -13478,8 +13497,9 @@ var encodeStdout = (data) => {
|
|
|
13478
13497
|
};
|
|
13479
13498
|
|
|
13480
13499
|
// src/lib/terminal-server.ts
|
|
13500
|
+
init_layout_store();
|
|
13481
13501
|
init_logger();
|
|
13482
|
-
var
|
|
13502
|
+
var log4 = createLogger("terminal");
|
|
13483
13503
|
var MSG_STDIN2 = 0;
|
|
13484
13504
|
var MSG_RESIZE2 = 2;
|
|
13485
13505
|
var MSG_HEARTBEAT = 3;
|
|
@@ -13622,7 +13642,7 @@ var handleConnection = async (ws, request, sessionId) => {
|
|
|
13622
13642
|
if (clientId) {
|
|
13623
13643
|
connections.forEach((conn2) => {
|
|
13624
13644
|
if (conn2.clientId === clientId && !conn2.cleaned) {
|
|
13625
|
-
|
|
13645
|
+
log4.debug(`replacing existing connection for clientId: ${clientId}`);
|
|
13626
13646
|
conn2.detaching = true;
|
|
13627
13647
|
cleanup(conn2);
|
|
13628
13648
|
}
|
|
@@ -13633,7 +13653,7 @@ var handleConnection = async (ws, request, sessionId) => {
|
|
|
13633
13653
|
let evicted = 0;
|
|
13634
13654
|
for (const [oldWs, oldConn] of connections) {
|
|
13635
13655
|
if (evicted >= toEvict) break;
|
|
13636
|
-
|
|
13656
|
+
log4.debug(`evicting stale connection for session: ${oldConn.sessionName}`);
|
|
13637
13657
|
oldConn.detaching = true;
|
|
13638
13658
|
cleanup(oldConn);
|
|
13639
13659
|
if (oldWs.readyState === import_websocket.default.OPEN || oldWs.readyState === import_websocket.default.CONNECTING) {
|
|
@@ -13643,7 +13663,7 @@ var handleConnection = async (ws, request, sessionId) => {
|
|
|
13643
13663
|
}
|
|
13644
13664
|
}
|
|
13645
13665
|
if (connections.size >= MAX_CONNECTIONS) {
|
|
13646
|
-
|
|
13666
|
+
log4.warn(`connection rejected: max connections (${MAX_CONNECTIONS}) reached`);
|
|
13647
13667
|
ws.close(1013, "Max connections exceeded");
|
|
13648
13668
|
return;
|
|
13649
13669
|
}
|
|
@@ -13712,9 +13732,9 @@ var handleConnection = async (ws, request, sessionId) => {
|
|
|
13712
13732
|
break;
|
|
13713
13733
|
}
|
|
13714
13734
|
case MSG_KILL_SESSION: {
|
|
13715
|
-
|
|
13735
|
+
log4.debug(`kill session requested: ${sessionName}`);
|
|
13716
13736
|
killSession(sessionName).catch((err) => {
|
|
13717
|
-
|
|
13737
|
+
log4.error(`kill session failed: ${err instanceof Error ? err.message : err}`);
|
|
13718
13738
|
});
|
|
13719
13739
|
break;
|
|
13720
13740
|
}
|
|
@@ -13727,7 +13747,7 @@ var handleConnection = async (ws, request, sessionId) => {
|
|
|
13727
13747
|
cleanup(conn);
|
|
13728
13748
|
});
|
|
13729
13749
|
ws.on("error", (err) => {
|
|
13730
|
-
|
|
13750
|
+
log4.error(`websocket error: ${err.message}`);
|
|
13731
13751
|
if (!conn) return;
|
|
13732
13752
|
conn.detaching = true;
|
|
13733
13753
|
cleanup(conn);
|
|
@@ -13736,7 +13756,10 @@ var handleConnection = async (ws, request, sessionId) => {
|
|
|
13736
13756
|
sessionName = sessionId;
|
|
13737
13757
|
const exists = await hasSession(sessionId);
|
|
13738
13758
|
if (!exists) {
|
|
13739
|
-
|
|
13759
|
+
log4.warn(`session not found: ${sessionName}`);
|
|
13760
|
+
await reconcileTabCwd(sessionName).catch((err) => {
|
|
13761
|
+
log4.warn(`reconcile cwd failed: ${err instanceof Error ? err.message : err}`);
|
|
13762
|
+
});
|
|
13740
13763
|
ws.close(1011, "Session not found");
|
|
13741
13764
|
return;
|
|
13742
13765
|
}
|
|
@@ -13746,7 +13769,7 @@ var handleConnection = async (ws, request, sessionId) => {
|
|
|
13746
13769
|
try {
|
|
13747
13770
|
ptyProcess = attachToSession(sessionName, currentCols, currentRows);
|
|
13748
13771
|
} catch (err) {
|
|
13749
|
-
|
|
13772
|
+
log4.error(`tmux attach failed: ${err instanceof Error ? err.message : err}`);
|
|
13750
13773
|
ws.close(1011, "Session attach failed");
|
|
13751
13774
|
return;
|
|
13752
13775
|
}
|
|
@@ -13759,7 +13782,7 @@ var handleConnection = async (ws, request, sessionId) => {
|
|
|
13759
13782
|
if (ws.readyState !== import_websocket.default.OPEN) return;
|
|
13760
13783
|
ptyProcess = attachToSession(sessionName, currentCols, currentRows);
|
|
13761
13784
|
} catch (err) {
|
|
13762
|
-
|
|
13785
|
+
log4.error(`tmux session creation failed: ${err instanceof Error ? err.message : err}`);
|
|
13763
13786
|
ws.close(1011, "Session create failed");
|
|
13764
13787
|
return;
|
|
13765
13788
|
}
|
|
@@ -13819,7 +13842,7 @@ var handleConnection = async (ws, request, sessionId) => {
|
|
|
13819
13842
|
if (newEnd <= conn.throttleUntil) return;
|
|
13820
13843
|
conn.throttleUntil = newEnd;
|
|
13821
13844
|
if (!wasActive) {
|
|
13822
|
-
|
|
13845
|
+
log4.debug(`[throttle] window started ${THROTTLE_WINDOW_MS}ms reason=${reason} session=${sessionName} pid=${ptyPid}`);
|
|
13823
13846
|
}
|
|
13824
13847
|
if (conn.throttleInterval) return;
|
|
13825
13848
|
conn.throttleInterval = setInterval(() => {
|
|
@@ -13827,7 +13850,7 @@ var handleConnection = async (ws, request, sessionId) => {
|
|
|
13827
13850
|
if (Date.now() < conn.throttleUntil) return;
|
|
13828
13851
|
clearInterval(conn.throttleInterval);
|
|
13829
13852
|
conn.throttleInterval = null;
|
|
13830
|
-
|
|
13853
|
+
log4.debug(`[throttle] window ended session=${sessionName} pid=${ptyPid}`);
|
|
13831
13854
|
}, THROTTLE_FLUSH_INTERVAL_MS);
|
|
13832
13855
|
};
|
|
13833
13856
|
startThrottleWindow("initial");
|
|
@@ -13846,7 +13869,7 @@ var handleConnection = async (ws, request, sessionId) => {
|
|
|
13846
13869
|
conn.disposables.push(
|
|
13847
13870
|
ptyProcess.onExit(({ exitCode, signal }) => {
|
|
13848
13871
|
if (conn.cleaned) return;
|
|
13849
|
-
|
|
13872
|
+
log4.debug(
|
|
13850
13873
|
`pty exited (pid: ${ptyProcess.pid}, code: ${exitCode}, signal: ${signal}, detaching: ${conn.detaching})`
|
|
13851
13874
|
);
|
|
13852
13875
|
cleanup(conn, !conn.detaching);
|
|
@@ -13857,27 +13880,27 @@ var handleConnection = async (ws, request, sessionId) => {
|
|
|
13857
13880
|
// src/lib/install-server.ts
|
|
13858
13881
|
init_wrapper();
|
|
13859
13882
|
var pty2 = __toESM(require("node-pty"));
|
|
13860
|
-
var
|
|
13883
|
+
var import_os4 = __toESM(require("os"));
|
|
13861
13884
|
|
|
13862
13885
|
// src/lib/config-store.ts
|
|
13863
|
-
var
|
|
13864
|
-
var
|
|
13865
|
-
var
|
|
13886
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
13887
|
+
var import_path4 = __toESM(require("path"));
|
|
13888
|
+
var import_os3 = __toESM(require("os"));
|
|
13866
13889
|
var import_crypto = __toESM(require("crypto"));
|
|
13867
13890
|
init_sync_server();
|
|
13868
13891
|
init_logger();
|
|
13869
|
-
var
|
|
13870
|
-
var
|
|
13871
|
-
var CONFIG_FILE =
|
|
13872
|
-
var
|
|
13873
|
-
if (!
|
|
13874
|
-
var
|
|
13892
|
+
var log5 = createLogger("config");
|
|
13893
|
+
var BASE_DIR2 = import_path4.default.join(import_os3.default.homedir(), ".purplemux");
|
|
13894
|
+
var CONFIG_FILE = import_path4.default.join(BASE_DIR2, "config.json");
|
|
13895
|
+
var g4 = globalThis;
|
|
13896
|
+
if (!g4.__ptConfigLock) g4.__ptConfigLock = Promise.resolve();
|
|
13897
|
+
var withLock2 = async (fn) => {
|
|
13875
13898
|
let release;
|
|
13876
13899
|
const next2 = new Promise((r) => {
|
|
13877
13900
|
release = r;
|
|
13878
13901
|
});
|
|
13879
|
-
const prev =
|
|
13880
|
-
|
|
13902
|
+
const prev = g4.__ptConfigLock;
|
|
13903
|
+
g4.__ptConfigLock = next2;
|
|
13881
13904
|
await prev;
|
|
13882
13905
|
try {
|
|
13883
13906
|
return await fn();
|
|
@@ -13890,7 +13913,7 @@ var emptyConfig = () => ({
|
|
|
13890
13913
|
});
|
|
13891
13914
|
var readConfig = async () => {
|
|
13892
13915
|
try {
|
|
13893
|
-
const raw = await
|
|
13916
|
+
const raw = await import_promises3.default.readFile(CONFIG_FILE, "utf-8");
|
|
13894
13917
|
return JSON.parse(raw);
|
|
13895
13918
|
} catch {
|
|
13896
13919
|
return null;
|
|
@@ -13899,18 +13922,18 @@ var readConfig = async () => {
|
|
|
13899
13922
|
var writeConfig = async (data) => {
|
|
13900
13923
|
const { updatedAt: _, ...rest } = data;
|
|
13901
13924
|
const contentKey = JSON.stringify(rest);
|
|
13902
|
-
if (
|
|
13925
|
+
if (g4.__ptConfigContentCache === contentKey) return;
|
|
13903
13926
|
data.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13904
13927
|
const tmpFile = CONFIG_FILE + ".tmp";
|
|
13905
13928
|
try {
|
|
13906
|
-
await
|
|
13907
|
-
await
|
|
13929
|
+
await import_promises3.default.writeFile(tmpFile, JSON.stringify(data, null, 2), { mode: 384 });
|
|
13930
|
+
await import_promises3.default.rename(tmpFile, CONFIG_FILE);
|
|
13908
13931
|
} catch (err) {
|
|
13909
|
-
await
|
|
13932
|
+
await import_promises3.default.unlink(tmpFile).catch(() => {
|
|
13910
13933
|
});
|
|
13911
13934
|
throw err;
|
|
13912
13935
|
}
|
|
13913
|
-
|
|
13936
|
+
g4.__ptConfigContentCache = contentKey;
|
|
13914
13937
|
broadcastSync({ type: "config" });
|
|
13915
13938
|
};
|
|
13916
13939
|
var SCRYPT_KEYLEN = 64;
|
|
@@ -13932,7 +13955,7 @@ var getConfig = async () => {
|
|
|
13932
13955
|
const data = await readConfig();
|
|
13933
13956
|
return data ?? emptyConfig();
|
|
13934
13957
|
};
|
|
13935
|
-
var updateConfig = async (updates) =>
|
|
13958
|
+
var updateConfig = async (updates) => withLock2(async () => {
|
|
13936
13959
|
const data = await readConfig() ?? emptyConfig();
|
|
13937
13960
|
Object.assign(data, updates);
|
|
13938
13961
|
await writeConfig(data);
|
|
@@ -13942,14 +13965,14 @@ var needsSetup = async () => {
|
|
|
13942
13965
|
return !isHashedPassword(data?.authPassword);
|
|
13943
13966
|
};
|
|
13944
13967
|
var initConfigStore = async () => {
|
|
13945
|
-
await
|
|
13968
|
+
await import_promises3.default.mkdir(BASE_DIR2, { recursive: true });
|
|
13946
13969
|
const existing = await readConfig();
|
|
13947
13970
|
if (existing) {
|
|
13948
|
-
|
|
13971
|
+
log5.debug("config.json loaded");
|
|
13949
13972
|
return;
|
|
13950
13973
|
}
|
|
13951
13974
|
await writeConfig(emptyConfig());
|
|
13952
|
-
|
|
13975
|
+
log5.info("Initial config.json created (onboarding required)");
|
|
13953
13976
|
};
|
|
13954
13977
|
var getDangerouslySkipPermissions = async () => {
|
|
13955
13978
|
const data = await readConfig();
|
|
@@ -13961,7 +13984,7 @@ var generateSecret = () => import_crypto.default.randomBytes(32).toString("hex")
|
|
|
13961
13984
|
init_shell_env();
|
|
13962
13985
|
init_pristine_env();
|
|
13963
13986
|
init_logger();
|
|
13964
|
-
var
|
|
13987
|
+
var log6 = createLogger("install");
|
|
13965
13988
|
var RUNTIME_INSTALL_COMMANDS = /* @__PURE__ */ new Set([
|
|
13966
13989
|
"tmux-install",
|
|
13967
13990
|
"tmux-upgrade",
|
|
@@ -14028,7 +14051,7 @@ var handleInstallConnection = async (ws, request) => {
|
|
|
14028
14051
|
if (activeConn) {
|
|
14029
14052
|
cleanup2();
|
|
14030
14053
|
}
|
|
14031
|
-
const shell =
|
|
14054
|
+
const shell = import_os4.default.userInfo().shell || PRISTINE_ENV.SHELL || (process.platform === "darwin" ? "/bin/zsh" : "/bin/bash");
|
|
14032
14055
|
const cols = parseInt(url2.searchParams.get("cols") || "", 10) || 80;
|
|
14033
14056
|
const rows = parseInt(url2.searchParams.get("rows") || "", 10) || 24;
|
|
14034
14057
|
let ptyProcess;
|
|
@@ -14044,12 +14067,12 @@ var handleInstallConnection = async (ws, request) => {
|
|
|
14044
14067
|
}
|
|
14045
14068
|
});
|
|
14046
14069
|
} catch (err) {
|
|
14047
|
-
|
|
14070
|
+
log6.error(`pty spawn failed: ${err instanceof Error ? err.message : err}`);
|
|
14048
14071
|
ws.close(1011, "PTY spawn failed");
|
|
14049
14072
|
return;
|
|
14050
14073
|
}
|
|
14051
14074
|
activeConn = { ws, pty: ptyProcess };
|
|
14052
|
-
|
|
14075
|
+
log6.info(`install session started: ${command} (pid: ${ptyProcess.pid})`);
|
|
14053
14076
|
const disposables = [];
|
|
14054
14077
|
disposables.push(
|
|
14055
14078
|
ptyProcess.onData((data) => {
|
|
@@ -14059,7 +14082,7 @@ var handleInstallConnection = async (ws, request) => {
|
|
|
14059
14082
|
);
|
|
14060
14083
|
disposables.push(
|
|
14061
14084
|
ptyProcess.onExit(() => {
|
|
14062
|
-
|
|
14085
|
+
log6.info(`install pty exited: ${command}`);
|
|
14063
14086
|
disposeAll(disposables);
|
|
14064
14087
|
if (ws.readyState === import_websocket.default.OPEN) {
|
|
14065
14088
|
ws.close(1e3, "Process exited");
|
|
@@ -14118,335 +14141,8 @@ init_wrapper();
|
|
|
14118
14141
|
var import_fs5 = require("fs");
|
|
14119
14142
|
var import_fs6 = require("fs");
|
|
14120
14143
|
|
|
14121
|
-
// src/lib/session-
|
|
14122
|
-
var
|
|
14123
|
-
var import_fs = require("fs");
|
|
14124
|
-
var import_path5 = __toESM(require("path"));
|
|
14125
|
-
var import_os5 = __toESM(require("os"));
|
|
14126
|
-
var import_child_process3 = require("child_process");
|
|
14127
|
-
var import_util3 = require("util");
|
|
14128
|
-
|
|
14129
|
-
// src/lib/preflight.ts
|
|
14130
|
-
var import_child_process2 = require("child_process");
|
|
14131
|
-
var import_os4 = __toESM(require("os"));
|
|
14132
|
-
var import_path4 = __toESM(require("path"));
|
|
14133
|
-
var import_util2 = require("util");
|
|
14134
|
-
init_shell_env();
|
|
14135
|
-
init_pristine_env();
|
|
14136
|
-
var execFile2 = (0, import_util2.promisify)(import_child_process2.execFile);
|
|
14137
|
-
var CMD_TIMEOUT2 = 5e3;
|
|
14138
|
-
var shellPathCache = null;
|
|
14139
|
-
var shellPathPromise = null;
|
|
14140
|
-
var defaultShell2 = () => import_os4.default.userInfo().shell || defaultShell();
|
|
14141
|
-
var resolveShellPathAsync = async () => {
|
|
14142
|
-
const shell = defaultShell2();
|
|
14143
|
-
try {
|
|
14144
|
-
const { stdout } = await execFile2(shell, ["-ilc", 'echo -n "$PATH"'], {
|
|
14145
|
-
timeout: CMD_TIMEOUT2,
|
|
14146
|
-
env: {
|
|
14147
|
-
...buildShellEnv(),
|
|
14148
|
-
SHELL: shell,
|
|
14149
|
-
DISABLE_AUTO_UPDATE: "true",
|
|
14150
|
-
ZSH_TMUX_AUTOSTARTED: "true"
|
|
14151
|
-
}
|
|
14152
|
-
});
|
|
14153
|
-
return stdout.toString().trim();
|
|
14154
|
-
} catch {
|
|
14155
|
-
return PRISTINE_ENV.PATH || "";
|
|
14156
|
-
}
|
|
14157
|
-
};
|
|
14158
|
-
var initShellPath = async () => {
|
|
14159
|
-
shellPathCache = await resolveShellPathAsync();
|
|
14160
|
-
};
|
|
14161
|
-
var getShellPath = async () => {
|
|
14162
|
-
if (shellPathCache) return shellPathCache;
|
|
14163
|
-
if (!shellPathPromise) {
|
|
14164
|
-
shellPathPromise = resolveShellPathAsync().then((result) => {
|
|
14165
|
-
shellPathCache = result;
|
|
14166
|
-
shellPathPromise = null;
|
|
14167
|
-
return result;
|
|
14168
|
-
});
|
|
14169
|
-
}
|
|
14170
|
-
return shellPathPromise;
|
|
14171
|
-
};
|
|
14172
|
-
var CLAUDE_KNOWN_DIRS = [import_path4.default.join(import_os4.default.homedir(), ".local", "bin")];
|
|
14173
|
-
|
|
14174
|
-
// src/lib/session-detection.ts
|
|
14175
|
-
init_platform();
|
|
14176
|
-
var execFile3 = (0, import_util3.promisify)(import_child_process3.execFile);
|
|
14177
|
-
var CLAUDE_DIR = import_path5.default.join(import_os5.default.homedir(), ".claude");
|
|
14178
|
-
var SESSIONS_DIR = import_path5.default.join(CLAUDE_DIR, "sessions");
|
|
14179
|
-
var PROJECTS_DIR = import_path5.default.join(CLAUDE_DIR, "projects");
|
|
14180
|
-
var CLAUDE_KNOWN_PATHS = [import_path5.default.join(import_os5.default.homedir(), ".local", "bin", "claude")];
|
|
14181
|
-
var PID_POLL_INTERVAL = 1e4;
|
|
14182
|
-
var SESSION_DIR_DEBOUNCE = 200;
|
|
14183
|
-
var INSTALL_CHECK_INTERVAL = 6e4;
|
|
14184
|
-
var MAX_SANITIZED_LENGTH = 200;
|
|
14185
|
-
var toClaudeProjectName = (dirPath) => {
|
|
14186
|
-
const sanitized = dirPath.replace(/[^a-zA-Z0-9]/g, "-");
|
|
14187
|
-
if (sanitized.length <= MAX_SANITIZED_LENGTH) {
|
|
14188
|
-
return sanitized;
|
|
14189
|
-
}
|
|
14190
|
-
const hash2 = simpleHash(dirPath);
|
|
14191
|
-
return `${sanitized.slice(0, MAX_SANITIZED_LENGTH)}-${hash2}`;
|
|
14192
|
-
};
|
|
14193
|
-
var simpleHash = (str) => {
|
|
14194
|
-
let hash2 = 0;
|
|
14195
|
-
for (let i = 0; i < str.length; i++) {
|
|
14196
|
-
hash2 = (hash2 << 5) - hash2 + str.charCodeAt(i) | 0;
|
|
14197
|
-
}
|
|
14198
|
-
return Math.abs(hash2).toString(36);
|
|
14199
|
-
};
|
|
14200
|
-
var isProcessRunning = (pid) => new Promise((resolve) => {
|
|
14201
|
-
(0, import_child_process3.execFile)("ps", ["-p", String(pid)], (err) => {
|
|
14202
|
-
resolve(!err);
|
|
14203
|
-
});
|
|
14204
|
-
});
|
|
14205
|
-
var getChildPids = async (parentPid) => {
|
|
14206
|
-
try {
|
|
14207
|
-
const { stdout } = await execFile3("pgrep", ["-P", String(parentPid)]);
|
|
14208
|
-
return stdout.trim().split("\n").map((s) => parseInt(s, 10)).filter((n) => !Number.isNaN(n));
|
|
14209
|
-
} catch {
|
|
14210
|
-
return [];
|
|
14211
|
-
}
|
|
14212
|
-
};
|
|
14213
|
-
var getProcessCwd = async (pid) => {
|
|
14214
|
-
if (isLinux) {
|
|
14215
|
-
try {
|
|
14216
|
-
return await import_promises3.default.readlink(`/proc/${pid}/cwd`);
|
|
14217
|
-
} catch {
|
|
14218
|
-
return null;
|
|
14219
|
-
}
|
|
14220
|
-
}
|
|
14221
|
-
try {
|
|
14222
|
-
const { stdout } = await execFile3("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"]);
|
|
14223
|
-
const line = stdout.split("\n").find((l) => l.startsWith("n/"));
|
|
14224
|
-
return line ? line.slice(1) : null;
|
|
14225
|
-
} catch {
|
|
14226
|
-
return null;
|
|
14227
|
-
}
|
|
14228
|
-
};
|
|
14229
|
-
var getClaudeSessionFromArgs = async (childPids) => {
|
|
14230
|
-
for (const pid of childPids) {
|
|
14231
|
-
try {
|
|
14232
|
-
const { stdout } = await execFile3("ps", ["-p", String(pid), "-o", "args="]);
|
|
14233
|
-
const args = stdout.trim();
|
|
14234
|
-
const match = args.match(/claude\s+--resume\s+([0-9a-f-]{36})/);
|
|
14235
|
-
if (match) {
|
|
14236
|
-
const cwd = await getProcessCwd(pid);
|
|
14237
|
-
return { pid, sessionId: match[1], cwd };
|
|
14238
|
-
}
|
|
14239
|
-
} catch {
|
|
14240
|
-
continue;
|
|
14241
|
-
}
|
|
14242
|
-
}
|
|
14243
|
-
return null;
|
|
14244
|
-
};
|
|
14245
|
-
var readPidFile = async (filePath) => {
|
|
14246
|
-
try {
|
|
14247
|
-
const raw = await import_promises3.default.readFile(filePath, "utf-8");
|
|
14248
|
-
const data = JSON.parse(raw);
|
|
14249
|
-
if (!data.pid || !data.sessionId) return null;
|
|
14250
|
-
return data;
|
|
14251
|
-
} catch {
|
|
14252
|
-
return null;
|
|
14253
|
-
}
|
|
14254
|
-
};
|
|
14255
|
-
var findJsonlPath = async (projectDir, sessionId) => {
|
|
14256
|
-
const jsonlPath = import_path5.default.join(projectDir, `${sessionId}.jsonl`);
|
|
14257
|
-
try {
|
|
14258
|
-
await import_promises3.default.access(jsonlPath);
|
|
14259
|
-
return jsonlPath;
|
|
14260
|
-
} catch {
|
|
14261
|
-
return null;
|
|
14262
|
-
}
|
|
14263
|
-
};
|
|
14264
|
-
var isClaudeInstalled = async () => {
|
|
14265
|
-
try {
|
|
14266
|
-
await execFile3("claude", ["--version"], { timeout: 5e3, env: { ...process.env, PATH: await getShellPath() } });
|
|
14267
|
-
return true;
|
|
14268
|
-
} catch {
|
|
14269
|
-
for (const p of CLAUDE_KNOWN_PATHS) {
|
|
14270
|
-
try {
|
|
14271
|
-
await import_promises3.default.access(p);
|
|
14272
|
-
return true;
|
|
14273
|
-
} catch {
|
|
14274
|
-
}
|
|
14275
|
-
}
|
|
14276
|
-
return false;
|
|
14277
|
-
}
|
|
14278
|
-
};
|
|
14279
|
-
var isClaudeRunning = async (panePid, preloadedChildPids) => {
|
|
14280
|
-
const childPids = preloadedChildPids ?? await getChildPids(panePid);
|
|
14281
|
-
for (const pid of childPids) {
|
|
14282
|
-
try {
|
|
14283
|
-
const { stdout } = await execFile3("ps", ["-p", String(pid), "-o", "args="]);
|
|
14284
|
-
if (stdout.trim().includes("claude")) return true;
|
|
14285
|
-
} catch {
|
|
14286
|
-
continue;
|
|
14287
|
-
}
|
|
14288
|
-
}
|
|
14289
|
-
return false;
|
|
14290
|
-
};
|
|
14291
|
-
var detectActiveSession = async (panePid, preloadedChildPids) => {
|
|
14292
|
-
try {
|
|
14293
|
-
await import_promises3.default.access(CLAUDE_DIR);
|
|
14294
|
-
} catch {
|
|
14295
|
-
const installed = await isClaudeInstalled();
|
|
14296
|
-
const status = installed ? "not-initialized" : "not-installed";
|
|
14297
|
-
return { status, sessionId: null, jsonlPath: null, pid: null, startedAt: null, cwd: null };
|
|
14298
|
-
}
|
|
14299
|
-
const directChildPids = preloadedChildPids ?? await getChildPids(panePid);
|
|
14300
|
-
if (directChildPids.length === 0) {
|
|
14301
|
-
return { status: "not-running", sessionId: null, jsonlPath: null, pid: null, startedAt: null, cwd: null };
|
|
14302
|
-
}
|
|
14303
|
-
const grandchildPids = (await Promise.all(directChildPids.map(getChildPids))).flat();
|
|
14304
|
-
const allPids = [...directChildPids, ...grandchildPids];
|
|
14305
|
-
const childPidSet = new Set(allPids);
|
|
14306
|
-
try {
|
|
14307
|
-
const pidFiles = await import_promises3.default.readdir(SESSIONS_DIR);
|
|
14308
|
-
const jsonFiles = pidFiles.filter((f) => f.endsWith(".json"));
|
|
14309
|
-
for (const file2 of jsonFiles) {
|
|
14310
|
-
const data = await readPidFile(import_path5.default.join(SESSIONS_DIR, file2));
|
|
14311
|
-
if (!data) continue;
|
|
14312
|
-
if (!childPidSet.has(data.pid)) continue;
|
|
14313
|
-
let processArgs = "";
|
|
14314
|
-
try {
|
|
14315
|
-
const { stdout } = await execFile3("ps", ["-p", String(data.pid), "-o", "args="]);
|
|
14316
|
-
processArgs = stdout.trim();
|
|
14317
|
-
} catch {
|
|
14318
|
-
try {
|
|
14319
|
-
await import_promises3.default.unlink(import_path5.default.join(SESSIONS_DIR, file2));
|
|
14320
|
-
} catch {
|
|
14321
|
-
}
|
|
14322
|
-
continue;
|
|
14323
|
-
}
|
|
14324
|
-
if (!processArgs.includes("claude")) {
|
|
14325
|
-
try {
|
|
14326
|
-
await import_promises3.default.unlink(import_path5.default.join(SESSIONS_DIR, file2));
|
|
14327
|
-
} catch {
|
|
14328
|
-
}
|
|
14329
|
-
continue;
|
|
14330
|
-
}
|
|
14331
|
-
const projectName = toClaudeProjectName(data.cwd);
|
|
14332
|
-
const projectDir = import_path5.default.join(PROJECTS_DIR, projectName);
|
|
14333
|
-
let jsonlPath = await findJsonlPath(projectDir, data.sessionId);
|
|
14334
|
-
let effectiveSessionId = data.sessionId;
|
|
14335
|
-
if (!jsonlPath) {
|
|
14336
|
-
const resumeMatch = processArgs.match(/--resume\s+([0-9a-f-]{36})/);
|
|
14337
|
-
if (resumeMatch) {
|
|
14338
|
-
const resumeSessionId = resumeMatch[1];
|
|
14339
|
-
const resumeJsonlPath = await findJsonlPath(projectDir, resumeSessionId);
|
|
14340
|
-
if (resumeJsonlPath) {
|
|
14341
|
-
jsonlPath = resumeJsonlPath;
|
|
14342
|
-
effectiveSessionId = resumeSessionId;
|
|
14343
|
-
}
|
|
14344
|
-
}
|
|
14345
|
-
}
|
|
14346
|
-
return {
|
|
14347
|
-
status: "running",
|
|
14348
|
-
sessionId: effectiveSessionId,
|
|
14349
|
-
jsonlPath,
|
|
14350
|
-
pid: data.pid,
|
|
14351
|
-
startedAt: data.startedAt,
|
|
14352
|
-
cwd: data.cwd
|
|
14353
|
-
};
|
|
14354
|
-
}
|
|
14355
|
-
} catch {
|
|
14356
|
-
}
|
|
14357
|
-
const fromArgs = await getClaudeSessionFromArgs(allPids);
|
|
14358
|
-
if (fromArgs) {
|
|
14359
|
-
const cwd = fromArgs.cwd;
|
|
14360
|
-
if (cwd) {
|
|
14361
|
-
const projectName = toClaudeProjectName(cwd);
|
|
14362
|
-
const projectDir = import_path5.default.join(PROJECTS_DIR, projectName);
|
|
14363
|
-
const jsonlPath = await findJsonlPath(projectDir, fromArgs.sessionId);
|
|
14364
|
-
return {
|
|
14365
|
-
status: "running",
|
|
14366
|
-
sessionId: fromArgs.sessionId,
|
|
14367
|
-
jsonlPath,
|
|
14368
|
-
pid: fromArgs.pid,
|
|
14369
|
-
startedAt: null,
|
|
14370
|
-
cwd
|
|
14371
|
-
};
|
|
14372
|
-
}
|
|
14373
|
-
}
|
|
14374
|
-
return { status: "not-running", sessionId: null, jsonlPath: null, pid: null, startedAt: null, cwd: null };
|
|
14375
|
-
};
|
|
14376
|
-
var watchSessionsDir = (panePid, onChange, options) => {
|
|
14377
|
-
let watcher = null;
|
|
14378
|
-
let pidPollTimer = null;
|
|
14379
|
-
let debounceTimer = null;
|
|
14380
|
-
let installCheckTimer = null;
|
|
14381
|
-
let currentPid = null;
|
|
14382
|
-
let stopped = false;
|
|
14383
|
-
const pollPid = async () => {
|
|
14384
|
-
if (stopped || !currentPid) return;
|
|
14385
|
-
const running = await isProcessRunning(currentPid);
|
|
14386
|
-
if (!running && !stopped) {
|
|
14387
|
-
currentPid = null;
|
|
14388
|
-
const info = await detectActiveSession(panePid);
|
|
14389
|
-
onChange(info);
|
|
14390
|
-
}
|
|
14391
|
-
};
|
|
14392
|
-
const handleSessionDirChange = () => {
|
|
14393
|
-
if (stopped) return;
|
|
14394
|
-
if (debounceTimer) clearTimeout(debounceTimer);
|
|
14395
|
-
debounceTimer = setTimeout(async () => {
|
|
14396
|
-
if (stopped) return;
|
|
14397
|
-
const info = await detectActiveSession(panePid);
|
|
14398
|
-
if (info.pid) currentPid = info.pid;
|
|
14399
|
-
onChange(info);
|
|
14400
|
-
}, SESSION_DIR_DEBOUNCE);
|
|
14401
|
-
};
|
|
14402
|
-
const tryWatch = () => {
|
|
14403
|
-
if (stopped) return;
|
|
14404
|
-
try {
|
|
14405
|
-
watcher = (0, import_fs.watch)(SESSIONS_DIR, handleSessionDirChange);
|
|
14406
|
-
watcher.on("error", () => {
|
|
14407
|
-
});
|
|
14408
|
-
if (installCheckTimer) {
|
|
14409
|
-
clearInterval(installCheckTimer);
|
|
14410
|
-
installCheckTimer = null;
|
|
14411
|
-
}
|
|
14412
|
-
} catch {
|
|
14413
|
-
if (!installCheckTimer) {
|
|
14414
|
-
installCheckTimer = setInterval(async () => {
|
|
14415
|
-
if (stopped) return;
|
|
14416
|
-
try {
|
|
14417
|
-
await import_promises3.default.access(SESSIONS_DIR);
|
|
14418
|
-
tryWatch();
|
|
14419
|
-
const info = await detectActiveSession(panePid);
|
|
14420
|
-
if (info.pid) currentPid = info.pid;
|
|
14421
|
-
onChange(info);
|
|
14422
|
-
} catch {
|
|
14423
|
-
}
|
|
14424
|
-
}, INSTALL_CHECK_INTERVAL);
|
|
14425
|
-
}
|
|
14426
|
-
}
|
|
14427
|
-
};
|
|
14428
|
-
tryWatch();
|
|
14429
|
-
pidPollTimer = setInterval(pollPid, PID_POLL_INTERVAL);
|
|
14430
|
-
if (!options?.skipInitial) {
|
|
14431
|
-
detectActiveSession(panePid).then((info) => {
|
|
14432
|
-
if (stopped) return;
|
|
14433
|
-
if (info.pid) currentPid = info.pid;
|
|
14434
|
-
onChange(info);
|
|
14435
|
-
});
|
|
14436
|
-
}
|
|
14437
|
-
return {
|
|
14438
|
-
stop: () => {
|
|
14439
|
-
stopped = true;
|
|
14440
|
-
if (watcher) watcher.close();
|
|
14441
|
-
if (pidPollTimer) clearInterval(pidPollTimer);
|
|
14442
|
-
if (debounceTimer) clearTimeout(debounceTimer);
|
|
14443
|
-
if (installCheckTimer) clearInterval(installCheckTimer);
|
|
14444
|
-
}
|
|
14445
|
-
};
|
|
14446
|
-
};
|
|
14447
|
-
|
|
14448
|
-
// src/lib/session-parser.ts
|
|
14449
|
-
var import_promises4 = __toESM(require("fs/promises"));
|
|
14144
|
+
// src/lib/session-parser.ts
|
|
14145
|
+
var import_promises4 = __toESM(require("fs/promises"));
|
|
14450
14146
|
|
|
14451
14147
|
// node_modules/zod/v4/classic/external.js
|
|
14452
14148
|
var external_exports = {};
|
|
@@ -29318,125 +29014,655 @@ var import_readline = require("readline");
|
|
|
29318
29014
|
init_tmux();
|
|
29319
29015
|
|
|
29320
29016
|
// src/lib/session-list.ts
|
|
29017
|
+
var import_path7 = __toESM(require("path"));
|
|
29018
|
+
|
|
29019
|
+
// src/lib/session-detection.ts
|
|
29020
|
+
var import_promises5 = __toESM(require("fs/promises"));
|
|
29021
|
+
var import_fs = require("fs");
|
|
29321
29022
|
var import_path6 = __toESM(require("path"));
|
|
29322
|
-
|
|
29323
|
-
|
|
29023
|
+
var import_os6 = __toESM(require("os"));
|
|
29024
|
+
var import_child_process3 = require("child_process");
|
|
29025
|
+
var import_util5 = require("util");
|
|
29324
29026
|
|
|
29325
|
-
// src/lib/
|
|
29326
|
-
var
|
|
29327
|
-
var
|
|
29328
|
-
|
|
29329
|
-
|
|
29330
|
-
|
|
29331
|
-
|
|
29332
|
-
|
|
29333
|
-
|
|
29334
|
-
|
|
29335
|
-
|
|
29336
|
-
|
|
29337
|
-
|
|
29338
|
-
const
|
|
29339
|
-
|
|
29340
|
-
|
|
29341
|
-
|
|
29342
|
-
|
|
29343
|
-
|
|
29344
|
-
|
|
29345
|
-
|
|
29346
|
-
|
|
29347
|
-
|
|
29348
|
-
|
|
29349
|
-
|
|
29350
|
-
|
|
29351
|
-
|
|
29352
|
-
}
|
|
29353
|
-
return { get, set: set2, isStale, clear };
|
|
29027
|
+
// src/lib/preflight.ts
|
|
29028
|
+
var import_child_process2 = require("child_process");
|
|
29029
|
+
var import_os5 = __toESM(require("os"));
|
|
29030
|
+
var import_path5 = __toESM(require("path"));
|
|
29031
|
+
var import_util4 = require("util");
|
|
29032
|
+
init_shell_env();
|
|
29033
|
+
init_pristine_env();
|
|
29034
|
+
var execFile2 = (0, import_util4.promisify)(import_child_process2.execFile);
|
|
29035
|
+
var CMD_TIMEOUT2 = 5e3;
|
|
29036
|
+
var shellPathCache = null;
|
|
29037
|
+
var shellPathPromise = null;
|
|
29038
|
+
var defaultShell2 = () => import_os5.default.userInfo().shell || defaultShell();
|
|
29039
|
+
var resolveShellPathAsync = async () => {
|
|
29040
|
+
const shell = defaultShell2();
|
|
29041
|
+
try {
|
|
29042
|
+
const { stdout } = await execFile2(shell, ["-ilc", 'echo -n "$PATH"'], {
|
|
29043
|
+
timeout: CMD_TIMEOUT2,
|
|
29044
|
+
env: {
|
|
29045
|
+
...buildShellEnv(),
|
|
29046
|
+
SHELL: shell,
|
|
29047
|
+
DISABLE_AUTO_UPDATE: "true",
|
|
29048
|
+
ZSH_TMUX_AUTOSTARTED: "true"
|
|
29049
|
+
}
|
|
29050
|
+
});
|
|
29051
|
+
return stdout.toString().trim();
|
|
29052
|
+
} catch {
|
|
29053
|
+
return PRISTINE_ENV.PATH || "";
|
|
29054
|
+
}
|
|
29354
29055
|
};
|
|
29355
|
-
|
|
29356
|
-
|
|
29357
|
-
var log6 = createLogger("session-list");
|
|
29358
|
-
var PROJECTS_DIR2 = import_path6.default.join(
|
|
29359
|
-
process.env.HOME || process.env.USERPROFILE || "/",
|
|
29360
|
-
".claude",
|
|
29361
|
-
"projects"
|
|
29362
|
-
);
|
|
29363
|
-
var metaCache = createMetaCache();
|
|
29364
|
-
var cwdToProjectPath = (cwd) => {
|
|
29365
|
-
const projectName = toClaudeProjectName(cwd);
|
|
29366
|
-
return import_path6.default.join(PROJECTS_DIR2, projectName);
|
|
29056
|
+
var initShellPath = async () => {
|
|
29057
|
+
shellPathCache = await resolveShellPathAsync();
|
|
29367
29058
|
};
|
|
29059
|
+
var getShellPath = async () => {
|
|
29060
|
+
if (shellPathCache) return shellPathCache;
|
|
29061
|
+
if (!shellPathPromise) {
|
|
29062
|
+
shellPathPromise = resolveShellPathAsync().then((result) => {
|
|
29063
|
+
shellPathCache = result;
|
|
29064
|
+
shellPathPromise = null;
|
|
29065
|
+
return result;
|
|
29066
|
+
});
|
|
29067
|
+
}
|
|
29068
|
+
return shellPathPromise;
|
|
29069
|
+
};
|
|
29070
|
+
var CLAUDE_KNOWN_DIRS = [import_path5.default.join(import_os5.default.homedir(), ".local", "bin")];
|
|
29368
29071
|
|
|
29369
|
-
// src/lib/
|
|
29370
|
-
|
|
29371
|
-
|
|
29372
|
-
|
|
29373
|
-
|
|
29374
|
-
|
|
29375
|
-
|
|
29376
|
-
|
|
29377
|
-
|
|
29378
|
-
|
|
29379
|
-
|
|
29380
|
-
var
|
|
29381
|
-
|
|
29382
|
-
|
|
29383
|
-
|
|
29384
|
-
|
|
29385
|
-
|
|
29386
|
-
|
|
29387
|
-
|
|
29388
|
-
var
|
|
29389
|
-
|
|
29390
|
-
|
|
29391
|
-
|
|
29392
|
-
|
|
29393
|
-
|
|
29394
|
-
|
|
29395
|
-
|
|
29396
|
-
|
|
29397
|
-
|
|
29398
|
-
|
|
29399
|
-
|
|
29400
|
-
|
|
29401
|
-
|
|
29402
|
-
|
|
29403
|
-
|
|
29404
|
-
|
|
29405
|
-
|
|
29406
|
-
|
|
29407
|
-
|
|
29408
|
-
var
|
|
29409
|
-
|
|
29410
|
-
|
|
29411
|
-
|
|
29412
|
-
|
|
29413
|
-
|
|
29414
|
-
|
|
29415
|
-
|
|
29416
|
-
|
|
29417
|
-
|
|
29418
|
-
|
|
29419
|
-
|
|
29420
|
-
|
|
29421
|
-
|
|
29422
|
-
|
|
29423
|
-
|
|
29424
|
-
|
|
29425
|
-
const
|
|
29426
|
-
|
|
29427
|
-
|
|
29428
|
-
|
|
29429
|
-
|
|
29430
|
-
|
|
29431
|
-
|
|
29432
|
-
|
|
29433
|
-
|
|
29434
|
-
|
|
29435
|
-
|
|
29436
|
-
|
|
29437
|
-
|
|
29438
|
-
|
|
29439
|
-
|
|
29072
|
+
// src/lib/session-detection.ts
|
|
29073
|
+
init_platform();
|
|
29074
|
+
var execFile3 = (0, import_util5.promisify)(import_child_process3.execFile);
|
|
29075
|
+
var CLAUDE_DIR = import_path6.default.join(import_os6.default.homedir(), ".claude");
|
|
29076
|
+
var SESSIONS_DIR = import_path6.default.join(CLAUDE_DIR, "sessions");
|
|
29077
|
+
var PROJECTS_DIR = import_path6.default.join(CLAUDE_DIR, "projects");
|
|
29078
|
+
var CLAUDE_KNOWN_PATHS = [import_path6.default.join(import_os6.default.homedir(), ".local", "bin", "claude")];
|
|
29079
|
+
var PID_POLL_INTERVAL = 1e4;
|
|
29080
|
+
var SESSION_DIR_DEBOUNCE = 200;
|
|
29081
|
+
var INSTALL_CHECK_INTERVAL = 6e4;
|
|
29082
|
+
var MAX_SANITIZED_LENGTH = 200;
|
|
29083
|
+
var toClaudeProjectName = (dirPath) => {
|
|
29084
|
+
const sanitized = dirPath.replace(/[^a-zA-Z0-9]/g, "-");
|
|
29085
|
+
if (sanitized.length <= MAX_SANITIZED_LENGTH) {
|
|
29086
|
+
return sanitized;
|
|
29087
|
+
}
|
|
29088
|
+
const hash2 = simpleHash(dirPath);
|
|
29089
|
+
return `${sanitized.slice(0, MAX_SANITIZED_LENGTH)}-${hash2}`;
|
|
29090
|
+
};
|
|
29091
|
+
var simpleHash = (str) => {
|
|
29092
|
+
let hash2 = 0;
|
|
29093
|
+
for (let i = 0; i < str.length; i++) {
|
|
29094
|
+
hash2 = (hash2 << 5) - hash2 + str.charCodeAt(i) | 0;
|
|
29095
|
+
}
|
|
29096
|
+
return Math.abs(hash2).toString(36);
|
|
29097
|
+
};
|
|
29098
|
+
var isProcessRunning = (pid) => new Promise((resolve) => {
|
|
29099
|
+
(0, import_child_process3.execFile)("ps", ["-p", String(pid)], (err) => {
|
|
29100
|
+
resolve(!err);
|
|
29101
|
+
});
|
|
29102
|
+
});
|
|
29103
|
+
var getChildPids = async (parentPid) => {
|
|
29104
|
+
try {
|
|
29105
|
+
const { stdout } = await execFile3("pgrep", ["-P", String(parentPid)]);
|
|
29106
|
+
return stdout.trim().split("\n").map((s) => parseInt(s, 10)).filter((n) => !Number.isNaN(n));
|
|
29107
|
+
} catch {
|
|
29108
|
+
return [];
|
|
29109
|
+
}
|
|
29110
|
+
};
|
|
29111
|
+
var getProcessCwd = async (pid) => {
|
|
29112
|
+
if (isLinux) {
|
|
29113
|
+
try {
|
|
29114
|
+
return await import_promises5.default.readlink(`/proc/${pid}/cwd`);
|
|
29115
|
+
} catch {
|
|
29116
|
+
return null;
|
|
29117
|
+
}
|
|
29118
|
+
}
|
|
29119
|
+
try {
|
|
29120
|
+
const { stdout } = await execFile3("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"]);
|
|
29121
|
+
const line = stdout.split("\n").find((l) => l.startsWith("n/"));
|
|
29122
|
+
return line ? line.slice(1) : null;
|
|
29123
|
+
} catch {
|
|
29124
|
+
return null;
|
|
29125
|
+
}
|
|
29126
|
+
};
|
|
29127
|
+
var getClaudeSessionFromArgs = async (childPids) => {
|
|
29128
|
+
for (const pid of childPids) {
|
|
29129
|
+
try {
|
|
29130
|
+
const { stdout } = await execFile3("ps", ["-p", String(pid), "-o", "args="]);
|
|
29131
|
+
const args = stdout.trim();
|
|
29132
|
+
const match = args.match(/claude\s+--resume\s+([0-9a-f-]{36})/);
|
|
29133
|
+
if (match) {
|
|
29134
|
+
const cwd = await getProcessCwd(pid);
|
|
29135
|
+
return { pid, sessionId: match[1], cwd };
|
|
29136
|
+
}
|
|
29137
|
+
} catch {
|
|
29138
|
+
continue;
|
|
29139
|
+
}
|
|
29140
|
+
}
|
|
29141
|
+
return null;
|
|
29142
|
+
};
|
|
29143
|
+
var readPidFile = async (filePath) => {
|
|
29144
|
+
try {
|
|
29145
|
+
const raw = await import_promises5.default.readFile(filePath, "utf-8");
|
|
29146
|
+
const data = JSON.parse(raw);
|
|
29147
|
+
if (!data.pid || !data.sessionId) return null;
|
|
29148
|
+
return data;
|
|
29149
|
+
} catch {
|
|
29150
|
+
return null;
|
|
29151
|
+
}
|
|
29152
|
+
};
|
|
29153
|
+
var findJsonlPath = async (projectDir, sessionId) => {
|
|
29154
|
+
const jsonlPath = import_path6.default.join(projectDir, `${sessionId}.jsonl`);
|
|
29155
|
+
try {
|
|
29156
|
+
await import_promises5.default.access(jsonlPath);
|
|
29157
|
+
return jsonlPath;
|
|
29158
|
+
} catch {
|
|
29159
|
+
return null;
|
|
29160
|
+
}
|
|
29161
|
+
};
|
|
29162
|
+
var isClaudeInstalled = async () => {
|
|
29163
|
+
try {
|
|
29164
|
+
await execFile3("claude", ["--version"], { timeout: 5e3, env: { ...process.env, PATH: await getShellPath() } });
|
|
29165
|
+
return true;
|
|
29166
|
+
} catch {
|
|
29167
|
+
for (const p of CLAUDE_KNOWN_PATHS) {
|
|
29168
|
+
try {
|
|
29169
|
+
await import_promises5.default.access(p);
|
|
29170
|
+
return true;
|
|
29171
|
+
} catch {
|
|
29172
|
+
}
|
|
29173
|
+
}
|
|
29174
|
+
return false;
|
|
29175
|
+
}
|
|
29176
|
+
};
|
|
29177
|
+
var isClaudeRunning = async (panePid, preloadedChildPids) => {
|
|
29178
|
+
const childPids = preloadedChildPids ?? await getChildPids(panePid);
|
|
29179
|
+
for (const pid of childPids) {
|
|
29180
|
+
try {
|
|
29181
|
+
const { stdout } = await execFile3("ps", ["-p", String(pid), "-o", "args="]);
|
|
29182
|
+
if (stdout.trim().includes("claude")) return true;
|
|
29183
|
+
} catch {
|
|
29184
|
+
continue;
|
|
29185
|
+
}
|
|
29186
|
+
}
|
|
29187
|
+
return false;
|
|
29188
|
+
};
|
|
29189
|
+
var detectActiveSession = async (panePid, preloadedChildPids) => {
|
|
29190
|
+
try {
|
|
29191
|
+
await import_promises5.default.access(CLAUDE_DIR);
|
|
29192
|
+
} catch {
|
|
29193
|
+
const installed = await isClaudeInstalled();
|
|
29194
|
+
const status = installed ? "not-initialized" : "not-installed";
|
|
29195
|
+
return { status, sessionId: null, jsonlPath: null, pid: null, startedAt: null, cwd: null };
|
|
29196
|
+
}
|
|
29197
|
+
const directChildPids = preloadedChildPids ?? await getChildPids(panePid);
|
|
29198
|
+
if (directChildPids.length === 0) {
|
|
29199
|
+
return { status: "not-running", sessionId: null, jsonlPath: null, pid: null, startedAt: null, cwd: null };
|
|
29200
|
+
}
|
|
29201
|
+
const grandchildPids = (await Promise.all(directChildPids.map(getChildPids))).flat();
|
|
29202
|
+
const allPids = [...directChildPids, ...grandchildPids];
|
|
29203
|
+
const childPidSet = new Set(allPids);
|
|
29204
|
+
try {
|
|
29205
|
+
const pidFiles = await import_promises5.default.readdir(SESSIONS_DIR);
|
|
29206
|
+
const jsonFiles = pidFiles.filter((f) => f.endsWith(".json"));
|
|
29207
|
+
for (const file2 of jsonFiles) {
|
|
29208
|
+
const data = await readPidFile(import_path6.default.join(SESSIONS_DIR, file2));
|
|
29209
|
+
if (!data) continue;
|
|
29210
|
+
if (!childPidSet.has(data.pid)) continue;
|
|
29211
|
+
let processArgs = "";
|
|
29212
|
+
try {
|
|
29213
|
+
const { stdout } = await execFile3("ps", ["-p", String(data.pid), "-o", "args="]);
|
|
29214
|
+
processArgs = stdout.trim();
|
|
29215
|
+
} catch {
|
|
29216
|
+
try {
|
|
29217
|
+
await import_promises5.default.unlink(import_path6.default.join(SESSIONS_DIR, file2));
|
|
29218
|
+
} catch {
|
|
29219
|
+
}
|
|
29220
|
+
continue;
|
|
29221
|
+
}
|
|
29222
|
+
if (!processArgs.includes("claude")) {
|
|
29223
|
+
try {
|
|
29224
|
+
await import_promises5.default.unlink(import_path6.default.join(SESSIONS_DIR, file2));
|
|
29225
|
+
} catch {
|
|
29226
|
+
}
|
|
29227
|
+
continue;
|
|
29228
|
+
}
|
|
29229
|
+
const projectName = toClaudeProjectName(data.cwd);
|
|
29230
|
+
const projectDir = import_path6.default.join(PROJECTS_DIR, projectName);
|
|
29231
|
+
let jsonlPath = await findJsonlPath(projectDir, data.sessionId);
|
|
29232
|
+
let effectiveSessionId = data.sessionId;
|
|
29233
|
+
if (!jsonlPath) {
|
|
29234
|
+
const resumeMatch = processArgs.match(/--resume\s+([0-9a-f-]{36})/);
|
|
29235
|
+
if (resumeMatch) {
|
|
29236
|
+
const resumeSessionId = resumeMatch[1];
|
|
29237
|
+
const resumeJsonlPath = await findJsonlPath(projectDir, resumeSessionId);
|
|
29238
|
+
if (resumeJsonlPath) {
|
|
29239
|
+
jsonlPath = resumeJsonlPath;
|
|
29240
|
+
effectiveSessionId = resumeSessionId;
|
|
29241
|
+
}
|
|
29242
|
+
}
|
|
29243
|
+
}
|
|
29244
|
+
return {
|
|
29245
|
+
status: "running",
|
|
29246
|
+
sessionId: effectiveSessionId,
|
|
29247
|
+
jsonlPath,
|
|
29248
|
+
pid: data.pid,
|
|
29249
|
+
startedAt: data.startedAt,
|
|
29250
|
+
cwd: data.cwd
|
|
29251
|
+
};
|
|
29252
|
+
}
|
|
29253
|
+
} catch {
|
|
29254
|
+
}
|
|
29255
|
+
const fromArgs = await getClaudeSessionFromArgs(allPids);
|
|
29256
|
+
if (fromArgs) {
|
|
29257
|
+
const cwd = fromArgs.cwd;
|
|
29258
|
+
if (cwd) {
|
|
29259
|
+
const projectName = toClaudeProjectName(cwd);
|
|
29260
|
+
const projectDir = import_path6.default.join(PROJECTS_DIR, projectName);
|
|
29261
|
+
const jsonlPath = await findJsonlPath(projectDir, fromArgs.sessionId);
|
|
29262
|
+
return {
|
|
29263
|
+
status: "running",
|
|
29264
|
+
sessionId: fromArgs.sessionId,
|
|
29265
|
+
jsonlPath,
|
|
29266
|
+
pid: fromArgs.pid,
|
|
29267
|
+
startedAt: null,
|
|
29268
|
+
cwd
|
|
29269
|
+
};
|
|
29270
|
+
}
|
|
29271
|
+
}
|
|
29272
|
+
return { status: "not-running", sessionId: null, jsonlPath: null, pid: null, startedAt: null, cwd: null };
|
|
29273
|
+
};
|
|
29274
|
+
var watchSessionsDir = (panePid, onChange, options) => {
|
|
29275
|
+
let watcher = null;
|
|
29276
|
+
let pidPollTimer = null;
|
|
29277
|
+
let debounceTimer = null;
|
|
29278
|
+
let installCheckTimer = null;
|
|
29279
|
+
let currentPid = null;
|
|
29280
|
+
let stopped = false;
|
|
29281
|
+
const pollPid = async () => {
|
|
29282
|
+
if (stopped || !currentPid) return;
|
|
29283
|
+
const running = await isProcessRunning(currentPid);
|
|
29284
|
+
if (!running && !stopped) {
|
|
29285
|
+
currentPid = null;
|
|
29286
|
+
const info = await detectActiveSession(panePid);
|
|
29287
|
+
onChange(info);
|
|
29288
|
+
}
|
|
29289
|
+
};
|
|
29290
|
+
const handleSessionDirChange = () => {
|
|
29291
|
+
if (stopped) return;
|
|
29292
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
29293
|
+
debounceTimer = setTimeout(async () => {
|
|
29294
|
+
if (stopped) return;
|
|
29295
|
+
const info = await detectActiveSession(panePid);
|
|
29296
|
+
if (info.pid) currentPid = info.pid;
|
|
29297
|
+
onChange(info);
|
|
29298
|
+
}, SESSION_DIR_DEBOUNCE);
|
|
29299
|
+
};
|
|
29300
|
+
const tryWatch = () => {
|
|
29301
|
+
if (stopped) return;
|
|
29302
|
+
try {
|
|
29303
|
+
watcher = (0, import_fs.watch)(SESSIONS_DIR, handleSessionDirChange);
|
|
29304
|
+
watcher.on("error", () => {
|
|
29305
|
+
});
|
|
29306
|
+
if (installCheckTimer) {
|
|
29307
|
+
clearInterval(installCheckTimer);
|
|
29308
|
+
installCheckTimer = null;
|
|
29309
|
+
}
|
|
29310
|
+
} catch {
|
|
29311
|
+
if (!installCheckTimer) {
|
|
29312
|
+
installCheckTimer = setInterval(async () => {
|
|
29313
|
+
if (stopped) return;
|
|
29314
|
+
try {
|
|
29315
|
+
await import_promises5.default.access(SESSIONS_DIR);
|
|
29316
|
+
tryWatch();
|
|
29317
|
+
const info = await detectActiveSession(panePid);
|
|
29318
|
+
if (info.pid) currentPid = info.pid;
|
|
29319
|
+
onChange(info);
|
|
29320
|
+
} catch {
|
|
29321
|
+
}
|
|
29322
|
+
}, INSTALL_CHECK_INTERVAL);
|
|
29323
|
+
}
|
|
29324
|
+
}
|
|
29325
|
+
};
|
|
29326
|
+
tryWatch();
|
|
29327
|
+
pidPollTimer = setInterval(pollPid, PID_POLL_INTERVAL);
|
|
29328
|
+
if (!options?.skipInitial) {
|
|
29329
|
+
detectActiveSession(panePid).then((info) => {
|
|
29330
|
+
if (stopped) return;
|
|
29331
|
+
if (info.pid) currentPid = info.pid;
|
|
29332
|
+
onChange(info);
|
|
29333
|
+
});
|
|
29334
|
+
}
|
|
29335
|
+
return {
|
|
29336
|
+
stop: () => {
|
|
29337
|
+
stopped = true;
|
|
29338
|
+
if (watcher) watcher.close();
|
|
29339
|
+
if (pidPollTimer) clearInterval(pidPollTimer);
|
|
29340
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
29341
|
+
if (installCheckTimer) clearInterval(installCheckTimer);
|
|
29342
|
+
}
|
|
29343
|
+
};
|
|
29344
|
+
};
|
|
29345
|
+
|
|
29346
|
+
// src/lib/session-list.ts
|
|
29347
|
+
init_tmux();
|
|
29348
|
+
init_logger();
|
|
29349
|
+
|
|
29350
|
+
// src/lib/session-meta-cache.ts
|
|
29351
|
+
var CACHE_TTL = 3e4;
|
|
29352
|
+
var createMetaCache = () => {
|
|
29353
|
+
const store = /* @__PURE__ */ new Map();
|
|
29354
|
+
const get = (sessionId) => {
|
|
29355
|
+
const entry = store.get(sessionId);
|
|
29356
|
+
if (!entry) return void 0;
|
|
29357
|
+
if (Date.now() - entry.cachedAt > CACHE_TTL) {
|
|
29358
|
+
store.delete(sessionId);
|
|
29359
|
+
return void 0;
|
|
29360
|
+
}
|
|
29361
|
+
return entry.meta;
|
|
29362
|
+
};
|
|
29363
|
+
const set2 = (sessionId, meta3, mtime) => {
|
|
29364
|
+
store.set(sessionId, { meta: meta3, mtime, cachedAt: Date.now() });
|
|
29365
|
+
};
|
|
29366
|
+
const isStale = (sessionId, currentMtime) => {
|
|
29367
|
+
const entry = store.get(sessionId);
|
|
29368
|
+
if (!entry) return true;
|
|
29369
|
+
if (Date.now() - entry.cachedAt > CACHE_TTL) {
|
|
29370
|
+
store.delete(sessionId);
|
|
29371
|
+
return true;
|
|
29372
|
+
}
|
|
29373
|
+
return entry.mtime !== currentMtime;
|
|
29374
|
+
};
|
|
29375
|
+
const clear = () => {
|
|
29376
|
+
store.clear();
|
|
29377
|
+
};
|
|
29378
|
+
return { get, set: set2, isStale, clear };
|
|
29379
|
+
};
|
|
29380
|
+
|
|
29381
|
+
// src/lib/session-list.ts
|
|
29382
|
+
var log7 = createLogger("session-list");
|
|
29383
|
+
var PROJECTS_DIR2 = import_path7.default.join(
|
|
29384
|
+
process.env.HOME || process.env.USERPROFILE || "/",
|
|
29385
|
+
".claude",
|
|
29386
|
+
"projects"
|
|
29387
|
+
);
|
|
29388
|
+
var metaCache = createMetaCache();
|
|
29389
|
+
var cwdToProjectPath = (cwd) => {
|
|
29390
|
+
const projectName = toClaudeProjectName(cwd);
|
|
29391
|
+
return import_path7.default.join(PROJECTS_DIR2, projectName);
|
|
29392
|
+
};
|
|
29393
|
+
|
|
29394
|
+
// src/lib/timeline-server.ts
|
|
29395
|
+
init_layout_store();
|
|
29396
|
+
|
|
29397
|
+
// src/lib/status-manager.ts
|
|
29398
|
+
init_wrapper();
|
|
29399
|
+
init_workspace_store();
|
|
29400
|
+
init_layout_store();
|
|
29401
|
+
init_tmux();
|
|
29402
|
+
|
|
29403
|
+
// src/lib/hook-settings.ts
|
|
29404
|
+
var import_promises8 = __toESM(require("fs/promises"));
|
|
29405
|
+
var import_path11 = __toESM(require("path"));
|
|
29406
|
+
var import_os9 = __toESM(require("os"));
|
|
29407
|
+
init_logger();
|
|
29408
|
+
|
|
29409
|
+
// src/lib/statusline-script.ts
|
|
29410
|
+
var import_path10 = __toESM(require("path"));
|
|
29411
|
+
var import_os8 = __toESM(require("os"));
|
|
29412
|
+
var BASE_DIR4 = import_path10.default.join(import_os8.default.homedir(), ".purplemux");
|
|
29413
|
+
var STATUSLINE_SCRIPT_PATH = import_path10.default.join(BASE_DIR4, "statusline.sh");
|
|
29414
|
+
var RATE_LIMITS_FILE = import_path10.default.join(BASE_DIR4, "rate-limits.json");
|
|
29415
|
+
var STATUSLINE_SCRIPT_CONTENT = `#!/bin/sh
|
|
29416
|
+
PORT_FILE="$HOME/.purplemux/port"
|
|
29417
|
+
TOKEN_FILE="$HOME/.purplemux/cli-token"
|
|
29418
|
+
[ -f "$PORT_FILE" ] || exit 0
|
|
29419
|
+
[ -f "$TOKEN_FILE" ] || exit 0
|
|
29420
|
+
PORT=$(cat "$PORT_FILE")
|
|
29421
|
+
TOKEN=$(cat "$TOKEN_FILE")
|
|
29422
|
+
curl -sf --max-time 2 -X POST \\
|
|
29423
|
+
-H 'Content-Type: application/json' \\
|
|
29424
|
+
-H "x-pmux-token: \${TOKEN}" \\
|
|
29425
|
+
--data-binary @- \\
|
|
29426
|
+
"http://localhost:\${PORT}/api/status/statusline" 2>/dev/null || exit 0
|
|
29427
|
+
`;
|
|
29428
|
+
|
|
29429
|
+
// src/lib/hook-settings.ts
|
|
29430
|
+
var log10 = createLogger("hooks");
|
|
29431
|
+
var BASE_DIR5 = import_path11.default.join(import_os9.default.homedir(), ".purplemux");
|
|
29432
|
+
var HOOKS_FILE = import_path11.default.join(BASE_DIR5, "hooks.json");
|
|
29433
|
+
var PORT_FILE = import_path11.default.join(BASE_DIR5, "port");
|
|
29434
|
+
var HOOK_SCRIPT = import_path11.default.join(BASE_DIR5, "status-hook.sh");
|
|
29435
|
+
var HOOK_SETTINGS_PATH = HOOKS_FILE;
|
|
29436
|
+
var HOOK_SCRIPT_CONTENT = `#!/bin/sh
|
|
29437
|
+
EVENT="\${1:-poll}"
|
|
29438
|
+
PORT_FILE="$HOME/.purplemux/port"
|
|
29439
|
+
TOKEN_FILE="$HOME/.purplemux/cli-token"
|
|
29440
|
+
[ -f "$PORT_FILE" ] || exit 0
|
|
29441
|
+
[ -f "$TOKEN_FILE" ] || exit 0
|
|
29442
|
+
PORT=$(cat "$PORT_FILE")
|
|
29443
|
+
TOKEN=$(cat "$TOKEN_FILE")
|
|
29444
|
+
SESSION=$(tmux display-message -p '#{session_name}' 2>/dev/null) || SESSION=""
|
|
29445
|
+
|
|
29446
|
+
NOTIFICATION_TYPE=""
|
|
29447
|
+
if [ "$EVENT" = "notification" ]; then
|
|
29448
|
+
NOTIFICATION_TYPE=$(sed -n 's/.*"notification_type"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p')
|
|
29449
|
+
fi
|
|
29450
|
+
|
|
29451
|
+
PAYLOAD="{\\"event\\":\\"\${EVENT}\\",\\"session\\":\\"\${SESSION}\\""
|
|
29452
|
+
if [ -n "$NOTIFICATION_TYPE" ]; then
|
|
29453
|
+
PAYLOAD="\${PAYLOAD},\\"notificationType\\":\\"\${NOTIFICATION_TYPE}\\""
|
|
29454
|
+
fi
|
|
29455
|
+
PAYLOAD="\${PAYLOAD}}"
|
|
29456
|
+
|
|
29457
|
+
curl -s -X POST -o /dev/null -H 'Content-Type: application/json' -H "x-pmux-token: \${TOKEN}" -d "$PAYLOAD" "http://localhost:\${PORT}/api/status/hook" 2>/dev/null
|
|
29458
|
+
exit 0
|
|
29459
|
+
`;
|
|
29460
|
+
var hookEntry = (event, timeout = 3) => [
|
|
29461
|
+
{
|
|
29462
|
+
matcher: "",
|
|
29463
|
+
hooks: [
|
|
29464
|
+
{
|
|
29465
|
+
type: "command",
|
|
29466
|
+
command: `sh "${HOOK_SCRIPT}" ${event}`,
|
|
29467
|
+
timeout
|
|
29468
|
+
}
|
|
29469
|
+
]
|
|
29470
|
+
}
|
|
29471
|
+
];
|
|
29472
|
+
var buildHookSettings = () => ({
|
|
29473
|
+
hooks: {
|
|
29474
|
+
SessionStart: hookEntry("session-start"),
|
|
29475
|
+
UserPromptSubmit: hookEntry("prompt-submit"),
|
|
29476
|
+
Notification: hookEntry("notification"),
|
|
29477
|
+
Stop: hookEntry("stop"),
|
|
29478
|
+
StopFailure: hookEntry("stop"),
|
|
29479
|
+
PreCompact: hookEntry("pre-compact"),
|
|
29480
|
+
PostCompact: hookEntry("post-compact")
|
|
29481
|
+
},
|
|
29482
|
+
statusLine: {
|
|
29483
|
+
type: "command",
|
|
29484
|
+
command: `sh "${STATUSLINE_SCRIPT_PATH}"`
|
|
29485
|
+
}
|
|
29486
|
+
});
|
|
29487
|
+
var ensureHookSettings = async (port) => {
|
|
29488
|
+
await import_promises8.default.mkdir(BASE_DIR5, { recursive: true });
|
|
29489
|
+
await import_promises8.default.writeFile(PORT_FILE, String(port), { mode: 384 });
|
|
29490
|
+
try {
|
|
29491
|
+
const existing = await import_promises8.default.readFile(HOOK_SCRIPT, "utf-8");
|
|
29492
|
+
if (existing !== HOOK_SCRIPT_CONTENT) {
|
|
29493
|
+
await import_promises8.default.writeFile(HOOK_SCRIPT, HOOK_SCRIPT_CONTENT, { mode: 493 });
|
|
29494
|
+
}
|
|
29495
|
+
} catch {
|
|
29496
|
+
await import_promises8.default.writeFile(HOOK_SCRIPT, HOOK_SCRIPT_CONTENT, { mode: 493 });
|
|
29497
|
+
}
|
|
29498
|
+
try {
|
|
29499
|
+
const existing = await import_promises8.default.readFile(STATUSLINE_SCRIPT_PATH, "utf-8");
|
|
29500
|
+
if (existing !== STATUSLINE_SCRIPT_CONTENT) {
|
|
29501
|
+
await import_promises8.default.writeFile(STATUSLINE_SCRIPT_PATH, STATUSLINE_SCRIPT_CONTENT, { mode: 493 });
|
|
29502
|
+
}
|
|
29503
|
+
} catch {
|
|
29504
|
+
await import_promises8.default.writeFile(STATUSLINE_SCRIPT_PATH, STATUSLINE_SCRIPT_CONTENT, { mode: 493 });
|
|
29505
|
+
}
|
|
29506
|
+
const settings = buildHookSettings();
|
|
29507
|
+
const content = JSON.stringify(settings, null, 2) + "\n";
|
|
29508
|
+
try {
|
|
29509
|
+
const existing = await import_promises8.default.readFile(HOOKS_FILE, "utf-8");
|
|
29510
|
+
if (existing === content) return;
|
|
29511
|
+
} catch {
|
|
29512
|
+
}
|
|
29513
|
+
await import_promises8.default.writeFile(HOOKS_FILE, content, { mode: 384 });
|
|
29514
|
+
log10.debug(`${HOOKS_FILE} created`);
|
|
29515
|
+
};
|
|
29516
|
+
var removePortFile = async () => {
|
|
29517
|
+
try {
|
|
29518
|
+
await import_promises8.default.unlink(PORT_FILE);
|
|
29519
|
+
} catch {
|
|
29520
|
+
}
|
|
29521
|
+
};
|
|
29522
|
+
|
|
29523
|
+
// src/lib/claude-command.ts
|
|
29524
|
+
init_claude_prompt();
|
|
29525
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
29526
|
+
var isValidSessionId = (id) => typeof id === "string" && UUID_RE.test(id);
|
|
29527
|
+
var buildClaudeFlags = async (workspaceId) => {
|
|
29528
|
+
const skipPerms = await getDangerouslySkipPermissions();
|
|
29529
|
+
const parts = [`--settings ${HOOK_SETTINGS_PATH}`];
|
|
29530
|
+
if (workspaceId) {
|
|
29531
|
+
parts.push(`--append-system-prompt-file ${getClaudePromptPath(workspaceId)}`);
|
|
29532
|
+
}
|
|
29533
|
+
if (skipPerms) parts.push("--dangerously-skip-permissions");
|
|
29534
|
+
return parts.join(" ");
|
|
29535
|
+
};
|
|
29536
|
+
var buildResumeCommand = async (sessionId, workspaceId) => {
|
|
29537
|
+
if (!isValidSessionId(sessionId)) {
|
|
29538
|
+
throw new Error(`Invalid session ID format: ${sessionId}`);
|
|
29539
|
+
}
|
|
29540
|
+
const flags = await buildClaudeFlags(workspaceId);
|
|
29541
|
+
return `claude --resume ${sessionId} ${flags}`;
|
|
29542
|
+
};
|
|
29543
|
+
|
|
29544
|
+
// src/lib/providers/claude/index.ts
|
|
29545
|
+
var claudeProvider = {
|
|
29546
|
+
id: "claude",
|
|
29547
|
+
displayName: "Claude Code",
|
|
29548
|
+
panelType: "claude-code",
|
|
29549
|
+
matchesProcess: (commandName) => commandName === "claude" || commandName === "node",
|
|
29550
|
+
isValidSessionId,
|
|
29551
|
+
detectActiveSession: (panePid, childPids) => detectActiveSession(panePid, childPids),
|
|
29552
|
+
isAgentRunning: (panePid, childPids) => isClaudeRunning(panePid, childPids),
|
|
29553
|
+
watchSessions: (panePid, onChange, options) => watchSessionsDir(panePid, onChange, options),
|
|
29554
|
+
buildResumeCommand: (sessionId, { workspaceId }) => buildResumeCommand(sessionId, workspaceId),
|
|
29555
|
+
buildLaunchCommand: async ({ workspaceId }) => {
|
|
29556
|
+
const flags = await buildClaudeFlags(workspaceId);
|
|
29557
|
+
return `claude ${flags}`;
|
|
29558
|
+
},
|
|
29559
|
+
readSessionId: (tab) => tab.claudeSessionId ?? null,
|
|
29560
|
+
writeSessionId: (tab, sessionId) => {
|
|
29561
|
+
tab.claudeSessionId = sessionId ?? null;
|
|
29562
|
+
},
|
|
29563
|
+
readJsonlPath: (tab) => tab.claudeJsonlPath ?? null,
|
|
29564
|
+
writeJsonlPath: (tab, jsonlPath) => {
|
|
29565
|
+
tab.claudeJsonlPath = jsonlPath ?? null;
|
|
29566
|
+
},
|
|
29567
|
+
readSummary: (tab) => tab.claudeSummary ?? null,
|
|
29568
|
+
writeSummary: (tab, summary) => {
|
|
29569
|
+
tab.claudeSummary = summary ?? null;
|
|
29570
|
+
}
|
|
29571
|
+
};
|
|
29572
|
+
|
|
29573
|
+
// src/lib/providers/registry.ts
|
|
29574
|
+
var g6 = globalThis;
|
|
29575
|
+
if (!g6.__ptAgentProviders) g6.__ptAgentProviders = /* @__PURE__ */ new Map();
|
|
29576
|
+
var providers = g6.__ptAgentProviders;
|
|
29577
|
+
var registerProvider = (provider) => {
|
|
29578
|
+
providers.set(provider.id, provider);
|
|
29579
|
+
};
|
|
29580
|
+
var getProviderByPanelType = (panelType) => {
|
|
29581
|
+
if (!panelType) return null;
|
|
29582
|
+
for (const provider of providers.values()) {
|
|
29583
|
+
if (provider.panelType === panelType) return provider;
|
|
29584
|
+
}
|
|
29585
|
+
return null;
|
|
29586
|
+
};
|
|
29587
|
+
var getProviderByProcessName = (commandName) => {
|
|
29588
|
+
for (const provider of providers.values()) {
|
|
29589
|
+
if (provider.matchesProcess(commandName)) return provider;
|
|
29590
|
+
}
|
|
29591
|
+
return null;
|
|
29592
|
+
};
|
|
29593
|
+
var listProviders = () => Array.from(providers.values());
|
|
29594
|
+
|
|
29595
|
+
// src/lib/providers/index.ts
|
|
29596
|
+
registerProvider(claudeProvider);
|
|
29597
|
+
var NOT_RUNNING = {
|
|
29598
|
+
status: "not-running",
|
|
29599
|
+
sessionId: null,
|
|
29600
|
+
jsonlPath: null,
|
|
29601
|
+
pid: null,
|
|
29602
|
+
startedAt: null,
|
|
29603
|
+
cwd: null
|
|
29604
|
+
};
|
|
29605
|
+
var NOT_INSTALLED = {
|
|
29606
|
+
status: "not-installed",
|
|
29607
|
+
sessionId: null,
|
|
29608
|
+
jsonlPath: null,
|
|
29609
|
+
pid: null,
|
|
29610
|
+
startedAt: null,
|
|
29611
|
+
cwd: null
|
|
29612
|
+
};
|
|
29613
|
+
var detectAnyActiveSession = async (panePid, childPids) => {
|
|
29614
|
+
const providers2 = listProviders();
|
|
29615
|
+
let allNotInstalled = providers2.length > 0;
|
|
29616
|
+
for (const provider of providers2) {
|
|
29617
|
+
const info = await provider.detectActiveSession(panePid, childPids);
|
|
29618
|
+
if (info.status === "running") return { provider, info };
|
|
29619
|
+
if (info.status !== "not-installed") allNotInstalled = false;
|
|
29620
|
+
}
|
|
29621
|
+
return { provider: null, info: allNotInstalled ? NOT_INSTALLED : NOT_RUNNING };
|
|
29622
|
+
};
|
|
29623
|
+
|
|
29624
|
+
// src/lib/status-manager.ts
|
|
29625
|
+
init_tab_title();
|
|
29626
|
+
|
|
29627
|
+
// src/lib/rate-limits-watcher.ts
|
|
29628
|
+
var import_fs2 = __toESM(require("fs"));
|
|
29629
|
+
var import_promises9 = __toESM(require("fs/promises"));
|
|
29630
|
+
var import_path12 = __toESM(require("path"));
|
|
29631
|
+
init_logger();
|
|
29632
|
+
var log11 = createLogger("rate-limits");
|
|
29633
|
+
var DEBOUNCE_MS = 500;
|
|
29634
|
+
var WATCH_DIR = import_path12.default.dirname(RATE_LIMITS_FILE);
|
|
29635
|
+
var WATCH_FILENAME = import_path12.default.basename(RATE_LIMITS_FILE);
|
|
29636
|
+
var createRateLimitsWatcher = (onChange) => {
|
|
29637
|
+
let watcher = null;
|
|
29638
|
+
let debounceTimer = null;
|
|
29639
|
+
let lastTs = 0;
|
|
29640
|
+
const readAndNotify = async () => {
|
|
29641
|
+
try {
|
|
29642
|
+
const raw = await import_promises9.default.readFile(RATE_LIMITS_FILE, "utf-8");
|
|
29643
|
+
const data = JSON.parse(raw);
|
|
29644
|
+
if (data.ts && data.ts !== lastTs) {
|
|
29645
|
+
lastTs = data.ts;
|
|
29646
|
+
onChange(data);
|
|
29647
|
+
}
|
|
29648
|
+
} catch {
|
|
29649
|
+
}
|
|
29650
|
+
};
|
|
29651
|
+
const start2 = () => {
|
|
29652
|
+
readAndNotify();
|
|
29653
|
+
try {
|
|
29654
|
+
watcher = import_fs2.default.watch(WATCH_DIR, (_eventType, filename) => {
|
|
29655
|
+
if (filename !== WATCH_FILENAME) return;
|
|
29656
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
29657
|
+
debounceTimer = setTimeout(readAndNotify, DEBOUNCE_MS);
|
|
29658
|
+
});
|
|
29659
|
+
watcher.on("error", () => {
|
|
29660
|
+
log11.debug(`${WATCH_DIR} watch error, retrying...`);
|
|
29661
|
+
stop();
|
|
29662
|
+
setTimeout(start2, 5e3);
|
|
29663
|
+
});
|
|
29664
|
+
} catch {
|
|
29665
|
+
log11.debug(`${WATCH_DIR} not available yet, retrying...`);
|
|
29440
29666
|
setTimeout(start2, 5e3);
|
|
29441
29667
|
}
|
|
29442
29668
|
};
|
|
@@ -29618,23 +29844,23 @@ var parsePermissionOptions = (paneContent) => {
|
|
|
29618
29844
|
};
|
|
29619
29845
|
|
|
29620
29846
|
// src/lib/session-history.ts
|
|
29621
|
-
var
|
|
29622
|
-
var
|
|
29623
|
-
var
|
|
29847
|
+
var import_promises10 = __toESM(require("fs/promises"));
|
|
29848
|
+
var import_path13 = __toESM(require("path"));
|
|
29849
|
+
var import_os10 = __toESM(require("os"));
|
|
29624
29850
|
init_logger();
|
|
29625
|
-
var
|
|
29851
|
+
var log12 = createLogger("session-history");
|
|
29626
29852
|
var MAX_ENTRIES = 200;
|
|
29627
|
-
var
|
|
29628
|
-
var SESSION_HISTORY_FILE =
|
|
29629
|
-
var
|
|
29630
|
-
if (!
|
|
29853
|
+
var BASE_DIR6 = import_path13.default.join(import_os10.default.homedir(), ".purplemux");
|
|
29854
|
+
var SESSION_HISTORY_FILE = import_path13.default.join(BASE_DIR6, "session-history.json");
|
|
29855
|
+
var g7 = globalThis;
|
|
29856
|
+
if (!g7.__purplemuxSessionHistoryLock) g7.__purplemuxSessionHistoryLock = Promise.resolve();
|
|
29631
29857
|
var withLock3 = async (fn) => {
|
|
29632
29858
|
let release;
|
|
29633
29859
|
const next2 = new Promise((r) => {
|
|
29634
29860
|
release = r;
|
|
29635
29861
|
});
|
|
29636
|
-
const prev =
|
|
29637
|
-
|
|
29862
|
+
const prev = g7.__purplemuxSessionHistoryLock;
|
|
29863
|
+
g7.__purplemuxSessionHistoryLock = next2;
|
|
29638
29864
|
await prev;
|
|
29639
29865
|
try {
|
|
29640
29866
|
return await fn();
|
|
@@ -29646,16 +29872,16 @@ var emptyData = () => ({ version: 1, entries: [] });
|
|
|
29646
29872
|
var readSessionHistory = async () => {
|
|
29647
29873
|
let raw;
|
|
29648
29874
|
try {
|
|
29649
|
-
raw = await
|
|
29875
|
+
raw = await import_promises10.default.readFile(SESSION_HISTORY_FILE, "utf-8");
|
|
29650
29876
|
} catch {
|
|
29651
29877
|
return emptyData();
|
|
29652
29878
|
}
|
|
29653
29879
|
try {
|
|
29654
29880
|
return JSON.parse(raw);
|
|
29655
29881
|
} catch {
|
|
29656
|
-
|
|
29882
|
+
log12.warn("Failed to parse session-history.json, starting empty");
|
|
29657
29883
|
try {
|
|
29658
|
-
await
|
|
29884
|
+
await import_promises10.default.copyFile(SESSION_HISTORY_FILE, SESSION_HISTORY_FILE.replace(/\.json$/, ".json.bak"));
|
|
29659
29885
|
} catch {
|
|
29660
29886
|
}
|
|
29661
29887
|
return emptyData();
|
|
@@ -29663,17 +29889,17 @@ var readSessionHistory = async () => {
|
|
|
29663
29889
|
};
|
|
29664
29890
|
var writeSessionHistory = async (data) => {
|
|
29665
29891
|
const contentKey = JSON.stringify(data.entries);
|
|
29666
|
-
if (
|
|
29892
|
+
if (g7.__purplemuxSessionHistoryContentCache === contentKey) return;
|
|
29667
29893
|
const tmpFile = SESSION_HISTORY_FILE + ".tmp";
|
|
29668
29894
|
try {
|
|
29669
|
-
await
|
|
29670
|
-
await
|
|
29895
|
+
await import_promises10.default.writeFile(tmpFile, JSON.stringify(data, null, 2), { mode: 384 });
|
|
29896
|
+
await import_promises10.default.rename(tmpFile, SESSION_HISTORY_FILE);
|
|
29671
29897
|
} catch (err) {
|
|
29672
|
-
await
|
|
29898
|
+
await import_promises10.default.unlink(tmpFile).catch(() => {
|
|
29673
29899
|
});
|
|
29674
29900
|
throw err;
|
|
29675
29901
|
}
|
|
29676
|
-
|
|
29902
|
+
g7.__purplemuxSessionHistoryContentCache = contentKey;
|
|
29677
29903
|
};
|
|
29678
29904
|
var addSessionHistoryEntry = async (entry) => {
|
|
29679
29905
|
await withLock3(async () => {
|
|
@@ -29704,20 +29930,20 @@ var getSessionHistory = async () => {
|
|
|
29704
29930
|
var import_web_push = __toESM(require_src2());
|
|
29705
29931
|
|
|
29706
29932
|
// src/lib/push-subscriptions.ts
|
|
29707
|
-
var
|
|
29708
|
-
var
|
|
29709
|
-
var
|
|
29710
|
-
var
|
|
29711
|
-
var SUBS_FILE =
|
|
29712
|
-
var
|
|
29713
|
-
if (!
|
|
29933
|
+
var import_promises11 = __toESM(require("fs/promises"));
|
|
29934
|
+
var import_path14 = __toESM(require("path"));
|
|
29935
|
+
var import_os11 = __toESM(require("os"));
|
|
29936
|
+
var BASE_DIR7 = import_path14.default.join(import_os11.default.homedir(), ".purplemux");
|
|
29937
|
+
var SUBS_FILE = import_path14.default.join(BASE_DIR7, "push-subscriptions.json");
|
|
29938
|
+
var g8 = globalThis;
|
|
29939
|
+
if (!g8.__ptPushLock) g8.__ptPushLock = Promise.resolve();
|
|
29714
29940
|
var withLock4 = async (fn) => {
|
|
29715
29941
|
let release;
|
|
29716
29942
|
const next2 = new Promise((r) => {
|
|
29717
29943
|
release = r;
|
|
29718
29944
|
});
|
|
29719
|
-
const prev =
|
|
29720
|
-
|
|
29945
|
+
const prev = g8.__ptPushLock;
|
|
29946
|
+
g8.__ptPushLock = next2;
|
|
29721
29947
|
await prev;
|
|
29722
29948
|
try {
|
|
29723
29949
|
return await fn();
|
|
@@ -29727,20 +29953,20 @@ var withLock4 = async (fn) => {
|
|
|
29727
29953
|
};
|
|
29728
29954
|
var readSubs = async () => {
|
|
29729
29955
|
try {
|
|
29730
|
-
const raw = await
|
|
29956
|
+
const raw = await import_promises11.default.readFile(SUBS_FILE, "utf-8");
|
|
29731
29957
|
return JSON.parse(raw);
|
|
29732
29958
|
} catch {
|
|
29733
29959
|
return [];
|
|
29734
29960
|
}
|
|
29735
29961
|
};
|
|
29736
29962
|
var writeSubs = async (subs) => {
|
|
29737
|
-
await
|
|
29963
|
+
await import_promises11.default.mkdir(BASE_DIR7, { recursive: true });
|
|
29738
29964
|
const tmp = SUBS_FILE + ".tmp";
|
|
29739
29965
|
try {
|
|
29740
|
-
await
|
|
29741
|
-
await
|
|
29966
|
+
await import_promises11.default.writeFile(tmp, JSON.stringify(subs, null, 2), { mode: 384 });
|
|
29967
|
+
await import_promises11.default.rename(tmp, SUBS_FILE);
|
|
29742
29968
|
} catch (err) {
|
|
29743
|
-
await
|
|
29969
|
+
await import_promises11.default.unlink(tmp).catch(() => {
|
|
29744
29970
|
});
|
|
29745
29971
|
throw err;
|
|
29746
29972
|
}
|
|
@@ -29773,50 +29999,50 @@ if (!gActive.__ptSessionPushTarget) gActive.__ptSessionPushTarget = /* @__PURE__
|
|
|
29773
29999
|
var sessionPushTarget = gActive.__ptSessionPushTarget;
|
|
29774
30000
|
|
|
29775
30001
|
// src/lib/vapid-keys.ts
|
|
29776
|
-
var
|
|
29777
|
-
var
|
|
29778
|
-
var
|
|
30002
|
+
var import_promises12 = __toESM(require("fs/promises"));
|
|
30003
|
+
var import_path15 = __toESM(require("path"));
|
|
30004
|
+
var import_os12 = __toESM(require("os"));
|
|
29779
30005
|
var _webpush = __toESM(require_src2());
|
|
29780
30006
|
init_logger();
|
|
29781
30007
|
var webpush = _webpush.default ?? _webpush;
|
|
29782
|
-
var
|
|
29783
|
-
var
|
|
29784
|
-
var VAPID_FILE =
|
|
30008
|
+
var log13 = createLogger("vapid");
|
|
30009
|
+
var BASE_DIR8 = import_path15.default.join(import_os12.default.homedir(), ".purplemux");
|
|
30010
|
+
var VAPID_FILE = import_path15.default.join(BASE_DIR8, "vapid-keys.json");
|
|
29785
30011
|
var cachedKeys = null;
|
|
29786
30012
|
var getVAPIDKeys = async () => {
|
|
29787
30013
|
if (cachedKeys) return cachedKeys;
|
|
29788
30014
|
try {
|
|
29789
|
-
const raw = await
|
|
30015
|
+
const raw = await import_promises12.default.readFile(VAPID_FILE, "utf-8");
|
|
29790
30016
|
cachedKeys = JSON.parse(raw);
|
|
29791
30017
|
return cachedKeys;
|
|
29792
30018
|
} catch {
|
|
29793
30019
|
}
|
|
29794
30020
|
const keys = webpush.generateVAPIDKeys();
|
|
29795
30021
|
cachedKeys = { publicKey: keys.publicKey, privateKey: keys.privateKey };
|
|
29796
|
-
await
|
|
30022
|
+
await import_promises12.default.mkdir(BASE_DIR8, { recursive: true });
|
|
29797
30023
|
const tmp = VAPID_FILE + ".tmp";
|
|
29798
30024
|
try {
|
|
29799
|
-
await
|
|
29800
|
-
await
|
|
30025
|
+
await import_promises12.default.writeFile(tmp, JSON.stringify(cachedKeys, null, 2), { mode: 384 });
|
|
30026
|
+
await import_promises12.default.rename(tmp, VAPID_FILE);
|
|
29801
30027
|
} catch (err) {
|
|
29802
|
-
await
|
|
30028
|
+
await import_promises12.default.unlink(tmp).catch(() => {
|
|
29803
30029
|
});
|
|
29804
30030
|
throw err;
|
|
29805
30031
|
}
|
|
29806
|
-
|
|
30032
|
+
log13.info("VAPID keys generated");
|
|
29807
30033
|
return cachedKeys;
|
|
29808
30034
|
};
|
|
29809
30035
|
|
|
29810
30036
|
// src/lib/status-manager.ts
|
|
29811
30037
|
init_nanoid();
|
|
29812
|
-
var
|
|
30038
|
+
var import_promises13 = __toESM(require("fs/promises"));
|
|
29813
30039
|
var import_fs3 = require("fs");
|
|
29814
|
-
var
|
|
29815
|
-
var
|
|
30040
|
+
var import_path16 = __toESM(require("path"));
|
|
30041
|
+
var log14 = createLogger("status");
|
|
29816
30042
|
var hookLog = createLogger("hooks");
|
|
29817
30043
|
var sessionIdFromJsonlPath = (jsonlPath) => {
|
|
29818
30044
|
if (!jsonlPath) return null;
|
|
29819
|
-
return
|
|
30045
|
+
return import_path16.default.basename(jsonlPath, ".jsonl");
|
|
29820
30046
|
};
|
|
29821
30047
|
var INPUT_REQUESTING_NOTIFICATION_TYPES = /* @__PURE__ */ new Set(["permission_prompt", "worker_permission_prompt"]);
|
|
29822
30048
|
var COMPACT_STALE_MS = 6e4;
|
|
@@ -29935,7 +30161,7 @@ var scanLines = (lines, elapsed) => {
|
|
|
29935
30161
|
};
|
|
29936
30162
|
var checkJsonlIdle = async (jsonlPath) => {
|
|
29937
30163
|
try {
|
|
29938
|
-
const stat = await
|
|
30164
|
+
const stat = await import_promises13.default.stat(jsonlPath);
|
|
29939
30165
|
if (stat.size === 0) return { idle: true, stale: false, lastAssistantSnippet: null, currentAction: null, reset: false, lastEntryTs: null, staleMs: 0, interrupted: false };
|
|
29940
30166
|
const cached3 = jsonlIdleCache.get(jsonlPath);
|
|
29941
30167
|
if (cached3 && cached3.mtimeMs === stat.mtimeMs) {
|
|
@@ -29948,7 +30174,7 @@ var checkJsonlIdle = async (jsonlPath) => {
|
|
|
29948
30174
|
}
|
|
29949
30175
|
return { idle: false, stale: false, lastAssistantSnippet: cached3.lastAssistantSnippet, currentAction: cached3.currentAction, reset: cached3.reset, lastEntryTs: cached3.lastEntryTs, staleMs: cached3.staleMs, interrupted: cached3.interrupted };
|
|
29950
30176
|
}
|
|
29951
|
-
const handle = await
|
|
30177
|
+
const handle = await import_promises13.default.open(jsonlPath, "r");
|
|
29952
30178
|
try {
|
|
29953
30179
|
const elapsed = Date.now() - stat.mtimeMs;
|
|
29954
30180
|
const readSize = Math.min(stat.size, JSONL_TAIL_SIZE);
|
|
@@ -29980,9 +30206,9 @@ var checkJsonlIdle = async (jsonlPath) => {
|
|
|
29980
30206
|
var parseJsonlStats = async (jsonlPath) => {
|
|
29981
30207
|
const empty = { toolUsage: {}, touchedFiles: [], lastAssistantText: null, lastUserText: null, firstUserTs: null, lastAssistantTs: null, turnDurationMs: null };
|
|
29982
30208
|
try {
|
|
29983
|
-
const stat = await
|
|
30209
|
+
const stat = await import_promises13.default.stat(jsonlPath);
|
|
29984
30210
|
if (stat.size === 0) return empty;
|
|
29985
|
-
const handle = await
|
|
30211
|
+
const handle = await import_promises13.default.open(jsonlPath, "r");
|
|
29986
30212
|
try {
|
|
29987
30213
|
const readSize = Math.min(stat.size, JSONL_EXTENDED_TAIL_SIZE);
|
|
29988
30214
|
const buffer = Buffer.alloc(readSize);
|
|
@@ -30054,7 +30280,7 @@ var parseClaudePaneTitle = (paneTitle) => {
|
|
|
30054
30280
|
const cleaned = paneTitle.replace(CLAUDE_TITLE_RE, "").trim();
|
|
30055
30281
|
return cleaned || null;
|
|
30056
30282
|
};
|
|
30057
|
-
var
|
|
30283
|
+
var g9 = globalThis;
|
|
30058
30284
|
var StatusManager = class _StatusManager {
|
|
30059
30285
|
constructor() {
|
|
30060
30286
|
this.tabs = /* @__PURE__ */ new Map();
|
|
@@ -30091,10 +30317,11 @@ var StatusManager = class _StatusManager {
|
|
|
30091
30317
|
const tabs = collectAllTabs(layout.root);
|
|
30092
30318
|
for (const tab of tabs) {
|
|
30093
30319
|
const paneInfo = panesInfo.get(tab.sessionName);
|
|
30094
|
-
const
|
|
30320
|
+
const provider = getProviderByPanelType(tab.panelType);
|
|
30321
|
+
const detected = await this.readTabMetadata(paneInfo, provider);
|
|
30095
30322
|
const persisted = tab.cliState ?? "idle";
|
|
30096
30323
|
const cliState = persisted === "busy" ? "unknown" : persisted;
|
|
30097
|
-
const { terminalStatus, listeningPorts } =
|
|
30324
|
+
const { terminalStatus, listeningPorts } = provider ? { terminalStatus: "idle", listeningPorts: [] } : await this.detectTerminalStatus(paneInfo);
|
|
30098
30325
|
const currentProcess = paneInfo?.command;
|
|
30099
30326
|
const paneTitle = paneInfo ? `${paneInfo.command}|${paneInfo.path}` : void 0;
|
|
30100
30327
|
const syntheticLastEvent = cliState === "needs-input" ? { name: "notification", at: Date.now(), seq: 0 } : null;
|
|
@@ -30124,7 +30351,7 @@ var StatusManager = class _StatusManager {
|
|
|
30124
30351
|
this.startJsonlWatch(tab.id, detected.jsonlPath);
|
|
30125
30352
|
}
|
|
30126
30353
|
if (cliState === "unknown") {
|
|
30127
|
-
this.resolveUnknown(tab.id).catch((err) =>
|
|
30354
|
+
this.resolveUnknown(tab.id).catch((err) => log14.warn("resolveUnknown failed: %s", err));
|
|
30128
30355
|
}
|
|
30129
30356
|
}
|
|
30130
30357
|
}
|
|
@@ -30132,10 +30359,11 @@ var StatusManager = class _StatusManager {
|
|
|
30132
30359
|
async resolveUnknown(tabId) {
|
|
30133
30360
|
const entry = this.tabs.get(tabId);
|
|
30134
30361
|
if (!entry || entry.cliState !== "unknown") return;
|
|
30362
|
+
const provider = getProviderByPanelType(entry.panelType);
|
|
30135
30363
|
const paneInfo = (await getAllPanesInfo()).get(entry.tmuxSession);
|
|
30136
30364
|
const childPids = paneInfo?.pid ? await getChildPids(paneInfo.pid) : [];
|
|
30137
|
-
const
|
|
30138
|
-
if (!
|
|
30365
|
+
const agentRunning = paneInfo?.pid && provider ? await provider.isAgentRunning(paneInfo.pid, childPids) : false;
|
|
30366
|
+
if (!agentRunning) {
|
|
30139
30367
|
this.applyCliState(tabId, entry, "idle", { silent: true });
|
|
30140
30368
|
this.persistToLayout(entry);
|
|
30141
30369
|
this.broadcastUpdate(tabId, entry);
|
|
@@ -30151,13 +30379,13 @@ var StatusManager = class _StatusManager {
|
|
|
30151
30379
|
}
|
|
30152
30380
|
}
|
|
30153
30381
|
}
|
|
30154
|
-
async readTabMetadata(paneInfo) {
|
|
30382
|
+
async readTabMetadata(paneInfo, provider) {
|
|
30155
30383
|
const empty = { lastAssistantSnippet: null, currentAction: null, jsonlPath: null };
|
|
30156
|
-
if (!paneInfo || !paneInfo.pid) return empty;
|
|
30384
|
+
if (!paneInfo || !paneInfo.pid || !provider) return empty;
|
|
30157
30385
|
const childPids = await getChildPids(paneInfo.pid);
|
|
30158
|
-
const
|
|
30159
|
-
if (!
|
|
30160
|
-
const session = await detectActiveSession(paneInfo.pid, childPids);
|
|
30386
|
+
const running = await provider.isAgentRunning(paneInfo.pid, childPids);
|
|
30387
|
+
if (!running) return empty;
|
|
30388
|
+
const session = await provider.detectActiveSession(paneInfo.pid, childPids);
|
|
30161
30389
|
if (session.status !== "running" || !session.jsonlPath) {
|
|
30162
30390
|
return { lastAssistantSnippet: null, currentAction: null, jsonlPath: session.jsonlPath ?? null };
|
|
30163
30391
|
}
|
|
@@ -30185,7 +30413,7 @@ var StatusManager = class _StatusManager {
|
|
|
30185
30413
|
this.currentInterval = this.getPollingInterval();
|
|
30186
30414
|
this.pollingTimer = setInterval(() => {
|
|
30187
30415
|
this.poll().catch((err) => {
|
|
30188
|
-
|
|
30416
|
+
log14.error({ err }, "Polling error");
|
|
30189
30417
|
});
|
|
30190
30418
|
}, this.currentInterval);
|
|
30191
30419
|
}
|
|
@@ -30210,13 +30438,14 @@ var StatusManager = class _StatusManager {
|
|
|
30210
30438
|
knownTabIds.add(tab.id);
|
|
30211
30439
|
const existing = this.tabs.get(tab.id);
|
|
30212
30440
|
const paneInfo = panesInfo.get(tab.sessionName);
|
|
30213
|
-
const
|
|
30441
|
+
const provider = getProviderByPanelType(tab.panelType);
|
|
30442
|
+
const { terminalStatus, listeningPorts } = provider ? { terminalStatus: "idle", listeningPorts: [] } : await this.detectTerminalStatus(paneInfo);
|
|
30214
30443
|
const currentProcess = paneInfo?.command;
|
|
30215
30444
|
const newPaneTitle = paneInfo ? `${paneInfo.command}|${paneInfo.path}` : void 0;
|
|
30216
30445
|
if (!existing) {
|
|
30217
30446
|
const persisted = tab.cliState ?? "idle";
|
|
30218
30447
|
const initialState = persisted === "busy" ? "unknown" : persisted;
|
|
30219
|
-
const detected = await this.readTabMetadata(paneInfo);
|
|
30448
|
+
const detected = await this.readTabMetadata(paneInfo, provider);
|
|
30220
30449
|
const syntheticLastEvent = initialState === "needs-input" ? { name: "notification", at: Date.now(), seq: 0 } : null;
|
|
30221
30450
|
const entry = {
|
|
30222
30451
|
cliState: initialState,
|
|
@@ -30241,14 +30470,14 @@ var StatusManager = class _StatusManager {
|
|
|
30241
30470
|
this.persistToLayout(entry);
|
|
30242
30471
|
this.broadcastUpdate(tab.id, entry);
|
|
30243
30472
|
if (initialState === "unknown") {
|
|
30244
|
-
this.resolveUnknown(tab.id).catch((err) =>
|
|
30473
|
+
this.resolveUnknown(tab.id).catch((err) => log14.warn("resolveUnknown failed: %s", err));
|
|
30245
30474
|
}
|
|
30246
30475
|
continue;
|
|
30247
30476
|
}
|
|
30248
30477
|
const processChanged = existing.currentProcess !== currentProcess;
|
|
30249
30478
|
const messageChanged = existing.lastUserMessage !== tab.lastUserMessage;
|
|
30250
30479
|
const panelTypeChanged = existing.panelType !== tab.panelType;
|
|
30251
|
-
const refreshed = await this.readTabMetadata(paneInfo);
|
|
30480
|
+
const refreshed = await this.readTabMetadata(paneInfo, provider);
|
|
30252
30481
|
existing.tabName = tab.name || (newPaneTitle ? formatTabTitle(newPaneTitle) : "");
|
|
30253
30482
|
existing.currentProcess = currentProcess;
|
|
30254
30483
|
existing.paneTitle = newPaneTitle;
|
|
@@ -30289,9 +30518,9 @@ var StatusManager = class _StatusManager {
|
|
|
30289
30518
|
}
|
|
30290
30519
|
if (existing.cliState === "busy" && existing.lastEvent && now - existing.lastEvent.at > BUSY_STUCK_MS) {
|
|
30291
30520
|
const childPids = paneInfo?.pid ? await getChildPids(paneInfo.pid) : [];
|
|
30292
|
-
const
|
|
30293
|
-
if (!
|
|
30294
|
-
|
|
30521
|
+
const agentRunning = paneInfo?.pid && provider ? await provider.isAgentRunning(paneInfo.pid, childPids) : false;
|
|
30522
|
+
if (!agentRunning) {
|
|
30523
|
+
log14.info({ tabId: tab.id }, "busy stuck \u2014 agent process gone, forcing idle");
|
|
30295
30524
|
this.applyCliState(tab.id, existing, "idle", { silent: true });
|
|
30296
30525
|
this.persistToLayout(existing);
|
|
30297
30526
|
this.broadcastUpdate(tab.id, existing);
|
|
@@ -30352,17 +30581,17 @@ var StatusManager = class _StatusManager {
|
|
|
30352
30581
|
if (newState === "ready-for-review" && entry.jsonlPath) {
|
|
30353
30582
|
const delay = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
30354
30583
|
delay(500).then(() => this.saveSessionHistory(tabId, entry, prevBusySince, false)).catch((err) => {
|
|
30355
|
-
|
|
30584
|
+
log14.warn("Failed to save session history: %s", err);
|
|
30356
30585
|
});
|
|
30357
30586
|
}
|
|
30358
30587
|
if (newState === "ready-for-review" && !opts.silent) {
|
|
30359
30588
|
this.sendWebPush(tabId, entry, "review").catch((err) => {
|
|
30360
|
-
|
|
30589
|
+
log14.warn("Web push failed: %s", err);
|
|
30361
30590
|
});
|
|
30362
30591
|
}
|
|
30363
30592
|
if (newState === "needs-input" && !opts.silent) {
|
|
30364
30593
|
this.sendWebPush(tabId, entry, "needs-input").catch((err) => {
|
|
30365
|
-
|
|
30594
|
+
log14.warn("Web push failed: %s", err);
|
|
30366
30595
|
});
|
|
30367
30596
|
}
|
|
30368
30597
|
const shouldWatch = (newState === "busy" || newState === "needs-input") && entry.jsonlPath;
|
|
@@ -30413,7 +30642,7 @@ var StatusManager = class _StatusManager {
|
|
|
30413
30642
|
updateSessionHistoryDismissedAt(tabId, dismissedAt).then((updated) => {
|
|
30414
30643
|
if (updated) this.broadcast({ type: "session-history:update", entry: updated });
|
|
30415
30644
|
}).catch((err) => {
|
|
30416
|
-
|
|
30645
|
+
log14.warn("Failed to update session history dismissedAt: %s", err);
|
|
30417
30646
|
});
|
|
30418
30647
|
}
|
|
30419
30648
|
ackNotificationInput(tabId, seq) {
|
|
@@ -30431,7 +30660,7 @@ var StatusManager = class _StatusManager {
|
|
|
30431
30660
|
if (!entry) return { recovered: false, reason: "no-entry" };
|
|
30432
30661
|
if (entry.cliState !== "unknown") return { recovered: false, reason: "not-unknown" };
|
|
30433
30662
|
const content = await capturePaneAtWidth(entry.tmuxSession, 120, 50).catch((err) => {
|
|
30434
|
-
|
|
30663
|
+
log14.warn("recoverUnknownIfPending capture failed: %s", err);
|
|
30435
30664
|
return null;
|
|
30436
30665
|
});
|
|
30437
30666
|
if (!content) return { recovered: false, reason: "capture-failed" };
|
|
@@ -30557,7 +30786,7 @@ var StatusManager = class _StatusManager {
|
|
|
30557
30786
|
const entry = this.tabs.get(tabId);
|
|
30558
30787
|
if (entry && (entry.cliState === "busy" || entry.cliState === "needs-input") && entry.lastUserMessage) {
|
|
30559
30788
|
this.saveSessionHistory(tabId, entry, entry.busySince, true).catch((err) => {
|
|
30560
|
-
|
|
30789
|
+
log14.warn("Failed to save cancelled session history: %s", err);
|
|
30561
30790
|
});
|
|
30562
30791
|
}
|
|
30563
30792
|
this.stopJsonlWatch(tabId);
|
|
@@ -30662,7 +30891,7 @@ var StatusManager = class _StatusManager {
|
|
|
30662
30891
|
if (cwd) {
|
|
30663
30892
|
const candidate = `${cwdToProjectPath(cwd)}/${tab.claudeSessionId}.jsonl`;
|
|
30664
30893
|
try {
|
|
30665
|
-
await
|
|
30894
|
+
await import_promises13.default.access(candidate);
|
|
30666
30895
|
jsonlPath = candidate;
|
|
30667
30896
|
} catch {
|
|
30668
30897
|
}
|
|
@@ -30677,8 +30906,8 @@ var StatusManager = class _StatusManager {
|
|
|
30677
30906
|
if (!jsonlPath) {
|
|
30678
30907
|
const panePid = await getSessionPanePid(tmuxSession);
|
|
30679
30908
|
if (panePid) {
|
|
30680
|
-
const
|
|
30681
|
-
jsonlPath =
|
|
30909
|
+
const { info } = await detectAnyActiveSession(panePid);
|
|
30910
|
+
jsonlPath = info.jsonlPath;
|
|
30682
30911
|
}
|
|
30683
30912
|
}
|
|
30684
30913
|
if (!jsonlPath) return;
|
|
@@ -30692,7 +30921,7 @@ var StatusManager = class _StatusManager {
|
|
|
30692
30921
|
const existing = this.jsonlWatchers.get(tabId);
|
|
30693
30922
|
if (existing?.jsonlPath === jsonlPath) return;
|
|
30694
30923
|
if (existing) this.stopJsonlWatch(tabId);
|
|
30695
|
-
|
|
30924
|
+
log14.debug("startJsonlWatch tabId=%s path=%s", tabId, jsonlPath);
|
|
30696
30925
|
try {
|
|
30697
30926
|
const watcher = (0, import_fs3.watch)(jsonlPath, () => {
|
|
30698
30927
|
const w = this.jsonlWatchers.get(tabId);
|
|
@@ -30713,7 +30942,7 @@ var StatusManager = class _StatusManager {
|
|
|
30713
30942
|
stopJsonlWatch(tabId) {
|
|
30714
30943
|
const w = this.jsonlWatchers.get(tabId);
|
|
30715
30944
|
if (!w) return;
|
|
30716
|
-
|
|
30945
|
+
log14.debug("stopJsonlWatch tabId=%s", tabId);
|
|
30717
30946
|
if (w.debounceTimer) clearTimeout(w.debounceTimer);
|
|
30718
30947
|
try {
|
|
30719
30948
|
w.watcher.close();
|
|
@@ -30809,140 +31038,21 @@ var StatusManager = class _StatusManager {
|
|
|
30809
31038
|
if (status === 410 || status === 404) {
|
|
30810
31039
|
await removeSubscription(sub.endpoint);
|
|
30811
31040
|
}
|
|
30812
|
-
|
|
31041
|
+
log14.warn("Web push send error: %s", status);
|
|
30813
31042
|
}
|
|
30814
31043
|
}
|
|
30815
31044
|
}
|
|
30816
31045
|
};
|
|
30817
31046
|
var getStatusManager = () => {
|
|
30818
|
-
if (!
|
|
31047
|
+
if (!g9.__ptStatusManager) {
|
|
30819
31048
|
const manager = new StatusManager();
|
|
30820
|
-
|
|
31049
|
+
g9.__ptStatusManager = manager;
|
|
30821
31050
|
setLayoutReconciler({
|
|
30822
31051
|
reconcileWorkspaceTabs: (wsId, validTabIds) => manager.reconcileWorkspaceTabs(wsId, validTabIds),
|
|
30823
31052
|
removeWorkspaceTabs: (wsId) => manager.removeWorkspaceTabs(wsId)
|
|
30824
31053
|
});
|
|
30825
31054
|
}
|
|
30826
|
-
return
|
|
30827
|
-
};
|
|
30828
|
-
|
|
30829
|
-
// src/lib/hook-settings.ts
|
|
30830
|
-
var import_promises13 = __toESM(require("fs/promises"));
|
|
30831
|
-
var import_path16 = __toESM(require("path"));
|
|
30832
|
-
var import_os12 = __toESM(require("os"));
|
|
30833
|
-
init_logger();
|
|
30834
|
-
var log14 = createLogger("hooks");
|
|
30835
|
-
var BASE_DIR8 = import_path16.default.join(import_os12.default.homedir(), ".purplemux");
|
|
30836
|
-
var HOOKS_FILE = import_path16.default.join(BASE_DIR8, "hooks.json");
|
|
30837
|
-
var PORT_FILE = import_path16.default.join(BASE_DIR8, "port");
|
|
30838
|
-
var HOOK_SCRIPT = import_path16.default.join(BASE_DIR8, "status-hook.sh");
|
|
30839
|
-
var HOOK_SETTINGS_PATH = HOOKS_FILE;
|
|
30840
|
-
var HOOK_SCRIPT_CONTENT = `#!/bin/sh
|
|
30841
|
-
EVENT="\${1:-poll}"
|
|
30842
|
-
PORT_FILE="$HOME/.purplemux/port"
|
|
30843
|
-
TOKEN_FILE="$HOME/.purplemux/cli-token"
|
|
30844
|
-
[ -f "$PORT_FILE" ] || exit 0
|
|
30845
|
-
[ -f "$TOKEN_FILE" ] || exit 0
|
|
30846
|
-
PORT=$(cat "$PORT_FILE")
|
|
30847
|
-
TOKEN=$(cat "$TOKEN_FILE")
|
|
30848
|
-
SESSION=$(tmux display-message -p '#{session_name}' 2>/dev/null) || SESSION=""
|
|
30849
|
-
|
|
30850
|
-
NOTIFICATION_TYPE=""
|
|
30851
|
-
if [ "$EVENT" = "notification" ]; then
|
|
30852
|
-
NOTIFICATION_TYPE=$(sed -n 's/.*"notification_type"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p')
|
|
30853
|
-
fi
|
|
30854
|
-
|
|
30855
|
-
PAYLOAD="{\\"event\\":\\"\${EVENT}\\",\\"session\\":\\"\${SESSION}\\""
|
|
30856
|
-
if [ -n "$NOTIFICATION_TYPE" ]; then
|
|
30857
|
-
PAYLOAD="\${PAYLOAD},\\"notificationType\\":\\"\${NOTIFICATION_TYPE}\\""
|
|
30858
|
-
fi
|
|
30859
|
-
PAYLOAD="\${PAYLOAD}}"
|
|
30860
|
-
|
|
30861
|
-
curl -s -X POST -o /dev/null -H 'Content-Type: application/json' -H "x-pmux-token: \${TOKEN}" -d "$PAYLOAD" "http://localhost:\${PORT}/api/status/hook" 2>/dev/null
|
|
30862
|
-
exit 0
|
|
30863
|
-
`;
|
|
30864
|
-
var hookEntry = (event, timeout = 3) => [
|
|
30865
|
-
{
|
|
30866
|
-
matcher: "",
|
|
30867
|
-
hooks: [
|
|
30868
|
-
{
|
|
30869
|
-
type: "command",
|
|
30870
|
-
command: `sh "${HOOK_SCRIPT}" ${event}`,
|
|
30871
|
-
timeout
|
|
30872
|
-
}
|
|
30873
|
-
]
|
|
30874
|
-
}
|
|
30875
|
-
];
|
|
30876
|
-
var buildHookSettings = () => ({
|
|
30877
|
-
hooks: {
|
|
30878
|
-
SessionStart: hookEntry("session-start"),
|
|
30879
|
-
UserPromptSubmit: hookEntry("prompt-submit"),
|
|
30880
|
-
Notification: hookEntry("notification"),
|
|
30881
|
-
Stop: hookEntry("stop"),
|
|
30882
|
-
StopFailure: hookEntry("stop"),
|
|
30883
|
-
PreCompact: hookEntry("pre-compact"),
|
|
30884
|
-
PostCompact: hookEntry("post-compact")
|
|
30885
|
-
},
|
|
30886
|
-
statusLine: {
|
|
30887
|
-
type: "command",
|
|
30888
|
-
command: `sh "${STATUSLINE_SCRIPT_PATH}"`
|
|
30889
|
-
}
|
|
30890
|
-
});
|
|
30891
|
-
var ensureHookSettings = async (port) => {
|
|
30892
|
-
await import_promises13.default.mkdir(BASE_DIR8, { recursive: true });
|
|
30893
|
-
await import_promises13.default.writeFile(PORT_FILE, String(port), { mode: 384 });
|
|
30894
|
-
try {
|
|
30895
|
-
const existing = await import_promises13.default.readFile(HOOK_SCRIPT, "utf-8");
|
|
30896
|
-
if (existing !== HOOK_SCRIPT_CONTENT) {
|
|
30897
|
-
await import_promises13.default.writeFile(HOOK_SCRIPT, HOOK_SCRIPT_CONTENT, { mode: 493 });
|
|
30898
|
-
}
|
|
30899
|
-
} catch {
|
|
30900
|
-
await import_promises13.default.writeFile(HOOK_SCRIPT, HOOK_SCRIPT_CONTENT, { mode: 493 });
|
|
30901
|
-
}
|
|
30902
|
-
try {
|
|
30903
|
-
const existing = await import_promises13.default.readFile(STATUSLINE_SCRIPT_PATH, "utf-8");
|
|
30904
|
-
if (existing !== STATUSLINE_SCRIPT_CONTENT) {
|
|
30905
|
-
await import_promises13.default.writeFile(STATUSLINE_SCRIPT_PATH, STATUSLINE_SCRIPT_CONTENT, { mode: 493 });
|
|
30906
|
-
}
|
|
30907
|
-
} catch {
|
|
30908
|
-
await import_promises13.default.writeFile(STATUSLINE_SCRIPT_PATH, STATUSLINE_SCRIPT_CONTENT, { mode: 493 });
|
|
30909
|
-
}
|
|
30910
|
-
const settings = buildHookSettings();
|
|
30911
|
-
const content = JSON.stringify(settings, null, 2) + "\n";
|
|
30912
|
-
try {
|
|
30913
|
-
const existing = await import_promises13.default.readFile(HOOKS_FILE, "utf-8");
|
|
30914
|
-
if (existing === content) return;
|
|
30915
|
-
} catch {
|
|
30916
|
-
}
|
|
30917
|
-
await import_promises13.default.writeFile(HOOKS_FILE, content, { mode: 384 });
|
|
30918
|
-
log14.debug(`${HOOKS_FILE} created`);
|
|
30919
|
-
};
|
|
30920
|
-
var removePortFile = async () => {
|
|
30921
|
-
try {
|
|
30922
|
-
await import_promises13.default.unlink(PORT_FILE);
|
|
30923
|
-
} catch {
|
|
30924
|
-
}
|
|
30925
|
-
};
|
|
30926
|
-
|
|
30927
|
-
// src/lib/claude-command.ts
|
|
30928
|
-
init_claude_prompt();
|
|
30929
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
30930
|
-
var isValidSessionId = (id) => typeof id === "string" && UUID_RE.test(id);
|
|
30931
|
-
var buildClaudeFlags = async (workspaceId) => {
|
|
30932
|
-
const skipPerms = await getDangerouslySkipPermissions();
|
|
30933
|
-
const parts = [`--settings ${HOOK_SETTINGS_PATH}`];
|
|
30934
|
-
if (workspaceId) {
|
|
30935
|
-
parts.push(`--append-system-prompt-file ${getClaudePromptPath(workspaceId)}`);
|
|
30936
|
-
}
|
|
30937
|
-
if (skipPerms) parts.push("--dangerously-skip-permissions");
|
|
30938
|
-
return parts.join(" ");
|
|
30939
|
-
};
|
|
30940
|
-
var buildResumeCommand = async (sessionId, workspaceId) => {
|
|
30941
|
-
if (!isValidSessionId(sessionId)) {
|
|
30942
|
-
throw new Error(`Invalid session ID format: ${sessionId}`);
|
|
30943
|
-
}
|
|
30944
|
-
const flags = await buildClaudeFlags(workspaceId);
|
|
30945
|
-
return `claude --resume ${sessionId} ${flags}`;
|
|
31055
|
+
return g9.__ptStatusManager;
|
|
30946
31056
|
};
|
|
30947
31057
|
|
|
30948
31058
|
// src/lib/session-stats.ts
|
|
@@ -31086,10 +31196,10 @@ var findLastUserMessage = (entries) => {
|
|
|
31086
31196
|
}
|
|
31087
31197
|
return null;
|
|
31088
31198
|
};
|
|
31089
|
-
var subscribeAndUpdateSummary = async (ws, jsonlPath, sessionId, sessionName) => {
|
|
31090
|
-
const jsonlSummary = await subscribeToFile(ws, jsonlPath, sessionId, sessionName);
|
|
31199
|
+
var subscribeAndUpdateSummary = async (ws, jsonlPath, sessionId, sessionName, provider) => {
|
|
31200
|
+
const jsonlSummary = await subscribeToFile(ws, jsonlPath, sessionId, sessionName, provider);
|
|
31091
31201
|
const summary = await resolveClaudeSummary(sessionName, jsonlSummary);
|
|
31092
|
-
await
|
|
31202
|
+
await updateTabAgentSummary(sessionName, provider, summary).catch(() => {
|
|
31093
31203
|
});
|
|
31094
31204
|
};
|
|
31095
31205
|
var broadcastToWatcher = (watcherKey, msg) => {
|
|
@@ -31159,19 +31269,17 @@ var processFileChange = async (fw) => {
|
|
|
31159
31269
|
if (partialReads.length > 0) {
|
|
31160
31270
|
await Promise.all(partialReads);
|
|
31161
31271
|
}
|
|
31162
|
-
|
|
31163
|
-
|
|
31164
|
-
|
|
31165
|
-
|
|
31166
|
-
|
|
31167
|
-
getStatusManager().notifyLastUserMessage(fw.sessionName, lastMsg);
|
|
31168
|
-
}
|
|
31272
|
+
const lastMsg = findLastUserMessage(newEntries);
|
|
31273
|
+
if (lastMsg) {
|
|
31274
|
+
await updateTabLastUserMessage(fw.sessionName, lastMsg).catch(() => {
|
|
31275
|
+
});
|
|
31276
|
+
getStatusManager().notifyLastUserMessage(fw.sessionName, lastMsg);
|
|
31169
31277
|
}
|
|
31170
|
-
if (!fw.summaryResolved &&
|
|
31278
|
+
if (!fw.summaryResolved && newEntries.some((e) => e.type === "assistant-message")) {
|
|
31171
31279
|
fw.summaryResolved = true;
|
|
31172
31280
|
const summary = await resolveClaudeSummary(fw.sessionName, void 0);
|
|
31173
31281
|
if (summary) {
|
|
31174
|
-
await
|
|
31282
|
+
await updateTabAgentSummary(fw.sessionName, fw.provider, summary).catch(() => {
|
|
31175
31283
|
});
|
|
31176
31284
|
}
|
|
31177
31285
|
}
|
|
@@ -31264,7 +31372,7 @@ var computeInitMeta = (entries, fileSize, createdAtOverride, customTitle) => {
|
|
|
31264
31372
|
customTitle
|
|
31265
31373
|
};
|
|
31266
31374
|
};
|
|
31267
|
-
var subscribeToFile = async (ws, jsonlPath, sessionId, sessionName) => {
|
|
31375
|
+
var subscribeToFile = async (ws, jsonlPath, sessionId, sessionName, provider) => {
|
|
31268
31376
|
if (!(0, import_fs6.existsSync)(jsonlPath)) {
|
|
31269
31377
|
sendJson(ws, { type: "timeline:init", entries: [], sessionId: sessionId ?? "", totalEntries: 0, startByteOffset: 0, hasMore: false, jsonlPath });
|
|
31270
31378
|
return void 0;
|
|
@@ -31284,7 +31392,8 @@ var subscribeToFile = async (ws, jsonlPath, sessionId, sessionName) => {
|
|
|
31284
31392
|
connections: /* @__PURE__ */ new Set(),
|
|
31285
31393
|
debounceTimer: null,
|
|
31286
31394
|
retryCount: 0,
|
|
31287
|
-
sessionName
|
|
31395
|
+
sessionName,
|
|
31396
|
+
provider,
|
|
31288
31397
|
summaryResolved: false,
|
|
31289
31398
|
processing: false,
|
|
31290
31399
|
pendingChange: false,
|
|
@@ -31293,9 +31402,6 @@ var subscribeToFile = async (ws, jsonlPath, sessionId, sessionName) => {
|
|
|
31293
31402
|
fileWatchers.set(jsonlPath, fw);
|
|
31294
31403
|
}
|
|
31295
31404
|
fw.connections.add(ws);
|
|
31296
|
-
if (sessionName && !fw.sessionName) {
|
|
31297
|
-
fw.sessionName = sessionName;
|
|
31298
|
-
}
|
|
31299
31405
|
const result = await readTailEntries(jsonlPath, MAX_INIT_ENTRIES);
|
|
31300
31406
|
if (result.errorCount > 0) {
|
|
31301
31407
|
sendJson(ws, {
|
|
@@ -31362,7 +31468,7 @@ var cancelJsonlWatcher = (sessionName) => {
|
|
|
31362
31468
|
pendingJsonlWatchers.delete(sessionName);
|
|
31363
31469
|
}
|
|
31364
31470
|
};
|
|
31365
|
-
var watchForJsonlFile = (sessionName, sessionId, cwd) => {
|
|
31471
|
+
var watchForJsonlFile = (sessionName, sessionId, cwd, provider) => {
|
|
31366
31472
|
cancelJsonlWatcher(sessionName);
|
|
31367
31473
|
const projectDir = cwdToProjectPath(cwd);
|
|
31368
31474
|
const jsonlFilename = `${sessionId}.jsonl`;
|
|
@@ -31385,7 +31491,7 @@ var watchForJsonlFile = (sessionName, sessionId, cwd) => {
|
|
|
31385
31491
|
const onJsonlFound = async () => {
|
|
31386
31492
|
stop();
|
|
31387
31493
|
pendingJsonlWatchers.delete(sessionName);
|
|
31388
|
-
await
|
|
31494
|
+
await updateTabAgentSessionId(sessionName, provider, sessionId).catch(() => {
|
|
31389
31495
|
});
|
|
31390
31496
|
const wsConns = getSessionConnections(sessionName);
|
|
31391
31497
|
for (const c of wsConns) {
|
|
@@ -31394,7 +31500,7 @@ var watchForJsonlFile = (sessionName, sessionId, cwd) => {
|
|
|
31394
31500
|
unsubscribeFromFile(c.ws, c.currentJsonlPath);
|
|
31395
31501
|
}
|
|
31396
31502
|
c.currentJsonlPath = expectedJsonlPath;
|
|
31397
|
-
await subscribeAndUpdateSummary(c.ws, expectedJsonlPath, sessionId, sessionName);
|
|
31503
|
+
await subscribeAndUpdateSummary(c.ws, expectedJsonlPath, sessionId, sessionName, provider);
|
|
31398
31504
|
}
|
|
31399
31505
|
}
|
|
31400
31506
|
};
|
|
@@ -31489,9 +31595,9 @@ var handleResumeMessage = async (ws, conn, payload) => {
|
|
|
31489
31595
|
return;
|
|
31490
31596
|
}
|
|
31491
31597
|
const parsed = parseSessionName(tmuxSession);
|
|
31492
|
-
const resumeCmd = await buildResumeCommand(sessionId, parsed?.wsId);
|
|
31598
|
+
const resumeCmd = await conn.provider.buildResumeCommand(sessionId, { workspaceId: parsed?.wsId });
|
|
31493
31599
|
await sendKeys(tmuxSession, resumeCmd);
|
|
31494
|
-
await
|
|
31600
|
+
await updateTabAgentSessionId(conn.sessionName, conn.provider, sessionId).catch(() => {
|
|
31495
31601
|
});
|
|
31496
31602
|
const jsonlPath = await resolveJsonlPath(tmuxSession, sessionId);
|
|
31497
31603
|
sendJson(ws, {
|
|
@@ -31504,12 +31610,12 @@ var handleResumeMessage = async (ws, conn, payload) => {
|
|
|
31504
31610
|
unsubscribeFromFile(ws, conn.currentJsonlPath);
|
|
31505
31611
|
}
|
|
31506
31612
|
conn.currentJsonlPath = jsonlPath;
|
|
31507
|
-
await subscribeAndUpdateSummary(ws, jsonlPath, sessionId, conn.sessionName);
|
|
31613
|
+
await subscribeAndUpdateSummary(ws, jsonlPath, sessionId, conn.sessionName, conn.provider);
|
|
31508
31614
|
} else {
|
|
31509
31615
|
sendEmptyInit(ws, sessionId);
|
|
31510
31616
|
const cwd = await getSessionCwd(tmuxSession);
|
|
31511
31617
|
if (cwd) {
|
|
31512
|
-
watchForJsonlFile(conn.sessionName, sessionId, cwd);
|
|
31618
|
+
watchForJsonlFile(conn.sessionName, sessionId, cwd, conn.provider);
|
|
31513
31619
|
}
|
|
31514
31620
|
}
|
|
31515
31621
|
} catch (err) {
|
|
@@ -31530,6 +31636,12 @@ var handleTimelineConnection = async (ws, request) => {
|
|
|
31530
31636
|
ws.close(1008, "Missing session parameter");
|
|
31531
31637
|
return;
|
|
31532
31638
|
}
|
|
31639
|
+
const panelType = url2.searchParams.get("panelType") ?? "claude-code";
|
|
31640
|
+
const provider = getProviderByPanelType(panelType);
|
|
31641
|
+
if (!provider) {
|
|
31642
|
+
ws.close(1008, "Unknown panel type");
|
|
31643
|
+
return;
|
|
31644
|
+
}
|
|
31533
31645
|
const panePid = await getSessionPanePid(sessionName);
|
|
31534
31646
|
if (!panePid) {
|
|
31535
31647
|
sendEmptyInit(ws);
|
|
@@ -31550,6 +31662,7 @@ var handleTimelineConnection = async (ws, request) => {
|
|
|
31550
31662
|
ws,
|
|
31551
31663
|
sessionName,
|
|
31552
31664
|
panePid,
|
|
31665
|
+
provider,
|
|
31553
31666
|
heartbeatTimer,
|
|
31554
31667
|
lastHeartbeat,
|
|
31555
31668
|
currentJsonlPath: null,
|
|
@@ -31572,14 +31685,14 @@ var handleTimelineConnection = async (ws, request) => {
|
|
|
31572
31685
|
unsubscribeFromFile(ws, conn.currentJsonlPath);
|
|
31573
31686
|
}
|
|
31574
31687
|
conn.currentJsonlPath = msg.jsonlPath;
|
|
31575
|
-
await subscribeToFile(ws, msg.jsonlPath, void 0, conn.sessionName);
|
|
31688
|
+
await subscribeToFile(ws, msg.jsonlPath, void 0, conn.sessionName, conn.provider);
|
|
31576
31689
|
} else if (msg.type === "timeline:unsubscribe") {
|
|
31577
31690
|
if (conn.currentJsonlPath) {
|
|
31578
31691
|
unsubscribeFromFile(ws, conn.currentJsonlPath);
|
|
31579
31692
|
conn.currentJsonlPath = null;
|
|
31580
31693
|
}
|
|
31581
31694
|
} else if (msg.type === "timeline:resume" && msg.sessionId && msg.tmuxSession) {
|
|
31582
|
-
if (!isValidSessionId(msg.sessionId)) {
|
|
31695
|
+
if (!conn.provider.isValidSessionId(msg.sessionId)) {
|
|
31583
31696
|
sendJson(ws, { type: "timeline:resume-error", message: "Invalid session ID format" });
|
|
31584
31697
|
} else {
|
|
31585
31698
|
await handleResumeMessage(ws, conn, {
|
|
@@ -31597,61 +31710,61 @@ var handleTimelineConnection = async (ws, request) => {
|
|
|
31597
31710
|
log16.error(`websocket error: ${err.message}`);
|
|
31598
31711
|
cleanup3(conn);
|
|
31599
31712
|
});
|
|
31600
|
-
const
|
|
31601
|
-
const sessionInfo = await detectActiveSession(panePid);
|
|
31713
|
+
const hintSessionId = url2.searchParams.get("agentSessionId") ?? url2.searchParams.get("claudeSessionId");
|
|
31714
|
+
const sessionInfo = await provider.detectActiveSession(panePid);
|
|
31602
31715
|
if (conn.cleaned) return;
|
|
31603
31716
|
if (sessionInfo.status === "not-installed") {
|
|
31604
|
-
sendJson(ws, { type: "timeline:error", code: "not-installed", message:
|
|
31717
|
+
sendJson(ws, { type: "timeline:error", code: "not-installed", message: `${provider.displayName} is not installed` });
|
|
31605
31718
|
sendEmptyInit(ws);
|
|
31606
31719
|
return;
|
|
31607
31720
|
}
|
|
31608
|
-
if (
|
|
31609
|
-
await
|
|
31721
|
+
if (hintSessionId && sessionInfo.status === "not-running") {
|
|
31722
|
+
await updateTabAgentSessionId(conn.sessionName, provider, null).catch(() => {
|
|
31610
31723
|
});
|
|
31611
|
-
await
|
|
31724
|
+
await updateTabAgentSummary(conn.sessionName, provider, null).catch(() => {
|
|
31612
31725
|
});
|
|
31613
31726
|
}
|
|
31614
|
-
const
|
|
31727
|
+
const isAgentStarting = sessionInfo.status === "not-running" && !hintSessionId && await provider.isAgentRunning(panePid);
|
|
31615
31728
|
if (sessionInfo.status === "running" && sessionInfo.sessionId) {
|
|
31616
31729
|
sendJson(ws, {
|
|
31617
31730
|
type: "timeline:session-changed",
|
|
31618
31731
|
newSessionId: sessionInfo.sessionId,
|
|
31619
31732
|
reason: "session-waiting"
|
|
31620
31733
|
});
|
|
31621
|
-
} else if (
|
|
31734
|
+
} else if (isAgentStarting) {
|
|
31622
31735
|
sendJson(ws, {
|
|
31623
31736
|
type: "timeline:session-changed",
|
|
31624
31737
|
newSessionId: "",
|
|
31625
31738
|
reason: "session-waiting"
|
|
31626
31739
|
});
|
|
31627
31740
|
}
|
|
31628
|
-
const effectiveSessionId = sessionInfo.sessionId ??
|
|
31741
|
+
const effectiveSessionId = sessionInfo.sessionId ?? hintSessionId;
|
|
31629
31742
|
if (sessionInfo.jsonlPath) {
|
|
31630
31743
|
conn.currentJsonlPath = sessionInfo.jsonlPath;
|
|
31631
31744
|
if (sessionInfo.sessionId) {
|
|
31632
|
-
await
|
|
31745
|
+
await updateTabAgentSessionId(conn.sessionName, provider, sessionInfo.sessionId).catch(() => {
|
|
31633
31746
|
});
|
|
31634
31747
|
}
|
|
31635
|
-
await subscribeAndUpdateSummary(ws, sessionInfo.jsonlPath, sessionInfo.sessionId ?? void 0, conn.sessionName);
|
|
31748
|
+
await subscribeAndUpdateSummary(ws, sessionInfo.jsonlPath, sessionInfo.sessionId ?? void 0, conn.sessionName, provider);
|
|
31636
31749
|
} else if (effectiveSessionId) {
|
|
31637
31750
|
if (sessionInfo.sessionId) {
|
|
31638
|
-
await
|
|
31751
|
+
await updateTabAgentSessionId(conn.sessionName, provider, sessionInfo.sessionId).catch(() => {
|
|
31639
31752
|
});
|
|
31640
31753
|
}
|
|
31641
31754
|
const jsonlPath = await resolveJsonlPath(sessionName, effectiveSessionId);
|
|
31642
31755
|
if (jsonlPath) {
|
|
31643
31756
|
conn.currentJsonlPath = jsonlPath;
|
|
31644
|
-
await subscribeAndUpdateSummary(ws, jsonlPath, effectiveSessionId, conn.sessionName);
|
|
31757
|
+
await subscribeAndUpdateSummary(ws, jsonlPath, effectiveSessionId, conn.sessionName, provider);
|
|
31645
31758
|
} else {
|
|
31646
31759
|
sendEmptyInit(ws, effectiveSessionId);
|
|
31647
31760
|
}
|
|
31648
|
-
} else if (!
|
|
31761
|
+
} else if (!isAgentStarting) {
|
|
31649
31762
|
sendEmptyInit(ws);
|
|
31650
31763
|
}
|
|
31651
31764
|
if (conn.cleaned) return;
|
|
31652
31765
|
const wsKey = sessionName;
|
|
31653
31766
|
if (!sessionWatchers.has(wsKey)) {
|
|
31654
|
-
const sw =
|
|
31767
|
+
const sw = provider.watchSessions(panePid, async (newInfo) => {
|
|
31655
31768
|
const wsConns = getSessionConnections(sessionName);
|
|
31656
31769
|
for (const c of wsConns) {
|
|
31657
31770
|
if (newInfo.jsonlPath && newInfo.jsonlPath !== c.currentJsonlPath) {
|
|
@@ -31661,7 +31774,7 @@ var handleTimelineConnection = async (ws, request) => {
|
|
|
31661
31774
|
}
|
|
31662
31775
|
c.currentJsonlPath = newInfo.jsonlPath;
|
|
31663
31776
|
if (newInfo.sessionId) {
|
|
31664
|
-
await
|
|
31777
|
+
await updateTabAgentSessionId(sessionName, provider, newInfo.sessionId).catch(() => {
|
|
31665
31778
|
});
|
|
31666
31779
|
}
|
|
31667
31780
|
sendJson(c.ws, {
|
|
@@ -31669,16 +31782,16 @@ var handleTimelineConnection = async (ws, request) => {
|
|
|
31669
31782
|
newSessionId: newInfo.sessionId ?? "",
|
|
31670
31783
|
reason: "new-session-started"
|
|
31671
31784
|
});
|
|
31672
|
-
await subscribeAndUpdateSummary(c.ws, newInfo.jsonlPath, newInfo.sessionId ?? void 0, sessionName);
|
|
31785
|
+
await subscribeAndUpdateSummary(c.ws, newInfo.jsonlPath, newInfo.sessionId ?? void 0, sessionName, provider);
|
|
31673
31786
|
} else if (!newInfo.jsonlPath && newInfo.status === "not-running") {
|
|
31674
31787
|
cancelJsonlWatcher(sessionName);
|
|
31675
31788
|
if (c.currentJsonlPath) {
|
|
31676
31789
|
unsubscribeFromFile(c.ws, c.currentJsonlPath);
|
|
31677
31790
|
c.currentJsonlPath = null;
|
|
31678
31791
|
}
|
|
31679
|
-
await
|
|
31792
|
+
await updateTabAgentSessionId(sessionName, provider, null).catch(() => {
|
|
31680
31793
|
});
|
|
31681
|
-
await
|
|
31794
|
+
await updateTabAgentSummary(sessionName, provider, null).catch(() => {
|
|
31682
31795
|
});
|
|
31683
31796
|
sendJson(c.ws, {
|
|
31684
31797
|
type: "timeline:session-changed",
|
|
@@ -31689,7 +31802,7 @@ var handleTimelineConnection = async (ws, request) => {
|
|
|
31689
31802
|
}
|
|
31690
31803
|
if (newInfo.status === "running" && !newInfo.jsonlPath) {
|
|
31691
31804
|
if (newInfo.sessionId) {
|
|
31692
|
-
await
|
|
31805
|
+
await updateTabAgentSessionId(sessionName, provider, newInfo.sessionId).catch(() => {
|
|
31693
31806
|
});
|
|
31694
31807
|
}
|
|
31695
31808
|
for (const c of wsConns) {
|
|
@@ -31713,7 +31826,7 @@ var handleTimelineConnection = async (ws, request) => {
|
|
|
31713
31826
|
const hasActiveSubscription = wsConns.some((c) => c.currentJsonlPath !== null);
|
|
31714
31827
|
if (!hasActiveSubscription && wsConns.length > 0) {
|
|
31715
31828
|
cancelJsonlWatcher(sessionName);
|
|
31716
|
-
watchForJsonlFile(sessionName, newInfo.sessionId, newInfo.cwd);
|
|
31829
|
+
watchForJsonlFile(sessionName, newInfo.sessionId, newInfo.cwd, provider);
|
|
31717
31830
|
}
|
|
31718
31831
|
}
|
|
31719
31832
|
}
|
|
@@ -31721,13 +31834,13 @@ var handleTimelineConnection = async (ws, request) => {
|
|
|
31721
31834
|
sessionWatchers.set(wsKey, sw);
|
|
31722
31835
|
}
|
|
31723
31836
|
if (sessionInfo.status === "running" && !sessionInfo.jsonlPath && sessionInfo.sessionId && sessionInfo.cwd) {
|
|
31724
|
-
watchForJsonlFile(sessionName, sessionInfo.sessionId, sessionInfo.cwd);
|
|
31837
|
+
watchForJsonlFile(sessionName, sessionInfo.sessionId, sessionInfo.cwd, provider);
|
|
31725
31838
|
}
|
|
31726
|
-
if (
|
|
31727
|
-
const recheckInfo = await detectActiveSession(panePid);
|
|
31839
|
+
if (isAgentStarting) {
|
|
31840
|
+
const recheckInfo = await provider.detectActiveSession(panePid);
|
|
31728
31841
|
if (conn.cleaned) return;
|
|
31729
31842
|
if (recheckInfo.status === "running" && recheckInfo.sessionId && !conn.currentJsonlPath) {
|
|
31730
|
-
await
|
|
31843
|
+
await updateTabAgentSessionId(sessionName, provider, recheckInfo.sessionId).catch(() => {
|
|
31731
31844
|
});
|
|
31732
31845
|
if (recheckInfo.jsonlPath) {
|
|
31733
31846
|
conn.currentJsonlPath = recheckInfo.jsonlPath;
|
|
@@ -31736,7 +31849,7 @@ var handleTimelineConnection = async (ws, request) => {
|
|
|
31736
31849
|
newSessionId: recheckInfo.sessionId,
|
|
31737
31850
|
reason: "new-session-started"
|
|
31738
31851
|
});
|
|
31739
|
-
await subscribeAndUpdateSummary(ws, recheckInfo.jsonlPath, recheckInfo.sessionId, sessionName);
|
|
31852
|
+
await subscribeAndUpdateSummary(ws, recheckInfo.jsonlPath, recheckInfo.sessionId, sessionName, provider);
|
|
31740
31853
|
} else if (recheckInfo.cwd) {
|
|
31741
31854
|
sendJson(ws, {
|
|
31742
31855
|
type: "timeline:session-changed",
|
|
@@ -31744,7 +31857,7 @@ var handleTimelineConnection = async (ws, request) => {
|
|
|
31744
31857
|
reason: "session-waiting"
|
|
31745
31858
|
});
|
|
31746
31859
|
sendEmptyInit(ws, recheckInfo.sessionId, true);
|
|
31747
|
-
watchForJsonlFile(sessionName, recheckInfo.sessionId, recheckInfo.cwd);
|
|
31860
|
+
watchForJsonlFile(sessionName, recheckInfo.sessionId, recheckInfo.cwd, provider);
|
|
31748
31861
|
}
|
|
31749
31862
|
} else {
|
|
31750
31863
|
sendEmptyInit(ws, "", true);
|
|
@@ -31841,7 +31954,7 @@ var import_fs8 = __toESM(require("fs"));
|
|
|
31841
31954
|
var import_path20 = __toESM(require("path"));
|
|
31842
31955
|
var import_os15 = __toESM(require("os"));
|
|
31843
31956
|
var TOKEN_FILE = import_path20.default.join(import_os15.default.homedir(), ".purplemux", "cli-token");
|
|
31844
|
-
var
|
|
31957
|
+
var g10 = globalThis;
|
|
31845
31958
|
var readTokenFile = () => {
|
|
31846
31959
|
try {
|
|
31847
31960
|
const value = import_fs8.default.readFileSync(TOKEN_FILE, "utf-8").trim();
|
|
@@ -31858,14 +31971,14 @@ var writeTokenFile = (value) => {
|
|
|
31858
31971
|
}
|
|
31859
31972
|
};
|
|
31860
31973
|
var getCliToken = () => {
|
|
31861
|
-
if (
|
|
31974
|
+
if (g10.__ptCliToken) return g10.__ptCliToken;
|
|
31862
31975
|
const existing = readTokenFile();
|
|
31863
31976
|
if (existing) {
|
|
31864
|
-
|
|
31977
|
+
g10.__ptCliToken = existing;
|
|
31865
31978
|
return existing;
|
|
31866
31979
|
}
|
|
31867
31980
|
const fresh = (0, import_crypto2.randomBytes)(32).toString("hex");
|
|
31868
|
-
|
|
31981
|
+
g10.__ptCliToken = fresh;
|
|
31869
31982
|
writeTokenFile(fresh);
|
|
31870
31983
|
return fresh;
|
|
31871
31984
|
};
|
|
@@ -32011,14 +32124,17 @@ var findAutoResumeTargets = async () => {
|
|
|
32011
32124
|
if (!layout) continue;
|
|
32012
32125
|
const tabs = collectAllTabs(layout.root);
|
|
32013
32126
|
for (const tab of tabs) {
|
|
32014
|
-
|
|
32015
|
-
|
|
32016
|
-
|
|
32017
|
-
|
|
32018
|
-
|
|
32019
|
-
|
|
32020
|
-
|
|
32021
|
-
|
|
32127
|
+
const provider = getProviderByPanelType(tab.panelType);
|
|
32128
|
+
if (!provider) continue;
|
|
32129
|
+
const sessionId = provider.readSessionId(tab);
|
|
32130
|
+
if (!sessionId) continue;
|
|
32131
|
+
targets.push({
|
|
32132
|
+
workspaceId: ws.id,
|
|
32133
|
+
tabId: tab.id,
|
|
32134
|
+
tmuxSession: tab.sessionName,
|
|
32135
|
+
sessionId,
|
|
32136
|
+
provider
|
|
32137
|
+
});
|
|
32022
32138
|
}
|
|
32023
32139
|
}
|
|
32024
32140
|
return targets;
|
|
@@ -32032,15 +32148,18 @@ var sendResumeKeys = async (target) => {
|
|
|
32032
32148
|
return false;
|
|
32033
32149
|
}
|
|
32034
32150
|
if (!SAFE_SHELLS2.has(command)) {
|
|
32035
|
-
|
|
32036
|
-
|
|
32151
|
+
const runningProvider = getProviderByProcessName(command);
|
|
32152
|
+
if (runningProvider && runningProvider.id === target.provider.id) {
|
|
32153
|
+
log19.debug(`${target.provider.displayName} already running, skip: ${target.tmuxSession}`);
|
|
32037
32154
|
return true;
|
|
32038
32155
|
}
|
|
32039
32156
|
log19.debug(`Non-shell process running (${command}), skip: ${target.tmuxSession}`);
|
|
32040
32157
|
return false;
|
|
32041
32158
|
}
|
|
32042
|
-
const resumeCmd = await buildResumeCommand(target.
|
|
32043
|
-
|
|
32159
|
+
const resumeCmd = await target.provider.buildResumeCommand(target.sessionId, {
|
|
32160
|
+
workspaceId: target.workspaceId
|
|
32161
|
+
});
|
|
32162
|
+
log19.debug(`Sending resume: ${target.tmuxSession} \u2192 ${target.sessionId}`);
|
|
32044
32163
|
await sendKeys(target.tmuxSession, resumeCmd);
|
|
32045
32164
|
return true;
|
|
32046
32165
|
} catch (err) {
|
|
@@ -32380,7 +32499,7 @@ init_logger();
|
|
|
32380
32499
|
// package.json
|
|
32381
32500
|
var package_default = {
|
|
32382
32501
|
name: "purplemux",
|
|
32383
|
-
version: "0.2.
|
|
32502
|
+
version: "0.2.5",
|
|
32384
32503
|
description: "Manage multiple Claude Code sessions from your browser",
|
|
32385
32504
|
license: "MIT",
|
|
32386
32505
|
author: "subicura",
|
|
@@ -32417,6 +32536,8 @@ var package_default = {
|
|
|
32417
32536
|
scripts: {
|
|
32418
32537
|
dev: "tsx watch server.ts",
|
|
32419
32538
|
build: "next build && node scripts/post-build.js && tsup",
|
|
32539
|
+
"build:landing": "eleventy",
|
|
32540
|
+
"dev:landing": "eleventy --serve",
|
|
32420
32541
|
"build:server": "tsup",
|
|
32421
32542
|
"build:electron:main": "tsup --config electron/tsup.config.ts",
|
|
32422
32543
|
"build:electron": "next build && node scripts/post-build.js --electron && tsup && pnpm build:electron:main",
|
|
@@ -32485,6 +32606,7 @@ var package_default = {
|
|
|
32485
32606
|
zustand: "^5.0.12"
|
|
32486
32607
|
},
|
|
32487
32608
|
devDependencies: {
|
|
32609
|
+
"@11ty/eleventy": "^3.1.5",
|
|
32488
32610
|
"@electron/rebuild": "^4.0.3",
|
|
32489
32611
|
"@tailwindcss/postcss": "^4",
|
|
32490
32612
|
"@types/node": "^24.12.0",
|
|
@@ -32513,7 +32635,7 @@ var package_default = {
|
|
|
32513
32635
|
hono: ">=4.12.14",
|
|
32514
32636
|
"@hono/node-server": ">=1.19.13",
|
|
32515
32637
|
lodash: ">=4.18.0",
|
|
32516
|
-
"@xmldom/xmldom": "^0.8.
|
|
32638
|
+
"@xmldom/xmldom": "^0.8.13",
|
|
32517
32639
|
"picomatch@>=2.0.0 <3.0.0": ">=2.3.2",
|
|
32518
32640
|
"picomatch@>=4.0.0 <5.0.0": ">=4.0.4"
|
|
32519
32641
|
}
|