arcane-agents 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/client/assets/{index-HfXsReQG.js → index-DNXJVqF0.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/server/server/bootstrap/serverContext.js +6 -2
- package/dist/server/server/bootstrapApp.js +9 -3
- package/dist/server/server/cli.js +122 -6
- package/dist/server/server/config/loadConfig.js +10 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -476,6 +476,36 @@ server:
|
|
|
476
476
|
port: 7600
|
|
477
477
|
```
|
|
478
478
|
|
|
479
|
+
## Named Sessions
|
|
480
|
+
|
|
481
|
+
Named sessions let you run independent instances of Arcane Agents, each with its
|
|
482
|
+
own worker state (SQLite DB) and tmux session. Config (projects, runtimes,
|
|
483
|
+
shortcuts) stays shared.
|
|
484
|
+
|
|
485
|
+
### Usage
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
# Start the default session (same as before)
|
|
489
|
+
arcane-agents start
|
|
490
|
+
|
|
491
|
+
# Start a named session with separate state
|
|
492
|
+
arcane-agents start --session side-project
|
|
493
|
+
arcane-agents start -s experiments
|
|
494
|
+
|
|
495
|
+
# List all sessions that have been created
|
|
496
|
+
arcane-agents sessions list
|
|
497
|
+
|
|
498
|
+
# Delete a named session and all its data
|
|
499
|
+
arcane-agents sessions delete side-project
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### How it works
|
|
503
|
+
|
|
504
|
+
- The default session uses `~/.local/state/arcane-agents/arcane-agents.db` (unchanged).
|
|
505
|
+
- Named sessions store their DB under `~/.local/state/arcane-agents/sessions/<name>/arcane-agents.db`.
|
|
506
|
+
- Each named session gets its own tmux session (`arcane-agents-<name>` by default).
|
|
507
|
+
- Config paths are shared across all sessions.
|
|
508
|
+
|
|
479
509
|
## Development Commands
|
|
480
510
|
|
|
481
511
|
```bash
|
|
@@ -80,4 +80,4 @@ WARNING: This link could potentially be dangerous`)){let s=window.open();if(s){t
|
|
|
80
80
|
`,t)}paste(e){this._core.paste(e)}refresh(e,t){this._verifyIntegers(e,t),this._core.refresh(e,t)}reset(){this._core.reset()}clearTextureAtlas(){this._core.clearTextureAtlas()}loadAddon(e){this._addonManager.loadAddon(this,e)}static get strings(){return{get promptLabel(){return sh.get()},set promptLabel(e){sh.set(e)},get tooMuchOutput(){return nh.get()},set tooMuchOutput(e){nh.set(e)}}}_verifyIntegers(...e){for(Jr of e)if(Jr===1/0||isNaN(Jr)||Jr%1!==0)throw new Error("This API only accepts integers")}_verifyPositiveIntegers(...e){for(Jr of e)if(Jr&&(Jr===1/0||isNaN(Jr)||Jr%1!==0||Jr<0))throw new Error("This API only accepts positive integers")}};const Aw=`
|
|
81
81
|
`,Ow={background:"#1a1b26",foreground:"#c0caf5",cursor:"#c0caf5",cursorAccent:"#1a1b26",selectionBackground:"rgba(122, 162, 247, 0.28)",selectionInactiveBackground:"rgba(122, 162, 247, 0.2)",black:"#15161e",red:"#f7768e",green:"#9ece6a",yellow:"#e0af68",blue:"#7aa2f7",magenta:"#bb9af7",cyan:"#7dcfff",white:"#a9b1d6",brightBlack:"#414868",brightRed:"#f7768e",brightGreen:"#9ece6a",brightYellow:"#e0af68",brightBlue:"#7aa2f7",brightMagenta:"#bb9af7",brightCyan:"#7dcfff",brightWhite:"#c0caf5"};function zw({workerId:e,workerName:t,focusRequestKey:s}){const n=k.useRef(null),l=k.useRef(null),c=k.useRef(null),h=k.useRef(null),f=k.useRef(null),_=k.useRef(void 0),d=k.useCallback(()=>{l.current?.focus()},[]),g=k.useCallback(()=>{const w=h.current,v=l.current;!w||!v||w.readyState===WebSocket.OPEN&&w.send(JSON.stringify({type:"resize",cols:v.cols,rows:v.rows}))},[]),y=k.useCallback(()=>{const w=n.current,v=c.current,S=l.current;if(!w||!v||!S)return!1;const E=w.clientWidth,P=w.clientHeight;if(E<8||P<8||w.getClientRects().length===0)return!1;try{v.fit()}catch{return!1}return S.cols>0&&S.rows>0},[]),C=k.useCallback((w=16)=>{f.current&&cancelAnimationFrame(f.current);const v=S=>{if(y()){g();return}S<=0||(f.current=requestAnimationFrame(()=>{v(S-1)}))};f.current=requestAnimationFrame(()=>{v(w)})},[y,g]);return k.useEffect(()=>{if(!n.current)return;const w=new Iw({cursorBlink:!1,cursorStyle:"block",cursorInactiveStyle:"block",fontSize:13,lineHeight:1,theme:Ow}),v=new E0;w.loadAddon(v),w.attachCustomKeyEventHandler(E=>{if(!Ww(E))return!0;E.preventDefault(),E.stopPropagation();const P=h.current;return P?.readyState===WebSocket.OPEN&&P.send(Aw),!1}),w.open(n.current),w.writeln("Select an agent to connect its terminal."),l.current=w,c.current=v,C();const S=new ResizeObserver(()=>{C()});return S.observe(n.current),()=>{S.disconnect(),f.current&&(cancelAnimationFrame(f.current),f.current=null),h.current?.close(),w.dispose(),l.current=null,c.current=null}},[C]),k.useEffect(()=>{const w=l.current;if(!w)return;if(h.current?.close(),h.current=null,w.clear(),C(),!e){w.writeln("Select an agent to connect its terminal.");return}w.writeln(`Connecting to ${t??e}...`);const v=window.location.protocol==="https:"?"wss":"ws",S=new WebSocket(`${v}://${window.location.host}/api/terminal/${e}`);h.current=S;const E=w.onData(P=>{S.readyState===WebSocket.OPEN&&S.send(P)});return S.addEventListener("open",()=>{g(),C(24)}),S.addEventListener("message",P=>{typeof P.data=="string"?w.write(P.data):P.data instanceof Blob&&P.data.text().then(z=>{w.write(z)})}),S.addEventListener("close",()=>{w.writeln(`\r
|
|
82
82
|
[terminal disconnected]`)}),S.addEventListener("error",()=>{w.writeln(`\r
|
|
83
|
-
[terminal connection error]`)}),()=>{E.dispose(),S.close(),h.current===S&&(h.current=null)}},[C,g,e,t]),k.useEffect(()=>{if(!e||s===void 0||_.current===s)return;_.current=s,d();const w=setTimeout(()=>{d()},0);return()=>{clearTimeout(w)}},[s,d,e]),M.jsx("div",{className:"terminal-panel",ref:n})}function Ww(e){return e.type==="keydown"&&e.key==="Enter"&&e.shiftKey&&!e.ctrlKey&&!e.metaKey&&!e.altKey}function Fw({activeWorkers:e,selectedWorkers:t,terminalWorker:s,terminalFocused:n,selectedGroupActiveIndex:l,setSelectedGroupActiveIndex:c,setFocusedSelectedWorkerId:h,rallyCommandInputRef:f,rallyCommandDraft:_,rallyCommandSending:d,rallyCommandResultText:g,onRallyCommandDraftChange:y,onSendRallyCommand:C,rosterEntries:w,completionPendingWorkerIds:v,rosterActiveIndex:S,setRosterActiveIndex:E,onActivateRosterIndex:P,onOpenSelectedInTerminal:z,terminalFocusToken:H}){const I=k.useMemo(()=>new Set(v),[v]),G=v.length;return M.jsxs("div",{className:`terminal-column${s?" terminal-column-selected":""}${s&&n?" terminal-column-focused":""}`,children:[M.jsxs("div",{className:"terminal-header",children:[M.jsx("div",{className:"terminal-header-title",children:t.length>1&&!s?`${t.length} selected agents`:s?`${s.displayName??s.name} (${s.status})`:`Agents (${e.length})`}),!s&&G>0?M.jsxs("div",{className:"terminal-ready-chip",title:"Agents finished but not yet reviewed in terminal",children:["✦ ",G," ready"]}):null,s?M.jsx("button",{className:"terminal-open-external",onClick:()=>{z()},disabled:s.status==="stopped",title:"Open in external terminal",type:"button",children:"↗"}):null]}),t.length>1&&!s?M.jsxs("div",{className:"worker-roster",children:[M.jsx("div",{className:"worker-roster-section-label",children:"Selected Group"}),t.map((j,oe)=>M.jsx("button",{className:`worker-roster-item ${oe===l?"active":""}`,onMouseEnter:()=>c(oe),onClick:()=>{c(oe),h(j.id)},type:"button",children:M.jsxs("div",{className:"worker-roster-main",children:[M.jsx("img",{className:"worker-roster-avatar",src:`/api/assets/characters/${encodeURIComponent(j.avatarType)}/rotations/south.png`,alt:"",loading:"lazy","aria-hidden":"true"}),M.jsxs("div",{className:"worker-roster-text",children:[M.jsxs("div",{className:"worker-roster-name-row",children:[M.jsx("div",{className:"worker-roster-name",children:j.displayName??j.name}),I.has(j.id)?M.jsx("span",{className:"worker-complete-badge",title:"Finished and waiting for review",children:"READY"}):null]}),M.jsxs("div",{className:"worker-roster-meta",children:[j.projectId," · ",j.runtimeId," · ",j.status]}),j.activityText?M.jsx("div",{className:"worker-roster-activity",children:j.activityText}):null]})]})},j.id)),M.jsxs("form",{className:"rally-command-card",onSubmit:j=>{j.preventDefault(),C()},children:[M.jsxs("div",{className:"rally-command-header",children:[M.jsx("div",{className:"rally-command-title",children:"Rally Command"}),M.jsxs("div",{className:"rally-command-count",children:[t.length," agents"]})]}),M.jsx("textarea",{ref:f,className:"input rally-command-input",value:_,onChange:j=>{y(j.target.value)},onKeyDown:j=>{const oe=!j.shiftKey&&!j.ctrlKey&&!j.metaKey&&!j.altKey,Z=(j.ctrlKey||j.metaKey)&&!j.shiftKey&&!j.altKey;j.key==="Enter"&&(oe||Z)&&(j.preventDefault(),C())},placeholder:"Type once, send to all selected agents (use $NAME for per-agent names)...",disabled:d,rows:3}),M.jsxs("div",{className:"rally-command-actions",children:[M.jsx("div",{className:"rally-command-hint",children:"Enter sends, Shift+Enter adds a new line, $NAME inserts each agent's name"}),M.jsx("button",{className:"bar-btn",type:"submit",disabled:d||_.length===0,children:d?"Sending...":`Send to ${t.length}`})]}),g?M.jsx("div",{className:"rally-command-result",children:g}):null]})]}):s?M.jsx(zw,{workerId:s.id,workerName:s.displayName??s.name,focusRequestKey:H}):M.jsx("div",{className:"worker-roster",children:w.length===0?M.jsx("div",{className:"worker-roster-empty",children:"No active agents yet. Summon one from the bottom bar."}):w.map((j,oe)=>M.jsxs("div",{children:[j.kind==="shortcut"&&(oe===0||w[oe-1]?.kind!=="shortcut")?M.jsx("div",{className:"worker-roster-section-label",children:"Summon"}):null,j.kind==="worker"?M.jsx("button",{className:`worker-roster-item ${oe===S?"active":""}`,onMouseEnter:()=>E(oe),onClick:()=>P(oe),type:"button",children:M.jsxs("div",{className:"worker-roster-main",children:[M.jsx("img",{className:"worker-roster-avatar",src:`/api/assets/characters/${encodeURIComponent(j.worker.avatarType)}/rotations/south.png`,alt:"",loading:"lazy","aria-hidden":"true"}),M.jsxs("div",{className:"worker-roster-text",children:[M.jsxs("div",{className:"worker-roster-name-row",children:[M.jsx("div",{className:"worker-roster-name",children:j.worker.displayName??j.worker.name}),I.has(j.worker.id)?M.jsx("span",{className:"worker-complete-badge",title:"Finished and waiting for review",children:"READY"}):null]}),M.jsxs("div",{className:"worker-roster-meta",children:[j.worker.projectId," · ",j.worker.runtimeId," · ",j.worker.status]}),j.worker.activityText?M.jsx("div",{className:"worker-roster-activity",children:j.worker.activityText}):null]})]})}):M.jsx("button",{className:`worker-roster-item worker-roster-item-summon ${oe===S?"active":""}`,onMouseEnter:()=>E(oe),onClick:()=>P(oe),type:"button",children:M.jsxs("div",{className:"worker-roster-main",children:[j.shortcut.avatar?M.jsx("img",{className:"worker-roster-avatar worker-roster-summon-avatar",src:`/api/assets/characters/${encodeURIComponent(j.shortcut.avatar)}/rotations/south.png`,alt:"",loading:"lazy","aria-hidden":"true"}):M.jsx("div",{className:"worker-roster-summon-glyph","aria-hidden":"true",children:"+"}),M.jsxs("div",{className:"worker-roster-text",children:[M.jsx("div",{className:"worker-roster-name",children:j.shortcut.label}),M.jsxs("div",{className:"worker-roster-meta",children:[j.shortcut.project," · ",j.shortcut.runtime]}),M.jsx("div",{className:"worker-roster-activity",children:rv(j.shortcut.hotkeys)})]})]})})]},j.kind==="worker"?j.worker.id:`shortcut-${j.shortcutIndex}-${j.shortcut.label}`))})]})}function Hw(e,t){if(t.killConfirmWorkerIds.length>0)return Al(e)?(e.preventDefault(),t.confirmKillSelection(),!0):(e.preventDefault(),t.closeKillConfirm(),!0);if(!t.isEditableTarget(e.target)&&!t.isTerminalTarget(e.target)){const s=t.findMatchingShortcutIndexes(t.shortcutHotkeyBindings,e);if(s.length>0){e.preventDefault(),e.stopPropagation();for(const n of s)t.runSpawn({shortcutIndex:n});return!0}}if(e.key==="Escape")return t.renameModalOpen?(e.preventDefault(),t.closeRenameModal(),!0):t.batchSpawnDialogOpen?(e.preventDefault(),t.setBatchSpawnDialogOpen(!1),!0):t.shortcutsOverlayOpen?(e.preventDefault(),t.setShortcutsOverlayOpen(!1),!0):t.paletteOpen||t.spawnDialogOpen?(e.preventDefault(),t.setPaletteOpen(!1),t.setSpawnDialogOpen(!1),!0):(t.isTerminalTarget(e.target)||t.selectedWorkerId&&(e.preventDefault(),t.applySelection([])),!0);if(t.isTerminalEscapeShortcut(e)){if(!t.renameModalOpen&&!t.batchSpawnDialogOpen&&!t.shortcutsOverlayOpen&&!t.paletteOpen&&!t.spawnDialogOpen&&t.selectedWorkers.length>1&&t.focusedSelectedWorkerId){const n=t.escapeTerminalFocus();return e.preventDefault(),n&&e.stopPropagation(),t.setFocusedSelectedWorkerId(void 0),!0}if(t.escapeTerminalFocus())return e.preventDefault(),e.stopPropagation(),!0;if(!t.renameModalOpen&&!t.batchSpawnDialogOpen&&!t.shortcutsOverlayOpen&&!t.paletteOpen&&!t.spawnDialogOpen&&t.selectedWorkerIds.length>0){if(e.preventDefault(),t.selectedWorkerId){const n=t.rosterEntries.findIndex(l=>l.kind==="worker"&&l.worker.id===t.selectedWorkerId);n>=0&&t.setRosterActiveIndex(n)}t.applySelection([])}return!0}return e.key==="?"&&!e.ctrlKey&&!e.metaKey&&!e.altKey?((!t.isEditableTarget(e.target)||t.shortcutsOverlayOpen)&&(e.preventDefault(),t.setShortcutsOverlayOpen(s=>!s)),!0):(e.code==="BracketLeft"||e.code==="BracketRight"||e.key==="="&&!e.shiftKey)&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target)&&!t.isTerminalTarget(e.target)?(e.preventDefault(),e.code==="BracketLeft"?t.nudgeMapColumnRatio(e.shiftKey?-1:-t.mapColumnRatioStep):e.code==="BracketRight"?t.nudgeMapColumnRatio(e.shiftKey?1:t.mapColumnRatioStep):t.resetMapColumnRatio(),!0):!1}function jw(e,t){if(e.key==="Tab"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target))return t.isTerminalTarget(e.target)?!0:(e.preventDefault(),t.selectedWorkers.length>1?(t.cycleSelectedGroupFocus(e.shiftKey?-1:1),!0):(t.cycleSelection(e.shiftKey?-1:1),!0));if(e.code==="Period"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target))return t.isTerminalTarget(e.target)||(e.preventDefault(),t.cycleIdleSelection(e.shiftKey?-1:1)),!0;if(e.code==="Comma"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target))return t.isTerminalTarget(e.target)||(e.preventDefault(),t.cycleIdleSelection(-1)),!0;const s=t.parseControlGroupDigit(e);if(s===void 0)return!1;if((e.ctrlKey||e.metaKey)&&!e.altKey&&t.selectedWorkerIds.length>0)return e.preventDefault(),t.setControlGroups(n=>{const l=new Set(t.selectedWorkerIds),c=n[s]??[],h=new Set(c);if(c.length===t.selectedWorkerIds.length&&t.selectedWorkerIds.every(d=>h.has(d))){const d={...n};return delete d[s],d}const _={...n};for(const[d,g]of Object.entries(_)){const y=Number(d);!Number.isInteger(y)||y<0||y>9||y===s||!Array.isArray(g)||(_[y]=g.filter(C=>!l.has(C)))}return _[s]=[...t.selectedWorkerIds],_}),!0;if(!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey&&!t.isEditableTarget(e.target)){const n=t.controlGroupByDigitRef.current[s]??[];if(n.length===0)return!0;const l=new Set(t.activeWorkers.map(h=>h.id)),c=n.filter(h=>l.has(h));if(c.length===0)return t.setControlGroups(h=>{if(!(s in h))return h;const f={...h};return delete f[s],f}),!0;e.preventDefault(),t.applySelection(c,{center:c.length===1})}return!0}function $w(e,t){if(t.isEditableTarget(e.target))return!0;const s=e.key.toLowerCase();if((s==="k"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(t.inSelectedGroupView?e.shiftKey:t.selectedWorkerIds.length===1||!e.shiftKey)||e.key==="Delete")&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&t.selectedWorkerIds.length>0)return e.preventDefault(),t.onKillSelected(),!0;if(t.selectedWorkers.length>1&&!t.isTerminalTarget(e.target)){if(t.inSelectedGroupView&&s==="c"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey)return t.focusRallyCommandInput()&&e.preventDefault(),!0;if((s==="j"||s==="k")&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey){e.preventDefault();const l=s==="j"?1:-1,c=t.clampNumber(t.selectedGroupActiveIndex+l,0,t.selectedWorkers.length-1),h=t.selectedWorkers[c];return t.setSelectedGroupActiveIndex(c),t.focusedSelectedWorkerId&&h&&t.setFocusedSelectedWorkerId(h.id),!0}if(Al(e)){const l=t.selectedWorkers.find(c=>c.id===t.focusedSelectedWorkerId)??t.selectedWorkers[t.selectedGroupActiveIndex]??t.selectedWorkers[0];return l&&(e.preventDefault(),t.setFocusedSelectedWorkerId(l.id),t.requestTerminalFocus()),!0}}if(t.selectedWorkerIds.length===0&&t.rosterEntries.length>0&&!t.isTerminalTarget(e.target)){if(s==="k"&&e.shiftKey&&!e.ctrlKey&&!e.metaKey&&!e.altKey)return e.preventDefault(),t.onKillRosterActive(),!0;if(s==="n"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey&&t.firstSummonEntryIndex!==void 0)return e.preventDefault(),t.setRosterActiveIndex(t.firstSummonEntryIndex),!0;if((s==="j"||s==="k")&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey)return e.preventDefault(),t.setRosterActiveIndex(l=>{const c=s==="j"?1:-1;return t.clampNumber(l+c,0,t.rosterEntries.length-1)}),!0;if(Al(e))return e.preventDefault(),t.onActivateRosterIndex(t.rosterActiveIndex),!0}if(s==="r"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&t.selectedWorkers.length>0){e.preventDefault();const l=t.selectedWorkers.length>1?t.selectedWorkers.find(c=>c.id===t.focusedSelectedWorkerId):void 0;return t.openRenameForWorkers(l?[l]:t.selectedWorkers),!0}return s==="m"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&t.selectedWorkers.length>0?(e.preventDefault(),t.onToggleMovementModeSelected(),!0):s==="s"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey&&t.selectedWorkers.length>1?(e.preventDefault(),t.onScatterSelected(),!0):Al(e)&&t.selectedWorkerId?(e.preventDefault(),t.requestTerminalFocus(),!0):e.key!=="/"||e.metaKey||e.ctrlKey||e.altKey?!1:(e.preventDefault(),t.setPaletteOpen(!0),t.setSpawnDialogOpen(!1),t.setShortcutsOverlayOpen(!1),!0)}function Al(e){return e.key==="Enter"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey}function Kw(e){const t=k.useRef(e);t.current=e,k.useEffect(()=>{const s=n=>{const l=t.current;Uw(n,l)||Hw(n,l)||jw(n,l)||$w(n,l)};return window.addEventListener("keydown",s,!0),()=>window.removeEventListener("keydown",s,!0)},[])}function Uw(e,t){return!(!t.isTerminalTarget(e.target)||t.killConfirmWorkerIds.length>0||t.renameModalOpen||t.batchSpawnDialogOpen||t.shortcutsOverlayOpen||t.paletteOpen||t.spawnDialogOpen||e.key==="Escape"||t.isTerminalEscapeShortcut(e))}async function wi(e,t){const s=await fetch(e,{headers:{"Content-Type":"application/json",...t?.headers??{}},...t});if(!s.ok){const n=await s.json().catch(()=>({error:s.statusText}));throw new Error(n.error??`Request failed: ${s.status}`)}if(s.status!==204)return await s.json()}function Vw(){return wi("/api/config")}async function Qp(){return(await wi("/api/workers")).workers}function Jp(e){return wi("/api/workers/spawn",{method:"POST",body:JSON.stringify(e)})}function Yw(e){return wi(`/api/workers/${e}/stop`,{method:"POST"})}function qw(e,t,s){return wi(`/api/workers/${e}/position`,{method:"PATCH",body:JSON.stringify({x:t,y:s})})}function Zp(e,t){return wi(`/api/workers/${e}/rename`,{method:"PATCH",body:JSON.stringify({displayName:t})})}function Xw(e,t){return wi(`/api/workers/${e}/movement-mode`,{method:"PATCH",body:JSON.stringify({movementMode:t})})}function Gw(e){return wi(`/api/workers/${e}/open-terminal`,{method:"POST"})}function e_(e,t,s=!0){return wi("/api/workers/broadcast-input",{method:"POST",body:JSON.stringify({workerIds:e,text:t,submit:s})})}const Qw=3e4;function Jw(e){const[t,s]=k.useState(null),[n,l]=k.useState([]),[c,h]=k.useState(!1);return k.useEffect(()=>{Promise.all([Vw(),Qp()]).then(([f,_])=>{s(f),l(_),h(!0)}).catch(f=>{const _=f instanceof Error?f.message:"Failed to load Arcane Agents data";e(_)})},[e]),k.useEffect(()=>{let f=null,_=null,d=null,g=!1,y=!1;const C=async()=>{if(!(g||y)){y=!0;try{const E=await Qp();if(g)return;l(E),h(!0)}catch{}finally{y=!1}}},w=()=>{document.visibilityState==="visible"&&C()},v=()=>{C()};function S(){if(g)return;const E=window.location.protocol==="https:"?"wss":"ws";f=new WebSocket(`${E}://${window.location.host}/api/ws`),f.addEventListener("open",()=>{e(void 0),C()}),f.addEventListener("message",P=>{const z=JSON.parse(String(P.data));if(z.type==="init"){s(z.config),l(z.workers),h(!0);return}if(z.type==="worker-created"||z.type==="worker-updated"){l(H=>Ss(H,z.worker));return}z.type==="worker-removed"&&l(H=>H.filter(I=>I.id!==z.workerId))}),f.addEventListener("error",()=>{e("Realtime connection failed. Retrying...")}),f.addEventListener("close",()=>{g||(_=setTimeout(S,2e3))})}return d=setInterval(()=>{C()},Qw),window.addEventListener("focus",v),document.addEventListener("visibilitychange",w),S(),()=>{g=!0,_&&clearTimeout(_),d&&clearInterval(d),window.removeEventListener("focus",v),document.removeEventListener("visibilitychange",w),f?.close()}},[e]),{config:t,workers:n,setWorkers:l,workersHydrated:c}}function Zw(e,t){const[s,n]=k.useState(()=>Gg()),l=k.useRef(s),[c,h]=k.useState(()=>Jg());return k.useEffect(()=>{l.current=s,Qg(s)},[s]),k.useEffect(()=>{Zg(c)},[c]),k.useEffect(()=>{if(!t)return;const f=new Set(e.map(_=>_.id));n(_=>{let d=!1;const g={..._};for(const[y,C]of Object.entries(g)){if(!Array.isArray(C)||C.length===0){delete g[Number(y)],d=!0;continue}const w=C.filter(v=>f.has(v));w.length!==C.length&&(w.length===0?delete g[Number(y)]:g[Number(y)]=w,d=!0)}return d?g:_})},[e,t]),{controlGroups:s,setControlGroups:n,controlGroupByDigitRef:l,mapColumnRatio:c,setMapColumnRatio:f=>{h(_=>{const d=vi(f,lo,ao);return d===_?_:d})},nudgeMapColumnRatio:f=>{h(_=>vi(_+f,lo,ao))},resetMapColumnRatio:()=>{h(Jn)}}}function e1(e,t,s){const[n,l]=k.useState([]),[c,h]=k.useState(0),[f,_]=k.useState(void 0),[d,g]=k.useState(void 0),[y,C]=k.useState(0),[w,v]=k.useState(0),[S,E]=k.useState(void 0),P=n.length===1?n[0]:void 0,z=k.useMemo(()=>new Set(n),[n]),H=k.useMemo(()=>e.filter(b=>z.has(b.id)),[e,z]),I=k.useMemo(()=>e.filter(b=>b.status==="idle"),[e]),G=k.useCallback((b,W)=>{const B=Array.from(new Set(b));l(B),E(void 0);const A=B.length===1?B[0]:void 0;W?.center&&A&&(_(A),h(q=>q+1)),W?.focusTerminal&&A&&g(q=>(q??0)+1)},[]),j=k.useCallback(()=>{g(b=>(b??0)+1)},[]),oe=k.useCallback(b=>{G(b?[b]:[])},[G]),Z=k.useCallback(b=>{G(b)},[G]),ee=k.useCallback(b=>{G([b],{focusTerminal:!0})},[G]),se=k.useCallback(b=>{if(e.length===0)return;const W=e.findIndex(N=>N.id===P),A=((W>=0?W:b>0?-1:0)+b+e.length)%e.length,q=e[A];q&&G([q.id],{center:!0})},[e,G,P]),U=k.useCallback(b=>{if(I.length===0)return;const W=I.findIndex(N=>N.id===P),A=((W>=0?W:b>0?-1:0)+b+I.length)%I.length,q=I[A];q&&G([q.id],{center:!0})},[G,I,P]),D=k.useCallback(b=>{if(H.length<=1)return;const W=S?H.findIndex(N=>N.id===S):vi(w,0,H.length-1),A=((W>=0?W:b>0?-1:0)+b+H.length)%H.length,q=H[A];q&&(v(A),E(q.id))},[S,w,H]);return k.useEffect(()=>{if(t.length===0){C(0);return}if(P){const b=t.findIndex(W=>W.kind==="worker"&&W.worker.id===P);b>=0&&C(b);return}C(b=>vi(b,0,t.length-1))},[t,P]),k.useEffect(()=>{if(H.length<=1){v(0),E(void 0),s?.();return}if(S){const b=H.findIndex(W=>W.id===S);if(b>=0){v(b);return}E(void 0)}v(b=>vi(b,0,H.length-1))},[S,s,H]),k.useEffect(()=>{const b=new Set(e.map(W=>W.id));l(W=>W.filter(B=>b.has(B)))},[e]),{selectedWorkerIds:n,setSelectedWorkerIds:l,selectedWorkerId:P,selectedWorkers:H,mapCenterToken:c,mapCenterWorkerId:f,terminalFocusToken:d,rosterActiveIndex:y,setRosterActiveIndex:C,selectedGroupActiveIndex:w,setSelectedGroupActiveIndex:v,focusedSelectedWorkerId:S,setFocusedSelectedWorkerId:E,applySelection:G,requestTerminalFocus:j,onSelectWorker:oe,onSelectionChange:Z,onActivateWorker:ee,cycleSelection:se,cycleIdleSelection:U,cycleSelectedGroupFocus:D}}function t1(e){const[t,s]=k.useState(!1);return k.useEffect(()=>{s(!1),document.activeElement instanceof HTMLElement&&document.activeElement.closest(".terminal-panel")&&document.activeElement.blur()},[e]),k.useEffect(()=>{const n=()=>{s(Ul(document.activeElement))},l=()=>{setTimeout(n,0)},c=()=>{s(!1)};return window.addEventListener("focusin",n,!0),window.addEventListener("focusout",l,!0),window.addEventListener("blur",c),n(),()=>{window.removeEventListener("focusin",n,!0),window.removeEventListener("focusout",l,!0),window.removeEventListener("blur",c)}},[]),t}function r1({workers:e,reviewedWorkerId:t}){const[s,n]=k.useState([]),l=k.useRef(new Map),c=k.useRef(t);return k.useEffect(()=>{c.current=t},[t]),k.useEffect(()=>{const h=new Set(e.map(g=>g.id)),f=new Set,_=new Set;for(const g of e)if(l.current.get(g.id)==="working"&&g.status==="idle"){if(g.id===c.current)continue;_.add(g.id)}else g.status!=="idle"&&f.add(g.id);const d=new Map;for(const g of e)d.set(g.id,g.status);l.current=d,n(g=>{const C=[...g.filter(w=>h.has(w)&&!f.has(w))];for(const w of _)C.includes(w)||C.push(w);return C.length===g.length&&C.every((w,v)=>w===g[v])?g:C})},[e]),k.useEffect(()=>{t&&n(h=>h.filter(f=>f!==t))},[t]),{pendingCompletionWorkerIds:s}}function i1(e){const[t,s]=k.useState([]),n=k.useCallback(c=>{s(h=>[{worker:c,startedAtMs:Date.now()},...h.filter(f=>f.worker.id!==c.id)])},[]),l=k.useCallback(c=>{s(h=>h.filter(f=>f.worker.id!==c))},[]);return k.useEffect(()=>{const c=setInterval(()=>{const h=Date.now();s(f=>f.filter(_=>h-_.startedAtMs<e))},80);return()=>{clearInterval(c)}},[e]),{fadingWorkers:t,queueWorkerFade:n,removeWorkerFade:l}}const s1=1600,Zc=["move","selected"],n1={move:["move.mp3","move_variant_1.mp3","move_variant_2.mp3","move_variant_3.mp3"],selected:["selected.mp3","selected_variant_1.mp3","selected_variant_2.mp3","selected_variant_3.mp3"]};function o1({config:e,workers:t,workersHydrated:s,selectedWorkerIds:n}){const l=e?.audio.enableSound??!0,c=k.useRef(new Map),h=k.useRef(new Set),f=k.useRef(new Map),_=k.useRef(!1),d=k.useRef(!1),g=k.useRef(new Map),y=k.useRef(new Set),C=k.useRef(new Map),w=k.useRef(new Map),v=k.useRef(new Set),S=k.useRef(null);k.useEffect(()=>{f.current=new Map(t.map(U=>[U.id,U]))},[t]);const E=k.useCallback((U,D)=>`/api/assets/characters/${encodeURIComponent(U)}/voice-lines/${D}.mp3`,[]),P=k.useCallback((U,D)=>`/api/assets/characters/${encodeURIComponent(U)}/voice-lines/${encodeURIComponent(D)}`,[]),z=k.useCallback((U,D)=>{const b=g.current.get(U)?.[D]??[];return b.length>0?b:n1[D].map(W=>P(U,W))},[P]),H=k.useCallback(U=>{if(v.current.has(U))return;v.current.add(U);const D=new Audio(U);D.preload="auto",D.addEventListener("canplay",()=>{w.current.set(U,!0)},{once:!0}),D.addEventListener("error",()=>{w.current.set(U,!1)},{once:!0}),D.load()},[]),I=k.useCallback(U=>{if(!l||w.current.get(U)===!1)return;const D=S.current;D&&(D.pause(),D.currentTime=0);const b=new Audio(U);b.preload="auto",b.addEventListener("canplay",()=>{w.current.set(U,!0)},{once:!0}),b.addEventListener("error",()=>{w.current.set(U,!1)},{once:!0}),S.current=b,b.play().catch(()=>{})},[l]),G=k.useCallback((U,D)=>{I(E(U.avatarType,D))},[I,E]),j=k.useCallback(U=>{if(!l)return;const D=U.filter(A=>w.current.get(A)===!0),b=U.filter(A=>w.current.get(A)!==!1),W=D.length>0?D:b,B=t_(W);B&&I(B)},[I,l]),oe=k.useCallback(U=>{const D=z(U.avatarType,"selected");j(D)},[j,z]),Z=k.useCallback(U=>{const D=z(U.avatarType,"move");j(D)},[j,z]),ee=k.useCallback(async U=>{try{const D=await fetch(`/api/avatars/${encodeURIComponent(U)}/voice-lines`);if(!D.ok)return;const b=await D.json();if(!Array.isArray(b.files))return;const W=b.files.filter(A=>typeof A=="string"&&A.toLowerCase().endsWith(".mp3")).sort((A,q)=>A.localeCompare(q)),B={move:[],selected:[]};for(const A of Zc)B[A]=W.filter(q=>q.toLowerCase().startsWith(A)).map(q=>P(U,q));g.current.set(U,B);for(const A of Zc)for(const q of B[A])H(q)}catch{y.current.delete(U)}},[H,P]);k.useEffect(()=>{const U=Array.from(new Set(t.map(D=>D.avatarType)));for(const D of U)y.current.has(D)||(y.current.add(D),ee(D))},[ee,t]),k.useEffect(()=>{if(!l)return;const U=Array.from(new Set(t.map(b=>b.avatarType))),D=["arrive","attention","complete","death"];for(const b of U){for(const W of D)H(E(b,W));for(const W of Zc)for(const B of z(b,W))H(B)}},[H,E,z,l,t]),k.useEffect(()=>{if(!s)return;const U=new Map(t.map(B=>[B.id,B])),D=c.current,b=C.current,W=performance.now();if(!_.current){_.current=!0,c.current=U;return}for(const B of t){const A=D.get(B.id);if(!A){G(B,"arrive"),b.set(B.id,W+s1);continue}l1(A.status,B.status)&&G(B,"attention"),a1(A.status,B.status)&&!h1(B)&&G(B,"complete")}for(const[B,A]of D.entries())U.has(B)||(G(A,"death"),b.delete(B));c.current=U},[G,t,s]),k.useEffect(()=>{if(!s)return;const U=new Set(n),D=h.current;if(!d.current){d.current=!0,h.current=U;return}const b=n.filter(N=>!D.has(N)),W=C.current,B=performance.now(),A=b.filter(N=>{const K=W.get(N);return K===void 0?!0:K<=B?(W.delete(N),!0):!1}),q=t_(A);if(q){const N=f.current.get(q);N&&oe(N)}h.current=U},[oe,n,s]);const se=k.useCallback(U=>{const D=f.current.get(U);D&&Z(D)},[Z]);return k.useEffect(()=>()=>{const U=S.current;U&&(U.pause(),U.currentTime=0,S.current=null)},[]),{playMoveVoiceLine:se}}function l1(e,t){return e!=="attention"&&t==="attention"}function a1(e,t){return e==="working"&&t==="idle"}const c1=1e4;function h1(e){return Date.now()-new Date(e.createdAt).getTime()<c1}function t_(e){if(e.length===0)return;const t=Math.floor(Math.random()*e.length);return e[t]}function u1({workers:e,activeWorkers:t,renameModalOpen:s,setRenameModalOpen:n,renameTargetWorkerIds:l,setRenameTargetWorkerIds:c,killConfirmWorkerIds:h,setKillConfirmWorkerIds:f,setRenameDraft:_}){const d=k.useMemo(()=>{if(l.length===0)return[];const v=new Map(e.map(S=>[S.id,S]));return l.map(S=>v.get(S)).filter(S=>!!S)},[l,e]),g=k.useMemo(()=>{if(h.length===0)return[];const v=new Map(e.map(S=>[S.id,S]));return h.map(S=>v.get(S)).filter(S=>!!S)},[h,e]),y=k.useCallback(()=>{n(!1),c([])},[n,c]),C=k.useCallback(()=>{f([])},[f]),w=k.useCallback(v=>{v.length!==0&&(_(v.length===1?v[0].displayName??v[0].name:""),c(v.map(S=>S.id)),n(!0))},[_,n,c]);return k.useEffect(()=>{if(!s||l.length===0)return;const v=new Set(t.map(S=>S.id));l.some(S=>v.has(S))||y()},[t,y,s,l]),k.useEffect(()=>{if(h.length===0)return;const v=new Set(t.map(S=>S.id));h.some(S=>v.has(S))||C()},[t,C,h]),{renameTargetWorkers:d,killConfirmWorkers:g,closeRenameModal:y,closeKillConfirm:C,openRenameForWorkers:w}}function d1({workers:e,selectedWorkerIds:t,setSelectedWorkerIds:s,rosterEntries:n,rosterActiveIndex:l,setKillConfirmWorkerIds:c,closeKillConfirm:h,killConfirmWorkerIds:f,queueWorkerFade:_,removeWorkerFade:d,setWorkers:g,showError:y}){const C=k.useCallback(async E=>{const P=e.find(z=>z.id===E);if(P){h(),_(P);try{const z=await Yw(E);g(H=>H.filter(I=>I.id!==z.workerId)),s(H=>H.filter(I=>I!==z.workerId))}catch(z){d(E),y(z)}}},[h,_,d,s,g,y,e]),w=k.useCallback(()=>{t.length!==0&&c(t)},[t,c]),v=k.useCallback(()=>{const E=n[l];!E||E.kind!=="worker"||c([E.worker.id])},[l,n,c]),S=k.useCallback(()=>{if(f.length===0)return;const E=[...f];h();for(const P of E)C(P)},[h,f,C]);return{onKillSelected:w,onKillRosterActive:v,confirmKillSelection:S}}function f1({setWorkers:e,selectedWorkers:t,terminalWorkerId:s,applySelection:n,setSpawnDialogOpen:l,setPaletteOpen:c,renameTargetWorkerIds:h,closeRenameModal:f,showError:_}){const d=k.useCallback(async S=>{try{const E=t.map(H=>H.id),P="shortcutIndex"in S&&E.length>0?{...S,spawnNearWorkerIds:E}:S,z=await Jp(P);e(H=>Ss(H,z)),n([z.id],{center:!0}),l(!1),c(!1)}catch(E){_(E)}},[n,t,c,l,e,_]),g=k.useCallback(async(S,E)=>{const P=[];let z;for(let H=0;H<S.length;H++){E(H,S.length);const I=S[H],G={...I.input,displayName:I.displayName,spawnNearWorkerIds:z?[z]:t.map(j=>j.id).slice(0,1)};try{const j=await Jp(G);e(oe=>Ss(oe,j)),P.push(j.id),z=j.id}catch(j){_(j);break}}E(P.length,S.length),P.length>0&&n(P,{center:!0})},[n,t,e,_]),y=k.useCallback(async S=>{const E=[...h];if(E.length===0){f();return}try{if(E.length===1){const P=await Zp(E[0],S);e(z=>Ss(z,P))}else{const P=S.trim(),z=await Promise.all(E.map((H,I)=>Zp(H,P.length>0?`${P} ${I+1}`:"")));e(H=>{let I=H;for(const G of z)I=Ss(I,G);return I})}f()}catch(P){_(P)}},[f,h,e,_]),C=k.useCallback(async()=>{if(t.length===0)return;const S=t.every(E=>E.movementMode==="hold")?"wander":"hold";try{const E=await Promise.all(t.map(P=>Xw(P.id,S)));e(P=>{let z=P;for(const H of E)z=Ss(z,H);return z})}catch(E){_(E)}},[t,e,_]),w=k.useCallback(async()=>{if(s)try{await Gw(s)}catch(S){_(S)}},[_,s]),v=k.useCallback((S,E)=>{qw(S,E.x,E.y).then(P=>{e(z=>Ss(z,P))}).catch(P=>{_(P)})},[e,_]);return{runSpawn:d,runBatchSpawn:g,submitRename:y,onToggleMovementModeSelected:C,onOpenSelectedInTerminal:w,onPositionCommit:v}}function p1({selectedWorkers:e,rallyCommandDraft:t,setRallyCommandDraft:s,rallyCommandSending:n,setRallyCommandSending:l,rallyCommandResultText:c,setRallyCommandResultText:h,showError:f}){const _=k.useCallback(async()=>{if(n)return;const g=e.map(y=>y.id);if(!(g.length<=1)){if(t.length===0){h("Enter a command to broadcast.");return}l(!0),h(void 0);try{const C=t.includes("$NAME")?sv(await Promise.all(e.map(async w=>{const v=t.replace(/\$NAME/g,w.displayName??w.name);try{return await e_([w.id],v,!0)}catch(S){return{requestedCount:1,deliveredWorkerIds:[],skippedWorkerIds:[],failed:[{workerId:w.id,error:S instanceof Error?S.message:"Failed to send input"}]}}}))):await e_(g,t,!0);s(""),h(iv(C))}catch(y){f(y)}finally{l(!1)}}},[t,n,e,s,h,l,f]),d=k.useCallback(g=>{s(g),c&&h(void 0)},[c,s,h]);return{onSendRallyCommand:_,onRallyCommandDraftChange:d}}function _1({workers:e,activeWorkers:t,setWorkers:s,selectedWorkers:n,selectedWorkerIds:l,setSelectedWorkerIds:c,focusedSelectedWorkerId:h,terminalWorkerId:f,rosterEntries:_,rosterActiveIndex:d,setRosterActiveIndex:g,applySelection:y,setSpawnDialogOpen:C,setPaletteOpen:w,renameModalOpen:v,setRenameModalOpen:S,renameTargetWorkerIds:E,setRenameTargetWorkerIds:P,setRenameDraft:z,killConfirmWorkerIds:H,setKillConfirmWorkerIds:I,rallyCommandDraft:G,setRallyCommandDraft:j,rallyCommandSending:oe,setRallyCommandSending:Z,rallyCommandResultText:ee,setRallyCommandResultText:se,queueWorkerFade:U,removeWorkerFade:D,setErrorText:b}){const W=k.useCallback(xe=>{b(xe instanceof Error?xe.message:"Unknown request failure")},[b]),{renameTargetWorkers:B,killConfirmWorkers:A,closeRenameModal:q,closeKillConfirm:N,openRenameForWorkers:K}=u1({workers:e,activeWorkers:t,renameModalOpen:v,setRenameModalOpen:S,renameTargetWorkerIds:E,setRenameTargetWorkerIds:P,killConfirmWorkerIds:H,setKillConfirmWorkerIds:I,setRenameDraft:z}),{runSpawn:Q,runBatchSpawn:T,submitRename:V,onToggleMovementModeSelected:de,onOpenSelectedInTerminal:fe,onPositionCommit:he}=f1({setWorkers:s,selectedWorkers:n,terminalWorkerId:f,applySelection:y,setSpawnDialogOpen:C,setPaletteOpen:w,renameTargetWorkerIds:E,closeRenameModal:q,showError:W}),{onKillSelected:pe,onKillRosterActive:ge,confirmKillSelection:Re}=d1({workers:e,selectedWorkerIds:l,setSelectedWorkerIds:c,rosterEntries:_,rosterActiveIndex:d,setKillConfirmWorkerIds:I,closeKillConfirm:N,killConfirmWorkerIds:H,queueWorkerFade:U,removeWorkerFade:D,setWorkers:s,showError:W}),{onSendRallyCommand:ye,onRallyCommandDraftChange:Fe}=p1({selectedWorkers:n,rallyCommandDraft:G,setRallyCommandDraft:j,rallyCommandSending:oe,setRallyCommandSending:Z,rallyCommandResultText:ee,setRallyCommandResultText:se,showError:W}),We=k.useCallback(xe=>{const Ke=_[xe];if(Ke){if(g(xe),Ke.kind==="worker"){y([Ke.worker.id],{center:!0});return}Q({shortcutIndex:Ke.shortcutIndex})}},[y,_,Q,g]),je=k.useCallback(()=>{if(n.length===0)return;const xe=n.length>1?n.find(Ke=>Ke.id===h):void 0;K(xe?[xe]:n)},[h,K,n]);return{renameTargetWorkers:B,killConfirmWorkers:A,runSpawn:Q,runBatchSpawn:T,closeRenameModal:q,closeKillConfirm:N,openRenameForWorkers:K,submitRename:V,onKillSelected:pe,onKillRosterActive:ge,confirmKillSelection:Re,onToggleMovementModeSelected:de,onActivateRosterIndex:We,onOpenSelectedInTerminal:fe,onSendRallyCommand:ye,onRallyCommandDraftChange:Fe,onRenameSelected:je,onPositionCommit:he}}function m1(e){const t=[];return e.forEach((s,n)=>{for(const l of s.hotkeys??[]){const c=y1(l);c&&t.push({shortcutIndex:n,hotkey:c})}}),t}function g1(e,t){const s=[],n=new Set;for(const l of e)v1(l.hotkey,t)&&(n.has(l.shortcutIndex)||(n.add(l.shortcutIndex),s.push(l.shortcutIndex)));return s}function v1(e,t){return t.ctrlKey!==e.ctrl||t.metaKey!==e.meta||t.altKey!==e.alt||t.shiftKey!==e.shift?!1:C1(t.key)===e.key?!0:e.code?t.code===e.code:!1}function y1(e){const t=S1(e);if(t.length===0)return;let s=!1,n=!1,l=!1,c=!1,h;for(const _ of t){const d=_.trim().toLowerCase();if(d){if(d==="ctrl"||d==="control"){s=!0;continue}if(d==="cmd"||d==="meta"||d==="super"){n=!0;continue}if(d==="alt"||d==="option"){l=!0;continue}if(d==="shift"){c=!0;continue}if(h)return;h=_}}if(!h)return;const f=w1(h);if(f)return{key:f.key,code:f.code,ctrl:s,meta:n,alt:l,shift:c}}function S1(e){const t=e.trim().replace(/\s+/g,"");if(!t)return[];if(t.includes("+"))return t.split("+").filter(l=>l.length>0);const s=t.toLowerCase();return s.includes("ctrl-")||s.includes("control-")||s.includes("cmd-")||s.includes("meta-")||s.includes("super-")||s.includes("alt-")||s.includes("option-")||s.includes("shift-")?t.split("-").filter(l=>l.length>0):[t]}function w1(e){const t=e.trim();if(!t)return;const s=t.toLowerCase();if(s==="space"||s==="spacebar")return{key:" ",code:"Space"};if(s==="esc")return{key:"escape",code:"Escape"};if(s==="return")return{key:"enter",code:"Enter"};if(s==="up")return{key:"arrowup",code:"ArrowUp"};if(s==="down")return{key:"arrowdown",code:"ArrowDown"};if(s==="left")return{key:"arrowleft",code:"ArrowLeft"};if(s==="right")return{key:"arrowright",code:"ArrowRight"};if(/^key[a-z]$/.test(s)){const n=s.slice(3);return{key:n,code:`Key${n.toUpperCase()}`}}if(/^digit[0-9]$/.test(s)){const n=s.slice(5);return{key:n,code:`Digit${n}`}}if(/^numpad[0-9]$/.test(s)){const n=s.slice(6);return{key:n,code:`Numpad${n}`}}if(/^f[0-9]{1,2}$/.test(s))return{key:s,code:s.toUpperCase()};if(t.length===1){if(/^[a-z]$/i.test(t)){const n=t.toLowerCase();return{key:n,code:`Key${n.toUpperCase()}`}}return/^[0-9]$/.test(t)?{key:t}:{key:t}}return{key:s}}function C1(e){const t=e.toLowerCase();return t==="spacebar"?" ":t}function k1(){const[e,t]=k.useState(!1),[s,n]=k.useState(!1),[l,c]=k.useState(!1),[h,f]=k.useState(!1),[_,d]=k.useState(!1),[g,y]=k.useState([]),[C,w]=k.useState(""),[v,S]=k.useState([]),[E,P]=k.useState(""),[z,H]=k.useState(!1),[I,G]=k.useState(void 0),[j,oe]=k.useState(void 0),[Z,ee]=k.useState([]),[se,U]=k.useState(0),D=k.useRef(null),{config:b,workers:W,setWorkers:B,workersHydrated:A}=Jw(oe),{fadingWorkers:q,queueWorkerFade:N,removeWorkerFade:K}=i1(Xg),Q=k.useMemo(()=>W.filter(te=>te.status!=="stopped"),[W]),{controlGroups:T,setControlGroups:V,controlGroupByDigitRef:de,mapColumnRatio:fe,setMapColumnRatio:he,nudgeMapColumnRatio:pe,resetMapColumnRatio:ge}=Zw(Q,A),Re=k.useRef(null),ye=k.useRef(void 0),[Fe,We]=k.useState(!1),je=k.useMemo(()=>b?.shortcuts??[],[b]),xe=k.useMemo(()=>m1(je),[je]),Ke=k.useMemo(()=>[...Q.map(te=>({kind:"worker",worker:te})),...je.map((te,Ce)=>({kind:"shortcut",shortcut:te,shortcutIndex:Ce}))],[Q,je]),jr=k.useMemo(()=>{const te=Ke.findIndex(Ce=>Ce.kind==="shortcut");return te>=0?te:void 0},[Ke]),at=k.useCallback(()=>{P(""),G(void 0)},[]),{selectedWorkerIds:zt,setSelectedWorkerIds:_r,selectedWorkerId:ct,selectedWorkers:Xe,mapCenterToken:Kt,mapCenterWorkerId:$r,terminalFocusToken:Zi,rosterActiveIndex:ii,setRosterActiveIndex:Ci,selectedGroupActiveIndex:ki,setSelectedGroupActiveIndex:si,focusedSelectedWorkerId:Ut,setFocusedSelectedWorkerId:es,applySelection:Dr,requestTerminalFocus:mr,onSelectWorker:Ae,onSelectionChange:Ts,onActivateWorker:ts,cycleSelection:gr,cycleIdleSelection:vr,cycleSelectedGroupFocus:rs}=e1(Q,Ke,at),yr=k.useMemo(()=>Q.find(te=>te.id===ct),[Q,ct]),Sr=k.useMemo(()=>{if(Ut)return Xe.find(te=>te.id===Ut)},[Ut,Xe]),Kr=yr??(Xe.length>1?Sr:void 0),Et=Kr?.id,or=Xe.length>1&&!Kr,bi=t1(Et),{pendingCompletionWorkerIds:is}=r1({workers:Q,reviewedWorkerId:Et}),{playMoveVoiceLine:xi}=o1({config:b,workers:Q,workersHydrated:A,selectedWorkerIds:zt}),Ur=k.useCallback(()=>{const te=document.activeElement;return!(te instanceof HTMLElement)||!te.closest(".terminal-panel")?!1:(te.blur(),document.querySelector(".map-canvas")?.focus(),!0)},[]),Ps=k.useCallback(()=>{const te=D.current;if(!te)return!1;te.focus();const Ce=te.value.length;return te.setSelectionRange(Ce,Ce),!0},[]),Ei=k.useCallback(()=>{if(Xe.length<2)return;const te=Xe.reduce((Qe,mt)=>Qe+mt.position.x,0)/Xe.length,Ce=Xe.reduce((Qe,mt)=>Qe+mt.position.y,0)/Xe.length,Le=80+Xe.length*20,De=Xe.map(Qe=>({workerId:Qe.id,target:{x:te+(Math.random()-.5)*Le*2,y:Ce+(Math.random()-.5)*Le*2}}));ee(De),U(Qe=>Qe+1)},[Xe]),ss=k.useCallback(te=>{const Ce=Re.current;if(!Ce)return;const Le=Ce.getBoundingClientRect(),De=Math.max(1,Le.width-Bc),Qe=te-Le.left-Bc/2,mt=vi(Qe/De,lo,ao);he(mt)},[he]);k.useEffect(()=>{if(Fe)return document.body.classList.add("split-pane-dragging"),()=>{document.body.classList.remove("split-pane-dragging")}},[Fe]);const{renameTargetWorkers:ni,killConfirmWorkers:ns,runSpawn:lr,runBatchSpawn:ln,closeRenameModal:Vr,closeKillConfirm:Nr,openRenameForWorkers:Mi,submitRename:X,onKillSelected:Y,onKillRosterActive:le,confirmKillSelection:ue,onToggleMovementModeSelected:_e,onActivateRosterIndex:Ne,onOpenSelectedInTerminal:Oe,onSendRallyCommand:ht,onRallyCommandDraftChange:tt,onRenameSelected:Ue,onPositionCommit:Jt}=_1({workers:W,activeWorkers:Q,setWorkers:B,selectedWorkers:Xe,selectedWorkerIds:zt,setSelectedWorkerIds:_r,focusedSelectedWorkerId:Ut,terminalWorkerId:Et,rosterEntries:Ke,rosterActiveIndex:ii,setRosterActiveIndex:Ci,applySelection:Dr,setSpawnDialogOpen:t,setPaletteOpen:n,renameModalOpen:_,setRenameModalOpen:d,renameTargetWorkerIds:v,setRenameTargetWorkerIds:S,setRenameDraft:w,killConfirmWorkerIds:g,setKillConfirmWorkerIds:y,rallyCommandDraft:E,setRallyCommandDraft:P,rallyCommandSending:z,setRallyCommandSending:H,rallyCommandResultText:I,setRallyCommandResultText:G,queueWorkerFade:N,removeWorkerFade:K,setErrorText:oe});return Kw({activeWorkers:Q,applySelection:Dr,batchSpawnDialogOpen:l,clampNumber:vi,closeKillConfirm:Nr,closeRenameModal:Vr,confirmKillSelection:ue,controlGroupByDigitRef:de,cycleIdleSelection:vr,cycleSelectedGroupFocus:rs,cycleSelection:gr,escapeTerminalFocus:Ur,findMatchingShortcutIndexes:g1,firstSummonEntryIndex:jr,focusRallyCommandInput:Ps,focusedSelectedWorkerId:Ut,inSelectedGroupView:or,isEditableTarget:$h,isTerminalEscapeShortcut:ov,isTerminalTarget:ev,killConfirmWorkerIds:g,mapColumnRatioStep:jh,nudgeMapColumnRatio:pe,onActivateRosterIndex:Ne,onKillRosterActive:le,onKillSelected:Y,onScatterSelected:Ei,onToggleMovementModeSelected:_e,openRenameForWorkers:Mi,paletteOpen:s,parseControlGroupDigit:nv,renameModalOpen:_,requestTerminalFocus:mr,resetMapColumnRatio:ge,rosterActiveIndex:ii,rosterEntries:Ke,runSpawn:lr,selectedGroupActiveIndex:ki,selectedWorkerId:ct,selectedWorkerIds:zt,selectedWorkers:Xe,setBatchSpawnDialogOpen:c,setControlGroups:V,setFocusedSelectedWorkerId:es,setPaletteOpen:n,setRosterActiveIndex:Ci,setSelectedGroupActiveIndex:si,setShortcutsOverlayOpen:f,setSpawnDialogOpen:t,shortcutHotkeyBindings:xe,shortcutsOverlayOpen:h,spawnDialogOpen:e}),M.jsxs("div",{ref:Re,className:"app-shell",style:{gridTemplateColumns:`minmax(0px, ${fe.toFixed(3)}fr) ${Bc}px minmax(0px, ${(1-fe).toFixed(3)}fr)`},children:[M.jsxs("div",{className:"map-column",children:[M.jsx(S0,{workers:Q,fadingWorkers:q,selectedWorkerId:ct,selectedWorkerIds:zt,focusedSelectedWorkerId:Ut,terminalFocusedSelected:!!(ct&&bi),terminalFocusedWorkerId:bi?Et:void 0,controlGroups:T,completionPendingWorkerIds:is,onSelect:Ae,onSelectionChange:Ts,onActivateWorker:ts,onMoveOrderIssued:xi,onPositionCommit:Jt,externalMoveOrders:Z,externalMoveOrderToken:se,centerOnWorkerId:$r,centerRequestKey:Kt}),M.jsx(lv,{shortcuts:b?.shortcuts??[],selectedWorker:yr,selectedWorkers:Xe,onSpawnShortcut:te=>{lr({shortcutIndex:te})},onOpenSpawnDialog:()=>{t(!0),n(!1)},onOpenPalette:()=>{n(!0),t(!1)},onDeselect:()=>Ae(void 0),onKillSelected:()=>{Y()},onRenameSelected:()=>{Ue()},onToggleMovementMode:()=>{_e()},onScatterSelected:()=>{Ei()}})]}),M.jsx("div",{className:`layout-divider${Fe?" layout-divider-active":""}`,role:"separator","aria-label":"Resize map and terminal columns","aria-orientation":"vertical",onPointerDown:te=>{te.button===0&&(te.preventDefault(),ye.current=te.pointerId,We(!0),te.currentTarget.setPointerCapture(te.pointerId),ss(te.clientX))},onPointerMove:te=>{ye.current===te.pointerId&&(te.preventDefault(),ss(te.clientX))},onPointerUp:te=>{ye.current===te.pointerId&&(te.preventDefault(),te.currentTarget.hasPointerCapture(te.pointerId)&&te.currentTarget.releasePointerCapture(te.pointerId),ye.current=void 0,We(!1))},onPointerCancel:te=>{ye.current===te.pointerId&&(te.currentTarget.hasPointerCapture(te.pointerId)&&te.currentTarget.releasePointerCapture(te.pointerId),ye.current=void 0,We(!1))}}),M.jsx(Fw,{activeWorkers:Q,selectedWorkers:Xe,terminalWorker:Kr,terminalFocused:bi,selectedGroupActiveIndex:ki,setSelectedGroupActiveIndex:si,setFocusedSelectedWorkerId:es,rallyCommandInputRef:D,rallyCommandDraft:E,rallyCommandSending:z,rallyCommandResultText:I,onRallyCommandDraftChange:tt,onSendRallyCommand:ht,rosterEntries:Ke,completionPendingWorkerIds:is,rosterActiveIndex:ii,setRosterActiveIndex:Ci,onActivateRosterIndex:Ne,onOpenSelectedInTerminal:Oe,terminalFocusToken:Zi}),b?M.jsx(k0,{open:e,projects:b.projects,runtimes:b.runtimes,onClose:()=>t(!1),onSpawn:(te,Ce)=>{lr({projectId:te,runtimeId:Ce})}}):null,b?M.jsx(cv,{open:s,config:b,onClose:()=>n(!1),onSpawnShortcut:te=>{lr({shortcutIndex:te})},onSpawnProjectRuntime:(te,Ce)=>{lr({projectId:te,runtimeId:Ce})},onOpenBatchSpawn:()=>{n(!1),c(!0)}}):null,b?M.jsx(av,{open:l,config:b,onClose:()=>c(!1),onBatchSpawn:ln}):null,M.jsx(C0,{open:h,onClose:()=>{f(!1)}}),M.jsx(hv,{workerIds:g,workers:ns,onClose:Nr,onConfirm:ue}),M.jsx(w0,{open:_,targetWorkerIds:v,targetWorkers:ni,initialDraft:C,onClose:Vr,onSubmit:X}),j?M.jsx("div",{className:"error-toast",onClick:()=>oe(void 0),children:j}):null]})}const em=document.getElementById("root");if(!em)throw new Error("Missing #root container");qg.createRoot(em).render(M.jsx(k.StrictMode,{children:M.jsx(k1,{})}));
|
|
83
|
+
[terminal connection error]`)}),()=>{E.dispose(),S.close(),h.current===S&&(h.current=null)}},[C,g,e,t]),k.useEffect(()=>{if(!e||s===void 0||_.current===s)return;_.current=s,d();const w=setTimeout(()=>{d()},0);return()=>{clearTimeout(w)}},[s,d,e]),M.jsx("div",{className:"terminal-panel",ref:n})}function Ww(e){return e.type==="keydown"&&e.key==="Enter"&&e.shiftKey&&!e.ctrlKey&&!e.metaKey&&!e.altKey}function Fw({activeWorkers:e,selectedWorkers:t,terminalWorker:s,terminalFocused:n,selectedGroupActiveIndex:l,setSelectedGroupActiveIndex:c,setFocusedSelectedWorkerId:h,rallyCommandInputRef:f,rallyCommandDraft:_,rallyCommandSending:d,rallyCommandResultText:g,onRallyCommandDraftChange:y,onSendRallyCommand:C,rosterEntries:w,completionPendingWorkerIds:v,rosterActiveIndex:S,setRosterActiveIndex:E,onActivateRosterIndex:P,onOpenSelectedInTerminal:z,terminalFocusToken:H}){const I=k.useMemo(()=>new Set(v),[v]),G=v.length;return M.jsxs("div",{className:`terminal-column${s?" terminal-column-selected":""}${s&&n?" terminal-column-focused":""}`,children:[M.jsxs("div",{className:"terminal-header",children:[M.jsx("div",{className:"terminal-header-title",children:t.length>1&&!s?`${t.length} selected agents`:s?`${s.displayName??s.name} (${s.status})`:`Agents (${e.length})`}),!s&&G>0?M.jsxs("div",{className:"terminal-ready-chip",title:"Agents finished but not yet reviewed in terminal",children:["✦ ",G," ready"]}):null,s?M.jsx("button",{className:"terminal-open-external",onClick:()=>{z()},disabled:s.status==="stopped",title:"Open in external terminal",type:"button",children:"↗"}):null]}),t.length>1&&!s?M.jsxs("div",{className:"worker-roster",children:[M.jsx("div",{className:"worker-roster-section-label",children:"Selected Group"}),t.map((j,oe)=>M.jsx("button",{className:`worker-roster-item ${oe===l?"active":""}`,onMouseEnter:()=>c(oe),onClick:()=>{c(oe),h(j.id)},type:"button",children:M.jsxs("div",{className:"worker-roster-main",children:[M.jsx("img",{className:"worker-roster-avatar",src:`/api/assets/characters/${encodeURIComponent(j.avatarType)}/rotations/south.png`,alt:"",loading:"lazy","aria-hidden":"true"}),M.jsxs("div",{className:"worker-roster-text",children:[M.jsxs("div",{className:"worker-roster-name-row",children:[M.jsx("div",{className:"worker-roster-name",children:j.displayName??j.name}),I.has(j.id)?M.jsx("span",{className:"worker-complete-badge",title:"Finished and waiting for review",children:"READY"}):null]}),M.jsxs("div",{className:"worker-roster-meta",children:[j.projectId," · ",j.runtimeId," · ",j.status]}),j.activityText?M.jsx("div",{className:"worker-roster-activity",children:j.activityText}):null]})]})},j.id)),M.jsxs("form",{className:"rally-command-card",onSubmit:j=>{j.preventDefault(),C()},children:[M.jsxs("div",{className:"rally-command-header",children:[M.jsx("div",{className:"rally-command-title",children:"Rally Command"}),M.jsxs("div",{className:"rally-command-count",children:[t.length," agents"]})]}),M.jsx("textarea",{ref:f,className:"input rally-command-input",value:_,onChange:j=>{y(j.target.value)},onKeyDown:j=>{const oe=!j.shiftKey&&!j.ctrlKey&&!j.metaKey&&!j.altKey,Z=(j.ctrlKey||j.metaKey)&&!j.shiftKey&&!j.altKey;j.key==="Enter"&&(oe||Z)&&(j.preventDefault(),C())},placeholder:"Type once, send to all selected agents (use $NAME for per-agent names)...",disabled:d,rows:3}),M.jsxs("div",{className:"rally-command-actions",children:[M.jsx("div",{className:"rally-command-hint",children:"Enter sends, Shift+Enter adds a new line, $NAME inserts each agent's name"}),M.jsx("button",{className:"bar-btn",type:"submit",disabled:d||_.length===0,children:d?"Sending...":`Send to ${t.length}`})]}),g?M.jsx("div",{className:"rally-command-result",children:g}):null]})]}):s?M.jsx(zw,{workerId:s.id,workerName:s.displayName??s.name,focusRequestKey:H}):M.jsx("div",{className:"worker-roster",children:w.length===0?M.jsx("div",{className:"worker-roster-empty",children:"No active agents yet. Summon one from the bottom bar."}):w.map((j,oe)=>M.jsxs("div",{children:[j.kind==="shortcut"&&(oe===0||w[oe-1]?.kind!=="shortcut")?M.jsx("div",{className:"worker-roster-section-label",children:"Summon"}):null,j.kind==="worker"?M.jsx("button",{className:`worker-roster-item ${oe===S?"active":""}`,onMouseEnter:()=>E(oe),onClick:()=>P(oe),type:"button",children:M.jsxs("div",{className:"worker-roster-main",children:[M.jsx("img",{className:"worker-roster-avatar",src:`/api/assets/characters/${encodeURIComponent(j.worker.avatarType)}/rotations/south.png`,alt:"",loading:"lazy","aria-hidden":"true"}),M.jsxs("div",{className:"worker-roster-text",children:[M.jsxs("div",{className:"worker-roster-name-row",children:[M.jsx("div",{className:"worker-roster-name",children:j.worker.displayName??j.worker.name}),I.has(j.worker.id)?M.jsx("span",{className:"worker-complete-badge",title:"Finished and waiting for review",children:"READY"}):null]}),M.jsxs("div",{className:"worker-roster-meta",children:[j.worker.projectId," · ",j.worker.runtimeId," · ",j.worker.status]}),j.worker.activityText?M.jsx("div",{className:"worker-roster-activity",children:j.worker.activityText}):null]})]})}):M.jsx("button",{className:`worker-roster-item worker-roster-item-summon ${oe===S?"active":""}`,onMouseEnter:()=>E(oe),onClick:()=>P(oe),type:"button",children:M.jsxs("div",{className:"worker-roster-main",children:[j.shortcut.avatar?M.jsx("img",{className:"worker-roster-avatar worker-roster-summon-avatar",src:`/api/assets/characters/${encodeURIComponent(j.shortcut.avatar)}/rotations/south.png`,alt:"",loading:"lazy","aria-hidden":"true"}):M.jsx("div",{className:"worker-roster-summon-glyph","aria-hidden":"true",children:"+"}),M.jsxs("div",{className:"worker-roster-text",children:[M.jsx("div",{className:"worker-roster-name",children:j.shortcut.label}),M.jsxs("div",{className:"worker-roster-meta",children:[j.shortcut.project," · ",j.shortcut.runtime]}),M.jsx("div",{className:"worker-roster-activity",children:rv(j.shortcut.hotkeys)})]})]})})]},j.kind==="worker"?j.worker.id:`shortcut-${j.shortcutIndex}-${j.shortcut.label}`))})]})}function Hw(e,t){if(t.killConfirmWorkerIds.length>0)return Al(e)?(e.preventDefault(),t.confirmKillSelection(),!0):(e.preventDefault(),t.closeKillConfirm(),!0);if(!t.isEditableTarget(e.target)&&!t.isTerminalTarget(e.target)){const s=t.findMatchingShortcutIndexes(t.shortcutHotkeyBindings,e);if(s.length>0){e.preventDefault(),e.stopPropagation();for(const n of s)t.runSpawn({shortcutIndex:n});return!0}}if(e.key==="Escape")return t.renameModalOpen?(e.preventDefault(),t.closeRenameModal(),!0):t.batchSpawnDialogOpen?(e.preventDefault(),t.setBatchSpawnDialogOpen(!1),!0):t.shortcutsOverlayOpen?(e.preventDefault(),t.setShortcutsOverlayOpen(!1),!0):t.paletteOpen||t.spawnDialogOpen?(e.preventDefault(),t.setPaletteOpen(!1),t.setSpawnDialogOpen(!1),!0):(t.isTerminalTarget(e.target)||t.selectedWorkerId&&(e.preventDefault(),t.applySelection([])),!0);if(t.isTerminalEscapeShortcut(e)){if(!t.renameModalOpen&&!t.batchSpawnDialogOpen&&!t.shortcutsOverlayOpen&&!t.paletteOpen&&!t.spawnDialogOpen&&t.selectedWorkers.length>1&&t.focusedSelectedWorkerId){const n=t.escapeTerminalFocus();return e.preventDefault(),n&&e.stopPropagation(),t.setFocusedSelectedWorkerId(void 0),!0}if(t.escapeTerminalFocus())return e.preventDefault(),e.stopPropagation(),!0;if(!t.renameModalOpen&&!t.batchSpawnDialogOpen&&!t.shortcutsOverlayOpen&&!t.paletteOpen&&!t.spawnDialogOpen&&t.selectedWorkerIds.length>0){if(e.preventDefault(),t.selectedWorkerId){const n=t.rosterEntries.findIndex(l=>l.kind==="worker"&&l.worker.id===t.selectedWorkerId);n>=0&&t.setRosterActiveIndex(n)}t.applySelection([])}return!0}return e.key==="?"&&!e.ctrlKey&&!e.metaKey&&!e.altKey?((!t.isEditableTarget(e.target)||t.shortcutsOverlayOpen)&&(e.preventDefault(),t.setShortcutsOverlayOpen(s=>!s)),!0):(e.code==="BracketLeft"||e.code==="BracketRight"||e.key==="="&&!e.shiftKey)&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target)&&!t.isTerminalTarget(e.target)?(e.preventDefault(),e.code==="BracketLeft"?t.nudgeMapColumnRatio(e.shiftKey?-1:-t.mapColumnRatioStep):e.code==="BracketRight"?t.nudgeMapColumnRatio(e.shiftKey?1:t.mapColumnRatioStep):t.resetMapColumnRatio(),!0):!1}function jw(e,t){if(e.key==="Tab"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target))return t.isTerminalTarget(e.target)?!0:(e.preventDefault(),t.selectedWorkers.length>1?(t.cycleSelectedGroupFocus(e.shiftKey?-1:1),!0):(t.cycleSelection(e.shiftKey?-1:1),!0));if(e.code==="Period"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target))return t.isTerminalTarget(e.target)||(e.preventDefault(),t.cycleIdleSelection(e.shiftKey?-1:1)),!0;if(e.code==="Comma"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!t.isEditableTarget(e.target))return t.isTerminalTarget(e.target)||(e.preventDefault(),t.cycleIdleSelection(-1)),!0;const s=t.parseControlGroupDigit(e);if(s===void 0)return!1;if((e.ctrlKey||e.metaKey)&&!e.altKey&&t.selectedWorkerIds.length>0)return e.preventDefault(),t.setControlGroups(n=>{const l=new Set(t.selectedWorkerIds),c=n[s]??[],h=new Set(c);if(c.length===t.selectedWorkerIds.length&&t.selectedWorkerIds.every(d=>h.has(d))){const d={...n};return delete d[s],d}const _={...n};for(const[d,g]of Object.entries(_)){const y=Number(d);!Number.isInteger(y)||y<0||y>9||y===s||!Array.isArray(g)||(_[y]=g.filter(C=>!l.has(C)))}return _[s]=[...t.selectedWorkerIds],_}),!0;if(!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey&&!t.isEditableTarget(e.target)){const n=t.controlGroupByDigitRef.current[s]??[];if(n.length===0)return!0;const l=new Set(t.activeWorkers.map(h=>h.id)),c=n.filter(h=>l.has(h));if(c.length===0)return t.setControlGroups(h=>{if(!(s in h))return h;const f={...h};return delete f[s],f}),!0;e.preventDefault(),t.applySelection(c,{center:c.length===1})}return!0}function $w(e,t){if(t.isEditableTarget(e.target))return!0;const s=e.key.toLowerCase();if((s==="k"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&(t.inSelectedGroupView?e.shiftKey:t.selectedWorkerIds.length===1||!e.shiftKey)||e.key==="Delete")&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&t.selectedWorkerIds.length>0)return e.preventDefault(),t.onKillSelected(),!0;if(t.selectedWorkers.length>1&&!t.isTerminalTarget(e.target)){if(t.inSelectedGroupView&&s==="c"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey)return t.focusRallyCommandInput()&&e.preventDefault(),!0;if((s==="j"||s==="k")&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey){e.preventDefault();const l=s==="j"?1:-1,c=t.clampNumber(t.selectedGroupActiveIndex+l,0,t.selectedWorkers.length-1),h=t.selectedWorkers[c];return t.setSelectedGroupActiveIndex(c),t.focusedSelectedWorkerId&&h&&t.setFocusedSelectedWorkerId(h.id),!0}if(Al(e)){const l=t.selectedWorkers.find(c=>c.id===t.focusedSelectedWorkerId)??t.selectedWorkers[t.selectedGroupActiveIndex]??t.selectedWorkers[0];return l&&(e.preventDefault(),t.setFocusedSelectedWorkerId(l.id),t.requestTerminalFocus()),!0}}if(t.selectedWorkerIds.length===0&&t.rosterEntries.length>0&&!t.isTerminalTarget(e.target)){if(s==="k"&&e.shiftKey&&!e.ctrlKey&&!e.metaKey&&!e.altKey)return e.preventDefault(),t.onKillRosterActive(),!0;if(s==="n"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey&&t.firstSummonEntryIndex!==void 0)return e.preventDefault(),t.setRosterActiveIndex(t.firstSummonEntryIndex),!0;if((s==="j"||s==="k")&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey)return e.preventDefault(),t.setRosterActiveIndex(l=>{const c=s==="j"?1:-1;return t.clampNumber(l+c,0,t.rosterEntries.length-1)}),!0;if(Al(e))return e.preventDefault(),t.onActivateRosterIndex(t.rosterActiveIndex),!0}if(s==="r"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&t.selectedWorkers.length>0){e.preventDefault();const l=t.selectedWorkers.length>1?t.selectedWorkers.find(c=>c.id===t.focusedSelectedWorkerId):void 0;return t.openRenameForWorkers(l?[l]:t.selectedWorkers),!0}return s==="m"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&t.selectedWorkers.length>0?(e.preventDefault(),t.onToggleMovementModeSelected(),!0):s==="s"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey&&t.selectedWorkers.length>1?(e.preventDefault(),t.onScatterSelected(),!0):Al(e)&&t.selectedWorkerId?(e.preventDefault(),t.requestTerminalFocus(),!0):e.key!=="/"||e.metaKey||e.ctrlKey||e.altKey?!1:(e.preventDefault(),t.setPaletteOpen(!0),t.setSpawnDialogOpen(!1),t.setShortcutsOverlayOpen(!1),!0)}function Al(e){return e.key==="Enter"&&!e.ctrlKey&&!e.metaKey&&!e.altKey&&!e.shiftKey}function Kw(e){const t=k.useRef(e);t.current=e,k.useEffect(()=>{const s=n=>{const l=t.current;Uw(n,l)||Hw(n,l)||jw(n,l)||$w(n,l)};return window.addEventListener("keydown",s,!0),()=>window.removeEventListener("keydown",s,!0)},[])}function Uw(e,t){return!(!t.isTerminalTarget(e.target)||t.killConfirmWorkerIds.length>0||t.renameModalOpen||t.batchSpawnDialogOpen||t.shortcutsOverlayOpen||t.paletteOpen||t.spawnDialogOpen||e.key==="Escape"||t.isTerminalEscapeShortcut(e))}async function wi(e,t){const s=await fetch(e,{headers:{"Content-Type":"application/json",...t?.headers??{}},...t});if(!s.ok){const n=await s.json().catch(()=>({error:s.statusText}));throw new Error(n.error??`Request failed: ${s.status}`)}if(s.status!==204)return await s.json()}function Vw(){return wi("/api/config")}async function Qp(){return(await wi("/api/workers")).workers}function Jp(e){return wi("/api/workers/spawn",{method:"POST",body:JSON.stringify(e)})}function Yw(e){return wi(`/api/workers/${e}/stop`,{method:"POST"})}function qw(e,t,s){return wi(`/api/workers/${e}/position`,{method:"PATCH",body:JSON.stringify({x:t,y:s})})}function Zp(e,t){return wi(`/api/workers/${e}/rename`,{method:"PATCH",body:JSON.stringify({displayName:t})})}function Xw(e,t){return wi(`/api/workers/${e}/movement-mode`,{method:"PATCH",body:JSON.stringify({movementMode:t})})}function Gw(e){return wi(`/api/workers/${e}/open-terminal`,{method:"POST"})}function e_(e,t,s=!0){return wi("/api/workers/broadcast-input",{method:"POST",body:JSON.stringify({workerIds:e,text:t,submit:s})})}const Qw=3e4;function Jw(e){const[t,s]=k.useState(null),[n,l]=k.useState([]),[c,h]=k.useState(!1);return k.useEffect(()=>{Promise.all([Vw(),Qp()]).then(([f,_])=>{s(f),l(_),h(!0)}).catch(f=>{const _=f instanceof Error?f.message:"Failed to load Arcane Agents data";e(_)})},[e]),k.useEffect(()=>{let f=null,_=null,d=null,g=!1,y=!1;const C=async()=>{if(!(g||y)){y=!0;try{const E=await Qp();if(g)return;l(E),h(!0)}catch{}finally{y=!1}}},w=()=>{document.visibilityState==="visible"&&C()},v=()=>{C()};function S(){if(g)return;const E=window.location.protocol==="https:"?"wss":"ws";f=new WebSocket(`${E}://${window.location.host}/api/ws`),f.addEventListener("open",()=>{e(void 0),C()}),f.addEventListener("message",P=>{const z=JSON.parse(String(P.data));if(z.type==="init"){s(z.config),l(z.workers),h(!0);return}if(z.type==="worker-created"||z.type==="worker-updated"){l(H=>Ss(H,z.worker));return}z.type==="worker-removed"&&l(H=>H.filter(I=>I.id!==z.workerId))}),f.addEventListener("error",()=>{e("Realtime connection failed. Retrying...")}),f.addEventListener("close",()=>{g||(_=setTimeout(S,2e3))})}return d=setInterval(()=>{C()},Qw),window.addEventListener("focus",v),document.addEventListener("visibilitychange",w),S(),()=>{g=!0,_&&clearTimeout(_),d&&clearInterval(d),window.removeEventListener("focus",v),document.removeEventListener("visibilitychange",w),f?.close()}},[e]),{config:t,workers:n,setWorkers:l,workersHydrated:c}}function Zw(e,t){const[s,n]=k.useState(()=>Gg()),l=k.useRef(s),[c,h]=k.useState(()=>Jg());return k.useEffect(()=>{l.current=s,Qg(s)},[s]),k.useEffect(()=>{Zg(c)},[c]),k.useEffect(()=>{if(!t)return;const f=new Set(e.map(_=>_.id));n(_=>{let d=!1;const g={..._};for(const[y,C]of Object.entries(g)){if(!Array.isArray(C)||C.length===0){delete g[Number(y)],d=!0;continue}const w=C.filter(v=>f.has(v));w.length!==C.length&&(w.length===0?delete g[Number(y)]:g[Number(y)]=w,d=!0)}return d?g:_})},[e,t]),{controlGroups:s,setControlGroups:n,controlGroupByDigitRef:l,mapColumnRatio:c,setMapColumnRatio:f=>{h(_=>{const d=vi(f,lo,ao);return d===_?_:d})},nudgeMapColumnRatio:f=>{h(_=>vi(_+f,lo,ao))},resetMapColumnRatio:()=>{h(Jn)}}}function e1(e,t,s){const[n,l]=k.useState([]),[c,h]=k.useState(0),[f,_]=k.useState(void 0),[d,g]=k.useState(void 0),[y,C]=k.useState(0),[w,v]=k.useState(0),[S,E]=k.useState(void 0),P=n.length===1?n[0]:void 0,z=k.useMemo(()=>new Set(n),[n]),H=k.useMemo(()=>e.filter(b=>z.has(b.id)),[e,z]),I=k.useMemo(()=>e.filter(b=>b.status==="idle"),[e]),G=k.useCallback((b,W)=>{const B=Array.from(new Set(b));l(B),E(void 0);const A=B.length===1?B[0]:void 0;W?.center&&A&&(_(A),h(q=>q+1)),W?.focusTerminal&&A?g(q=>(q??0)+1):g(void 0)},[]),j=k.useCallback(()=>{g(b=>(b??0)+1)},[]),oe=k.useCallback(b=>{G(b?[b]:[])},[G]),Z=k.useCallback(b=>{G(b)},[G]),ee=k.useCallback(b=>{G([b],{focusTerminal:!0})},[G]),se=k.useCallback(b=>{if(e.length===0)return;const W=e.findIndex(N=>N.id===P),A=((W>=0?W:b>0?-1:0)+b+e.length)%e.length,q=e[A];q&&G([q.id],{center:!0})},[e,G,P]),U=k.useCallback(b=>{if(I.length===0)return;const W=I.findIndex(N=>N.id===P),A=((W>=0?W:b>0?-1:0)+b+I.length)%I.length,q=I[A];q&&G([q.id],{center:!0})},[G,I,P]),D=k.useCallback(b=>{if(H.length<=1)return;const W=S?H.findIndex(N=>N.id===S):vi(w,0,H.length-1),A=((W>=0?W:b>0?-1:0)+b+H.length)%H.length,q=H[A];q&&(v(A),E(q.id))},[S,w,H]);return k.useEffect(()=>{if(t.length===0){C(0);return}if(P){const b=t.findIndex(W=>W.kind==="worker"&&W.worker.id===P);b>=0&&C(b);return}C(b=>vi(b,0,t.length-1))},[t,P]),k.useEffect(()=>{if(H.length<=1){v(0),E(void 0),s?.();return}if(S){const b=H.findIndex(W=>W.id===S);if(b>=0){v(b);return}E(void 0)}v(b=>vi(b,0,H.length-1))},[S,s,H]),k.useEffect(()=>{const b=new Set(e.map(W=>W.id));l(W=>W.filter(B=>b.has(B)))},[e]),{selectedWorkerIds:n,setSelectedWorkerIds:l,selectedWorkerId:P,selectedWorkers:H,mapCenterToken:c,mapCenterWorkerId:f,terminalFocusToken:d,rosterActiveIndex:y,setRosterActiveIndex:C,selectedGroupActiveIndex:w,setSelectedGroupActiveIndex:v,focusedSelectedWorkerId:S,setFocusedSelectedWorkerId:E,applySelection:G,requestTerminalFocus:j,onSelectWorker:oe,onSelectionChange:Z,onActivateWorker:ee,cycleSelection:se,cycleIdleSelection:U,cycleSelectedGroupFocus:D}}function t1(e){const[t,s]=k.useState(!1);return k.useEffect(()=>{s(!1),document.activeElement instanceof HTMLElement&&document.activeElement.closest(".terminal-panel")&&document.activeElement.blur()},[e]),k.useEffect(()=>{const n=()=>{s(Ul(document.activeElement))},l=()=>{setTimeout(n,0)},c=()=>{s(!1)};return window.addEventListener("focusin",n,!0),window.addEventListener("focusout",l,!0),window.addEventListener("blur",c),n(),()=>{window.removeEventListener("focusin",n,!0),window.removeEventListener("focusout",l,!0),window.removeEventListener("blur",c)}},[]),t}function r1({workers:e,reviewedWorkerId:t}){const[s,n]=k.useState([]),l=k.useRef(new Map),c=k.useRef(t);return k.useEffect(()=>{c.current=t},[t]),k.useEffect(()=>{const h=new Set(e.map(g=>g.id)),f=new Set,_=new Set;for(const g of e)if(l.current.get(g.id)==="working"&&g.status==="idle"){if(g.id===c.current)continue;_.add(g.id)}else g.status!=="idle"&&f.add(g.id);const d=new Map;for(const g of e)d.set(g.id,g.status);l.current=d,n(g=>{const C=[...g.filter(w=>h.has(w)&&!f.has(w))];for(const w of _)C.includes(w)||C.push(w);return C.length===g.length&&C.every((w,v)=>w===g[v])?g:C})},[e]),k.useEffect(()=>{t&&n(h=>h.filter(f=>f!==t))},[t]),{pendingCompletionWorkerIds:s}}function i1(e){const[t,s]=k.useState([]),n=k.useCallback(c=>{s(h=>[{worker:c,startedAtMs:Date.now()},...h.filter(f=>f.worker.id!==c.id)])},[]),l=k.useCallback(c=>{s(h=>h.filter(f=>f.worker.id!==c))},[]);return k.useEffect(()=>{const c=setInterval(()=>{const h=Date.now();s(f=>f.filter(_=>h-_.startedAtMs<e))},80);return()=>{clearInterval(c)}},[e]),{fadingWorkers:t,queueWorkerFade:n,removeWorkerFade:l}}const s1=1600,Zc=["move","selected"],n1={move:["move.mp3","move_variant_1.mp3","move_variant_2.mp3","move_variant_3.mp3"],selected:["selected.mp3","selected_variant_1.mp3","selected_variant_2.mp3","selected_variant_3.mp3"]};function o1({config:e,workers:t,workersHydrated:s,selectedWorkerIds:n}){const l=e?.audio.enableSound??!0,c=k.useRef(new Map),h=k.useRef(new Set),f=k.useRef(new Map),_=k.useRef(!1),d=k.useRef(!1),g=k.useRef(new Map),y=k.useRef(new Set),C=k.useRef(new Map),w=k.useRef(new Map),v=k.useRef(new Set),S=k.useRef(null);k.useEffect(()=>{f.current=new Map(t.map(U=>[U.id,U]))},[t]);const E=k.useCallback((U,D)=>`/api/assets/characters/${encodeURIComponent(U)}/voice-lines/${D}.mp3`,[]),P=k.useCallback((U,D)=>`/api/assets/characters/${encodeURIComponent(U)}/voice-lines/${encodeURIComponent(D)}`,[]),z=k.useCallback((U,D)=>{const b=g.current.get(U)?.[D]??[];return b.length>0?b:n1[D].map(W=>P(U,W))},[P]),H=k.useCallback(U=>{if(v.current.has(U))return;v.current.add(U);const D=new Audio(U);D.preload="auto",D.addEventListener("canplay",()=>{w.current.set(U,!0)},{once:!0}),D.addEventListener("error",()=>{w.current.set(U,!1)},{once:!0}),D.load()},[]),I=k.useCallback(U=>{if(!l||w.current.get(U)===!1)return;const D=S.current;D&&(D.pause(),D.currentTime=0);const b=new Audio(U);b.preload="auto",b.addEventListener("canplay",()=>{w.current.set(U,!0)},{once:!0}),b.addEventListener("error",()=>{w.current.set(U,!1)},{once:!0}),S.current=b,b.play().catch(()=>{})},[l]),G=k.useCallback((U,D)=>{I(E(U.avatarType,D))},[I,E]),j=k.useCallback(U=>{if(!l)return;const D=U.filter(A=>w.current.get(A)===!0),b=U.filter(A=>w.current.get(A)!==!1),W=D.length>0?D:b,B=t_(W);B&&I(B)},[I,l]),oe=k.useCallback(U=>{const D=z(U.avatarType,"selected");j(D)},[j,z]),Z=k.useCallback(U=>{const D=z(U.avatarType,"move");j(D)},[j,z]),ee=k.useCallback(async U=>{try{const D=await fetch(`/api/avatars/${encodeURIComponent(U)}/voice-lines`);if(!D.ok)return;const b=await D.json();if(!Array.isArray(b.files))return;const W=b.files.filter(A=>typeof A=="string"&&A.toLowerCase().endsWith(".mp3")).sort((A,q)=>A.localeCompare(q)),B={move:[],selected:[]};for(const A of Zc)B[A]=W.filter(q=>q.toLowerCase().startsWith(A)).map(q=>P(U,q));g.current.set(U,B);for(const A of Zc)for(const q of B[A])H(q)}catch{y.current.delete(U)}},[H,P]);k.useEffect(()=>{const U=Array.from(new Set(t.map(D=>D.avatarType)));for(const D of U)y.current.has(D)||(y.current.add(D),ee(D))},[ee,t]),k.useEffect(()=>{if(!l)return;const U=Array.from(new Set(t.map(b=>b.avatarType))),D=["arrive","attention","complete","death"];for(const b of U){for(const W of D)H(E(b,W));for(const W of Zc)for(const B of z(b,W))H(B)}},[H,E,z,l,t]),k.useEffect(()=>{if(!s)return;const U=new Map(t.map(B=>[B.id,B])),D=c.current,b=C.current,W=performance.now();if(!_.current){_.current=!0,c.current=U;return}for(const B of t){const A=D.get(B.id);if(!A){G(B,"arrive"),b.set(B.id,W+s1);continue}l1(A.status,B.status)&&G(B,"attention"),a1(A.status,B.status)&&!h1(B)&&G(B,"complete")}for(const[B,A]of D.entries())U.has(B)||(G(A,"death"),b.delete(B));c.current=U},[G,t,s]),k.useEffect(()=>{if(!s)return;const U=new Set(n),D=h.current;if(!d.current){d.current=!0,h.current=U;return}const b=n.filter(N=>!D.has(N)),W=C.current,B=performance.now(),A=b.filter(N=>{const K=W.get(N);return K===void 0?!0:K<=B?(W.delete(N),!0):!1}),q=t_(A);if(q){const N=f.current.get(q);N&&oe(N)}h.current=U},[oe,n,s]);const se=k.useCallback(U=>{const D=f.current.get(U);D&&Z(D)},[Z]);return k.useEffect(()=>()=>{const U=S.current;U&&(U.pause(),U.currentTime=0,S.current=null)},[]),{playMoveVoiceLine:se}}function l1(e,t){return e!=="attention"&&t==="attention"}function a1(e,t){return e==="working"&&t==="idle"}const c1=1e4;function h1(e){return Date.now()-new Date(e.createdAt).getTime()<c1}function t_(e){if(e.length===0)return;const t=Math.floor(Math.random()*e.length);return e[t]}function u1({workers:e,activeWorkers:t,renameModalOpen:s,setRenameModalOpen:n,renameTargetWorkerIds:l,setRenameTargetWorkerIds:c,killConfirmWorkerIds:h,setKillConfirmWorkerIds:f,setRenameDraft:_}){const d=k.useMemo(()=>{if(l.length===0)return[];const v=new Map(e.map(S=>[S.id,S]));return l.map(S=>v.get(S)).filter(S=>!!S)},[l,e]),g=k.useMemo(()=>{if(h.length===0)return[];const v=new Map(e.map(S=>[S.id,S]));return h.map(S=>v.get(S)).filter(S=>!!S)},[h,e]),y=k.useCallback(()=>{n(!1),c([])},[n,c]),C=k.useCallback(()=>{f([])},[f]),w=k.useCallback(v=>{v.length!==0&&(_(v.length===1?v[0].displayName??v[0].name:""),c(v.map(S=>S.id)),n(!0))},[_,n,c]);return k.useEffect(()=>{if(!s||l.length===0)return;const v=new Set(t.map(S=>S.id));l.some(S=>v.has(S))||y()},[t,y,s,l]),k.useEffect(()=>{if(h.length===0)return;const v=new Set(t.map(S=>S.id));h.some(S=>v.has(S))||C()},[t,C,h]),{renameTargetWorkers:d,killConfirmWorkers:g,closeRenameModal:y,closeKillConfirm:C,openRenameForWorkers:w}}function d1({workers:e,selectedWorkerIds:t,setSelectedWorkerIds:s,rosterEntries:n,rosterActiveIndex:l,setKillConfirmWorkerIds:c,closeKillConfirm:h,killConfirmWorkerIds:f,queueWorkerFade:_,removeWorkerFade:d,setWorkers:g,showError:y}){const C=k.useCallback(async E=>{const P=e.find(z=>z.id===E);if(P){h(),_(P);try{const z=await Yw(E);g(H=>H.filter(I=>I.id!==z.workerId)),s(H=>H.filter(I=>I!==z.workerId))}catch(z){d(E),y(z)}}},[h,_,d,s,g,y,e]),w=k.useCallback(()=>{t.length!==0&&c(t)},[t,c]),v=k.useCallback(()=>{const E=n[l];!E||E.kind!=="worker"||c([E.worker.id])},[l,n,c]),S=k.useCallback(()=>{if(f.length===0)return;const E=[...f];h();for(const P of E)C(P)},[h,f,C]);return{onKillSelected:w,onKillRosterActive:v,confirmKillSelection:S}}function f1({setWorkers:e,selectedWorkers:t,terminalWorkerId:s,applySelection:n,setSpawnDialogOpen:l,setPaletteOpen:c,renameTargetWorkerIds:h,closeRenameModal:f,showError:_}){const d=k.useCallback(async S=>{try{const E=t.map(H=>H.id),P="shortcutIndex"in S&&E.length>0?{...S,spawnNearWorkerIds:E}:S,z=await Jp(P);e(H=>Ss(H,z)),n([z.id],{center:!0}),l(!1),c(!1)}catch(E){_(E)}},[n,t,c,l,e,_]),g=k.useCallback(async(S,E)=>{const P=[];let z;for(let H=0;H<S.length;H++){E(H,S.length);const I=S[H],G={...I.input,displayName:I.displayName,spawnNearWorkerIds:z?[z]:t.map(j=>j.id).slice(0,1)};try{const j=await Jp(G);e(oe=>Ss(oe,j)),P.push(j.id),z=j.id}catch(j){_(j);break}}E(P.length,S.length),P.length>0&&n(P,{center:!0})},[n,t,e,_]),y=k.useCallback(async S=>{const E=[...h];if(E.length===0){f();return}try{if(E.length===1){const P=await Zp(E[0],S);e(z=>Ss(z,P))}else{const P=S.trim(),z=await Promise.all(E.map((H,I)=>Zp(H,P.length>0?`${P} ${I+1}`:"")));e(H=>{let I=H;for(const G of z)I=Ss(I,G);return I})}f()}catch(P){_(P)}},[f,h,e,_]),C=k.useCallback(async()=>{if(t.length===0)return;const S=t.every(E=>E.movementMode==="hold")?"wander":"hold";try{const E=await Promise.all(t.map(P=>Xw(P.id,S)));e(P=>{let z=P;for(const H of E)z=Ss(z,H);return z})}catch(E){_(E)}},[t,e,_]),w=k.useCallback(async()=>{if(s)try{await Gw(s)}catch(S){_(S)}},[_,s]),v=k.useCallback((S,E)=>{qw(S,E.x,E.y).then(P=>{e(z=>Ss(z,P))}).catch(P=>{_(P)})},[e,_]);return{runSpawn:d,runBatchSpawn:g,submitRename:y,onToggleMovementModeSelected:C,onOpenSelectedInTerminal:w,onPositionCommit:v}}function p1({selectedWorkers:e,rallyCommandDraft:t,setRallyCommandDraft:s,rallyCommandSending:n,setRallyCommandSending:l,rallyCommandResultText:c,setRallyCommandResultText:h,showError:f}){const _=k.useCallback(async()=>{if(n)return;const g=e.map(y=>y.id);if(!(g.length<=1)){if(t.length===0){h("Enter a command to broadcast.");return}l(!0),h(void 0);try{const C=t.includes("$NAME")?sv(await Promise.all(e.map(async w=>{const v=t.replace(/\$NAME/g,w.displayName??w.name);try{return await e_([w.id],v,!0)}catch(S){return{requestedCount:1,deliveredWorkerIds:[],skippedWorkerIds:[],failed:[{workerId:w.id,error:S instanceof Error?S.message:"Failed to send input"}]}}}))):await e_(g,t,!0);s(""),h(iv(C))}catch(y){f(y)}finally{l(!1)}}},[t,n,e,s,h,l,f]),d=k.useCallback(g=>{s(g),c&&h(void 0)},[c,s,h]);return{onSendRallyCommand:_,onRallyCommandDraftChange:d}}function _1({workers:e,activeWorkers:t,setWorkers:s,selectedWorkers:n,selectedWorkerIds:l,setSelectedWorkerIds:c,focusedSelectedWorkerId:h,terminalWorkerId:f,rosterEntries:_,rosterActiveIndex:d,setRosterActiveIndex:g,applySelection:y,setSpawnDialogOpen:C,setPaletteOpen:w,renameModalOpen:v,setRenameModalOpen:S,renameTargetWorkerIds:E,setRenameTargetWorkerIds:P,setRenameDraft:z,killConfirmWorkerIds:H,setKillConfirmWorkerIds:I,rallyCommandDraft:G,setRallyCommandDraft:j,rallyCommandSending:oe,setRallyCommandSending:Z,rallyCommandResultText:ee,setRallyCommandResultText:se,queueWorkerFade:U,removeWorkerFade:D,setErrorText:b}){const W=k.useCallback(xe=>{b(xe instanceof Error?xe.message:"Unknown request failure")},[b]),{renameTargetWorkers:B,killConfirmWorkers:A,closeRenameModal:q,closeKillConfirm:N,openRenameForWorkers:K}=u1({workers:e,activeWorkers:t,renameModalOpen:v,setRenameModalOpen:S,renameTargetWorkerIds:E,setRenameTargetWorkerIds:P,killConfirmWorkerIds:H,setKillConfirmWorkerIds:I,setRenameDraft:z}),{runSpawn:Q,runBatchSpawn:T,submitRename:V,onToggleMovementModeSelected:de,onOpenSelectedInTerminal:fe,onPositionCommit:he}=f1({setWorkers:s,selectedWorkers:n,terminalWorkerId:f,applySelection:y,setSpawnDialogOpen:C,setPaletteOpen:w,renameTargetWorkerIds:E,closeRenameModal:q,showError:W}),{onKillSelected:pe,onKillRosterActive:ge,confirmKillSelection:Re}=d1({workers:e,selectedWorkerIds:l,setSelectedWorkerIds:c,rosterEntries:_,rosterActiveIndex:d,setKillConfirmWorkerIds:I,closeKillConfirm:N,killConfirmWorkerIds:H,queueWorkerFade:U,removeWorkerFade:D,setWorkers:s,showError:W}),{onSendRallyCommand:ye,onRallyCommandDraftChange:Fe}=p1({selectedWorkers:n,rallyCommandDraft:G,setRallyCommandDraft:j,rallyCommandSending:oe,setRallyCommandSending:Z,rallyCommandResultText:ee,setRallyCommandResultText:se,showError:W}),We=k.useCallback(xe=>{const Ke=_[xe];if(Ke){if(g(xe),Ke.kind==="worker"){y([Ke.worker.id],{center:!0});return}Q({shortcutIndex:Ke.shortcutIndex})}},[y,_,Q,g]),je=k.useCallback(()=>{if(n.length===0)return;const xe=n.length>1?n.find(Ke=>Ke.id===h):void 0;K(xe?[xe]:n)},[h,K,n]);return{renameTargetWorkers:B,killConfirmWorkers:A,runSpawn:Q,runBatchSpawn:T,closeRenameModal:q,closeKillConfirm:N,openRenameForWorkers:K,submitRename:V,onKillSelected:pe,onKillRosterActive:ge,confirmKillSelection:Re,onToggleMovementModeSelected:de,onActivateRosterIndex:We,onOpenSelectedInTerminal:fe,onSendRallyCommand:ye,onRallyCommandDraftChange:Fe,onRenameSelected:je,onPositionCommit:he}}function m1(e){const t=[];return e.forEach((s,n)=>{for(const l of s.hotkeys??[]){const c=y1(l);c&&t.push({shortcutIndex:n,hotkey:c})}}),t}function g1(e,t){const s=[],n=new Set;for(const l of e)v1(l.hotkey,t)&&(n.has(l.shortcutIndex)||(n.add(l.shortcutIndex),s.push(l.shortcutIndex)));return s}function v1(e,t){return t.ctrlKey!==e.ctrl||t.metaKey!==e.meta||t.altKey!==e.alt||t.shiftKey!==e.shift?!1:C1(t.key)===e.key?!0:e.code?t.code===e.code:!1}function y1(e){const t=S1(e);if(t.length===0)return;let s=!1,n=!1,l=!1,c=!1,h;for(const _ of t){const d=_.trim().toLowerCase();if(d){if(d==="ctrl"||d==="control"){s=!0;continue}if(d==="cmd"||d==="meta"||d==="super"){n=!0;continue}if(d==="alt"||d==="option"){l=!0;continue}if(d==="shift"){c=!0;continue}if(h)return;h=_}}if(!h)return;const f=w1(h);if(f)return{key:f.key,code:f.code,ctrl:s,meta:n,alt:l,shift:c}}function S1(e){const t=e.trim().replace(/\s+/g,"");if(!t)return[];if(t.includes("+"))return t.split("+").filter(l=>l.length>0);const s=t.toLowerCase();return s.includes("ctrl-")||s.includes("control-")||s.includes("cmd-")||s.includes("meta-")||s.includes("super-")||s.includes("alt-")||s.includes("option-")||s.includes("shift-")?t.split("-").filter(l=>l.length>0):[t]}function w1(e){const t=e.trim();if(!t)return;const s=t.toLowerCase();if(s==="space"||s==="spacebar")return{key:" ",code:"Space"};if(s==="esc")return{key:"escape",code:"Escape"};if(s==="return")return{key:"enter",code:"Enter"};if(s==="up")return{key:"arrowup",code:"ArrowUp"};if(s==="down")return{key:"arrowdown",code:"ArrowDown"};if(s==="left")return{key:"arrowleft",code:"ArrowLeft"};if(s==="right")return{key:"arrowright",code:"ArrowRight"};if(/^key[a-z]$/.test(s)){const n=s.slice(3);return{key:n,code:`Key${n.toUpperCase()}`}}if(/^digit[0-9]$/.test(s)){const n=s.slice(5);return{key:n,code:`Digit${n}`}}if(/^numpad[0-9]$/.test(s)){const n=s.slice(6);return{key:n,code:`Numpad${n}`}}if(/^f[0-9]{1,2}$/.test(s))return{key:s,code:s.toUpperCase()};if(t.length===1){if(/^[a-z]$/i.test(t)){const n=t.toLowerCase();return{key:n,code:`Key${n.toUpperCase()}`}}return/^[0-9]$/.test(t)?{key:t}:{key:t}}return{key:s}}function C1(e){const t=e.toLowerCase();return t==="spacebar"?" ":t}function k1(){const[e,t]=k.useState(!1),[s,n]=k.useState(!1),[l,c]=k.useState(!1),[h,f]=k.useState(!1),[_,d]=k.useState(!1),[g,y]=k.useState([]),[C,w]=k.useState(""),[v,S]=k.useState([]),[E,P]=k.useState(""),[z,H]=k.useState(!1),[I,G]=k.useState(void 0),[j,oe]=k.useState(void 0),[Z,ee]=k.useState([]),[se,U]=k.useState(0),D=k.useRef(null),{config:b,workers:W,setWorkers:B,workersHydrated:A}=Jw(oe),{fadingWorkers:q,queueWorkerFade:N,removeWorkerFade:K}=i1(Xg),Q=k.useMemo(()=>W.filter(te=>te.status!=="stopped"),[W]),{controlGroups:T,setControlGroups:V,controlGroupByDigitRef:de,mapColumnRatio:fe,setMapColumnRatio:he,nudgeMapColumnRatio:pe,resetMapColumnRatio:ge}=Zw(Q,A),Re=k.useRef(null),ye=k.useRef(void 0),[Fe,We]=k.useState(!1),je=k.useMemo(()=>b?.shortcuts??[],[b]),xe=k.useMemo(()=>m1(je),[je]),Ke=k.useMemo(()=>[...Q.map(te=>({kind:"worker",worker:te})),...je.map((te,Ce)=>({kind:"shortcut",shortcut:te,shortcutIndex:Ce}))],[Q,je]),jr=k.useMemo(()=>{const te=Ke.findIndex(Ce=>Ce.kind==="shortcut");return te>=0?te:void 0},[Ke]),at=k.useCallback(()=>{P(""),G(void 0)},[]),{selectedWorkerIds:zt,setSelectedWorkerIds:_r,selectedWorkerId:ct,selectedWorkers:Xe,mapCenterToken:Kt,mapCenterWorkerId:$r,terminalFocusToken:Zi,rosterActiveIndex:ii,setRosterActiveIndex:Ci,selectedGroupActiveIndex:ki,setSelectedGroupActiveIndex:si,focusedSelectedWorkerId:Ut,setFocusedSelectedWorkerId:es,applySelection:Dr,requestTerminalFocus:mr,onSelectWorker:Ae,onSelectionChange:Ts,onActivateWorker:ts,cycleSelection:gr,cycleIdleSelection:vr,cycleSelectedGroupFocus:rs}=e1(Q,Ke,at),yr=k.useMemo(()=>Q.find(te=>te.id===ct),[Q,ct]),Sr=k.useMemo(()=>{if(Ut)return Xe.find(te=>te.id===Ut)},[Ut,Xe]),Kr=yr??(Xe.length>1?Sr:void 0),Et=Kr?.id,or=Xe.length>1&&!Kr,bi=t1(Et),{pendingCompletionWorkerIds:is}=r1({workers:Q,reviewedWorkerId:Et}),{playMoveVoiceLine:xi}=o1({config:b,workers:Q,workersHydrated:A,selectedWorkerIds:zt}),Ur=k.useCallback(()=>{const te=document.activeElement;return!(te instanceof HTMLElement)||!te.closest(".terminal-panel")?!1:(te.blur(),document.querySelector(".map-canvas")?.focus(),!0)},[]),Ps=k.useCallback(()=>{const te=D.current;if(!te)return!1;te.focus();const Ce=te.value.length;return te.setSelectionRange(Ce,Ce),!0},[]),Ei=k.useCallback(()=>{if(Xe.length<2)return;const te=Xe.reduce((Qe,mt)=>Qe+mt.position.x,0)/Xe.length,Ce=Xe.reduce((Qe,mt)=>Qe+mt.position.y,0)/Xe.length,Le=80+Xe.length*20,De=Xe.map(Qe=>({workerId:Qe.id,target:{x:te+(Math.random()-.5)*Le*2,y:Ce+(Math.random()-.5)*Le*2}}));ee(De),U(Qe=>Qe+1)},[Xe]),ss=k.useCallback(te=>{const Ce=Re.current;if(!Ce)return;const Le=Ce.getBoundingClientRect(),De=Math.max(1,Le.width-Bc),Qe=te-Le.left-Bc/2,mt=vi(Qe/De,lo,ao);he(mt)},[he]);k.useEffect(()=>{if(Fe)return document.body.classList.add("split-pane-dragging"),()=>{document.body.classList.remove("split-pane-dragging")}},[Fe]);const{renameTargetWorkers:ni,killConfirmWorkers:ns,runSpawn:lr,runBatchSpawn:ln,closeRenameModal:Vr,closeKillConfirm:Nr,openRenameForWorkers:Mi,submitRename:X,onKillSelected:Y,onKillRosterActive:le,confirmKillSelection:ue,onToggleMovementModeSelected:_e,onActivateRosterIndex:Ne,onOpenSelectedInTerminal:Oe,onSendRallyCommand:ht,onRallyCommandDraftChange:tt,onRenameSelected:Ue,onPositionCommit:Jt}=_1({workers:W,activeWorkers:Q,setWorkers:B,selectedWorkers:Xe,selectedWorkerIds:zt,setSelectedWorkerIds:_r,focusedSelectedWorkerId:Ut,terminalWorkerId:Et,rosterEntries:Ke,rosterActiveIndex:ii,setRosterActiveIndex:Ci,applySelection:Dr,setSpawnDialogOpen:t,setPaletteOpen:n,renameModalOpen:_,setRenameModalOpen:d,renameTargetWorkerIds:v,setRenameTargetWorkerIds:S,setRenameDraft:w,killConfirmWorkerIds:g,setKillConfirmWorkerIds:y,rallyCommandDraft:E,setRallyCommandDraft:P,rallyCommandSending:z,setRallyCommandSending:H,rallyCommandResultText:I,setRallyCommandResultText:G,queueWorkerFade:N,removeWorkerFade:K,setErrorText:oe});return Kw({activeWorkers:Q,applySelection:Dr,batchSpawnDialogOpen:l,clampNumber:vi,closeKillConfirm:Nr,closeRenameModal:Vr,confirmKillSelection:ue,controlGroupByDigitRef:de,cycleIdleSelection:vr,cycleSelectedGroupFocus:rs,cycleSelection:gr,escapeTerminalFocus:Ur,findMatchingShortcutIndexes:g1,firstSummonEntryIndex:jr,focusRallyCommandInput:Ps,focusedSelectedWorkerId:Ut,inSelectedGroupView:or,isEditableTarget:$h,isTerminalEscapeShortcut:ov,isTerminalTarget:ev,killConfirmWorkerIds:g,mapColumnRatioStep:jh,nudgeMapColumnRatio:pe,onActivateRosterIndex:Ne,onKillRosterActive:le,onKillSelected:Y,onScatterSelected:Ei,onToggleMovementModeSelected:_e,openRenameForWorkers:Mi,paletteOpen:s,parseControlGroupDigit:nv,renameModalOpen:_,requestTerminalFocus:mr,resetMapColumnRatio:ge,rosterActiveIndex:ii,rosterEntries:Ke,runSpawn:lr,selectedGroupActiveIndex:ki,selectedWorkerId:ct,selectedWorkerIds:zt,selectedWorkers:Xe,setBatchSpawnDialogOpen:c,setControlGroups:V,setFocusedSelectedWorkerId:es,setPaletteOpen:n,setRosterActiveIndex:Ci,setSelectedGroupActiveIndex:si,setShortcutsOverlayOpen:f,setSpawnDialogOpen:t,shortcutHotkeyBindings:xe,shortcutsOverlayOpen:h,spawnDialogOpen:e}),M.jsxs("div",{ref:Re,className:"app-shell",style:{gridTemplateColumns:`minmax(0px, ${fe.toFixed(3)}fr) ${Bc}px minmax(0px, ${(1-fe).toFixed(3)}fr)`},children:[M.jsxs("div",{className:"map-column",children:[M.jsx(S0,{workers:Q,fadingWorkers:q,selectedWorkerId:ct,selectedWorkerIds:zt,focusedSelectedWorkerId:Ut,terminalFocusedSelected:!!(ct&&bi),terminalFocusedWorkerId:bi?Et:void 0,controlGroups:T,completionPendingWorkerIds:is,onSelect:Ae,onSelectionChange:Ts,onActivateWorker:ts,onMoveOrderIssued:xi,onPositionCommit:Jt,externalMoveOrders:Z,externalMoveOrderToken:se,centerOnWorkerId:$r,centerRequestKey:Kt}),M.jsx(lv,{shortcuts:b?.shortcuts??[],selectedWorker:yr,selectedWorkers:Xe,onSpawnShortcut:te=>{lr({shortcutIndex:te})},onOpenSpawnDialog:()=>{t(!0),n(!1)},onOpenPalette:()=>{n(!0),t(!1)},onDeselect:()=>Ae(void 0),onKillSelected:()=>{Y()},onRenameSelected:()=>{Ue()},onToggleMovementMode:()=>{_e()},onScatterSelected:()=>{Ei()}})]}),M.jsx("div",{className:`layout-divider${Fe?" layout-divider-active":""}`,role:"separator","aria-label":"Resize map and terminal columns","aria-orientation":"vertical",onPointerDown:te=>{te.button===0&&(te.preventDefault(),ye.current=te.pointerId,We(!0),te.currentTarget.setPointerCapture(te.pointerId),ss(te.clientX))},onPointerMove:te=>{ye.current===te.pointerId&&(te.preventDefault(),ss(te.clientX))},onPointerUp:te=>{ye.current===te.pointerId&&(te.preventDefault(),te.currentTarget.hasPointerCapture(te.pointerId)&&te.currentTarget.releasePointerCapture(te.pointerId),ye.current=void 0,We(!1))},onPointerCancel:te=>{ye.current===te.pointerId&&(te.currentTarget.hasPointerCapture(te.pointerId)&&te.currentTarget.releasePointerCapture(te.pointerId),ye.current=void 0,We(!1))}}),M.jsx(Fw,{activeWorkers:Q,selectedWorkers:Xe,terminalWorker:Kr,terminalFocused:bi,selectedGroupActiveIndex:ki,setSelectedGroupActiveIndex:si,setFocusedSelectedWorkerId:es,rallyCommandInputRef:D,rallyCommandDraft:E,rallyCommandSending:z,rallyCommandResultText:I,onRallyCommandDraftChange:tt,onSendRallyCommand:ht,rosterEntries:Ke,completionPendingWorkerIds:is,rosterActiveIndex:ii,setRosterActiveIndex:Ci,onActivateRosterIndex:Ne,onOpenSelectedInTerminal:Oe,terminalFocusToken:Zi}),b?M.jsx(k0,{open:e,projects:b.projects,runtimes:b.runtimes,onClose:()=>t(!1),onSpawn:(te,Ce)=>{lr({projectId:te,runtimeId:Ce})}}):null,b?M.jsx(cv,{open:s,config:b,onClose:()=>n(!1),onSpawnShortcut:te=>{lr({shortcutIndex:te})},onSpawnProjectRuntime:(te,Ce)=>{lr({projectId:te,runtimeId:Ce})},onOpenBatchSpawn:()=>{n(!1),c(!0)}}):null,b?M.jsx(av,{open:l,config:b,onClose:()=>c(!1),onBatchSpawn:ln}):null,M.jsx(C0,{open:h,onClose:()=>{f(!1)}}),M.jsx(hv,{workerIds:g,workers:ns,onClose:Nr,onConfirm:ue}),M.jsx(w0,{open:_,targetWorkerIds:v,targetWorkers:ni,initialDraft:C,onClose:Vr,onSubmit:X}),j?M.jsx("div",{className:"error-toast",onClick:()=>oe(void 0),children:j}):null]})}const em=document.getElementById("root");if(!em)throw new Error("Missing #root container");qg.createRoot(em).render(M.jsx(k.StrictMode,{children:M.jsx(k1,{})}));
|
package/dist/client/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Arcane Agents</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-DNXJVqF0.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-CWU29xaz.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
@@ -13,12 +13,16 @@ const statusMonitor_1 = require("../status/statusMonitor");
|
|
|
13
13
|
const tmuxAdapter_1 = require("../tmux/tmuxAdapter");
|
|
14
14
|
const realtimeHub_1 = require("../ws/realtimeHub");
|
|
15
15
|
const terminalBridge_1 = require("../ws/terminalBridge");
|
|
16
|
-
async function createServerContext() {
|
|
17
|
-
const paths = (0, loadConfig_1.getArcaneAgentsPaths)();
|
|
16
|
+
async function createServerContext(sessionName) {
|
|
17
|
+
const paths = (0, loadConfig_1.getArcaneAgentsPaths)(sessionName);
|
|
18
18
|
node_fs_1.default.mkdirSync(paths.configDir, { recursive: true });
|
|
19
19
|
node_fs_1.default.mkdirSync(paths.stateDir, { recursive: true });
|
|
20
20
|
node_fs_1.default.mkdirSync(paths.cacheDir, { recursive: true });
|
|
21
21
|
const baseConfig = (0, loadConfig_1.loadResolvedConfig)(paths);
|
|
22
|
+
if ((0, loadConfig_1.isNonDefaultSession)(sessionName)) {
|
|
23
|
+
const baseTmuxName = baseConfig.backend.tmux.sessionName;
|
|
24
|
+
baseConfig.backend.tmux.sessionName = `${baseTmuxName}-${sessionName}`;
|
|
25
|
+
}
|
|
22
26
|
const discoveryService = new discovery_1.DiscoveryService();
|
|
23
27
|
const initialDiscovery = await discoveryService.discover(baseConfig);
|
|
24
28
|
for (const warning of initialDiscovery.warnings) {
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.bootstrap = bootstrap;
|
|
7
7
|
const node_http_1 = __importDefault(require("node:http"));
|
|
8
8
|
const httpApp_1 = require("./bootstrap/httpApp");
|
|
9
|
+
const loadConfig_1 = require("./config/loadConfig");
|
|
9
10
|
const serverContext_1 = require("./bootstrap/serverContext");
|
|
10
11
|
const shutdown_1 = require("./bootstrap/shutdown");
|
|
11
12
|
const websocketUpgrade_1 = require("./bootstrap/websocketUpgrade");
|
|
@@ -61,10 +62,15 @@ function renderStartupBanner() {
|
|
|
61
62
|
})
|
|
62
63
|
.join("\n");
|
|
63
64
|
}
|
|
64
|
-
async function bootstrap() {
|
|
65
|
+
async function bootstrap(sessionName) {
|
|
65
66
|
console.log(renderStartupBanner());
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
if ((0, loadConfig_1.isNonDefaultSession)(sessionName)) {
|
|
68
|
+
console.log(`[arcane-agents] launching Arcane Agents (session: ${sessionName})...`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.log("[arcane-agents] launching Arcane Agents...");
|
|
72
|
+
}
|
|
73
|
+
const context = await (0, serverContext_1.createServerContext)(sessionName);
|
|
68
74
|
context.statusMonitor.start();
|
|
69
75
|
const app = (0, httpApp_1.createHttpApp)(context);
|
|
70
76
|
const server = node_http_1.default.createServer(app);
|
|
@@ -7,13 +7,41 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
const node_child_process_1 = require("node:child_process");
|
|
8
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_readline_1 = __importDefault(require("node:readline"));
|
|
10
11
|
const bootstrapApp_1 = require("./bootstrapApp");
|
|
11
12
|
const loadConfig_1 = require("./config/loadConfig");
|
|
12
13
|
const appRoot_1 = require("./utils/appRoot");
|
|
13
14
|
const path_1 = require("./utils/path");
|
|
15
|
+
function extractSessionFlag(args) {
|
|
16
|
+
const remaining = [...args];
|
|
17
|
+
let sessionName;
|
|
18
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
19
|
+
if (remaining[i] === "--session" || remaining[i] === "-s") {
|
|
20
|
+
const value = remaining[i + 1];
|
|
21
|
+
if (!value || value.startsWith("-")) {
|
|
22
|
+
console.error("[arcane-agents] --session requires a name argument.");
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
sessionName = value;
|
|
26
|
+
remaining.splice(i, 2);
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
const eqMatch = remaining[i].match(/^(?:--session|-s)=(.+)$/);
|
|
30
|
+
if (eqMatch) {
|
|
31
|
+
sessionName = eqMatch[1];
|
|
32
|
+
remaining.splice(i, 1);
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (sessionName !== undefined && !/^[a-zA-Z0-9_-]+$/.test(sessionName)) {
|
|
37
|
+
console.error("[arcane-agents] session name must only contain letters, digits, hyphens, and underscores.");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
return { sessionName, remainingArgs: remaining };
|
|
41
|
+
}
|
|
14
42
|
async function runCli() {
|
|
15
43
|
(0, appRoot_1.setAppRoot)((0, appRoot_1.resolveAppRoot)());
|
|
16
|
-
const args = process.argv.slice(2);
|
|
44
|
+
const { sessionName, remainingArgs: args } = extractSessionFlag(process.argv.slice(2));
|
|
17
45
|
const firstArg = args[0];
|
|
18
46
|
if (firstArg === "--help" || firstArg === "-h") {
|
|
19
47
|
printHelp();
|
|
@@ -26,13 +54,15 @@ async function runCli() {
|
|
|
26
54
|
const [command = "start", ...commandArgs] = args;
|
|
27
55
|
switch (command) {
|
|
28
56
|
case "start":
|
|
29
|
-
return runStart();
|
|
57
|
+
return runStart(sessionName);
|
|
30
58
|
case "init":
|
|
31
59
|
return runInit(commandArgs);
|
|
32
60
|
case "config":
|
|
33
61
|
return runConfig(commandArgs);
|
|
34
62
|
case "doctor":
|
|
35
63
|
return runDoctor();
|
|
64
|
+
case "sessions":
|
|
65
|
+
return runSessions(commandArgs);
|
|
36
66
|
case "help":
|
|
37
67
|
printHelp();
|
|
38
68
|
return 0;
|
|
@@ -45,11 +75,11 @@ async function runCli() {
|
|
|
45
75
|
return 1;
|
|
46
76
|
}
|
|
47
77
|
}
|
|
48
|
-
async function runStart() {
|
|
78
|
+
async function runStart(sessionName) {
|
|
49
79
|
if (!process.env.NODE_ENV) {
|
|
50
80
|
process.env.NODE_ENV = "production";
|
|
51
81
|
}
|
|
52
|
-
const paths = (0, loadConfig_1.getArcaneAgentsPaths)();
|
|
82
|
+
const paths = (0, loadConfig_1.getArcaneAgentsPaths)(sessionName);
|
|
53
83
|
let configResult;
|
|
54
84
|
try {
|
|
55
85
|
configResult = ensureStarterConfig(paths);
|
|
@@ -63,7 +93,7 @@ async function runStart() {
|
|
|
63
93
|
console.log(`[arcane-agents] no config found; wrote starter config to ${paths.configPath}`);
|
|
64
94
|
console.log("[arcane-agents] next: edit it with 'arcane-agents config edit'.");
|
|
65
95
|
}
|
|
66
|
-
await (0, bootstrapApp_1.bootstrap)();
|
|
96
|
+
await (0, bootstrapApp_1.bootstrap)(sessionName);
|
|
67
97
|
return 0;
|
|
68
98
|
}
|
|
69
99
|
function runInit(args) {
|
|
@@ -235,6 +265,85 @@ Commands:
|
|
|
235
265
|
help Show this config help message
|
|
236
266
|
`);
|
|
237
267
|
}
|
|
268
|
+
async function runSessions(args) {
|
|
269
|
+
const [subcommand = "list", ...subcommandArgs] = args;
|
|
270
|
+
switch (subcommand) {
|
|
271
|
+
case "list":
|
|
272
|
+
break;
|
|
273
|
+
case "delete":
|
|
274
|
+
return runSessionsDelete(subcommandArgs);
|
|
275
|
+
default:
|
|
276
|
+
console.error(`[arcane-agents] unknown sessions command '${subcommand}'.`);
|
|
277
|
+
console.log("Usage: arcane-agents sessions [list|delete <name>]");
|
|
278
|
+
return 1;
|
|
279
|
+
}
|
|
280
|
+
const defaultPaths = (0, loadConfig_1.getArcaneAgentsPaths)();
|
|
281
|
+
const sessionsDir = node_path_1.default.join(defaultPaths.stateDir, "sessions");
|
|
282
|
+
const defaultDbPath = defaultPaths.dbPath;
|
|
283
|
+
const sessions = [];
|
|
284
|
+
if (node_fs_1.default.existsSync(defaultDbPath)) {
|
|
285
|
+
sessions.push("default");
|
|
286
|
+
}
|
|
287
|
+
if (node_fs_1.default.existsSync(sessionsDir)) {
|
|
288
|
+
try {
|
|
289
|
+
const entries = node_fs_1.default.readdirSync(sessionsDir, { withFileTypes: true });
|
|
290
|
+
for (const entry of entries) {
|
|
291
|
+
if (entry.isDirectory()) {
|
|
292
|
+
const dbPath = node_path_1.default.join(sessionsDir, entry.name, "arcane-agents.db");
|
|
293
|
+
if (node_fs_1.default.existsSync(dbPath)) {
|
|
294
|
+
sessions.push(entry.name);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
// no-op
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (sessions.length === 0) {
|
|
304
|
+
console.log("[arcane-agents] no sessions found.");
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
console.log("[arcane-agents] sessions:");
|
|
308
|
+
for (const session of sessions) {
|
|
309
|
+
console.log(` ${session}`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
async function runSessionsDelete(args) {
|
|
315
|
+
const name = args[0];
|
|
316
|
+
if (!name) {
|
|
317
|
+
console.error("[arcane-agents] usage: arcane-agents sessions delete <name>");
|
|
318
|
+
return 1;
|
|
319
|
+
}
|
|
320
|
+
if (name === "default") {
|
|
321
|
+
console.error("[arcane-agents] cannot delete the default session.");
|
|
322
|
+
return 1;
|
|
323
|
+
}
|
|
324
|
+
const sessionDir = (0, loadConfig_1.getArcaneAgentsPaths)(name).stateDir;
|
|
325
|
+
if (!node_fs_1.default.existsSync(sessionDir)) {
|
|
326
|
+
console.error(`[arcane-agents] session '${name}' not found.`);
|
|
327
|
+
return 1;
|
|
328
|
+
}
|
|
329
|
+
const answer = await promptConfirm(`Delete session '${name}' and all its data (${sessionDir})? [y/N] `);
|
|
330
|
+
if (!answer) {
|
|
331
|
+
console.log("[arcane-agents] aborted.");
|
|
332
|
+
return 0;
|
|
333
|
+
}
|
|
334
|
+
node_fs_1.default.rmSync(sessionDir, { recursive: true, force: true });
|
|
335
|
+
console.log(`[arcane-agents] deleted session '${name}'.`);
|
|
336
|
+
return 0;
|
|
337
|
+
}
|
|
338
|
+
function promptConfirm(question) {
|
|
339
|
+
const rl = node_readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
340
|
+
return new Promise((resolve) => {
|
|
341
|
+
rl.question(question, (answer) => {
|
|
342
|
+
rl.close();
|
|
343
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
}
|
|
238
347
|
function runDoctor() {
|
|
239
348
|
const checks = [];
|
|
240
349
|
const nodeVersion = process.versions.node;
|
|
@@ -357,9 +466,10 @@ function printHelp() {
|
|
|
357
466
|
console.log(`Arcane Agents CLI
|
|
358
467
|
|
|
359
468
|
Usage:
|
|
360
|
-
arcane-agents [start]
|
|
469
|
+
arcane-agents [start] [--session <name>]
|
|
361
470
|
arcane-agents init [--force]
|
|
362
471
|
arcane-agents config [path|show|edit]
|
|
472
|
+
arcane-agents sessions [list|delete <name>]
|
|
363
473
|
arcane-agents doctor
|
|
364
474
|
arcane-agents --help
|
|
365
475
|
arcane-agents --version
|
|
@@ -368,10 +478,16 @@ Commands:
|
|
|
368
478
|
start Start the Arcane Agents server
|
|
369
479
|
init Write ~/.config/arcane-agents/config.yaml from config.example.yaml
|
|
370
480
|
config Print, show, or edit config files
|
|
481
|
+
sessions List or delete named sessions
|
|
371
482
|
doctor Check dependencies and runtime command availability
|
|
372
483
|
help Show this help message
|
|
373
484
|
version Print CLI version
|
|
374
485
|
|
|
486
|
+
Options:
|
|
487
|
+
--session <name>, -s <name>
|
|
488
|
+
Run with a named session (separate DB and tmux session).
|
|
489
|
+
Default session uses the standard paths for backwards compatibility.
|
|
490
|
+
|
|
375
491
|
Config paths:
|
|
376
492
|
primary: ${paths.configPath}
|
|
377
493
|
local override: ${paths.localOverridePath}
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isNonDefaultSession = isNonDefaultSession;
|
|
6
7
|
exports.getArcaneAgentsPaths = getArcaneAgentsPaths;
|
|
7
8
|
exports.loadResolvedConfig = loadResolvedConfig;
|
|
8
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
@@ -10,9 +11,16 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
10
11
|
const yaml_1 = __importDefault(require("yaml"));
|
|
11
12
|
const schema_1 = require("./schema");
|
|
12
13
|
const path_1 = require("../utils/path");
|
|
13
|
-
function
|
|
14
|
+
function isNonDefaultSession(sessionName) {
|
|
15
|
+
return sessionName !== undefined && sessionName !== "default";
|
|
16
|
+
}
|
|
17
|
+
function getArcaneAgentsPaths(sessionName) {
|
|
14
18
|
const configDir = (0, path_1.resolveUserPath)("~/.config/arcane-agents");
|
|
15
|
-
const
|
|
19
|
+
const baseStateDir = (0, path_1.resolveUserPath)("~/.local/state/arcane-agents");
|
|
20
|
+
const isNamedSession = sessionName !== undefined && sessionName !== "default";
|
|
21
|
+
const stateDir = isNamedSession
|
|
22
|
+
? node_path_1.default.join(baseStateDir, "sessions", sessionName)
|
|
23
|
+
: baseStateDir;
|
|
16
24
|
return {
|
|
17
25
|
configDir,
|
|
18
26
|
configPath: node_path_1.default.join(configDir, "config.yaml"),
|