@openchambery/web 1.19.3-beta.4 → 1.19.3-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{AssistantView-C5rdZZo5.js → AssistantView-CPF6Nfb6.js} +1 -1
- package/dist/assets/{ChatContainer-BAnhC8Xu.js → ChatContainer-CldTRJmB.js} +5 -5
- package/dist/assets/{ContextPanel-DmmmVTxg.js → ContextPanel-BJnRefSW.js} +3 -3
- package/dist/assets/{DiagramView-fKtYRmMy.js → DiagramView-Co5-JMSr.js} +1 -1
- package/dist/assets/{MarkdownRendererImpl-DBwTTIyb.js → MarkdownRendererImpl-Cx10d-dT.js} +1 -1
- package/dist/assets/{MobileDetailNavigation-Ci8iQd1U.js → MobileDetailNavigation-ZJtJMDT3.js} +1 -1
- package/dist/assets/{MobileShareBridge-COlraBE5.js → MobileShareBridge-3NBuOdWs.js} +1 -1
- package/dist/assets/{MobileSurface-6AySCP_w.js → MobileSurface-BZQDdrqA.js} +1 -1
- package/dist/assets/{MultiRunWindow-CtzIc5H5.js → MultiRunWindow-BY2_dGd_.js} +1 -1
- package/dist/assets/{SettingsView-DplP6pCo.js → SettingsView-BsnYPviY.js} +1 -1
- package/dist/assets/{SettingsWindow-BstGL5r4.js → SettingsWindow-DT92dg19.js} +1 -1
- package/dist/assets/{TerminalView-Dzbpn3Mh.js → TerminalView-uqPJOhzP.js} +1 -1
- package/dist/assets/{ToolOutputDialog-iNhoUbun.js → ToolOutputDialog-_9sExuXj.js} +1 -1
- package/dist/assets/{appThemeRegistry-ChLdmqHp.js → appThemeRegistry-DL8zx2Tf.js} +2 -2
- package/dist/assets/{gitApi-16lIt8-p.js → gitApi-C5a0FHg7.js} +1 -1
- package/dist/assets/{insertionBoundaries-DxxTBrpg.js → insertionBoundaries-CN8Y824W.js} +1 -1
- package/dist/assets/{main-CblkZ_8-.js → main-BOEazbCD.js} +3 -3
- package/dist/assets/{main-0dycOVhR.js → main-ej4kLP0y.js} +2 -2
- package/dist/assets/{miniChat-CWYbXUX6.js → miniChat-h4TCod13.js} +2 -2
- package/dist/assets/{mobile-DVpq6s-q.js → mobile-D3ev3kF7.js} +2 -2
- package/dist/assets/{multirun-DzCqDyyy.js → multirun-Bb6ZUBgz.js} +1 -1
- package/dist/assets/{projectMeta-vDIfgkrT.js → projectMeta-DAnaHeES.js} +1 -1
- package/dist/assets/{radio-Cyoobx07.js → radio-iuo8w3_4.js} +1 -1
- package/dist/assets/{renderElectronMiniChatApp-BERJygjo.js → renderElectronMiniChatApp-aIjl55lT.js} +2 -2
- package/dist/assets/{renderMobileApp-DVGzx3FV.js → renderMobileApp-MTX_4lox.js} +5 -5
- package/dist/assets/runtimeConfig-DY2RRqNN.js +2 -0
- package/dist/assets/{runtimeEndpointReset-DQJpeX9Y.js → runtimeEndpointReset-CM9B-gNb.js} +1 -1
- package/dist/assets/{shikiHighlight-CBJY8Gag.js → shikiHighlight-jCavhi4H.js} +1 -1
- package/dist/assets/{useAppFontEffects-J5k1Vhy8.js → useAppFontEffects-I4H3yYVm.js} +2 -2
- package/dist/assets/{useDesktopWindowControlsLayout-xXnfxmE2.js → useDesktopWindowControlsLayout-DGrq4UFu.js} +1 -1
- package/dist/assets/{useEffectiveDirectory-DQC0Tb3_.js → useEffectiveDirectory-Cf6WHNi9.js} +1 -1
- package/dist/assets/{useMobileNavigationStore-DLXqazM8.js → useMobileNavigationStore-9NzHuLJl.js} +1 -1
- package/dist/assets/{useSessionAutoCleanup-C5gzJHC4.js → useSessionAutoCleanup-QOfL2Fdn.js} +1 -1
- package/dist/assets/{useWorkerHighlightedLines-DM2pcpju.js → useWorkerHighlightedLines-NWmW5faW.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/mini-chat.html +2 -2
- package/dist/mobile.html +2 -2
- package/package.json +1 -1
- package/server/index.js +7 -0
- package/server/lib/notifications/APNS.md +35 -0
- package/server/lib/notifications/DOCUMENTATION.md +8 -2
- package/server/lib/notifications/apns-runtime.js +558 -7
- package/server/lib/notifications/apns-runtime.test.js +475 -0
- package/server/lib/notifications/routes.js +55 -0
- package/server/lib/notifications/runtime.js +15 -4
- package/server/lib/notifications/runtime.test.js +108 -0
- package/server/lib/opencode/bootstrap-runtime.js +6 -0
- package/server/lib/opencode/bootstrap-runtime.test.js +21 -0
- package/dist/assets/runtimeConfig-wlHzE-5Y.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{j as t,t as n}from"./useUIStore-DOxLVeGb.js";import{Kt as r,kn as i,qt as a,u as o}from"./persistence-DE9RnVcA.js";import{h as s}from"./desktopHosts-BrrYPqKq.js";import{i as c,n as l,r as u}from"./desktopRelayRestore-DR60DATT.js";import{nt as d,rt as f}from"./vendor-base-ui-CnHfUmDN.js";import{Nt as p}from"./platform-MjT8iqKB.js";import{n as m}from"./vendor-react-DLPElpoQ.js";import{n as h}from"./vendor-tanstack-query-core-BWz0ose6.js";import{i as g,r as _}from"./vendor-tanstack-react-query-D7pys_c9.js";import{n as v}from"./clipboard-BXC1vZwE.js";import{i as y,t as b}from"./store-UweBeN2y.js";import{gr as ee,ia as x,ir as S,l as C,pr as w,ur as te}from"./useMobileNavigationStore-DLXqazM8.js";import{t as ne}from"./utils-C2R0YoC5.js";import{i as re,n as ie,r as ae,t as oe}from"./vendor-simplewebauthn-browser-Dwhcdg_5.js";import{n as se,r as ce}from"./radio-Cyoobx07.js";var T=m(),E=[`desktopHostSwitch`],D=class extends Error{result;host;constructor(e,t){super(e.reason),this.name=`DesktopHostSwitchError`,this.result=e,this.host=t}},le=(e,t)=>b(y.getState().dictionary,e,t),ue=async e=>{let{host:n}=e;if(n.id===`local`){let r=(e.localApiOrigin||``).trim();if(!r)throw new D({ok:!1,status:{status:`unreachable`,latencyMs:0},reason:`unreachable`},n);return t({apiBaseUrl:r,clientToken:e.localClientToken||null,runtimeKey:u(n)}),{ok:!0,via:`direct`,status:{status:`ok`,latencyMs:0}}}let r=await c(n,{cachedProbe:e.cachedProbe});if(!r.ok)throw new D(r,n);return r},O=null,de=()=>O||(O=new h(o,{mutationKey:E,mutationFn:ue,onError:(e,t)=>{e instanceof D&&e.result.reason!==`unsupported`&&ee.error(le(`desktopHostSwitcher.toast.instanceUnreachable`,{host:s(t.host.label)}))}}),O.subscribe(()=>void 0),O),fe=async e=>{if(l(e.host))return{ok:!0,via:`direct`,status:e.cachedProbe?.status===`ok`?e.cachedProbe:{status:`ok`,latencyMs:0}};if(o.isMutating({mutationKey:E})>0)return{ok:!1,status:{status:`unreachable`,latencyMs:0},reason:`unsupported`};try{return await de().mutate(e)}catch(e){if(e instanceof D)return e.result;throw e}},pe=()=>{let e=(0,T.c)(1),t;return e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t={mutationKey:E},e[0]=t):t=e[0],_(t)>0},me=()=>{let e=(0,T.c)(1),t;return e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t={filters:{mutationKey:E,status:`pending`},select:he},e[0]=t):t=e[0],g(t)[0]?.host.id??null};function he(e){return e.state.variables}var k=e=>typeof e==`object`&&!!e,A=(e,t)=>{let n=e[t];return typeof n==`string`?n:null},j=(e,t)=>{let n=e[t];return typeof n==`number`&&Number.isFinite(n)?n:null},M=(e,t)=>{let n=e[t];return typeof n==`boolean`?n:null},N=e=>Array.isArray(e)?e.filter(e=>typeof e==`string`):[],P=()=>r()?(e,t)=>a(e,t):null,F=e=>{if(!k(e))return;let t=M(e,`enabled`)??!1,n=A(e,`store`)?.toLowerCase()===`settings`?`settings`:`never`,r=A(e,`value`);return{enabled:t,store:n,...r?{value:r}:{}}},ge=e=>e===`remote`||e===`dynamic`?e:`local`,_e=e=>{if(!k(e))return null;let t=A(e,`id`);if(!t)return null;let n=M(e,`enabled`)??!0,r=ge(A(e,`type`)),i=A(e,`localHost`)||A(e,`local_host`)||void 0,a=j(e,`localPort`)??j(e,`local_port`)??void 0,o=A(e,`remoteHost`)||A(e,`remote_host`)||void 0,s=j(e,`remotePort`)??j(e,`remote_port`)??void 0;return{id:t,enabled:n,type:r,...i?{localHost:i}:{},...typeof a==`number`?{localPort:a}:{},...o?{remoteHost:o}:{},...typeof s==`number`?{remotePort:s}:{}}},ve=e=>{if(!k(e))return null;let t=A(e,`id`),n=A(e,`sshCommand`)||A(e,`ssh_command`);if(!t||!n)return null;let r=A(e,`nickname`),i=e.sshParsed,a=k(i)?{destination:A(i,`destination`)||``,args:N(i.args)}:void 0,o=k(e.remoteOpenchamber)?e.remoteOpenchamber:k(e.remote_openchamber)?e.remote_openchamber:{},s=k(e.localForward)?e.localForward:k(e.local_forward)?e.local_forward:{},c=k(e.auth)?e.auth:{},l=A(o,`mode`)?.toLowerCase()===`external`?`external`:`managed`,u=A(o,`installMethod`)||A(o,`install_method`),d=u===`npm`||u===`download_release`||u===`upload_bundle`?u:`bun`,f=A(s,`bindHost`)||A(s,`bind_host`)||`127.0.0.1`,p=f===`localhost`||f===`0.0.0.0`?f:`127.0.0.1`,m=(Array.isArray(e.portForwards)?e.portForwards:Array.isArray(e.port_forwards)?e.port_forwards:[]).map(e=>_e(e)).filter(e=>!!e),h=j(o,`preferredPort`)??j(o,`preferred_port`),g=j(s,`preferredLocalPort`)??j(s,`preferred_local_port`),_=F(c.sshPassword||c.ssh_password),v=F(c.openchamberPassword||c.openchamber_password);return{id:t,...r?{nickname:r}:{},sshCommand:n,...a&&a.destination?{sshParsed:a}:{},connectionTimeoutSec:j(e,`connectionTimeoutSec`)??j(e,`connection_timeout_sec`)??60,remoteOpenchamber:{mode:l,keepRunning:M(o,`keepRunning`)??M(o,`keep_running`)??!0,relayHost:M(o,`relayHost`)??M(o,`relay_host`)??!0,...h?{preferredPort:h}:{},installMethod:d,uploadBundleOverSsh:M(o,`uploadBundleOverSsh`)??M(o,`upload_bundle_over_ssh`)??!1},localForward:{...g?{preferredLocalPort:g}:{},bindHost:p},auth:{..._?{sshPassword:_}:{},...v?{openchamberPassword:v}:{}},portForwards:m}},ye=e=>{switch(e){case`config_resolved`:case`auth_check`:case`master_connecting`:case`remote_probe`:case`installing`:case`updating`:case`server_detecting`:case`server_starting`:case`forwarding`:case`ready`:case`degraded`:case`error`:return e;default:return`idle`}},I=e=>{if(!k(e))return null;let t=A(e,`id`);return t?{id:t,phase:ye(A(e,`phase`)),...A(e,`detail`)?{detail:A(e,`detail`)||void 0}:{},...A(e,`localUrl`)||A(e,`local_url`)?{localUrl:A(e,`localUrl`)||A(e,`local_url`)||void 0}:{},...typeof(j(e,`localPort`)??j(e,`local_port`))==`number`?{localPort:j(e,`localPort`)??j(e,`local_port`)??void 0}:{},...typeof(j(e,`remotePort`)??j(e,`remote_port`))==`number`?{remotePort:j(e,`remotePort`)??j(e,`remote_port`)??void 0}:{},startedByUs:M(e,`startedByUs`)??M(e,`started_by_us`)??!1,retryAttempt:j(e,`retryAttempt`)??j(e,`retry_attempt`)??0,requiresUserAction:M(e,`requiresUserAction`)??M(e,`requires_user_action`)??!1,...A(e,`errorCode`)||A(e,`error_code`)?{errorCode:A(e,`errorCode`)||A(e,`error_code`)||void 0}:{},updatedAtMs:j(e,`updatedAtMs`)??j(e,`updated_at_ms`)??Date.now()}:null},be=e=>{if(!k(e))return null;let t=A(e,`host`),n=A(e,`source`),r=A(e,`sshCommand`)||A(e,`ssh_command`);return!t||!n||!r?null:{host:t,source:n,sshCommand:r,pattern:M(e,`pattern`)??!1}},xe=(e,t)=>({id:e,sshCommand:t,connectionTimeoutSec:60,remoteOpenchamber:{mode:`managed`,keepRunning:!0,relayHost:!0,installMethod:`bun`,uploadBundleOverSsh:!1},localForward:{bindHost:`127.0.0.1`},auth:{},portForwards:[]}),Se=async()=>{let e=P();if(!e)return{instances:[]};let t=await e(`desktop_ssh_instances_get`);return k(t)?{instances:(Array.isArray(t.instances)?t.instances:Array.isArray(t.desktopSshInstances)?t.desktopSshInstances:[]).map(e=>ve(e)).filter(e=>!!e)}:{instances:[]}},Ce=async e=>{let t=P();t&&await t(`desktop_ssh_instances_set`,{config:{instances:e.instances}})},we=async()=>{let e=P();if(!e)return[];let t=await e(`desktop_ssh_import_hosts`);return Array.isArray(t)?t.map(e=>be(e)).filter(e=>!!e):[]},Te=async e=>{let t=P();t&&await t(`desktop_ssh_connect`,{id:e})},Ee=async e=>{let t=P();t&&await t(`desktop_ssh_disconnect`,{id:e})},De=async e=>{let t=P();if(!t)return[];let n=await t(`desktop_ssh_status`,{...e?{id:e}:{}});return Array.isArray(n)?n.map(e=>I(e)).filter(e=>!!e):[]},Oe=async(e,t)=>{let n=P();if(!n)return[];let r=await n(`desktop_ssh_logs`,{id:e,...typeof t==`number`?{limit:t}:{}});return Array.isArray(r)?r.filter(e=>typeof e==`string`):[]},ke=async e=>{let t=P();t&&await t(`desktop_ssh_logs_clear`,{id:e})},Ae=(e,t)=>{let n=Math.max(0,Math.trunc(Number(e.fileGroups)||0)),r=Math.max(0,Math.trunc(Number(e.singleFiles)||0)),i=Math.max(0,Math.trunc(Number(e.directories)||0));return{fileGroups:Array.from({length:n},()=>!0),singleFiles:Array.from({length:r},()=>!0),directories:Array.from({length:i},()=>!0),agentsRoot:!0,authFile:t?.includeAuthFile===!0}},je=e=>{if(!k(e))return null;let t=A(e,`path`),n=j(e,`bytes`);return!t||n===null?null:{path:t,bytes:n}},Me=e=>{if(!k(e))return null;let t=A(e,`path`),n=j(e,`fileCount`)??j(e,`file_count`),r=j(e,`bytes`);return!t||n===null||r===null?null:{path:t,fileCount:n,bytes:r}},Ne=e=>{if(e==null||!k(e))return null;let t=j(e,`fileCount`)??j(e,`file_count`),n=j(e,`bytes`);return t===null||n===null?null:{fileCount:t,bytes:n}},L=e=>{if(e==null||!k(e))return null;let t=j(e,`bytes`);return t===null?null:{bytes:t}},R=e=>{if(!k(e))return null;let t=Array.isArray(e.files)?e.files:null,n=Array.isArray(e.directories)?e.directories:null,r=N(e.deletes),i=j(e,`totalBytes`)??j(e,`total_bytes`);if(!t||!n||i===null||!(`agentsRoot`in e)&&!(`agents_root`in e))return null;let a=t.map(e=>je(e)).filter(e=>!!e),o=n.map(e=>Me(e)).filter(e=>!!e);if(a.length!==t.length||o.length!==n.length)return null;let s=e.agentsRoot??e.agents_root,c=s==null?null:Ne(s);if(s!=null&&c===null)return null;let l=e.authFile??e.auth_file,u=l==null?null:L(l),d=e.direction===`pull`||e.direction===`push`?e.direction:void 0,f=e.selections,p=k(f)?{fileGroups:Array.isArray(f.fileGroups)?f.fileGroups.map(e=>e!==!1):[],singleFiles:Array.isArray(f.singleFiles)?f.singleFiles.map(e=>e!==!1):[],directories:Array.isArray(f.directories)?f.directories.map(e=>e!==!1):[],agentsRoot:f.agentsRoot!==!1,authFile:f.authFile===!0}:void 0,m=z(e.selectionShape??e.selection_shape);return{files:a,directories:o,agentsRoot:c,authFile:u,deletes:r,totalBytes:i,...d?{direction:d}:{},...p?{selections:p}:{},...m?{selectionShape:m}:{}}},z=e=>{if(!k(e))return null;let t=j(e,`fileGroups`)??j(e,`file_groups`),n=j(e,`singleFiles`)??j(e,`single_files`),r=j(e,`directories`);return t===null||n===null||r===null||t<0||n<0||r<0?null:{fileGroups:t,singleFiles:n,directories:r}},Pe=e=>{if(!k(e))return null;let t=R(e.plan);if(!t)return null;let n=z(e.selectionShape??e.selection_shape)??t.selectionShape??null;return{plan:t,remoteExisting:N(e.remoteExisting??e.remote_existing),remoteAgentsRootExists:M(e,`remoteAgentsRootExists`)??M(e,`remote_agents_root_exists`)??!1,remoteAuthFileExists:M(e,`remoteAuthFileExists`)??M(e,`remote_auth_file_exists`)??!1,...n?{selectionShape:n}:{}}},Fe=e=>{if(!k(e)||M(e,`ok`)!==!0)return null;let t=j(e,`files`),n=j(e,`directories`),r=j(e,`deletes`),i=j(e,`totalBytes`)??j(e,`total_bytes`);if(t===null||n===null||r===null||i===null||!(`agentsRoot`in e)&&!(`agents_root`in e))return null;let a=e.agentsRoot??e.agents_root,o=null;if(a!=null){if(!k(a))return null;let e=j(a,`fileCount`)??j(a,`file_count`);if(e===null)return null;o={fileCount:e}}let s=e.authFile??e.auth_file,c=s==null?null:L(s);return{ok:!0,files:t,directories:n,deletes:r,totalBytes:i,agentsRoot:o,authFile:c}},Ie=async(e={})=>{let t=P();if(!t)return console.error(`[desktopSsh] config sync local scan skipped: desktop IPC bridge is unavailable in this window`),null;let n=await t(`desktop_ssh_sync_opencode_config`,{stage:`local`,...e.direction?{direction:e.direction}:{},...e.selections?{selections:e.selections}:{}});if(!k(n))return console.error(`[desktopSsh] config sync local scan returned a non-object payload`,n),null;let r=R(n.plan);if(!r)return console.error(`[desktopSsh] config sync local scan plan payload was not recognized`,n),null;let i=z(n.selectionShape??n.selection_shape)??r.selectionShape??null;return i?{...r,selectionShape:i}:r},Le=async(e,t={})=>{let n=P();if(!n)return console.error(`[desktopSsh] config sync preview skipped: desktop IPC bridge is unavailable in this window`),null;let r=await n(`desktop_ssh_sync_opencode_config`,{id:e,...t.targetKind?{targetKind:t.targetKind}:{},...t.direction?{direction:t.direction}:{},...t.selections?{selections:t.selections}:{}}),i=Pe(r);return i||console.error(`[desktopSsh] config sync preview payload was not recognized`,r),i},Re=async(e,t={})=>{let n=P();if(!n)return console.error(`[desktopSsh] config sync apply skipped: desktop IPC bridge is unavailable in this window`),null;let r=await n(`desktop_ssh_sync_opencode_config`,{id:e,apply:!0,...t.targetKind?{targetKind:t.targetKind}:{},...t.direction?{direction:t.direction}:{},...t.selections?{selections:t.selections}:{}}),i=Fe(r);return i||console.error(`[desktopSsh] config sync apply payload was not recognized`,r),i},ze=async(e,t={})=>{let n=P();if(!n)return[];let r=await n(`desktop_ssh_sync_runs_list`,{id:e,...t.targetKind?{targetKind:t.targetKind}:{}});return Array.isArray(r)?r.filter(e=>k(e)&&typeof e.syncRunId==`string`&&typeof e.targetId==`string`):[]},Be=async e=>{if(!r())return async()=>{};let t=window.__OPENCHAMBER_DESKTOP__?.listen;if(typeof t!=`function`)return async()=>{};let n=await t(`openchamber:ssh-instance-status`,t=>{let n=I(t?.payload);n&&e(n)});return async()=>{await n()}},Ve=new Set([`nodeRuntimeMissing`,`packageManagerMissing`,`nativeBinding`,`openchamberCliMissing`,`openchamberRegistry`,`openchamberInstall`,`openchamberCliIncompatible`,`opencodeInstall`,`serverStart`,`sshAuth`,`sshUnreachable`,`timeout`,`unknown`]),He=e=>typeof e==`string`&&Ve.has(e),Ue=e=>{let t=String(e||``);return/requires Node\.js \d+|no supported Node runtime/i.test(t)?`nodeRuntimeMissing`:/neither bun nor npm/i.test(t)?`packageManagerMissing`:/better-sqlite3|node_gyp_bins|gyp ERR|failed to prepare better-sqlite3/i.test(t)?`nativeBinding`:/OpenChamber installation completed but the executable is unavailable/i.test(t)?`openchamberCliMissing`:/could not reach the npm registry|ENOTFOUND|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|CERT_HAS_EXPIRED|UNABLE_TO_GET_ISSUER_CERT|self-signed certificate|registry\.npmjs|npm error code E404|npm ERR! code E404|getaddrinfo/i.test(t)?`openchamberRegistry`:/Failed to install OpenChamber/i.test(t)?`openchamberInstall`:/Unknown option:\s*--relay-host|does not advertise --relay-host|does not support --relay-host/i.test(t)||/Unknown option:/i.test(t)?`openchamberCliIncompatible`:/Failed to install OpenCode|OpenCode CLI/i.test(t)?`opencodeInstall`:/failed to become reachable|Managed OpenChamber server failed/i.test(t)?`serverStart`:/Permission denied|Authentication failed|publickey|keyboard-interactive/i.test(t)?`sshAuth`:/Could not resolve hostname|Connection refused|Connection timed out|ControlMaster connection timed out|SSH master process exited|Network is unreachable/i.test(t)?`sshUnreachable`:/Timed out waiting for SSH|Timed out waiting for forwarded/i.test(t)?`timeout`:`unknown`},B=(e,t)=>He(e)?e:Ue(t),V=e=>{switch(e){case`nodeRuntimeMissing`:return`desktopHostSwitcher.sshError.nodeRuntimeMissing.title`;case`packageManagerMissing`:return`desktopHostSwitcher.sshError.packageManagerMissing.title`;case`nativeBinding`:return`desktopHostSwitcher.sshError.nativeBinding.title`;case`openchamberCliMissing`:return`desktopHostSwitcher.sshError.openchamberCliMissing.title`;case`openchamberRegistry`:return`desktopHostSwitcher.sshError.openchamberRegistry.title`;case`openchamberInstall`:return`desktopHostSwitcher.sshError.openchamberInstall.title`;case`openchamberCliIncompatible`:return`desktopHostSwitcher.sshError.openchamberCliIncompatible.title`;case`opencodeInstall`:return`desktopHostSwitcher.sshError.opencodeInstall.title`;case`serverStart`:return`desktopHostSwitcher.sshError.serverStart.title`;case`sshAuth`:return`desktopHostSwitcher.sshError.sshAuth.title`;case`sshUnreachable`:return`desktopHostSwitcher.sshError.sshUnreachable.title`;case`timeout`:return`desktopHostSwitcher.sshError.timeout.title`;default:return`desktopHostSwitcher.sshError.unknown.title`}},H=e=>{switch(e){case`nodeRuntimeMissing`:return`desktopHostSwitcher.sshError.nodeRuntimeMissing.guidance`;case`packageManagerMissing`:return`desktopHostSwitcher.sshError.packageManagerMissing.guidance`;case`nativeBinding`:return`desktopHostSwitcher.sshError.nativeBinding.guidance`;case`openchamberCliMissing`:return`desktopHostSwitcher.sshError.openchamberCliMissing.guidance`;case`openchamberRegistry`:return`desktopHostSwitcher.sshError.openchamberRegistry.guidance`;case`openchamberInstall`:return`desktopHostSwitcher.sshError.openchamberInstall.guidance`;case`openchamberCliIncompatible`:return`desktopHostSwitcher.sshError.openchamberCliIncompatible.guidance`;case`opencodeInstall`:return`desktopHostSwitcher.sshError.opencodeInstall.guidance`;case`serverStart`:return`desktopHostSwitcher.sshError.serverStart.guidance`;case`sshAuth`:return`desktopHostSwitcher.sshError.sshAuth.guidance`;case`sshUnreachable`:return`desktopHostSwitcher.sshError.sshUnreachable.guidance`;case`timeout`:return`desktopHostSwitcher.sshError.timeout.guidance`;default:return`desktopHostSwitcher.sshError.unknown.guidance`}},U=e=>e===`unknown`||e===`openchamberCliIncompatible`,We=(e,t,n)=>{let r=typeof n==`string`?n.trim():``;return!U(t)||!r||r===e?e:`${e}\n${r}`},W=d();function Ge(e){let t=(0,T.c)(27),{errorCode:n,detail:r,className:i}=e,{t:a}=v(),o,s,c,l,u,d;if(t[0]!==i||t[1]!==r||t[2]!==n||t[3]!==a){let e=B(n,r);o=a(H(e));let f;t[10]===r?f=t[11]:(f=typeof r==`string`?r.trim():``,t[10]=r,t[11]=f),s=f,c=U(e)&&!!s&&s!==o,t[12]===i?d=t[13]:(d=ne(`space-y-1`,i),t[12]=i,t[13]=d),l=`typography-ui-label text-[var(--status-error)]`,u=a(V(e)),t[0]=i,t[1]=r,t[2]=n,t[3]=a,t[4]=o,t[5]=s,t[6]=c,t[7]=l,t[8]=u,t[9]=d}else o=t[4],s=t[5],c=t[6],l=t[7],u=t[8],d=t[9];let f;t[14]!==l||t[15]!==u?(f=(0,W.jsx)(`p`,{className:l,children:u}),t[14]=l,t[15]=u,t[16]=f):f=t[16];let p;t[17]===o?p=t[18]:(p=(0,W.jsx)(`p`,{className:`typography-meta text-muted-foreground`,children:o}),t[17]=o,t[18]=p);let m;t[19]!==s||t[20]!==c?(m=c?(0,W.jsx)(`p`,{className:`typography-meta break-words text-muted-foreground`,children:s}):null,t[19]=s,t[20]=c,t[21]=m):m=t[21];let h;return t[22]!==d||t[23]!==f||t[24]!==p||t[25]!==m?(h=(0,W.jsxs)(`div`,{className:d,children:[f,p,m]}),t[22]=d,t[23]=f,t[24]=p,t[25]=m,t[26]=h):h=t[26],h}var Ke=`/auth/passkey/authenticate/options`,qe=`/auth/passkey/authenticate/verify`,Je=`/auth/passkey/register/options`,Ye=`/auth/passkey/register/verify`,G=`/api/passkeys`,Xe=`/auth/passkey/status`,Ze=`/api/auth/reset`,K={enabled:!1,hasPasskeys:!1,passkeyCount:0,rpID:null},q=async(e,t)=>i(e,{method:`POST`,credentials:`include`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:t===void 0?void 0:JSON.stringify(t)}),J=async(e,t)=>{try{let t=await e.json();if(t&&typeof t.error==`string`&&t.error.trim())return t.error}catch{}return t},Qe=e=>e instanceof re&&e.code===`ERROR_CEREMONY_ABORTED`,$e=()=>{ae.cancelCeremony()},Y=()=>typeof window>`u`?{supported:!1,reason:`Passkeys are unavailable outside the browser.`}:window.isSecureContext?{supported:!0,reason:``}:{supported:!1,reason:`Passkeys require HTTPS or localhost.`},et=async()=>{let e=Y();if(!e.supported)throw Error(e.reason);let t=await q(Je,{label:typeof navigator.userAgent==`string`&&navigator.userAgent.trim()?navigator.userAgent:`This device`});if(!t.ok)throw Error(await J(t,`Could not start passkey setup.`));let{requestId:n,optionsJSON:r}=await t.json(),i=await q(Ye,{requestId:n,response:await ie({optionsJSON:r})});if(!i.ok)throw Error(await J(i,`Could not finish passkey setup.`));return i.json().catch(()=>null)},tt=async(e,t={})=>{let n=Y();if(!n.supported)throw Error(n.reason);let r=await q(Ke);if(!r.ok)throw Error(await J(r,`Passkey sign-in is not available right now.`));let{requestId:i,optionsJSON:a}=await r.json(),o=await q(qe,{requestId:i,response:await oe({optionsJSON:a}),trustDevice:e,issueClientToken:t.issueClientToken===!0,clientLabel:t.clientLabel,clientKind:t.clientKind,dedupeKey:t.dedupeKey});if(!o.ok)throw Error(await J(o,`Passkey sign-in failed.`));return o.json().catch(()=>null)},nt=async()=>{let e=await i(Xe,{method:`GET`,credentials:`include`,headers:{Accept:`application/json`}});if(!e.ok)return K;let t=await e.json().catch(()=>null);return{enabled:t?.enabled===!0,hasPasskeys:t?.hasPasskeys===!0,passkeyCount:typeof t?.passkeyCount==`number`?t.passkeyCount:0,rpID:typeof t?.rpID==`string`&&t.rpID?t.rpID:null}},rt=async()=>{let e=await i(G,{method:`GET`,credentials:`include`,headers:{Accept:`application/json`}});if(!e.ok)throw Error(await J(e,`Could not load passkeys.`));let t=await e.json().catch(()=>null);return Array.isArray(t?.passkeys)?t.passkeys:[]},it=async e=>{let t=await i(`${G}/${encodeURIComponent(e)}`,{method:`DELETE`,credentials:`include`,headers:{Accept:`application/json`}});if(!t.ok)throw Error(await J(t,`Could not remove passkey.`));return t.json().catch(()=>null)},at=async()=>{let e=await q(Ze);if(!e.ok)throw Error(await J(e,`Could not clear saved authentication.`));return e.json().catch(()=>null)},X=e=>b(y.getState().dictionary,e),ot=e=>typeof e!=`number`||!Number.isFinite(e)?null:Math.max(0,Math.min(100,Math.round(e))),st=e=>typeof e!=`number`||!Number.isFinite(e)?`-`:`${Math.round(e)}%`,ct=(e,t)=>e??st(t),lt=(e,t,n=`auto`)=>{if(!e)return t??``;try{let r=new Date(e);if(!Number.isFinite(r.getTime()))return t??``;let i=new Date;return r.toDateString()===i.toDateString()?ce(r,n,{fallback:t??``}):se(r,n,{month:`short`,day:`numeric`,weekday:`short`,hour:`numeric`,minute:`2-digit`})}catch{return t??``}},ut=e=>e===null?`safe`:e>=80?`critical`:e>=50?`warn`:`safe`,dt=e=>e===`5h`?X(`quota.window.5h`):e===`7d`?X(`quota.window.7d`):e===`7d-sonnet`?X(`quota.window.7dSonnet`):e===`7d-opus`?X(`quota.window.7dOpus`):e===`weekly`?X(`quota.window.weekly`):e===`daily`?X(`quota.window.daily`):e===`monthly`?X(`quota.window.monthly`):e===`credits`?X(`quota.window.credits`):e===`credits_balance`?X(`quota.window.creditsBalance`):e===`billing_cycle`?X(`quota.window.billingCycle`):e===`auto`?X(`quota.window.auto`):e===`api`?X(`quota.window.api`):e===`plan_limit`?X(`quota.window.planLimit`):e===`on_demand`?X(`quota.window.onDemand`):e===`session`?X(`quota.window.session`):e===`premium`?X(`quota.window.premium`):e===`chat`?X(`quota.window.chat`):e===`completions`?X(`quota.window.completions`):e===`premium_interactions`?X(`quota.window.premiumInteractions`):e,ft=e=>{let t=e.toLowerCase().trim();if(t===`5h`)return 18e3;if(t===`7d`||t===`weekly`||t===`7d-sonnet`||t===`7d-opus`)return 604800;if(t===`monthly`)return 2592e3;if(t===`24h`||t===`daily`)return 86400;if(t===`1h`)return 3600;let n=t.match(/^(\d+)h$/);if(n)return parseInt(n[1],10)*3600;let r=t.match(/^(\d+)d$/);return r?parseInt(r[1],10)*86400:null},pt=(e,t,n,r)=>{let i=n;if(i===null&&r&&(i=ft(r)),e===null||t===null||i===null||i<=0)return null;let a=Math.max(0,(t-Date.now())/1e3),o=Math.max(0,Math.min(i,i-a)),s=Math.max(0,Math.min(1,o/i)),c=e/100,l=e>=100&&a>0,u;u=s>.01?Math.min(999,c/s*100):e;let d;d=l?`exhausted`:c<=s?`on-track`:u<=130?`slightly-fast`:`too-fast`;let f=i>=432e3,p=f?86400:3600,m=f?`d`:`h`,h=i/p,g=e/Math.max(o/p,h*.01),_=Number.isFinite(g)?`${Math.min(999.9,Math.max(0,g)).toFixed(1)}%/${m}`:`-`,v=u>100?`+${Math.round(u)}%`:`${Math.round(u)}%`,y=null,b=i/86400;return b>=7&&(y=100/b),{elapsedRatio:s,usageRatio:c,predictedFinalPercent:u,remainingSeconds:a,isExhausted:l,elapsedSeconds:o,totalSeconds:i,status:d,paceRateText:_,predictText:v,dailyAllocationPercent:y}},mt=e=>{switch(e){case`exhausted`:case`too-fast`:return`var(--status-error)`;case`slightly-fast`:return`var(--status-warning)`;case`on-track`:return`var(--status-success)`}},ht=e=>{let t=Math.floor(Math.max(0,Math.floor(e))/60),n=Math.floor(t/60),r=Math.floor(n/24),i=n%24,a=t%60;return r>0?`${r}d ${i}h`:n>0?`${n}h`:t===0?`<1m`:`${a}m`},gt=e=>Math.min(100,Math.max(0,e*100)),_t=[`standalone`,`minimal-ui`,`fullscreen`,`window-controls-overlay`],vt=`openchamber.pwaRecentSessions`,yt=e=>typeof window>`u`||typeof window.matchMedia!=`function`?!1:window.matchMedia(`(display-mode: ${e})`).matches,bt=()=>typeof window>`u`?`browser`:typeof document<`u`&&document.referrer.startsWith(`android-app://`)?`twa`:window.navigator.standalone?`standalone`:_t.find(e=>yt(e))??`browser`,Z=e(f(),1),xt=864e5,Q=5,St=864e5,$=e=>e.time?.updated??e.time?.created??0,Ct=({sessions:e,currentSessionId:t,cutoffDays:n,keepRecent:r=Q,now:i=Date.now()})=>{if(!Array.isArray(e)||n<=0)return[];let a=i-n*xt,o=[...e].sort((e,t)=>$(t)-$(e)),s=new Set(o.slice(0,r).map(e=>e.id));return o.filter(e=>{if(!e?.id||s.has(e.id)||e.id===t||e.share)return!1;let n=$(e);return n?n<a:!1}).map(e=>e.id)},wt=e=>{let t=typeof e==`object`?e:void 0,r=t?.autoRun!==!1,i=typeof e==`boolean`?e:t?.enabled??!0,a=C(e=>e.currentSessionId),o=C(e=>e.isLoading),s=w(e=>e.activeSessions),c=w(e=>e.hasLoadedFullCatalog),l=n(e=>e.autoDeleteEnabled),u=n(e=>e.autoDeleteAfterDays),d=n(e=>e.sessionRetentionAction),f=n(e=>e.autoDeleteLastRunAt),m=n(e=>e.setAutoDeleteLastRunAt),[h,g]=Z.useState(!1),_=Z.useRef(!1);Z.useEffect(()=>{if(!i||!r||!l||u<=0)return;let e=window.setTimeout(()=>{S(x())},8e3);return()=>window.clearTimeout(e)},[u,l,r,i]);let v=Z.useMemo(()=>u<=0?[]:Ct({sessions:s,currentSessionId:a,cutoffDays:u}),[u,a,s]),y=Z.useCallback(async({force:e=!1}={})=>{if(_.current)return{completedIds:[],failedIds:[],action:d,skippedReason:`running`};if((!l||u<=0)&&!e)return{completedIds:[],failedIds:[],action:d,skippedReason:`disabled`};if(o)return{completedIds:[],failedIds:[],action:d,skippedReason:`loading`};let t=Date.now();if(!e&&f&&t-f<St)return{completedIds:[],failedIds:[],action:d,skippedReason:`cooldown`};let{activeSessions:n}=await S(x());if(n.length===0)return{completedIds:[],failedIds:[],action:d,skippedReason:`no-candidates`};let r=Ct({sessions:n,currentSessionId:a,cutoffDays:u,now:t});if(r.length===0)return m(t),{completedIds:[],failedIds:[],action:d,skippedReason:`no-candidates`};_.current=!0,g(!0);try{let e=new Map(n.map(e=>[e.id,e])),t=[],i=[];for(let n of r){let r=e.get(n),a=r?te(r):null;if(!a){i.push(n);continue}try{d===`archive`?await p.updateSession(n,{time:{archived:Date.now()}},a):await p.deleteSession(n,a),t.push(n)}catch{i.push(n)}}return d===`archive`?w.getState().archiveSessions(t):w.getState().removeSessions(t),{completedIds:t,failedIds:i,action:d}}finally{_.current=!1,g(!1),m(Date.now())}},[u,l,f,a,o,d,m]);return Z.useEffect(()=>{i&&r&&(!l||u<=0||o||!c||s.length===0||f&&Date.now()-f<St||y())},[u,l,f,r,i,c,s.length,o,y]),{candidates:v,isRunning:h,runCleanup:y,keepRecentCount:Q,action:d}};export{Te as A,Le as B,Ge as C,V as D,H as E,Oe as F,me as G,Be as H,ke as I,De as L,we as M,Se as N,Ae as O,Ce as P,Re as R,it as S,B as T,fe as U,ze as V,pe as W,rt as _,pt as a,et as b,ct as c,mt as d,ut as f,nt as g,K as h,gt as i,Ee as j,xe as k,ht as l,$e as m,vt as n,ot as o,tt as p,bt as r,lt as s,wt as t,dt as u,Y as v,We as w,at as x,Qe as y,Ie as z};
|
|
1
|
+
import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{j as t,t as n}from"./useUIStore-DOxLVeGb.js";import{Kt as r,kn as i,qt as a,u as o}from"./persistence-DE9RnVcA.js";import{h as s}from"./desktopHosts-BrrYPqKq.js";import{i as c,n as l,r as u}from"./desktopRelayRestore-DR60DATT.js";import{nt as d,rt as f}from"./vendor-base-ui-CnHfUmDN.js";import{Nt as p}from"./platform-MjT8iqKB.js";import{n as m}from"./vendor-react-DLPElpoQ.js";import{n as h}from"./vendor-tanstack-query-core-BWz0ose6.js";import{i as g,r as _}from"./vendor-tanstack-react-query-D7pys_c9.js";import{n as v}from"./clipboard-BXC1vZwE.js";import{i as y,t as b}from"./store-UweBeN2y.js";import{gr as ee,ia as x,ir as S,l as C,pr as w,ur as te}from"./useMobileNavigationStore-9NzHuLJl.js";import{t as ne}from"./utils-C2R0YoC5.js";import{i as re,n as ie,r as ae,t as oe}from"./vendor-simplewebauthn-browser-Dwhcdg_5.js";import{n as se,r as ce}from"./radio-iuo8w3_4.js";var T=m(),E=[`desktopHostSwitch`],D=class extends Error{result;host;constructor(e,t){super(e.reason),this.name=`DesktopHostSwitchError`,this.result=e,this.host=t}},le=(e,t)=>b(y.getState().dictionary,e,t),ue=async e=>{let{host:n}=e;if(n.id===`local`){let r=(e.localApiOrigin||``).trim();if(!r)throw new D({ok:!1,status:{status:`unreachable`,latencyMs:0},reason:`unreachable`},n);return t({apiBaseUrl:r,clientToken:e.localClientToken||null,runtimeKey:u(n)}),{ok:!0,via:`direct`,status:{status:`ok`,latencyMs:0}}}let r=await c(n,{cachedProbe:e.cachedProbe});if(!r.ok)throw new D(r,n);return r},O=null,de=()=>O||(O=new h(o,{mutationKey:E,mutationFn:ue,onError:(e,t)=>{e instanceof D&&e.result.reason!==`unsupported`&&ee.error(le(`desktopHostSwitcher.toast.instanceUnreachable`,{host:s(t.host.label)}))}}),O.subscribe(()=>void 0),O),fe=async e=>{if(l(e.host))return{ok:!0,via:`direct`,status:e.cachedProbe?.status===`ok`?e.cachedProbe:{status:`ok`,latencyMs:0}};if(o.isMutating({mutationKey:E})>0)return{ok:!1,status:{status:`unreachable`,latencyMs:0},reason:`unsupported`};try{return await de().mutate(e)}catch(e){if(e instanceof D)return e.result;throw e}},pe=()=>{let e=(0,T.c)(1),t;return e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t={mutationKey:E},e[0]=t):t=e[0],_(t)>0},me=()=>{let e=(0,T.c)(1),t;return e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t={filters:{mutationKey:E,status:`pending`},select:he},e[0]=t):t=e[0],g(t)[0]?.host.id??null};function he(e){return e.state.variables}var k=e=>typeof e==`object`&&!!e,A=(e,t)=>{let n=e[t];return typeof n==`string`?n:null},j=(e,t)=>{let n=e[t];return typeof n==`number`&&Number.isFinite(n)?n:null},M=(e,t)=>{let n=e[t];return typeof n==`boolean`?n:null},N=e=>Array.isArray(e)?e.filter(e=>typeof e==`string`):[],P=()=>r()?(e,t)=>a(e,t):null,F=e=>{if(!k(e))return;let t=M(e,`enabled`)??!1,n=A(e,`store`)?.toLowerCase()===`settings`?`settings`:`never`,r=A(e,`value`);return{enabled:t,store:n,...r?{value:r}:{}}},ge=e=>e===`remote`||e===`dynamic`?e:`local`,_e=e=>{if(!k(e))return null;let t=A(e,`id`);if(!t)return null;let n=M(e,`enabled`)??!0,r=ge(A(e,`type`)),i=A(e,`localHost`)||A(e,`local_host`)||void 0,a=j(e,`localPort`)??j(e,`local_port`)??void 0,o=A(e,`remoteHost`)||A(e,`remote_host`)||void 0,s=j(e,`remotePort`)??j(e,`remote_port`)??void 0;return{id:t,enabled:n,type:r,...i?{localHost:i}:{},...typeof a==`number`?{localPort:a}:{},...o?{remoteHost:o}:{},...typeof s==`number`?{remotePort:s}:{}}},ve=e=>{if(!k(e))return null;let t=A(e,`id`),n=A(e,`sshCommand`)||A(e,`ssh_command`);if(!t||!n)return null;let r=A(e,`nickname`),i=e.sshParsed,a=k(i)?{destination:A(i,`destination`)||``,args:N(i.args)}:void 0,o=k(e.remoteOpenchamber)?e.remoteOpenchamber:k(e.remote_openchamber)?e.remote_openchamber:{},s=k(e.localForward)?e.localForward:k(e.local_forward)?e.local_forward:{},c=k(e.auth)?e.auth:{},l=A(o,`mode`)?.toLowerCase()===`external`?`external`:`managed`,u=A(o,`installMethod`)||A(o,`install_method`),d=u===`npm`||u===`download_release`||u===`upload_bundle`?u:`bun`,f=A(s,`bindHost`)||A(s,`bind_host`)||`127.0.0.1`,p=f===`localhost`||f===`0.0.0.0`?f:`127.0.0.1`,m=(Array.isArray(e.portForwards)?e.portForwards:Array.isArray(e.port_forwards)?e.port_forwards:[]).map(e=>_e(e)).filter(e=>!!e),h=j(o,`preferredPort`)??j(o,`preferred_port`),g=j(s,`preferredLocalPort`)??j(s,`preferred_local_port`),_=F(c.sshPassword||c.ssh_password),v=F(c.openchamberPassword||c.openchamber_password);return{id:t,...r?{nickname:r}:{},sshCommand:n,...a&&a.destination?{sshParsed:a}:{},connectionTimeoutSec:j(e,`connectionTimeoutSec`)??j(e,`connection_timeout_sec`)??60,remoteOpenchamber:{mode:l,keepRunning:M(o,`keepRunning`)??M(o,`keep_running`)??!0,relayHost:M(o,`relayHost`)??M(o,`relay_host`)??!0,...h?{preferredPort:h}:{},installMethod:d,uploadBundleOverSsh:M(o,`uploadBundleOverSsh`)??M(o,`upload_bundle_over_ssh`)??!1},localForward:{...g?{preferredLocalPort:g}:{},bindHost:p},auth:{..._?{sshPassword:_}:{},...v?{openchamberPassword:v}:{}},portForwards:m}},ye=e=>{switch(e){case`config_resolved`:case`auth_check`:case`master_connecting`:case`remote_probe`:case`installing`:case`updating`:case`server_detecting`:case`server_starting`:case`forwarding`:case`ready`:case`degraded`:case`error`:return e;default:return`idle`}},I=e=>{if(!k(e))return null;let t=A(e,`id`);return t?{id:t,phase:ye(A(e,`phase`)),...A(e,`detail`)?{detail:A(e,`detail`)||void 0}:{},...A(e,`localUrl`)||A(e,`local_url`)?{localUrl:A(e,`localUrl`)||A(e,`local_url`)||void 0}:{},...typeof(j(e,`localPort`)??j(e,`local_port`))==`number`?{localPort:j(e,`localPort`)??j(e,`local_port`)??void 0}:{},...typeof(j(e,`remotePort`)??j(e,`remote_port`))==`number`?{remotePort:j(e,`remotePort`)??j(e,`remote_port`)??void 0}:{},startedByUs:M(e,`startedByUs`)??M(e,`started_by_us`)??!1,retryAttempt:j(e,`retryAttempt`)??j(e,`retry_attempt`)??0,requiresUserAction:M(e,`requiresUserAction`)??M(e,`requires_user_action`)??!1,...A(e,`errorCode`)||A(e,`error_code`)?{errorCode:A(e,`errorCode`)||A(e,`error_code`)||void 0}:{},updatedAtMs:j(e,`updatedAtMs`)??j(e,`updated_at_ms`)??Date.now()}:null},be=e=>{if(!k(e))return null;let t=A(e,`host`),n=A(e,`source`),r=A(e,`sshCommand`)||A(e,`ssh_command`);return!t||!n||!r?null:{host:t,source:n,sshCommand:r,pattern:M(e,`pattern`)??!1}},xe=(e,t)=>({id:e,sshCommand:t,connectionTimeoutSec:60,remoteOpenchamber:{mode:`managed`,keepRunning:!0,relayHost:!0,installMethod:`bun`,uploadBundleOverSsh:!1},localForward:{bindHost:`127.0.0.1`},auth:{},portForwards:[]}),Se=async()=>{let e=P();if(!e)return{instances:[]};let t=await e(`desktop_ssh_instances_get`);return k(t)?{instances:(Array.isArray(t.instances)?t.instances:Array.isArray(t.desktopSshInstances)?t.desktopSshInstances:[]).map(e=>ve(e)).filter(e=>!!e)}:{instances:[]}},Ce=async e=>{let t=P();t&&await t(`desktop_ssh_instances_set`,{config:{instances:e.instances}})},we=async()=>{let e=P();if(!e)return[];let t=await e(`desktop_ssh_import_hosts`);return Array.isArray(t)?t.map(e=>be(e)).filter(e=>!!e):[]},Te=async e=>{let t=P();t&&await t(`desktop_ssh_connect`,{id:e})},Ee=async e=>{let t=P();t&&await t(`desktop_ssh_disconnect`,{id:e})},De=async e=>{let t=P();if(!t)return[];let n=await t(`desktop_ssh_status`,{...e?{id:e}:{}});return Array.isArray(n)?n.map(e=>I(e)).filter(e=>!!e):[]},Oe=async(e,t)=>{let n=P();if(!n)return[];let r=await n(`desktop_ssh_logs`,{id:e,...typeof t==`number`?{limit:t}:{}});return Array.isArray(r)?r.filter(e=>typeof e==`string`):[]},ke=async e=>{let t=P();t&&await t(`desktop_ssh_logs_clear`,{id:e})},Ae=(e,t)=>{let n=Math.max(0,Math.trunc(Number(e.fileGroups)||0)),r=Math.max(0,Math.trunc(Number(e.singleFiles)||0)),i=Math.max(0,Math.trunc(Number(e.directories)||0));return{fileGroups:Array.from({length:n},()=>!0),singleFiles:Array.from({length:r},()=>!0),directories:Array.from({length:i},()=>!0),agentsRoot:!0,authFile:t?.includeAuthFile===!0}},je=e=>{if(!k(e))return null;let t=A(e,`path`),n=j(e,`bytes`);return!t||n===null?null:{path:t,bytes:n}},Me=e=>{if(!k(e))return null;let t=A(e,`path`),n=j(e,`fileCount`)??j(e,`file_count`),r=j(e,`bytes`);return!t||n===null||r===null?null:{path:t,fileCount:n,bytes:r}},Ne=e=>{if(e==null||!k(e))return null;let t=j(e,`fileCount`)??j(e,`file_count`),n=j(e,`bytes`);return t===null||n===null?null:{fileCount:t,bytes:n}},L=e=>{if(e==null||!k(e))return null;let t=j(e,`bytes`);return t===null?null:{bytes:t}},R=e=>{if(!k(e))return null;let t=Array.isArray(e.files)?e.files:null,n=Array.isArray(e.directories)?e.directories:null,r=N(e.deletes),i=j(e,`totalBytes`)??j(e,`total_bytes`);if(!t||!n||i===null||!(`agentsRoot`in e)&&!(`agents_root`in e))return null;let a=t.map(e=>je(e)).filter(e=>!!e),o=n.map(e=>Me(e)).filter(e=>!!e);if(a.length!==t.length||o.length!==n.length)return null;let s=e.agentsRoot??e.agents_root,c=s==null?null:Ne(s);if(s!=null&&c===null)return null;let l=e.authFile??e.auth_file,u=l==null?null:L(l),d=e.direction===`pull`||e.direction===`push`?e.direction:void 0,f=e.selections,p=k(f)?{fileGroups:Array.isArray(f.fileGroups)?f.fileGroups.map(e=>e!==!1):[],singleFiles:Array.isArray(f.singleFiles)?f.singleFiles.map(e=>e!==!1):[],directories:Array.isArray(f.directories)?f.directories.map(e=>e!==!1):[],agentsRoot:f.agentsRoot!==!1,authFile:f.authFile===!0}:void 0,m=z(e.selectionShape??e.selection_shape);return{files:a,directories:o,agentsRoot:c,authFile:u,deletes:r,totalBytes:i,...d?{direction:d}:{},...p?{selections:p}:{},...m?{selectionShape:m}:{}}},z=e=>{if(!k(e))return null;let t=j(e,`fileGroups`)??j(e,`file_groups`),n=j(e,`singleFiles`)??j(e,`single_files`),r=j(e,`directories`);return t===null||n===null||r===null||t<0||n<0||r<0?null:{fileGroups:t,singleFiles:n,directories:r}},Pe=e=>{if(!k(e))return null;let t=R(e.plan);if(!t)return null;let n=z(e.selectionShape??e.selection_shape)??t.selectionShape??null;return{plan:t,remoteExisting:N(e.remoteExisting??e.remote_existing),remoteAgentsRootExists:M(e,`remoteAgentsRootExists`)??M(e,`remote_agents_root_exists`)??!1,remoteAuthFileExists:M(e,`remoteAuthFileExists`)??M(e,`remote_auth_file_exists`)??!1,...n?{selectionShape:n}:{}}},Fe=e=>{if(!k(e)||M(e,`ok`)!==!0)return null;let t=j(e,`files`),n=j(e,`directories`),r=j(e,`deletes`),i=j(e,`totalBytes`)??j(e,`total_bytes`);if(t===null||n===null||r===null||i===null||!(`agentsRoot`in e)&&!(`agents_root`in e))return null;let a=e.agentsRoot??e.agents_root,o=null;if(a!=null){if(!k(a))return null;let e=j(a,`fileCount`)??j(a,`file_count`);if(e===null)return null;o={fileCount:e}}let s=e.authFile??e.auth_file,c=s==null?null:L(s);return{ok:!0,files:t,directories:n,deletes:r,totalBytes:i,agentsRoot:o,authFile:c}},Ie=async(e={})=>{let t=P();if(!t)return console.error(`[desktopSsh] config sync local scan skipped: desktop IPC bridge is unavailable in this window`),null;let n=await t(`desktop_ssh_sync_opencode_config`,{stage:`local`,...e.direction?{direction:e.direction}:{},...e.selections?{selections:e.selections}:{}});if(!k(n))return console.error(`[desktopSsh] config sync local scan returned a non-object payload`,n),null;let r=R(n.plan);if(!r)return console.error(`[desktopSsh] config sync local scan plan payload was not recognized`,n),null;let i=z(n.selectionShape??n.selection_shape)??r.selectionShape??null;return i?{...r,selectionShape:i}:r},Le=async(e,t={})=>{let n=P();if(!n)return console.error(`[desktopSsh] config sync preview skipped: desktop IPC bridge is unavailable in this window`),null;let r=await n(`desktop_ssh_sync_opencode_config`,{id:e,...t.targetKind?{targetKind:t.targetKind}:{},...t.direction?{direction:t.direction}:{},...t.selections?{selections:t.selections}:{}}),i=Pe(r);return i||console.error(`[desktopSsh] config sync preview payload was not recognized`,r),i},Re=async(e,t={})=>{let n=P();if(!n)return console.error(`[desktopSsh] config sync apply skipped: desktop IPC bridge is unavailable in this window`),null;let r=await n(`desktop_ssh_sync_opencode_config`,{id:e,apply:!0,...t.targetKind?{targetKind:t.targetKind}:{},...t.direction?{direction:t.direction}:{},...t.selections?{selections:t.selections}:{}}),i=Fe(r);return i||console.error(`[desktopSsh] config sync apply payload was not recognized`,r),i},ze=async(e,t={})=>{let n=P();if(!n)return[];let r=await n(`desktop_ssh_sync_runs_list`,{id:e,...t.targetKind?{targetKind:t.targetKind}:{}});return Array.isArray(r)?r.filter(e=>k(e)&&typeof e.syncRunId==`string`&&typeof e.targetId==`string`):[]},Be=async e=>{if(!r())return async()=>{};let t=window.__OPENCHAMBER_DESKTOP__?.listen;if(typeof t!=`function`)return async()=>{};let n=await t(`openchamber:ssh-instance-status`,t=>{let n=I(t?.payload);n&&e(n)});return async()=>{await n()}},Ve=new Set([`nodeRuntimeMissing`,`packageManagerMissing`,`nativeBinding`,`openchamberCliMissing`,`openchamberRegistry`,`openchamberInstall`,`openchamberCliIncompatible`,`opencodeInstall`,`serverStart`,`sshAuth`,`sshUnreachable`,`timeout`,`unknown`]),He=e=>typeof e==`string`&&Ve.has(e),Ue=e=>{let t=String(e||``);return/requires Node\.js \d+|no supported Node runtime/i.test(t)?`nodeRuntimeMissing`:/neither bun nor npm/i.test(t)?`packageManagerMissing`:/better-sqlite3|node_gyp_bins|gyp ERR|failed to prepare better-sqlite3/i.test(t)?`nativeBinding`:/OpenChamber installation completed but the executable is unavailable/i.test(t)?`openchamberCliMissing`:/could not reach the npm registry|ENOTFOUND|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|CERT_HAS_EXPIRED|UNABLE_TO_GET_ISSUER_CERT|self-signed certificate|registry\.npmjs|npm error code E404|npm ERR! code E404|getaddrinfo/i.test(t)?`openchamberRegistry`:/Failed to install OpenChamber/i.test(t)?`openchamberInstall`:/Unknown option:\s*--relay-host|does not advertise --relay-host|does not support --relay-host/i.test(t)||/Unknown option:/i.test(t)?`openchamberCliIncompatible`:/Failed to install OpenCode|OpenCode CLI/i.test(t)?`opencodeInstall`:/failed to become reachable|Managed OpenChamber server failed/i.test(t)?`serverStart`:/Permission denied|Authentication failed|publickey|keyboard-interactive/i.test(t)?`sshAuth`:/Could not resolve hostname|Connection refused|Connection timed out|ControlMaster connection timed out|SSH master process exited|Network is unreachable/i.test(t)?`sshUnreachable`:/Timed out waiting for SSH|Timed out waiting for forwarded/i.test(t)?`timeout`:`unknown`},B=(e,t)=>He(e)?e:Ue(t),V=e=>{switch(e){case`nodeRuntimeMissing`:return`desktopHostSwitcher.sshError.nodeRuntimeMissing.title`;case`packageManagerMissing`:return`desktopHostSwitcher.sshError.packageManagerMissing.title`;case`nativeBinding`:return`desktopHostSwitcher.sshError.nativeBinding.title`;case`openchamberCliMissing`:return`desktopHostSwitcher.sshError.openchamberCliMissing.title`;case`openchamberRegistry`:return`desktopHostSwitcher.sshError.openchamberRegistry.title`;case`openchamberInstall`:return`desktopHostSwitcher.sshError.openchamberInstall.title`;case`openchamberCliIncompatible`:return`desktopHostSwitcher.sshError.openchamberCliIncompatible.title`;case`opencodeInstall`:return`desktopHostSwitcher.sshError.opencodeInstall.title`;case`serverStart`:return`desktopHostSwitcher.sshError.serverStart.title`;case`sshAuth`:return`desktopHostSwitcher.sshError.sshAuth.title`;case`sshUnreachable`:return`desktopHostSwitcher.sshError.sshUnreachable.title`;case`timeout`:return`desktopHostSwitcher.sshError.timeout.title`;default:return`desktopHostSwitcher.sshError.unknown.title`}},H=e=>{switch(e){case`nodeRuntimeMissing`:return`desktopHostSwitcher.sshError.nodeRuntimeMissing.guidance`;case`packageManagerMissing`:return`desktopHostSwitcher.sshError.packageManagerMissing.guidance`;case`nativeBinding`:return`desktopHostSwitcher.sshError.nativeBinding.guidance`;case`openchamberCliMissing`:return`desktopHostSwitcher.sshError.openchamberCliMissing.guidance`;case`openchamberRegistry`:return`desktopHostSwitcher.sshError.openchamberRegistry.guidance`;case`openchamberInstall`:return`desktopHostSwitcher.sshError.openchamberInstall.guidance`;case`openchamberCliIncompatible`:return`desktopHostSwitcher.sshError.openchamberCliIncompatible.guidance`;case`opencodeInstall`:return`desktopHostSwitcher.sshError.opencodeInstall.guidance`;case`serverStart`:return`desktopHostSwitcher.sshError.serverStart.guidance`;case`sshAuth`:return`desktopHostSwitcher.sshError.sshAuth.guidance`;case`sshUnreachable`:return`desktopHostSwitcher.sshError.sshUnreachable.guidance`;case`timeout`:return`desktopHostSwitcher.sshError.timeout.guidance`;default:return`desktopHostSwitcher.sshError.unknown.guidance`}},U=e=>e===`unknown`||e===`openchamberCliIncompatible`,We=(e,t,n)=>{let r=typeof n==`string`?n.trim():``;return!U(t)||!r||r===e?e:`${e}\n${r}`},W=d();function Ge(e){let t=(0,T.c)(27),{errorCode:n,detail:r,className:i}=e,{t:a}=v(),o,s,c,l,u,d;if(t[0]!==i||t[1]!==r||t[2]!==n||t[3]!==a){let e=B(n,r);o=a(H(e));let f;t[10]===r?f=t[11]:(f=typeof r==`string`?r.trim():``,t[10]=r,t[11]=f),s=f,c=U(e)&&!!s&&s!==o,t[12]===i?d=t[13]:(d=ne(`space-y-1`,i),t[12]=i,t[13]=d),l=`typography-ui-label text-[var(--status-error)]`,u=a(V(e)),t[0]=i,t[1]=r,t[2]=n,t[3]=a,t[4]=o,t[5]=s,t[6]=c,t[7]=l,t[8]=u,t[9]=d}else o=t[4],s=t[5],c=t[6],l=t[7],u=t[8],d=t[9];let f;t[14]!==l||t[15]!==u?(f=(0,W.jsx)(`p`,{className:l,children:u}),t[14]=l,t[15]=u,t[16]=f):f=t[16];let p;t[17]===o?p=t[18]:(p=(0,W.jsx)(`p`,{className:`typography-meta text-muted-foreground`,children:o}),t[17]=o,t[18]=p);let m;t[19]!==s||t[20]!==c?(m=c?(0,W.jsx)(`p`,{className:`typography-meta break-words text-muted-foreground`,children:s}):null,t[19]=s,t[20]=c,t[21]=m):m=t[21];let h;return t[22]!==d||t[23]!==f||t[24]!==p||t[25]!==m?(h=(0,W.jsxs)(`div`,{className:d,children:[f,p,m]}),t[22]=d,t[23]=f,t[24]=p,t[25]=m,t[26]=h):h=t[26],h}var Ke=`/auth/passkey/authenticate/options`,qe=`/auth/passkey/authenticate/verify`,Je=`/auth/passkey/register/options`,Ye=`/auth/passkey/register/verify`,G=`/api/passkeys`,Xe=`/auth/passkey/status`,Ze=`/api/auth/reset`,K={enabled:!1,hasPasskeys:!1,passkeyCount:0,rpID:null},q=async(e,t)=>i(e,{method:`POST`,credentials:`include`,headers:{"Content-Type":`application/json`,Accept:`application/json`},body:t===void 0?void 0:JSON.stringify(t)}),J=async(e,t)=>{try{let t=await e.json();if(t&&typeof t.error==`string`&&t.error.trim())return t.error}catch{}return t},Qe=e=>e instanceof re&&e.code===`ERROR_CEREMONY_ABORTED`,$e=()=>{ae.cancelCeremony()},Y=()=>typeof window>`u`?{supported:!1,reason:`Passkeys are unavailable outside the browser.`}:window.isSecureContext?{supported:!0,reason:``}:{supported:!1,reason:`Passkeys require HTTPS or localhost.`},et=async()=>{let e=Y();if(!e.supported)throw Error(e.reason);let t=await q(Je,{label:typeof navigator.userAgent==`string`&&navigator.userAgent.trim()?navigator.userAgent:`This device`});if(!t.ok)throw Error(await J(t,`Could not start passkey setup.`));let{requestId:n,optionsJSON:r}=await t.json(),i=await q(Ye,{requestId:n,response:await ie({optionsJSON:r})});if(!i.ok)throw Error(await J(i,`Could not finish passkey setup.`));return i.json().catch(()=>null)},tt=async(e,t={})=>{let n=Y();if(!n.supported)throw Error(n.reason);let r=await q(Ke);if(!r.ok)throw Error(await J(r,`Passkey sign-in is not available right now.`));let{requestId:i,optionsJSON:a}=await r.json(),o=await q(qe,{requestId:i,response:await oe({optionsJSON:a}),trustDevice:e,issueClientToken:t.issueClientToken===!0,clientLabel:t.clientLabel,clientKind:t.clientKind,dedupeKey:t.dedupeKey});if(!o.ok)throw Error(await J(o,`Passkey sign-in failed.`));return o.json().catch(()=>null)},nt=async()=>{let e=await i(Xe,{method:`GET`,credentials:`include`,headers:{Accept:`application/json`}});if(!e.ok)return K;let t=await e.json().catch(()=>null);return{enabled:t?.enabled===!0,hasPasskeys:t?.hasPasskeys===!0,passkeyCount:typeof t?.passkeyCount==`number`?t.passkeyCount:0,rpID:typeof t?.rpID==`string`&&t.rpID?t.rpID:null}},rt=async()=>{let e=await i(G,{method:`GET`,credentials:`include`,headers:{Accept:`application/json`}});if(!e.ok)throw Error(await J(e,`Could not load passkeys.`));let t=await e.json().catch(()=>null);return Array.isArray(t?.passkeys)?t.passkeys:[]},it=async e=>{let t=await i(`${G}/${encodeURIComponent(e)}`,{method:`DELETE`,credentials:`include`,headers:{Accept:`application/json`}});if(!t.ok)throw Error(await J(t,`Could not remove passkey.`));return t.json().catch(()=>null)},at=async()=>{let e=await q(Ze);if(!e.ok)throw Error(await J(e,`Could not clear saved authentication.`));return e.json().catch(()=>null)},X=e=>b(y.getState().dictionary,e),ot=e=>typeof e!=`number`||!Number.isFinite(e)?null:Math.max(0,Math.min(100,Math.round(e))),st=e=>typeof e!=`number`||!Number.isFinite(e)?`-`:`${Math.round(e)}%`,ct=(e,t)=>e??st(t),lt=(e,t,n=`auto`)=>{if(!e)return t??``;try{let r=new Date(e);if(!Number.isFinite(r.getTime()))return t??``;let i=new Date;return r.toDateString()===i.toDateString()?ce(r,n,{fallback:t??``}):se(r,n,{month:`short`,day:`numeric`,weekday:`short`,hour:`numeric`,minute:`2-digit`})}catch{return t??``}},ut=e=>e===null?`safe`:e>=80?`critical`:e>=50?`warn`:`safe`,dt=e=>e===`5h`?X(`quota.window.5h`):e===`7d`?X(`quota.window.7d`):e===`7d-sonnet`?X(`quota.window.7dSonnet`):e===`7d-opus`?X(`quota.window.7dOpus`):e===`weekly`?X(`quota.window.weekly`):e===`daily`?X(`quota.window.daily`):e===`monthly`?X(`quota.window.monthly`):e===`credits`?X(`quota.window.credits`):e===`credits_balance`?X(`quota.window.creditsBalance`):e===`billing_cycle`?X(`quota.window.billingCycle`):e===`auto`?X(`quota.window.auto`):e===`api`?X(`quota.window.api`):e===`plan_limit`?X(`quota.window.planLimit`):e===`on_demand`?X(`quota.window.onDemand`):e===`session`?X(`quota.window.session`):e===`premium`?X(`quota.window.premium`):e===`chat`?X(`quota.window.chat`):e===`completions`?X(`quota.window.completions`):e===`premium_interactions`?X(`quota.window.premiumInteractions`):e,ft=e=>{let t=e.toLowerCase().trim();if(t===`5h`)return 18e3;if(t===`7d`||t===`weekly`||t===`7d-sonnet`||t===`7d-opus`)return 604800;if(t===`monthly`)return 2592e3;if(t===`24h`||t===`daily`)return 86400;if(t===`1h`)return 3600;let n=t.match(/^(\d+)h$/);if(n)return parseInt(n[1],10)*3600;let r=t.match(/^(\d+)d$/);return r?parseInt(r[1],10)*86400:null},pt=(e,t,n,r)=>{let i=n;if(i===null&&r&&(i=ft(r)),e===null||t===null||i===null||i<=0)return null;let a=Math.max(0,(t-Date.now())/1e3),o=Math.max(0,Math.min(i,i-a)),s=Math.max(0,Math.min(1,o/i)),c=e/100,l=e>=100&&a>0,u;u=s>.01?Math.min(999,c/s*100):e;let d;d=l?`exhausted`:c<=s?`on-track`:u<=130?`slightly-fast`:`too-fast`;let f=i>=432e3,p=f?86400:3600,m=f?`d`:`h`,h=i/p,g=e/Math.max(o/p,h*.01),_=Number.isFinite(g)?`${Math.min(999.9,Math.max(0,g)).toFixed(1)}%/${m}`:`-`,v=u>100?`+${Math.round(u)}%`:`${Math.round(u)}%`,y=null,b=i/86400;return b>=7&&(y=100/b),{elapsedRatio:s,usageRatio:c,predictedFinalPercent:u,remainingSeconds:a,isExhausted:l,elapsedSeconds:o,totalSeconds:i,status:d,paceRateText:_,predictText:v,dailyAllocationPercent:y}},mt=e=>{switch(e){case`exhausted`:case`too-fast`:return`var(--status-error)`;case`slightly-fast`:return`var(--status-warning)`;case`on-track`:return`var(--status-success)`}},ht=e=>{let t=Math.floor(Math.max(0,Math.floor(e))/60),n=Math.floor(t/60),r=Math.floor(n/24),i=n%24,a=t%60;return r>0?`${r}d ${i}h`:n>0?`${n}h`:t===0?`<1m`:`${a}m`},gt=e=>Math.min(100,Math.max(0,e*100)),_t=[`standalone`,`minimal-ui`,`fullscreen`,`window-controls-overlay`],vt=`openchamber.pwaRecentSessions`,yt=e=>typeof window>`u`||typeof window.matchMedia!=`function`?!1:window.matchMedia(`(display-mode: ${e})`).matches,bt=()=>typeof window>`u`?`browser`:typeof document<`u`&&document.referrer.startsWith(`android-app://`)?`twa`:window.navigator.standalone?`standalone`:_t.find(e=>yt(e))??`browser`,Z=e(f(),1),xt=864e5,Q=5,St=864e5,$=e=>e.time?.updated??e.time?.created??0,Ct=({sessions:e,currentSessionId:t,cutoffDays:n,keepRecent:r=Q,now:i=Date.now()})=>{if(!Array.isArray(e)||n<=0)return[];let a=i-n*xt,o=[...e].sort((e,t)=>$(t)-$(e)),s=new Set(o.slice(0,r).map(e=>e.id));return o.filter(e=>{if(!e?.id||s.has(e.id)||e.id===t||e.share)return!1;let n=$(e);return n?n<a:!1}).map(e=>e.id)},wt=e=>{let t=typeof e==`object`?e:void 0,r=t?.autoRun!==!1,i=typeof e==`boolean`?e:t?.enabled??!0,a=C(e=>e.currentSessionId),o=C(e=>e.isLoading),s=w(e=>e.activeSessions),c=w(e=>e.hasLoadedFullCatalog),l=n(e=>e.autoDeleteEnabled),u=n(e=>e.autoDeleteAfterDays),d=n(e=>e.sessionRetentionAction),f=n(e=>e.autoDeleteLastRunAt),m=n(e=>e.setAutoDeleteLastRunAt),[h,g]=Z.useState(!1),_=Z.useRef(!1);Z.useEffect(()=>{if(!i||!r||!l||u<=0)return;let e=window.setTimeout(()=>{S(x())},8e3);return()=>window.clearTimeout(e)},[u,l,r,i]);let v=Z.useMemo(()=>u<=0?[]:Ct({sessions:s,currentSessionId:a,cutoffDays:u}),[u,a,s]),y=Z.useCallback(async({force:e=!1}={})=>{if(_.current)return{completedIds:[],failedIds:[],action:d,skippedReason:`running`};if((!l||u<=0)&&!e)return{completedIds:[],failedIds:[],action:d,skippedReason:`disabled`};if(o)return{completedIds:[],failedIds:[],action:d,skippedReason:`loading`};let t=Date.now();if(!e&&f&&t-f<St)return{completedIds:[],failedIds:[],action:d,skippedReason:`cooldown`};let{activeSessions:n}=await S(x());if(n.length===0)return{completedIds:[],failedIds:[],action:d,skippedReason:`no-candidates`};let r=Ct({sessions:n,currentSessionId:a,cutoffDays:u,now:t});if(r.length===0)return m(t),{completedIds:[],failedIds:[],action:d,skippedReason:`no-candidates`};_.current=!0,g(!0);try{let e=new Map(n.map(e=>[e.id,e])),t=[],i=[];for(let n of r){let r=e.get(n),a=r?te(r):null;if(!a){i.push(n);continue}try{d===`archive`?await p.updateSession(n,{time:{archived:Date.now()}},a):await p.deleteSession(n,a),t.push(n)}catch{i.push(n)}}return d===`archive`?w.getState().archiveSessions(t):w.getState().removeSessions(t),{completedIds:t,failedIds:i,action:d}}finally{_.current=!1,g(!1),m(Date.now())}},[u,l,f,a,o,d,m]);return Z.useEffect(()=>{i&&r&&(!l||u<=0||o||!c||s.length===0||f&&Date.now()-f<St||y())},[u,l,f,r,i,c,s.length,o,y]),{candidates:v,isRunning:h,runCleanup:y,keepRecentCount:Q,action:d}};export{Te as A,Le as B,Ge as C,V as D,H as E,Oe as F,me as G,Be as H,ke as I,De as L,we as M,Se as N,Ae as O,Ce as P,Re as R,it as S,B as T,fe as U,ze as V,pe as W,rt as _,pt as a,et as b,ct as c,mt as d,ut as f,nt as g,K as h,gt as i,Ee as j,xe as k,ht as l,$e as m,vt as n,ot as o,tt as p,bt as r,lt as s,wt as t,dt as u,Y as v,We as w,at as x,Qe as y,Ie as z};
|
package/dist/assets/{useWorkerHighlightedLines-DM2pcpju.js → useWorkerHighlightedLines-NWmW5faW.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{_ as t}from"./useUIStore-DOxLVeGb.js";import{$t as n,kn as r,xn as i}from"./persistence-DE9RnVcA.js";import{nt as a,rt as o}from"./vendor-base-ui-CnHfUmDN.js";import{n as s}from"./platform-MjT8iqKB.js";import{a as c,n as l}from"./vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js";import{n as u}from"./vendor-react-DLPElpoQ.js";import{n as d}from"./clipboard-BXC1vZwE.js";import{t as f}from"./utils-C2R0YoC5.js";import{n as p}from"./Icon-BLWVqBee.js";import{t as m}from"./button-CeCkEpvn.js";import{i as h,n as g,t as _}from"./tooltip-B__x-HDJ.js";import{n as v}from"./useThemeSystem-DMzWVJaW.js";import{c as y,n as b,o as x,r as S,s as C}from"./markdownTheme-H7_7lbuh.js";import{i as w}from"./appThemeRegistry-ChLdmqHp.js";import{d as T,n as E,t as D,u as ee}from"./markdown-worker-DqO-GnVj.js";import{t as te}from"./vendor-tanstack-react-virtual-tlDgH6PG.js";var O=e(o(),1),k=u(),A=a(),ne=`w-[min(30rem,calc(100cqw-1rem))] min-w-[280px] max-w-full overflow-hidden rounded-xl shadow-[inset_0_1px_0_0_rgba(255,255,255,0.8),inset_0_0_0_1px_rgba(0,0,0,0.04),0_0_0_1px_rgba(0,0,0,0.10),0_1px_2px_-0.5px_rgba(0,0,0,0.08),0_4px_8px_-2px_rgba(0,0,0,0.08),0_12px_20px_-4px_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.12),inset_0_0_0_1px_rgba(255,255,255,0.08),0_0_0_1px_rgba(0,0,0,0.36),0_1px_1px_-0.5px_rgba(0,0,0,0.22),0_3px_3px_-1.5px_rgba(0,0,0,0.20),0_6px_6px_-3px_rgba(0,0,0,0.16)]`,j={maxWidth:`min(28rem, calc(100cqw - 4ch))`,backgroundColor:`var(--surface-elevated)`,color:`var(--surface-elevated-foreground)`},M=`flex items-center justify-between gap-3 border-b border-[var(--surface-subtle)] px-3 py-2.5`,N=`text-xs font-medium leading-4 text-foreground md:text-[0.8125rem] md:leading-5`,re=`text-xs tabular-nums leading-4 text-muted-foreground md:text-[0.8125rem] md:leading-5`,P=`m-0 list-none divide-y divide-[var(--surface-subtle)] p-0`,F=`${P} max-h-[min(22rem,50vh)] overflow-y-auto`,ie=`${P} w-full`,ae={className:`p-0 rounded-none`,maxHeightClass:`max-h-[46vh]`,disableHorizontal:!0},I=`px-2.5 py-1.5 text-xs leading-4 md:py-2 md:text-[0.8125rem] md:leading-5`,oe=`flex min-w-0 items-center gap-1 text-xs leading-4 text-muted-foreground md:gap-1.5 md:text-[0.8125rem] md:leading-5`,se=`min-w-0 truncate text-xs leading-4 text-foreground md:text-[0.8125rem] md:leading-5`,ce=`inline-flex shrink-0 items-center gap-1 text-xs leading-4 tabular-nums text-muted-foreground md:text-[0.8125rem] md:leading-5`,le=`h-3 w-3 shrink-0 md:h-3.5 md:w-3.5`,ue={in_progress:`text-foreground`,pending:`text-foreground`,completed:`text-muted-foreground line-through`,cancelled:`text-muted-foreground line-through`},L={in_progress:`chat.statusRow.todo.status.inProgress`,pending:`chat.statusRow.todo.status.pending`,completed:`chat.statusRow.todo.status.completed`,cancelled:`chat.statusRow.todo.status.cancelled`},R=e=>{let t=(0,k.c)(26),{todo:n,isActive:r,activeRef:i}=e,{t:a}=d(),o=z.has(n.status)?n.status:`pending`,s=L[o],c;t[0]!==a||t[1]!==s?(c=a(s),t[0]=a,t[1]=s,t[2]=c):c=t[2];let l=c,u;t[3]===o?u=t[4]:(u=o===`in_progress`?(0,A.jsx)(p,{name:`record-circle`,className:`h-3.5 w-3.5 text-[var(--status-info)]`,"aria-hidden":`true`}):o===`completed`?(0,A.jsx)(p,{name:`checkbox-circle`,className:`h-3.5 w-3.5 text-[var(--status-success)]`,"aria-hidden":`true`}):(0,A.jsx)(p,{name:`time`,className:`h-3.5 w-3.5 text-muted-foreground`,"aria-hidden":`true`}),t[3]=o,t[4]=u);let m=u,v=r?`step`:void 0,y=r&&`bg-[var(--surface-muted)] before:absolute before:inset-y-2 before:left-0 before:w-0.5 before:rounded-full before:bg-[var(--status-info)]`,b;t[5]===y?b=t[6]:(b=f(`group relative grid min-w-0 grid-cols-[1rem_minmax(0,1fr)] items-start gap-2.5`,I,y),t[5]=y,t[6]=b);let x;t[7]!==m||t[8]!==l?(x=(0,A.jsx)(h,{asChild:!0,children:(0,A.jsx)(`span`,{className:`flex size-4 items-center justify-center`,"aria-label":l,children:m})}),t[7]=m,t[8]=l,t[9]=x):x=t[9];let S;t[10]===l?S=t[11]:(S=(0,A.jsx)(g,{side:`left`,sideOffset:6,children:l}),t[10]=l,t[11]=S);let C;t[12]!==x||t[13]!==S?(C=(0,A.jsxs)(_,{children:[x,S]}),t[12]=x,t[13]=S,t[14]=C):C=t[14];let w=ue[o],T;t[15]===w?T=t[16]:(T=f(`block whitespace-normal break-words`,w),t[15]=w,t[16]=T);let E;t[17]!==T||t[18]!==n.content?(E=(0,A.jsx)(`span`,{className:`min-w-0 flex-1`,children:(0,A.jsx)(`span`,{className:T,children:n.content})}),t[17]=T,t[18]=n.content,t[19]=E):E=t[19];let D;return t[20]!==i||t[21]!==E||t[22]!==v||t[23]!==b||t[24]!==C?(D=(0,A.jsxs)(`li`,{ref:i,"aria-current":v,className:b,children:[C,E]}),t[20]=i,t[21]=E,t[22]=v,t[23]=b,t[24]=C,t[25]=D):D=t[25],D},z=new Set([`in_progress`,`pending`,`completed`,`cancelled`]),B=`image/png`,V=c(`OpenChamberMedia`),H=e=>{let t=e.toLowerCase().split(`;`)[0]?.trim()||B;if(t===`image/jpeg`||t===`image/jpg`)return`jpg`;if(t===`image/webp`)return`webp`;if(t===`image/gif`)return`gif`;if(t===`image/svg+xml`)return`svg`;if(t===`image/png`)return`png`;let n=t.split(`/`)[1];return n&&/^[a-z0-9.+-]+$/i.test(n)?n:`png`},U=(e,t)=>{let n=`image.${H(t)}`,r=(((e||``).trim()||n).split(/[\\/]/).pop()||n).replace(/[^\w.\- ()[\]]+/g,`_`).replace(/^\.+/,``);return r?/\.[a-z0-9]{1,8}$/i.test(r)?r:`${r}.${H(t)}`:n},W=e=>new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{if(typeof r.result==`string`){t(r.result);return}n(Error(`Failed to encode image`))},r.onerror=()=>n(r.error??Error(`Failed to encode image`)),r.readAsDataURL(e)}),de=async e=>{let t=await W(e),n=t.indexOf(`,`);return n>=0?t.slice(n+1):t},fe=(e,t)=>{let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),window.setTimeout(()=>URL.revokeObjectURL(n),1e3)},G=async(e,t)=>{let n=await fetch(e,{signal:t,cache:`no-store`});if(!n.ok)throw Error(`Image fetch failed (${n.status})`);return n.blob()},pe=async(e,t=B)=>{if(!e.naturalWidth||!e.naturalHeight)return null;let n=e.currentSrc||e.src;if(n&&(/^(?:blob:|data:|openchamber-asset:)/i.test(n)||/^https?:/i.test(n)||/^\/api(?=\/|[?#]|$)/.test(n)))try{let e=await G(n);if(e.size>0)return e}catch{}try{let n=document.createElement(`canvas`);n.width=e.naturalWidth,n.height=e.naturalHeight;let r=n.getContext(`2d`);if(!r)return null;r.drawImage(e,0,0);let i=t.startsWith(`image/`)?t:B,a=await new Promise(e=>{n.toBlob(t=>e(t),i===`image/jpg`?`image/jpeg`:i)});return a&&a.size>0?a:null}catch{return null}},me=async(e,t)=>{let n=``;try{return n=await S(e,t??new AbortController().signal),await G(n,t)}finally{n&&C(n)}},he=async(e,t)=>{let n=e.effectiveDirectory??``,i=null;e.prefetchedBlob&&e.prefetchedBlob.size>0&&(i=e.prefetchedBlob);let a=e.displayUrl?.trim();if(!i&&a)try{i=await G(a,t)}catch{i=null}if(!i){let a=e.sourceUrl.trim();if(!a)throw Error(`Image source is empty`);if(/^(?:data|blob):/i.test(a)||/^https?:/i.test(a)||/^\/api(?=\/|[?#]|$)/.test(a))i=await G(a,t);else{let e=y(a,n);if(x(e))try{i=await me(e.path,t)}catch{let n=await r(`/api/fs/raw`,{method:`GET`,cache:`no-store`,query:{path:e.path},signal:t});if(!n.ok)throw Error(`Image source request failed with status ${n.status}`);i=await n.blob()}else i=await G(e.source,t)}}if(!i||i.size<=0)throw Error(`Image is empty`);let o=(e.mimeType||(i.type&&i.type.startsWith(`image/`)?i.type:``)||B).split(`;`)[0].trim()||B;return{blob:i,filename:U(e.filename,o),mimeType:o}},ge=()=>!s()||l===void 0?!1:l.isPluginAvailable(`OpenChamberMedia`),_e=async(e,t)=>{let{blob:r,filename:a,mimeType:o}=await he(e,t);if(ge()){let e=await de(r);return await V.saveImage({dataBase64:e,mimeType:o,filename:a}),`saved`}if(n()){let e=i()?.vscode;if(e?.saveImage){let t=await W(r);return await e.saveImage({fileName:a,dataUrl:t}),`saved`}}return fe(r,a),`downloaded`},ve=e=>e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`).replace(/'/g,`'`),ye=e=>e?Object.entries(e).map(([e,t])=>t==null?``:`${e.startsWith(`--`)?e:e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}:${typeof t==`number`?`${t}px`:t};`).join(``):``,K=(e,t,n)=>{let r=e.querySelector(`pre`);r&&(r.style.margin=`0`,r.style.background=`transparent`,r.style.whiteSpace=t?`pre-wrap`:`pre`,t&&(r.style.wordBreak=`break-word`,r.style.overflowWrap=`break-word`));let i=e.querySelector(`code`);if(i){let e=ye(n);e&&i.setAttribute(`style`,`${i.getAttribute(`style`)??``}${e}`)}},be=e=>`<pre><code>${ve(e)}</code></pre>`,xe=e=>{let t=(0,k.c)(21),{code:n,language:r,className:i,style:a,codeStyle:o,wrap:s}=e,c=s!==void 0&&s,{currentTheme:l}=v(),u=O.useRef(null),d;t[0]===l?d=t[1]:(d=b(l),t[0]=l,t[1]=d);let p=d,m,h;t[2]!==n||t[3]!==o||t[4]!==c?(m=()=>{let e=u.current;e&&(e.innerHTML=be(n),K(e,c,o))},h=[n,c,o],t[2]=n,t[3]=o,t[4]=c,t[5]=m,t[6]=h):(m=t[5],h=t[6]),O.useLayoutEffect(m,h);let g,_;t[7]!==n||t[8]!==o||t[9]!==r||t[10]!==c?(g=()=>{let e=u.current;if(!e)return;let t=!0,i=new AbortController;return D(n,(r||`text`).toLowerCase(),{signal:i.signal,priority:`visible`}).then(n=>{!t||!e||!n||(e.innerHTML=n,K(e,c,o))}),()=>{t=!1,i.abort()}},_=[n,r,c,o],t[7]=n,t[8]=o,t[9]=r,t[10]=c,t[11]=g,t[12]=_):(g=t[11],_=t[12]),O.useEffect(g,_);let y;t[13]===i?y=t[14]:(y=f(`typography-code`,i),t[13]=i,t[14]=y);let x=p,S;t[15]!==a||t[16]!==x?(S={...x,...a},t[15]=a,t[16]=x,t[17]=S):S=t[17];let C;return t[18]!==y||t[19]!==S?(C=(0,A.jsx)(`div`,{ref:u,className:y,style:S}),t[18]=y,t[19]=S,t[20]=C):C=t[20],C},Se=e=>{let t=e.replace(/^<file>\s*\n?/,``).replace(/\n?<\/file>\s*$/,``);return t=t.replace(/^\s*\d{5}\|\s?/gm,``),t.trim()},Ce=(e,t=``)=>{if(typeof e==`string`)return e;if(e==null)return t;if(typeof e==`number`||typeof e==`boolean`||typeof e==`bigint`)return String(e);try{return JSON.stringify(e)}catch{return t}},we=e=>e?e.includes(`<diagnostics`)||e.includes(`<file_diagnostics>`)||e.includes(`LSP errors detected`)||e.includes(`This file has errors`):!1,Te=e=>e?e.replace(/\n{0,2}LSP errors detected[\s\S]*?<diagnostics[^>]*>[\s\S]*?<\/diagnostics>/g,``).replace(/\n{0,2}This file has errors[\s\S]*?<\/file_diagnostics>/g,``).replace(/<diagnostics[^>]*>[\s\S]*?<\/diagnostics>/g,``).replace(/<file_diagnostics>[\s\S]*?<\/file_diagnostics>/g,``).trim():``,Ee=(e,t)=>!e||typeof e!=`object`?String(e):T(e,t||``),q=e=>{if(typeof e==`string`){let t=e.trim();return t.length>0?t:void 0}if(e&&typeof e==`object`){let t=e.patch;if(typeof t==`string`){let e=t.trim();return e.length>0?e:void 0}}},De=e=>{if(!e||typeof e!=`object`)return;let t=q(e.patch)??q(e.diff);if(t)return t;let n=Array.isArray(e.files)?e.files:[];for(let e of n){if(!e||typeof e!=`object`)continue;let t=q(e.patch)??q(e.diff);if(t)return t}},Oe=e=>{if(!e||typeof e!=`string`)return{data:null,isJson:!1};let t=e.trim();if(!t.startsWith(`{`)&&!t.startsWith(`[`)||!t.endsWith(`}`)&&!t.endsWith(`]`)||t.length<2)return{data:null,isJson:!1};try{let e=JSON.parse(t);return typeof e==`object`&&e?{data:e,isJson:!0}:{data:null,isJson:!1}}catch{return{data:null,isJson:!1}}},ke=(e,t,n)=>{let r=Se(e);if((t===`edit`||t===`multiedit`||t===`write`||t===`apply_patch`)&&we(r)&&(r=Te(r)),(t===`edit`||t===`multiedit`||t===`apply_patch`)&&r.trim().length===0){let e=De(n);if(e)return e}return r},Ae=e=>{let t=e.match(/<type>(file|directory)<\/type>/i)?.[1]?.toLowerCase()??`unknown`,n=(e.match(/<content>([\s\S]*?)<\/content>/i)?.[1]??e).replace(/\r\n/g,`
|
|
1
|
+
import{o as e}from"./rolldown-runtime-C0FnF6B9.js";import{_ as t}from"./useUIStore-DOxLVeGb.js";import{$t as n,kn as r,xn as i}from"./persistence-DE9RnVcA.js";import{nt as a,rt as o}from"./vendor-base-ui-CnHfUmDN.js";import{n as s}from"./platform-MjT8iqKB.js";import{a as c,n as l}from"./vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js";import{n as u}from"./vendor-react-DLPElpoQ.js";import{n as d}from"./clipboard-BXC1vZwE.js";import{t as f}from"./utils-C2R0YoC5.js";import{n as p}from"./Icon-BLWVqBee.js";import{t as m}from"./button-CeCkEpvn.js";import{i as h,n as g,t as _}from"./tooltip-B__x-HDJ.js";import{n as v}from"./useThemeSystem-DMzWVJaW.js";import{c as y,n as b,o as x,r as S,s as C}from"./markdownTheme-H7_7lbuh.js";import{i as w}from"./appThemeRegistry-DL8zx2Tf.js";import{d as T,n as E,t as D,u as ee}from"./markdown-worker-DqO-GnVj.js";import{t as te}from"./vendor-tanstack-react-virtual-tlDgH6PG.js";var O=e(o(),1),k=u(),A=a(),ne=`w-[min(30rem,calc(100cqw-1rem))] min-w-[280px] max-w-full overflow-hidden rounded-xl shadow-[inset_0_1px_0_0_rgba(255,255,255,0.8),inset_0_0_0_1px_rgba(0,0,0,0.04),0_0_0_1px_rgba(0,0,0,0.10),0_1px_2px_-0.5px_rgba(0,0,0,0.08),0_4px_8px_-2px_rgba(0,0,0,0.08),0_12px_20px_-4px_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.12),inset_0_0_0_1px_rgba(255,255,255,0.08),0_0_0_1px_rgba(0,0,0,0.36),0_1px_1px_-0.5px_rgba(0,0,0,0.22),0_3px_3px_-1.5px_rgba(0,0,0,0.20),0_6px_6px_-3px_rgba(0,0,0,0.16)]`,j={maxWidth:`min(28rem, calc(100cqw - 4ch))`,backgroundColor:`var(--surface-elevated)`,color:`var(--surface-elevated-foreground)`},M=`flex items-center justify-between gap-3 border-b border-[var(--surface-subtle)] px-3 py-2.5`,N=`text-xs font-medium leading-4 text-foreground md:text-[0.8125rem] md:leading-5`,re=`text-xs tabular-nums leading-4 text-muted-foreground md:text-[0.8125rem] md:leading-5`,P=`m-0 list-none divide-y divide-[var(--surface-subtle)] p-0`,F=`${P} max-h-[min(22rem,50vh)] overflow-y-auto`,ie=`${P} w-full`,ae={className:`p-0 rounded-none`,maxHeightClass:`max-h-[46vh]`,disableHorizontal:!0},I=`px-2.5 py-1.5 text-xs leading-4 md:py-2 md:text-[0.8125rem] md:leading-5`,oe=`flex min-w-0 items-center gap-1 text-xs leading-4 text-muted-foreground md:gap-1.5 md:text-[0.8125rem] md:leading-5`,se=`min-w-0 truncate text-xs leading-4 text-foreground md:text-[0.8125rem] md:leading-5`,ce=`inline-flex shrink-0 items-center gap-1 text-xs leading-4 tabular-nums text-muted-foreground md:text-[0.8125rem] md:leading-5`,le=`h-3 w-3 shrink-0 md:h-3.5 md:w-3.5`,ue={in_progress:`text-foreground`,pending:`text-foreground`,completed:`text-muted-foreground line-through`,cancelled:`text-muted-foreground line-through`},L={in_progress:`chat.statusRow.todo.status.inProgress`,pending:`chat.statusRow.todo.status.pending`,completed:`chat.statusRow.todo.status.completed`,cancelled:`chat.statusRow.todo.status.cancelled`},R=e=>{let t=(0,k.c)(26),{todo:n,isActive:r,activeRef:i}=e,{t:a}=d(),o=z.has(n.status)?n.status:`pending`,s=L[o],c;t[0]!==a||t[1]!==s?(c=a(s),t[0]=a,t[1]=s,t[2]=c):c=t[2];let l=c,u;t[3]===o?u=t[4]:(u=o===`in_progress`?(0,A.jsx)(p,{name:`record-circle`,className:`h-3.5 w-3.5 text-[var(--status-info)]`,"aria-hidden":`true`}):o===`completed`?(0,A.jsx)(p,{name:`checkbox-circle`,className:`h-3.5 w-3.5 text-[var(--status-success)]`,"aria-hidden":`true`}):(0,A.jsx)(p,{name:`time`,className:`h-3.5 w-3.5 text-muted-foreground`,"aria-hidden":`true`}),t[3]=o,t[4]=u);let m=u,v=r?`step`:void 0,y=r&&`bg-[var(--surface-muted)] before:absolute before:inset-y-2 before:left-0 before:w-0.5 before:rounded-full before:bg-[var(--status-info)]`,b;t[5]===y?b=t[6]:(b=f(`group relative grid min-w-0 grid-cols-[1rem_minmax(0,1fr)] items-start gap-2.5`,I,y),t[5]=y,t[6]=b);let x;t[7]!==m||t[8]!==l?(x=(0,A.jsx)(h,{asChild:!0,children:(0,A.jsx)(`span`,{className:`flex size-4 items-center justify-center`,"aria-label":l,children:m})}),t[7]=m,t[8]=l,t[9]=x):x=t[9];let S;t[10]===l?S=t[11]:(S=(0,A.jsx)(g,{side:`left`,sideOffset:6,children:l}),t[10]=l,t[11]=S);let C;t[12]!==x||t[13]!==S?(C=(0,A.jsxs)(_,{children:[x,S]}),t[12]=x,t[13]=S,t[14]=C):C=t[14];let w=ue[o],T;t[15]===w?T=t[16]:(T=f(`block whitespace-normal break-words`,w),t[15]=w,t[16]=T);let E;t[17]!==T||t[18]!==n.content?(E=(0,A.jsx)(`span`,{className:`min-w-0 flex-1`,children:(0,A.jsx)(`span`,{className:T,children:n.content})}),t[17]=T,t[18]=n.content,t[19]=E):E=t[19];let D;return t[20]!==i||t[21]!==E||t[22]!==v||t[23]!==b||t[24]!==C?(D=(0,A.jsxs)(`li`,{ref:i,"aria-current":v,className:b,children:[C,E]}),t[20]=i,t[21]=E,t[22]=v,t[23]=b,t[24]=C,t[25]=D):D=t[25],D},z=new Set([`in_progress`,`pending`,`completed`,`cancelled`]),B=`image/png`,V=c(`OpenChamberMedia`),H=e=>{let t=e.toLowerCase().split(`;`)[0]?.trim()||B;if(t===`image/jpeg`||t===`image/jpg`)return`jpg`;if(t===`image/webp`)return`webp`;if(t===`image/gif`)return`gif`;if(t===`image/svg+xml`)return`svg`;if(t===`image/png`)return`png`;let n=t.split(`/`)[1];return n&&/^[a-z0-9.+-]+$/i.test(n)?n:`png`},U=(e,t)=>{let n=`image.${H(t)}`,r=(((e||``).trim()||n).split(/[\\/]/).pop()||n).replace(/[^\w.\- ()[\]]+/g,`_`).replace(/^\.+/,``);return r?/\.[a-z0-9]{1,8}$/i.test(r)?r:`${r}.${H(t)}`:n},W=e=>new Promise((t,n)=>{let r=new FileReader;r.onload=()=>{if(typeof r.result==`string`){t(r.result);return}n(Error(`Failed to encode image`))},r.onerror=()=>n(r.error??Error(`Failed to encode image`)),r.readAsDataURL(e)}),de=async e=>{let t=await W(e),n=t.indexOf(`,`);return n>=0?t.slice(n+1):t},fe=(e,t)=>{let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),window.setTimeout(()=>URL.revokeObjectURL(n),1e3)},G=async(e,t)=>{let n=await fetch(e,{signal:t,cache:`no-store`});if(!n.ok)throw Error(`Image fetch failed (${n.status})`);return n.blob()},pe=async(e,t=B)=>{if(!e.naturalWidth||!e.naturalHeight)return null;let n=e.currentSrc||e.src;if(n&&(/^(?:blob:|data:|openchamber-asset:)/i.test(n)||/^https?:/i.test(n)||/^\/api(?=\/|[?#]|$)/.test(n)))try{let e=await G(n);if(e.size>0)return e}catch{}try{let n=document.createElement(`canvas`);n.width=e.naturalWidth,n.height=e.naturalHeight;let r=n.getContext(`2d`);if(!r)return null;r.drawImage(e,0,0);let i=t.startsWith(`image/`)?t:B,a=await new Promise(e=>{n.toBlob(t=>e(t),i===`image/jpg`?`image/jpeg`:i)});return a&&a.size>0?a:null}catch{return null}},me=async(e,t)=>{let n=``;try{return n=await S(e,t??new AbortController().signal),await G(n,t)}finally{n&&C(n)}},he=async(e,t)=>{let n=e.effectiveDirectory??``,i=null;e.prefetchedBlob&&e.prefetchedBlob.size>0&&(i=e.prefetchedBlob);let a=e.displayUrl?.trim();if(!i&&a)try{i=await G(a,t)}catch{i=null}if(!i){let a=e.sourceUrl.trim();if(!a)throw Error(`Image source is empty`);if(/^(?:data|blob):/i.test(a)||/^https?:/i.test(a)||/^\/api(?=\/|[?#]|$)/.test(a))i=await G(a,t);else{let e=y(a,n);if(x(e))try{i=await me(e.path,t)}catch{let n=await r(`/api/fs/raw`,{method:`GET`,cache:`no-store`,query:{path:e.path},signal:t});if(!n.ok)throw Error(`Image source request failed with status ${n.status}`);i=await n.blob()}else i=await G(e.source,t)}}if(!i||i.size<=0)throw Error(`Image is empty`);let o=(e.mimeType||(i.type&&i.type.startsWith(`image/`)?i.type:``)||B).split(`;`)[0].trim()||B;return{blob:i,filename:U(e.filename,o),mimeType:o}},ge=()=>!s()||l===void 0?!1:l.isPluginAvailable(`OpenChamberMedia`),_e=async(e,t)=>{let{blob:r,filename:a,mimeType:o}=await he(e,t);if(ge()){let e=await de(r);return await V.saveImage({dataBase64:e,mimeType:o,filename:a}),`saved`}if(n()){let e=i()?.vscode;if(e?.saveImage){let t=await W(r);return await e.saveImage({fileName:a,dataUrl:t}),`saved`}}return fe(r,a),`downloaded`},ve=e=>e.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`).replace(/'/g,`'`),ye=e=>e?Object.entries(e).map(([e,t])=>t==null?``:`${e.startsWith(`--`)?e:e.replace(/[A-Z]/g,e=>`-${e.toLowerCase()}`)}:${typeof t==`number`?`${t}px`:t};`).join(``):``,K=(e,t,n)=>{let r=e.querySelector(`pre`);r&&(r.style.margin=`0`,r.style.background=`transparent`,r.style.whiteSpace=t?`pre-wrap`:`pre`,t&&(r.style.wordBreak=`break-word`,r.style.overflowWrap=`break-word`));let i=e.querySelector(`code`);if(i){let e=ye(n);e&&i.setAttribute(`style`,`${i.getAttribute(`style`)??``}${e}`)}},be=e=>`<pre><code>${ve(e)}</code></pre>`,xe=e=>{let t=(0,k.c)(21),{code:n,language:r,className:i,style:a,codeStyle:o,wrap:s}=e,c=s!==void 0&&s,{currentTheme:l}=v(),u=O.useRef(null),d;t[0]===l?d=t[1]:(d=b(l),t[0]=l,t[1]=d);let p=d,m,h;t[2]!==n||t[3]!==o||t[4]!==c?(m=()=>{let e=u.current;e&&(e.innerHTML=be(n),K(e,c,o))},h=[n,c,o],t[2]=n,t[3]=o,t[4]=c,t[5]=m,t[6]=h):(m=t[5],h=t[6]),O.useLayoutEffect(m,h);let g,_;t[7]!==n||t[8]!==o||t[9]!==r||t[10]!==c?(g=()=>{let e=u.current;if(!e)return;let t=!0,i=new AbortController;return D(n,(r||`text`).toLowerCase(),{signal:i.signal,priority:`visible`}).then(n=>{!t||!e||!n||(e.innerHTML=n,K(e,c,o))}),()=>{t=!1,i.abort()}},_=[n,r,c,o],t[7]=n,t[8]=o,t[9]=r,t[10]=c,t[11]=g,t[12]=_):(g=t[11],_=t[12]),O.useEffect(g,_);let y;t[13]===i?y=t[14]:(y=f(`typography-code`,i),t[13]=i,t[14]=y);let x=p,S;t[15]!==a||t[16]!==x?(S={...x,...a},t[15]=a,t[16]=x,t[17]=S):S=t[17];let C;return t[18]!==y||t[19]!==S?(C=(0,A.jsx)(`div`,{ref:u,className:y,style:S}),t[18]=y,t[19]=S,t[20]=C):C=t[20],C},Se=e=>{let t=e.replace(/^<file>\s*\n?/,``).replace(/\n?<\/file>\s*$/,``);return t=t.replace(/^\s*\d{5}\|\s?/gm,``),t.trim()},Ce=(e,t=``)=>{if(typeof e==`string`)return e;if(e==null)return t;if(typeof e==`number`||typeof e==`boolean`||typeof e==`bigint`)return String(e);try{return JSON.stringify(e)}catch{return t}},we=e=>e?e.includes(`<diagnostics`)||e.includes(`<file_diagnostics>`)||e.includes(`LSP errors detected`)||e.includes(`This file has errors`):!1,Te=e=>e?e.replace(/\n{0,2}LSP errors detected[\s\S]*?<diagnostics[^>]*>[\s\S]*?<\/diagnostics>/g,``).replace(/\n{0,2}This file has errors[\s\S]*?<\/file_diagnostics>/g,``).replace(/<diagnostics[^>]*>[\s\S]*?<\/diagnostics>/g,``).replace(/<file_diagnostics>[\s\S]*?<\/file_diagnostics>/g,``).trim():``,Ee=(e,t)=>!e||typeof e!=`object`?String(e):T(e,t||``),q=e=>{if(typeof e==`string`){let t=e.trim();return t.length>0?t:void 0}if(e&&typeof e==`object`){let t=e.patch;if(typeof t==`string`){let e=t.trim();return e.length>0?e:void 0}}},De=e=>{if(!e||typeof e!=`object`)return;let t=q(e.patch)??q(e.diff);if(t)return t;let n=Array.isArray(e.files)?e.files:[];for(let e of n){if(!e||typeof e!=`object`)continue;let t=q(e.patch)??q(e.diff);if(t)return t}},Oe=e=>{if(!e||typeof e!=`string`)return{data:null,isJson:!1};let t=e.trim();if(!t.startsWith(`{`)&&!t.startsWith(`[`)||!t.endsWith(`}`)&&!t.endsWith(`]`)||t.length<2)return{data:null,isJson:!1};try{let e=JSON.parse(t);return typeof e==`object`&&e?{data:e,isJson:!0}:{data:null,isJson:!1}}catch{return{data:null,isJson:!1}}},ke=(e,t,n)=>{let r=Se(e);if((t===`edit`||t===`multiedit`||t===`write`||t===`apply_patch`)&&we(r)&&(r=Te(r)),(t===`edit`||t===`multiedit`||t===`apply_patch`)&&r.trim().length===0){let e=De(n);if(e)return e}return r},Ae=e=>{let t=e.match(/<type>(file|directory)<\/type>/i)?.[1]?.toLowerCase()??`unknown`,n=(e.match(/<content>([\s\S]*?)<\/content>/i)?.[1]??e).replace(/\r\n/g,`
|
|
2
2
|
`).split(`
|
|
3
3
|
`),r=e=>/\(\s*File has more lines\..*offset.*\)/i.test(e.trim());return{type:t,lines:n.map(e=>{let n=e.trim(),i=n.startsWith(`(`)&&n.endsWith(`)`)||r(n);if(t!==`directory`){let t=e.match(/^(\d+):\s?(.*)$/);if(t){let e=t[2],n=e.trim(),i=n.startsWith(`(`)&&n.endsWith(`)`)||r(n);return{lineNumber:i?null:Number(t[1]),text:e,isInfo:i}}}return{lineNumber:null,text:e,isInfo:i}}).filter((e,t,n)=>{if(e.text.trim().length>0)return!0;let r=n[t-1],i=n[t+1],a=!!(r?.isInfo||i?.isInfo),o=e.lineNumber!==null;return!(a&&o)})}},je=(e,n)=>{try{let r=e.trim().split(`
|
|
4
4
|
`).filter(Boolean);if(r.length===0)return null;let i=[];return r.forEach(e=>{let t=e.match(/^(\s*)(.+)$/);if(t){let[,e,n]=t,r=Math.floor(e.length/2),a=!n.endsWith(`/`);i.push({name:n.replace(/\/$/,``),depth:r,isFile:a})}}),(0,A.jsx)(`div`,{className:f(`w-full min-w-0 font-mono space-y-0.5`,n?.unstyled?null:`p-3 bg-muted/20 rounded-xl border border-border/30`),style:t.tool.popup,children:i.map((e,t)=>(0,A.jsx)(`div`,{className:`min-w-0`,style:{paddingLeft:`${e.depth*20}px`},children:e.isFile?(0,A.jsx)(`span`,{className:`text-foreground/90 block truncate`,children:e.name}):(0,A.jsxs)(`span`,{className:`font-semibold text-foreground block truncate`,children:[e.name,`/`]})},t))})}catch{return null}},Me={backgroundColor:`var(--status-info)`,opacity:.6},Ne=(e,n,r)=>{try{let i=e.trim().split(`
|
package/dist/index.html
CHANGED
|
@@ -552,7 +552,7 @@
|
|
|
552
552
|
opacity: 0.72;
|
|
553
553
|
}
|
|
554
554
|
</style>
|
|
555
|
-
<script type="module" crossorigin src="/assets/main-
|
|
555
|
+
<script type="module" crossorigin src="/assets/main-ej4kLP0y.js"></script>
|
|
556
556
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
|
|
557
557
|
<link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
|
|
558
558
|
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-B4dlGEM2.js">
|
|
@@ -569,7 +569,7 @@
|
|
|
569
569
|
<link rel="modulepreload" crossorigin href="/assets/terminalApi-Bnx0KvaV.js">
|
|
570
570
|
<link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
|
|
571
571
|
<link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
|
|
572
|
-
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-
|
|
572
|
+
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-DY2RRqNN.js">
|
|
573
573
|
<link rel="stylesheet" crossorigin href="/assets/src-eGuNWxHc.css">
|
|
574
574
|
</head>
|
|
575
575
|
<body class="h-full bg-background text-foreground">
|
package/dist/mini-chat.html
CHANGED
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
}
|
|
75
75
|
</style>
|
|
76
76
|
|
|
77
|
-
<script type="module" crossorigin src="/assets/miniChat-
|
|
77
|
+
<script type="module" crossorigin src="/assets/miniChat-h4TCod13.js"></script>
|
|
78
78
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
|
|
79
79
|
<link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
|
|
80
80
|
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-B4dlGEM2.js">
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
<link rel="modulepreload" crossorigin href="/assets/terminalApi-Bnx0KvaV.js">
|
|
92
92
|
<link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
|
|
93
93
|
<link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
|
|
94
|
-
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-
|
|
94
|
+
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-DY2RRqNN.js">
|
|
95
95
|
<link rel="stylesheet" crossorigin href="/assets/src-eGuNWxHc.css">
|
|
96
96
|
</head>
|
|
97
97
|
<body class="h-full bg-background text-foreground">
|
package/dist/mobile.html
CHANGED
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
})();
|
|
64
64
|
</script>
|
|
65
65
|
|
|
66
|
-
<script type="module" crossorigin src="/assets/mobile-
|
|
66
|
+
<script type="module" crossorigin src="/assets/mobile-D3ev3kF7.js"></script>
|
|
67
67
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
|
|
68
68
|
<link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
|
|
69
69
|
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-B4dlGEM2.js">
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
<link rel="modulepreload" crossorigin href="/assets/terminalApi-Bnx0KvaV.js">
|
|
81
81
|
<link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
|
|
82
82
|
<link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
|
|
83
|
-
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-
|
|
83
|
+
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-DY2RRqNN.js">
|
|
84
84
|
<link rel="stylesheet" crossorigin href="/assets/src-eGuNWxHc.css">
|
|
85
85
|
</head>
|
|
86
86
|
<body class="h-full bg-background text-foreground">
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -366,6 +366,9 @@ const apnsRuntime = createApnsRuntime({
|
|
|
366
366
|
const addOrUpdateApnsToken = (...args) => apnsRuntime.addOrUpdateApnsToken(...args);
|
|
367
367
|
const removeApnsToken = (...args) => apnsRuntime.removeApnsToken(...args);
|
|
368
368
|
const sendApnsToAllUiSessions = (...args) => apnsRuntime.sendApnsToAllUiSessions(...args);
|
|
369
|
+
const addOrUpdateLiveActivityToken = (...args) => apnsRuntime.addOrUpdateLiveActivityToken(...args);
|
|
370
|
+
const removeLiveActivityToken = (...args) => apnsRuntime.removeLiveActivityToken(...args);
|
|
371
|
+
const sendLiveActivityEnd = (...args) => apnsRuntime.sendLiveActivityEnd(...args);
|
|
369
372
|
|
|
370
373
|
const TERMINAL_INPUT_WS_MAX_REBINDS_PER_WINDOW = 128;
|
|
371
374
|
const TERMINAL_INPUT_WS_REBIND_WINDOW_MS = 60 * 1000;
|
|
@@ -660,6 +663,7 @@ const notificationTriggerRuntime = createNotificationTriggerRuntime({
|
|
|
660
663
|
broadcastUiNotification,
|
|
661
664
|
sendPushToAllUiSessions,
|
|
662
665
|
sendApnsToAllUiSessions,
|
|
666
|
+
sendLiveActivityEnd,
|
|
663
667
|
isAnyInteractiveClientVisible,
|
|
664
668
|
buildOpenCodeUrl,
|
|
665
669
|
getOpenCodeAuthHeaders,
|
|
@@ -1447,6 +1451,9 @@ async function main(options = {}) {
|
|
|
1447
1451
|
removePushSubscription,
|
|
1448
1452
|
addOrUpdateApnsToken,
|
|
1449
1453
|
removeApnsToken,
|
|
1454
|
+
addOrUpdateLiveActivityToken,
|
|
1455
|
+
removeLiveActivityToken,
|
|
1456
|
+
sendLiveActivityEnd,
|
|
1450
1457
|
updateUiVisibility,
|
|
1451
1458
|
clearPendingPushBadge: () => clearPendingPushBadge(),
|
|
1452
1459
|
isUiVisible,
|
|
@@ -29,6 +29,41 @@ registered them — so a leaked device token alone can't be used to push.
|
|
|
29
29
|
not application plaintext. Layer 1 (`openchamber-relay`) never sees these secrets.
|
|
30
30
|
5. Tapping a push deep-links to its session via the forwarded `sessionId`.
|
|
31
31
|
|
|
32
|
+
## Live Activity (update / end)
|
|
33
|
+
|
|
34
|
+
ActivityKit has issued **update/end push tokens** for an Activity that is
|
|
35
|
+
**already started on-device** since **iOS 16.1**. **Push-to-start** (creating an
|
|
36
|
+
Activity from APNs while the app is not running) is **iOS 17.2+** and is not
|
|
37
|
+
part of this Host path.
|
|
38
|
+
|
|
39
|
+
The native app registers a Live Activity token with **its own server**
|
|
40
|
+
(`POST /api/push/live-activity-token`: `token`, `activityId`, `sessionId`).
|
|
41
|
+
Those tokens are stored separately from alert APNs device tokens (same
|
|
42
|
+
`apns-tokens.json` file, `version` 2 `liveActivityTokensBySession`; v1 alert
|
|
43
|
+
data remains readable) and are never used for banner push. The Host binds them
|
|
44
|
+
on the relay at `POST /v1/push/register-live-activity-token` with
|
|
45
|
+
`{ token, platform: "ios", kind: "liveactivity", publicKeyJwk, ts, sig }` signed
|
|
46
|
+
over `${ts}.${token}.${platform}.${kind}`. Unregister uses
|
|
47
|
+
`/v1/push/unregister-live-activity-token` with the same signature, and only the
|
|
48
|
+
owning UI session may delete a local token.
|
|
49
|
+
|
|
50
|
+
On top-level session **completion** or **error** (child sessions and small-model
|
|
51
|
+
system sessions suppressed; independent of ordinary notification settings and UI
|
|
52
|
+
visibility) the Host calls `sendLiveActivityEnd`. Relay send is
|
|
53
|
+
`POST /v1/push/live-activity` with `{ tokens, event, contentState,
|
|
54
|
+
dismissalDate?, staleDate?, publicKeyJwk, ts, sig }` signed over
|
|
55
|
+
`${ts}.${sortedTokens}.${event}.${status}.${eventVersion}.${updatedAt}.${endedAt}.${dismissalDate}.${staleDate}`.
|
|
56
|
+
Direct mode uses HTTP/2 token auth, `apns-topic: ${bundleId}.push-type.liveactivity`,
|
|
57
|
+
`apns-push-type: liveactivity`, and no alert.
|
|
58
|
+
|
|
59
|
+
The APNs / relay payload **must not** carry `sessionId` or user/session content.
|
|
60
|
+
`contentState` is only `{ status, eventVersion, updatedAt, endedAt }`.
|
|
61
|
+
`complete` uses a 15-minute dismissal and `error` a 60-minute dismissal (same as
|
|
62
|
+
native). `staleDate = updatedAt + 20min` is for **update** events only. After a
|
|
63
|
+
successfully accepted `end`, the Host clears the local Activity tokens for those
|
|
64
|
+
devices. A Live Activity failure does not drop alert tokens or block ordinary
|
|
65
|
+
push. Do not log Live Activity tokens or session ids.
|
|
66
|
+
|
|
32
67
|
## Foreground suppression
|
|
33
68
|
|
|
34
69
|
APNs is **not** gated on UI visibility. A backgrounded WKWebView can't reliably report "hidden"
|
|
@@ -7,7 +7,7 @@ This module provides notification message preparation utilities for the web serv
|
|
|
7
7
|
- `packages/web/server/lib/notifications/index.js`: public entrypoint imported by `packages/web/server/index.js`.
|
|
8
8
|
- `packages/web/server/lib/notifications/routes.js`: route registration for push, visibility, and session status/attention endpoints.
|
|
9
9
|
- `packages/web/server/lib/notifications/push-runtime.js`: push subscription persistence, VAPID initialization, and UI visibility runtime.
|
|
10
|
-
- `packages/web/server/lib/notifications/apns-runtime.js`: native iOS APNs device-token persistence + delivery. Two modes: **relay** (default — sign + POST tokens + generic text to the isolated Push Relay in `packages/relay-server/src/push/`, which holds the project APNs key) and **direct** (fallback — sign ES256 JWT with Node crypto + HTTP/2, when `OPENCHAMBER_PUSH_RELAY_DISABLED=true`). Unless `OPENCHAMBER_PUSH_RELAY_URL` is set, the Host maps the effective Relay `wss`/`ws` URL to the same host as `https`/`http` `/v1/push/send`. Before each relay send, the runtime checks an in-memory registration success cache (keyed by current register URL + token + platform) and only POSTs `/v1/push/register-token` for cache misses; only successfully bound tokens are sent. Switching Relay/Push URL clears that cache so persisted tokens re-bind to the new origin on the next send or `reRegisterAllTokens()`. Each server has an auto-generated ECDSA P-256 keypair (`getOrCreateRelayKeypair`, persisted in settings); it binds tokens on the relay (`/v1/push/register-token`) and signs every relay request, so the relay only delivers to tokens bound to that server. APNs is the native app's sole notification channel (no local notifications) and is NOT gated on UI visibility — iOS suppresses the foreground banner instead. Mobile push carries only generic text (scenario title + session name) — see `APNS.md`.
|
|
10
|
+
- `packages/web/server/lib/notifications/apns-runtime.js`: native iOS APNs device-token persistence + delivery, plus a separate Live Activity token store and `end` send path. Two modes: **relay** (default — sign + POST tokens + generic text to the isolated Push Relay in `packages/relay-server/src/push/`, which holds the project APNs key) and **direct** (fallback — sign ES256 JWT with Node crypto + HTTP/2, when `OPENCHAMBER_PUSH_RELAY_DISABLED=true`). Unless `OPENCHAMBER_PUSH_RELAY_URL` is set, the Host maps the effective Relay `wss`/`ws` URL to the same host as `https`/`http` `/v1/push/send`. Before each relay send, the runtime checks an in-memory registration success cache (keyed by current register URL + token + platform) and only POSTs `/v1/push/register-token` for cache misses; only successfully bound tokens are sent. Switching Relay/Push URL clears that cache so persisted tokens re-bind to the new origin on the next send or `reRegisterAllTokens()`. Each server has an auto-generated ECDSA P-256 keypair (`getOrCreateRelayKeypair`, persisted in settings); it binds tokens on the relay (`/v1/push/register-token`) and signs every relay request, so the relay only delivers to tokens bound to that server. APNs is the native app's sole notification channel (no local notifications) and is NOT gated on UI visibility — iOS suppresses the foreground banner instead. Mobile push carries only generic text (scenario title + session name) — see `APNS.md`. Live Activity tokens are persisted beside alert tokens in the same file (`version` 2, `liveActivityTokensBySession`) without mixing the two token types; v1 files remain readable. Live Activity relay bind/send uses `/v1/push/register-live-activity-token`, `/v1/push/unregister-live-activity-token`, and `/v1/push/live-activity`.
|
|
11
11
|
- `packages/web/server/lib/notifications/emitter-runtime.js`: desktop/stdout + UI SSE notification emission runtime.
|
|
12
12
|
- `packages/web/server/lib/notifications/runtime.js`: trigger runtime for OpenCode event-driven notification fanout.
|
|
13
13
|
- `packages/web/server/lib/notifications/template-runtime.js`: notification template variables and session text/title enrichment runtime. Zen-model helpers are retained as compatibility stubs only.
|
|
@@ -27,6 +27,8 @@ This module provides notification message preparation utilities for the web serv
|
|
|
27
27
|
- `DELETE /api/push/subscribe`
|
|
28
28
|
- `POST /api/push/apns-token` (native iOS APNs device-token registration)
|
|
29
29
|
- `DELETE /api/push/apns-token`
|
|
30
|
+
- `POST /api/push/live-activity-token` (native iOS Live Activity token registration; UI-auth, length-limited `token` / `activityId` / `sessionId`)
|
|
31
|
+
- `DELETE /api/push/live-activity-token` (owning UI session only)
|
|
30
32
|
- `POST /api/push/visibility`
|
|
31
33
|
- `GET /api/push/visibility`
|
|
32
34
|
- `GET /api/notifications/stream`
|
|
@@ -46,6 +48,7 @@ This module provides notification message preparation utilities for the web serv
|
|
|
46
48
|
- `maybeSendPushForTrigger(payload)`
|
|
47
49
|
- Owns:
|
|
48
50
|
- top-level completion/question/permission trigger routing; completion is the sole task-status notification and child sessions plus small-model system sessions (`metadata.openchamber.smallModel.purpose`) are suppressed
|
|
51
|
+
- top-level completion/error Live Activity `end` (`sendLiveActivityEnd`), independent of ordinary push settings and UI visibility, with the same child/small-model suppression; duplicate terminal events are idempotent at the token store
|
|
49
52
|
- session meta cache for child-session and small-model suppression
|
|
50
53
|
- template resolution and fallback behavior
|
|
51
54
|
- native notification fanout and web push payload fanout
|
|
@@ -70,7 +73,10 @@ This module provides notification message preparation utilities for the web serv
|
|
|
70
73
|
- `addOrUpdateApnsToken(uiSessionToken, deviceToken, userAgent, platform?, locale?)` — also binds a newly-seen token on the relay (signed `/v1/push/register-token`). Persists app UI `locale` for localized push titles.
|
|
71
74
|
- `removeApnsToken(uiSessionToken, deviceToken)`
|
|
72
75
|
- `removeApnsTokenFromAllSessions(deviceToken)`
|
|
73
|
-
- `
|
|
76
|
+
- `addOrUpdateLiveActivityToken(uiSessionToken, token, activityId, sessionId)` — idempotent upsert bound to the UI session + OpenCode `sessionId` + ActivityKit `activityId`. Token refresh replaces that activity's previous token. Caps per UI session and lazily expires stale entries. Binds on the relay (`/v1/push/register-live-activity-token`, `kind: "liveactivity"`). Does not log tokens or session ids.
|
|
77
|
+
- `removeLiveActivityToken(uiSessionToken, token)` — owning UI session only; unregisters from the relay when no local copies remain.
|
|
78
|
+
- `sendLiveActivityEnd({ sessionId, status, eventVersion?, endedAt? })` — looks up Live Activity tokens for that OpenCode session and sends `event: "end"` with `contentState` `{ status, eventVersion, updatedAt, endedAt }` only. `complete` dismisses after 15 minutes, `error` after 60 minutes (same as native). `staleDate` (`updatedAt + 20min`) is update-only and omitted on end. `eventVersion` is monotonic per session using current milliseconds. Relay body follows the signed live-activity contract; direct mode uses token auth, topic `${bundleId}.push-type.liveactivity`, `apns-push-type: liveactivity`, and no alert. APNs payloads must not carry `sessionId` or user content. Successfully accepted tokens are cleared locally; failures do not drop alert tokens or block ordinary push.
|
|
79
|
+
- `sendApnsToAllUiSessions(payload)` — localizes scenario titles per stored token locale (`apns-titles.js`), groups tokens by locale, then signs + sends (no UI-visibility gate; iOS suppresses the foreground banner). Before a relay send, each token is checked against the in-memory registration success cache for the current register URL; cache hits skip `/v1/push/register-token`, misses are registered with bounded concurrency, and only successfully bound tokens are sent. Switching Relay/Push URL clears that cache so the next send re-binds persisted tokens to the new origin. No-ops with a single warning when APNs is unconfigured. Drops tokens on `410` / `BadDeviceToken` / `Unregistered`. Never sends Live Activity tokens.
|
|
74
80
|
- `reRegisterAllTokens()` — binds every persisted device token to the current Push origin (same success cache and URL-switch invalidation as send).
|
|
75
81
|
- `resolveApnsConfig()`
|
|
76
82
|
- Configuration (env first, then `settings.apnsConfig`): `OPENCHAMBER_PUSH_RELAY_URL` (optional send-URL override), `OPENCHAMBER_PUSH_RELAY_DISABLED`, `OPENCHAMBER_APNS_KEY_ID`, `OPENCHAMBER_APNS_TEAM_ID`, `OPENCHAMBER_APNS_P8` (PEM contents; literal `\n` accepted) or `OPENCHAMBER_APNS_P8_PATH`, `OPENCHAMBER_APNS_BUNDLE_ID` (default `com.yee94.openchamber`), `OPENCHAMBER_APNS_ENVIRONMENT` (`sandbox` default, or `production` for TestFlight and App Store).
|