arcane-agents 1.1.0 → 1.2.1
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-BCnWppkv.js +83 -0
- package/dist/client/assets/{index-CWU29xaz.css → index-Di_KBFPW.css} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/server/bootstrap/serverContext.js +9 -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/dist/server/server/http/routes/registerApiRoutes.js +10 -0
- package/dist/server/server/orchestrator/orchestratorService.js +29 -0
- package/dist/server/server/orchestrator/orchestratorService.test.js +58 -0
- package/dist/server/server/status/activityParser.js +1 -1
- package/dist/server/server/status/engine/signalContext.js +18 -6
- package/dist/server/server/status/engine/stateMachine/constants.js +5 -1
- package/dist/server/server/status/engine/stateMachine/decision.js +17 -0
- package/dist/server/server/status/engine/stateMachine/decision.test.js +63 -0
- package/dist/server/server/status/engine/stateMachine/helpers.js +4 -1
- package/dist/server/server/status/engine/stateMachine/helpers.test.js +6 -0
- package/dist/server/server/status/engine/stateMachine/idleBlockers.js +13 -0
- package/dist/server/server/status/engine/stateMachine/workingEvidence.js +23 -0
- package/dist/server/server/status/runtime/activityTextExtractors.js +40 -0
- package/dist/server/server/status/runtime/claudeSignals.js +114 -33
- package/dist/server/server/status/runtime/claudeSignals.test.js +23 -0
- package/dist/server/server/status/runtime/codexSignals.js +132 -0
- package/dist/server/server/status/runtime/codexSignals.test.js +47 -0
- package/dist/server/server/status/runtime/runtimeProcess.js +86 -0
- package/dist/server/server/status/runtime/sessionDetection.js +15 -0
- package/dist/server/server/status/runtimeSignals.js +8 -1
- package/dist/server/server/status/statusEvaluator.js +2 -1
- package/dist/server/server/status/statusMonitor.js +8 -1
- package/dist/server/server/status/statusMonitor.test.js +6 -0
- package/dist/server/server/status/statusPipeline.js +9 -1
- package/dist/server/server/ws/terminalBridge.js +11 -0
- package/package.json +1 -1
- package/dist/client/assets/index-HfXsReQG.js +0 -83
|
@@ -29,4 +29,4 @@
|
|
|
29
29
|
* The original design remains. The terminal itself
|
|
30
30
|
* has been extended to include xterm CSI codes, among
|
|
31
31
|
* other features.
|
|
32
|
-
*/.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;inset:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;inset:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{font-family:monospace;user-select:text;white-space:pre}.xterm .xterm-accessibility-tree>div{transform-origin:left;width:fit-content}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;background:#0000;transition:opacity .1s linear;z-index:11}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{position:absolute;display:none}.xterm .xterm-scrollable-element>.shadow.top{display:block;top:0;left:3px;height:3px;width:100%;box-shadow:var(--vscode-scrollbar-shadow, #000) 0 6px 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.left{display:block;top:3px;left:0;height:100%;width:3px;box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.top-left-corner{display:block;top:0;left:0;height:3px;width:3px}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}:root{color-scheme:dark;font-family:Trebuchet MS,Segoe UI,sans-serif;background:#0f1815;color:#e9f0df}*{box-sizing:border-box}html,body,#root{margin:0;width:100%;height:100%}body{background:radial-gradient(circle at 15% 10%,#2d5c4c,#162821 45%,#0d1412)}.app-shell{width:100%;height:100%;display:grid;grid-template-columns:minmax(380px,1.55fr) minmax(360px,1fr);gap:0;padding:10px}.layout-divider{position:relative;cursor:col-resize;touch-action:none}.layout-divider:before{content:"";position:absolute;top:8px;bottom:8px;left:50%;width:2px;border-radius:999px;transform:translate(-50%);background:#d6e6ba3d;transition:background-color .11s ease}.layout-divider:hover:before,.layout-divider.layout-divider-active:before{background:#eaf3d6ad}body.split-pane-dragging{cursor:col-resize;user-select:none}.map-column{min-height:0;display:flex;flex-direction:column;border:1px solid rgba(218,235,186,.22);border-radius:14px;overflow:hidden;background:#12221b99}.map-container{position:relative;flex:1;min-height:0}.map-canvas{width:100%;height:100%;display:block;cursor:pointer}.map-canvas:focus{outline:none}.map-tooltip{position:absolute;z-index:4;min-width:190px;max-width:280px;padding:8px 10px;font-size:12px;line-height:1.4;color:#f4f6e7;background:#0b100ee0;border:1px solid rgba(223,234,189,.22);border-radius:8px;pointer-events:none}.map-tooltip-title{font-weight:700;margin-bottom:2px}.bottom-bar{display:flex;align-items:center;gap:8px;height:74px;flex-shrink:0;padding:11px;overflow:hidden;border-top:1px solid rgba(218,235,186,.18);background:linear-gradient(180deg,#101a16eb,#0d1412f2)}.bar-btn{border:1px solid rgba(207,224,181,.34);border-radius:9px;background:#2b4336f2;color:#f0f5e3;padding:9px 13px;font-weight:600;cursor:pointer;transition:transform 90ms ease,background-color 90ms ease}.bar-btn:hover{transform:translateY(-1px);background:#3d5b49f2}.bar-btn:disabled{opacity:.5;cursor:not-allowed;transform:none}.bar-btn.subtle{background:#202a25f2}.bar-btn.danger{background:#682c2cf2;border-color:#dc8c828c}.bar-btn.accent{font-size:20px;line-height:1;padding:7px 13px}.selected-worker-meta{margin-right:auto;min-width:0}.selected-worker-name{font-weight:700;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.selected-worker-subline{font-size:12px;color:#eef3dfcc;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.terminal-column{min-height:0;border:1px solid rgba(218,235,186,.22);border-radius:14px;overflow:hidden;display:flex;flex-direction:column;background:#0c1311eb}.terminal-column.terminal-column-selected{border-color:#ecf2d45c}.terminal-column.terminal-column-focused{border-color:#88e9ffdb;box-shadow:0 0 0 1px #37adca73,0 0 22px #1a627338}.terminal-header{min-height:52px;padding:14px 16px;font-weight:700;border-bottom:1px solid rgba(218,235,186,.18);display:flex;align-items:center;justify-content:space-between;gap:10px}.terminal-header-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.terminal-ready-chip{flex:0 0 auto;border:1px solid rgba(255,233,164,.9);border-radius:999px;background:linear-gradient(110deg,#c1922dfa,#f6d273fa,#a77d22fa);background-size:220% 100%;color:#2f2205;padding:3px 10px;font-size:11px;font-weight:700;line-height:1.25;letter-spacing:.03em;box-shadow:inset 0 1px #fff8df8c,0 0 0 1px #70501252;animation:ready-badge-sheen 1.9s linear infinite}.terminal-open-external{border:1px solid rgba(207,224,181,.34);border-radius:8px;background:#20342af2;color:#f0f5e3;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;font-size:16px;line-height:1;cursor:pointer}.terminal-open-external:hover{background:#345041f2}.terminal-open-external:disabled{opacity:.45;cursor:not-allowed}.terminal-panel{flex:1;min-height:0;padding:8px}.worker-roster{flex:1;min-height:0;padding:10px;display:flex;flex-direction:column;gap:8px;overflow:auto}.worker-roster-section-label{margin:4px 2px 2px;font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#e5edd3c2}.worker-roster-item{border:1px solid rgba(208,224,181,.22);border-radius:9px;background:#14221be6;color:#f1f5e6;text-align:left;padding:9px 10px;cursor:pointer;display:flex;flex-direction:column;gap:2px}.worker-roster-main{display:flex;align-items:center;gap:10px;min-width:0}.worker-roster-avatar{width:42px;height:42px;image-rendering:pixelated;object-fit:contain;flex:0 0 auto}.worker-roster-text{min-width:0;display:flex;flex-direction:column;gap:2px}.worker-roster-item:hover{background:#20352aeb;border-color:#e2edc761}.worker-roster-item.active{border-color:#eff4d4bd;background:#355040eb}.worker-roster-item.worker-roster-item-summon{background:#1c2a22e6}.worker-roster-item.worker-roster-item-summon:hover{background:#283f32eb}.worker-roster-item.worker-roster-item-summon.active{border-color:#9de5b0d6;background:#345643f0}.worker-roster-summon-avatar{border-radius:10px;border:1px solid rgba(170,227,186,.44);background:#284636f2;padding:2px}.worker-roster-summon-glyph{width:42px;height:42px;border-radius:10px;border:1px solid rgba(170,227,186,.44);background:#284636f2;color:#e8f7de;display:inline-flex;align-items:center;justify-content:center;font-size:22px;font-weight:700;flex:0 0 auto}.worker-roster-name{min-width:0;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.worker-roster-name-row{min-width:0;display:flex;align-items:center;gap:6px}.worker-complete-badge{flex:0 0 auto;border-radius:999px;border:1px solid rgba(252,229,153,.88);background:linear-gradient(115deg,#b28426fa,#f4d06efa,#9e751dfa);background-size:220% 100%;color:#2d2106;display:inline-flex;align-items:center;justify-content:center;padding:1px 6px;font-size:9px;font-weight:700;letter-spacing:.05em;line-height:1.25;box-shadow:inset 0 1px #fff8df80;animation:ready-badge-sheen 2.05s linear infinite}.worker-roster-item.active .worker-complete-badge{border-color:#fff0b8f5;background:linear-gradient(115deg,#c4952cfc,#ffdd7efc,#ab8022fc);color:#2b1f05}@keyframes ready-badge-sheen{0%{background-position:200% 50%}to{background-position:-20% 50%}}.worker-roster-meta{font-size:12px;color:#e6ecd8c7}.worker-roster-activity{font-size:12px;color:#eef4e1db}.worker-roster-empty{border:1px dashed rgba(207,223,182,.28);border-radius:8px;padding:12px;color:#e8f0dad1;font-size:13px}.rally-command-card{margin-top:4px;padding:10px;border:1px solid rgba(185,225,198,.36);border-radius:10px;background:linear-gradient(180deg,#13261ff5,#0f1f1af5);display:flex;flex-direction:column;gap:8px}.rally-command-header{display:flex;align-items:center;justify-content:space-between;gap:10px}.rally-command-title{font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#e4f0cfeb}.rally-command-count{font-size:11px;color:#ddeac7b8}.rally-command-input{font-family:Consolas,Monaco,Lucida Console,monospace;min-height:78px;line-height:1.35;resize:vertical}.rally-command-actions{display:flex;align-items:center;justify-content:space-between;gap:10px}.rally-command-hint{font-size:11px;color:#d9e5c2c7}.rally-command-result{font-size:12px;color:#e9f1d5e6}.overlay{position:fixed;inset:0;z-index:25;display:grid;place-items:center;background:#050a08ad;backdrop-filter:blur(2px)}.overlay-no-blur{backdrop-filter:none}.dialog{width:min(880px,calc(100vw - 34px));max-height:min(78vh,740px);overflow:auto;padding:16px;border-radius:12px;border:1px solid rgba(222,236,193,.26);background:linear-gradient(180deg,#141e1af7,#0d1311fa)}.dialog-title{margin-bottom:12px;font-size:20px;font-weight:700}.input,.palette-input{width:100%;border-radius:9px;border:1px solid rgba(218,235,186,.32);background:#090f0df2;color:#ecf3e1;padding:10px 12px;font-size:14px}.dialog-grid{margin-top:12px;display:grid;grid-template-columns:1fr 1fr;gap:12px}.dialog-section-label{font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px;color:#e6ecd8d6}.option-list{display:flex;flex-direction:column;gap:6px;max-height:340px;overflow:auto}.option-btn,.palette-item{border:1px solid rgba(208,224,181,.22);border-radius:8px;background:#14221be6;color:#f1f5e6;text-align:left;padding:8px 10px;cursor:pointer;display:flex;flex-direction:column;gap:2px}.option-btn small,.palette-item small{color:#e2ebd2b8;font-size:11px}.option-btn.selected,.palette-item.active{border-color:#f0f3c8c7;background:#354f40eb}.dialog-actions{margin-top:14px;display:flex;justify-content:flex-end;gap:8px}.shortcuts-dialog{width:min(720px,calc(100vw - 34px))}.shortcut-grid{display:grid;grid-template-columns:1fr;gap:8px}.shortcut-row{display:grid;grid-template-columns:140px 1fr;gap:10px;align-items:center;padding:8px 10px;border-radius:8px;border:1px solid rgba(214,229,188,.16);background:#101a15d1}kbd{display:inline-block;min-width:52px;padding:4px 7px;border-radius:6px;border:1px solid rgba(233,242,206,.36);background:#0a0e0ceb;color:#edf4dd;font-size:12px;font-weight:700;text-align:center}.rename-dialog{width:min(540px,calc(100vw - 34px))}.rename-subtitle{margin-top:-2px;margin-bottom:10px;color:#e3ebd3c2;font-size:13px}.rename-form{display:flex;flex-direction:column}.kill-confirm-dialog{width:min(520px,calc(100vw - 34px))}.kill-confirm-copy{color:#e7eed9d9;font-size:14px;line-height:1.4}.kill-confirm-hint{margin-top:8px;color:#d6e0c0c2;font-size:12px}.palette{width:min(760px,calc(100vw - 30px));border-radius:12px;border:1px solid rgba(222,236,193,.26);background:linear-gradient(180deg,#121e18f7,#0d1411fa);padding:12px}.palette-list{margin-top:8px;max-height:410px;overflow:auto;display:flex;flex-direction:column;gap:6px}.palette-empty{padding:12px;font-size:13px;color:#e2ebd2cc}.error-toast{position:fixed;right:14px;bottom:14px;z-index:40;max-width:420px;border:1px solid rgba(242,169,158,.58);background:#4b1713e6;border-radius:8px;padding:10px 12px;cursor:pointer}.batch-spawn-dialog{width:min(640px,calc(100vw - 34px))}.batch-spawn-config-list{margin-top:8px}.batch-spawn-config-summary{display:flex;align-items:center;gap:10px;margin-bottom:10px}.batch-spawn-config-summary small{color:#e2ebd2b8;font-size:12px}.batch-spawn-back{border:1px solid rgba(207,224,181,.34);border-radius:8px;background:#20342af2;color:#f0f5e3;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;font-size:16px;cursor:pointer}.batch-spawn-back:hover{background:#345041f2}.batch-spawn-textarea{min-height:160px;resize:vertical;font-family:Consolas,Monaco,Lucida Console,monospace;line-height:1.4}.batch-spawn-footer{margin-top:12px;display:flex;align-items:center;justify-content:space-between;gap:10px}.batch-spawn-count{font-size:13px;color:#e2ebd2c7}.batch-spawn-progress{display:flex;align-items:center;gap:10px;flex:1}.batch-spawn-progress-bar{flex:1;height:6px;border-radius:3px;background:#daebba2e;overflow:hidden}.batch-spawn-progress-fill{height:100%;border-radius:3px;background:#8cdcaad9;transition:width .15s ease}.batch-spawn-progress-text{font-size:12px;color:#e2ebd2d1;white-space:nowrap}@media(max-width:960px){.app-shell{grid-template-columns:1fr;grid-template-rows:1fr minmax(280px,38vh)}.dialog-grid{grid-template-columns:1fr}}
|
|
32
|
+
*/.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;inset:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;inset:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{font-family:monospace;user-select:text;white-space:pre}.xterm .xterm-accessibility-tree>div{transform-origin:left;width:fit-content}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;background:#0000;transition:opacity .1s linear;z-index:11}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{position:absolute;display:none}.xterm .xterm-scrollable-element>.shadow.top{display:block;top:0;left:3px;height:3px;width:100%;box-shadow:var(--vscode-scrollbar-shadow, #000) 0 6px 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.left{display:block;top:3px;left:0;height:100%;width:3px;box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}.xterm .xterm-scrollable-element>.shadow.top-left-corner{display:block;top:0;left:0;height:3px;width:3px}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset}:root{color-scheme:dark;font-family:Trebuchet MS,Segoe UI,sans-serif;background:#0f1815;color:#e9f0df}*{box-sizing:border-box}html,body,#root{margin:0;width:100%;height:100%}body{background:radial-gradient(circle at 15% 10%,#2d5c4c,#162821 45%,#0d1412)}.app-shell{width:100%;height:100%;display:grid;grid-template-columns:minmax(380px,1.55fr) minmax(360px,1fr);gap:0;padding:10px}.layout-divider{position:relative;cursor:col-resize;touch-action:none}.layout-divider:before{content:"";position:absolute;top:8px;bottom:8px;left:50%;width:2px;border-radius:999px;transform:translate(-50%);background:#d6e6ba3d;transition:background-color .11s ease}.layout-divider:hover:before,.layout-divider.layout-divider-active:before{background:#eaf3d6ad}body.split-pane-dragging{cursor:col-resize;user-select:none}.map-column{min-height:0;display:flex;flex-direction:column;border:1px solid rgba(218,235,186,.22);border-radius:14px;overflow:hidden;background:#12221b99}.map-container{position:relative;flex:1;min-height:0}.map-canvas{width:100%;height:100%;display:block;cursor:pointer}.map-canvas:focus{outline:none}.map-tooltip{position:absolute;z-index:4;min-width:190px;max-width:280px;padding:8px 10px;font-size:12px;line-height:1.4;color:#f4f6e7;background:#0b100ee0;border:1px solid rgba(223,234,189,.22);border-radius:8px;pointer-events:none}.map-tooltip-title{font-weight:700;margin-bottom:2px}.bottom-bar{display:flex;align-items:center;gap:8px;height:74px;flex-shrink:0;padding:11px;overflow:hidden;border-top:1px solid rgba(218,235,186,.18);background:linear-gradient(180deg,#101a16eb,#0d1412f2)}.bar-btn{border:1px solid rgba(207,224,181,.34);border-radius:9px;background:#2b4336f2;color:#f0f5e3;padding:9px 13px;font-weight:600;cursor:pointer;transition:transform 90ms ease,background-color 90ms ease}.bar-btn:hover{transform:translateY(-1px);background:#3d5b49f2}.bar-btn:disabled{opacity:.5;cursor:not-allowed;transform:none}.bar-btn.subtle{background:#202a25f2}.bar-btn.danger{background:#682c2cf2;border-color:#dc8c828c}.bar-btn.accent{font-size:20px;line-height:1;padding:7px 13px}.selected-worker-meta{margin-right:auto;min-width:0}.selected-worker-name{font-weight:700;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.selected-worker-subline{font-size:12px;color:#eef3dfcc;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.terminal-column{min-height:0;border:1px solid rgba(218,235,186,.22);border-radius:14px;overflow:hidden;display:flex;flex-direction:column;background:#0c1311eb}.terminal-column.terminal-column-selected{border-color:#ecf2d45c}.terminal-column.terminal-column-focused{border-color:#88e9ffdb;box-shadow:0 0 0 1px #37adca73,0 0 22px #1a627338}.terminal-header{min-height:52px;padding:14px 16px;font-weight:700;border-bottom:1px solid rgba(218,235,186,.18);display:flex;align-items:center;justify-content:space-between;gap:10px}.terminal-header-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.terminal-ready-chip{flex:0 0 auto;border:1px solid rgba(255,233,164,.9);border-radius:999px;background:linear-gradient(110deg,#c1922dfa,#f6d273fa,#a77d22fa);background-size:220% 100%;color:#2f2205;padding:3px 10px;font-size:11px;font-weight:700;line-height:1.25;letter-spacing:.03em;box-shadow:inset 0 1px #fff8df8c,0 0 0 1px #70501252;animation:ready-badge-sheen 1.9s linear infinite}.terminal-open-external{border:1px solid rgba(207,224,181,.34);border-radius:8px;background:#20342af2;color:#f0f5e3;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;font-size:16px;line-height:1;cursor:pointer}.terminal-open-external:hover{background:#345041f2}.terminal-open-external:disabled{opacity:.45;cursor:not-allowed}.terminal-panel{flex:1;min-height:0;padding:8px}.worker-roster{flex:1;min-height:0;padding:10px;display:flex;flex-direction:column;gap:8px;overflow:auto}.worker-roster-section-label{margin:4px 2px 2px;font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#e5edd3c2}.worker-roster-item{border:1px solid rgba(208,224,181,.22);border-radius:9px;background:#14221be6;color:#f1f5e6;text-align:left;padding:9px 10px;cursor:pointer;display:flex;flex-direction:column;gap:2px}.worker-roster-main{display:flex;align-items:center;gap:10px;min-width:0}.worker-roster-avatar{width:42px;height:42px;image-rendering:pixelated;object-fit:contain;flex:0 0 auto}.worker-roster-text{min-width:0;display:flex;flex-direction:column;gap:2px}.worker-roster-item:hover{background:#20352aeb;border-color:#e2edc761}.worker-roster-item.active{border-color:#eff4d4bd;background:#355040eb}.worker-roster-item.worker-roster-item-summon{background:#1c2a22e6}.worker-roster-item.worker-roster-item-summon:hover{background:#283f32eb}.worker-roster-item.worker-roster-item-summon.active{border-color:#9de5b0d6;background:#345643f0}.worker-roster-summon-avatar{border-radius:10px;border:1px solid rgba(170,227,186,.44);background:#284636f2;padding:2px}.worker-roster-summon-glyph{width:42px;height:42px;border-radius:10px;border:1px solid rgba(170,227,186,.44);background:#284636f2;color:#e8f7de;display:inline-flex;align-items:center;justify-content:center;font-size:22px;font-weight:700;flex:0 0 auto}.worker-roster-name{min-width:0;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.worker-roster-name-row{min-width:0;display:flex;align-items:center;gap:6px}.worker-complete-badge{flex:0 0 auto;border-radius:999px;border:1px solid rgba(252,229,153,.88);background:linear-gradient(115deg,#b28426fa,#f4d06efa,#9e751dfa);background-size:220% 100%;color:#2d2106;display:inline-flex;align-items:center;justify-content:center;padding:1px 6px;font-size:9px;font-weight:700;letter-spacing:.05em;line-height:1.25;box-shadow:inset 0 1px #fff8df80;animation:ready-badge-sheen 2.05s linear infinite}.worker-roster-item.active .worker-complete-badge{border-color:#fff0b8f5;background:linear-gradient(115deg,#c4952cfc,#ffdd7efc,#ab8022fc);color:#2b1f05}@keyframes ready-badge-sheen{0%{background-position:200% 50%}to{background-position:-20% 50%}}.worker-roster-meta{font-size:12px;color:#e6ecd8c7}.worker-roster-activity{font-size:12px;color:#eef4e1db}.worker-roster-empty{border:1px dashed rgba(207,223,182,.28);border-radius:8px;padding:12px;color:#e8f0dad1;font-size:13px}.rally-command-card{margin-top:4px;padding:10px;border:1px solid rgba(185,225,198,.36);border-radius:10px;background:linear-gradient(180deg,#13261ff5,#0f1f1af5);display:flex;flex-direction:column;gap:8px}.rally-command-header{display:flex;align-items:center;justify-content:space-between;gap:10px}.rally-command-title{font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#e4f0cfeb}.rally-command-count{font-size:11px;color:#ddeac7b8}.rally-command-input{font-family:Consolas,Monaco,Lucida Console,monospace;min-height:78px;line-height:1.35;resize:vertical}.rally-command-actions{display:flex;align-items:center;justify-content:space-between;gap:10px}.rally-command-hint{font-size:11px;color:#d9e5c2c7}.rally-command-result{font-size:12px;color:#e9f1d5e6}.overlay{position:fixed;inset:0;z-index:25;display:grid;place-items:center;background:#050a08ad;backdrop-filter:blur(2px)}.overlay-no-blur{backdrop-filter:none}.dialog{width:min(880px,calc(100vw - 34px));max-height:min(78vh,740px);overflow:auto;padding:16px;border-radius:12px;border:1px solid rgba(222,236,193,.26);background:linear-gradient(180deg,#141e1af7,#0d1311fa)}.dialog-title{margin-bottom:12px;font-size:20px;font-weight:700}.input,.palette-input{width:100%;border-radius:9px;border:1px solid rgba(218,235,186,.32);background:#090f0df2;color:#ecf3e1;padding:10px 12px;font-size:14px}.dialog-grid{margin-top:12px;display:grid;grid-template-columns:1fr 1fr;gap:12px}.dialog-section-label{font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px;color:#e6ecd8d6}.option-list{display:flex;flex-direction:column;gap:6px;max-height:340px;overflow:auto}.option-btn,.palette-item{border:1px solid rgba(208,224,181,.22);border-radius:8px;background:#14221be6;color:#f1f5e6;text-align:left;padding:8px 10px;cursor:pointer;display:flex;flex-direction:column;gap:2px}.option-btn small,.palette-item small{color:#e2ebd2b8;font-size:11px}.option-btn.selected,.palette-item.active{border-color:#f0f3c8c7;background:#354f40eb}.dialog-actions{margin-top:14px;display:flex;justify-content:flex-end;gap:8px}.shortcuts-dialog{width:min(720px,calc(100vw - 34px))}.shortcut-grid{display:grid;grid-template-columns:1fr;gap:8px}.shortcut-row{display:grid;grid-template-columns:140px 1fr;gap:10px;align-items:center;padding:8px 10px;border-radius:8px;border:1px solid rgba(214,229,188,.16);background:#101a15d1}kbd{display:inline-block;min-width:52px;padding:4px 7px;border-radius:6px;border:1px solid rgba(233,242,206,.36);background:#0a0e0ceb;color:#edf4dd;font-size:12px;font-weight:700;text-align:center}.rename-dialog{width:min(540px,calc(100vw - 34px))}.rename-subtitle{margin-top:-2px;margin-bottom:10px;color:#e3ebd3c2;font-size:13px}.rename-form{display:flex;flex-direction:column}.confirm-dialog{width:min(520px,calc(100vw - 34px))}.confirm-copy{color:#e7eed9d9;font-size:14px;line-height:1.4}.confirm-hint{margin-top:8px;color:#d6e0c0c2;font-size:12px}.palette{width:min(760px,calc(100vw - 30px));border-radius:12px;border:1px solid rgba(222,236,193,.26);background:linear-gradient(180deg,#121e18f7,#0d1411fa);padding:12px}.palette-list{margin-top:8px;max-height:410px;overflow:auto;display:flex;flex-direction:column;gap:6px}.palette-empty{padding:12px;font-size:13px;color:#e2ebd2cc}.error-toast{position:fixed;right:14px;bottom:14px;z-index:40;max-width:420px;border:1px solid rgba(242,169,158,.58);background:#4b1713e6;border-radius:8px;padding:10px 12px;cursor:pointer}.batch-spawn-dialog{width:min(640px,calc(100vw - 34px))}.batch-spawn-config-list{margin-top:8px}.batch-spawn-config-summary{display:flex;align-items:center;gap:10px;margin-bottom:10px}.batch-spawn-config-summary small{color:#e2ebd2b8;font-size:12px}.batch-spawn-back{border:1px solid rgba(207,224,181,.34);border-radius:8px;background:#20342af2;color:#f0f5e3;width:30px;height:30px;display:inline-flex;align-items:center;justify-content:center;font-size:16px;cursor:pointer}.batch-spawn-back:hover{background:#345041f2}.batch-spawn-textarea{min-height:160px;resize:vertical;font-family:Consolas,Monaco,Lucida Console,monospace;line-height:1.4}.batch-spawn-footer{margin-top:12px;display:flex;align-items:center;justify-content:space-between;gap:10px}.batch-spawn-count{font-size:13px;color:#e2ebd2c7}.batch-spawn-progress{display:flex;align-items:center;gap:10px;flex:1}.batch-spawn-progress-bar{flex:1;height:6px;border-radius:3px;background:#daebba2e;overflow:hidden}.batch-spawn-progress-fill{height:100%;border-radius:3px;background:#8cdcaad9;transition:width .15s ease}.batch-spawn-progress-text{font-size:12px;color:#e2ebd2d1;white-space:nowrap}@media(max-width:960px){.app-shell{grid-template-columns:1fr;grid-template-rows:1fr minmax(280px,38vh)}.dialog-grid{grid-template-columns:1fr}}
|
package/dist/client/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
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-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-BCnWppkv.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Di_KBFPW.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
|
@@ -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) {
|
|
@@ -45,6 +49,9 @@ async function createServerContext() {
|
|
|
45
49
|
const terminalBridge = new terminalBridge_1.TerminalBridge(workers, {
|
|
46
50
|
onSubmittedInput: () => {
|
|
47
51
|
statusMonitor.requestPollSoon();
|
|
52
|
+
},
|
|
53
|
+
onTerminalOutput: () => {
|
|
54
|
+
statusMonitor.requestPollSoon(20);
|
|
48
55
|
}
|
|
49
56
|
});
|
|
50
57
|
return {
|
|
@@ -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"),
|
|
@@ -76,6 +76,16 @@ function registerApiRoutes(app, { orchestrator, hub, statusMonitor }) {
|
|
|
76
76
|
(0, errorResponse_1.handleRequestError)(res, error);
|
|
77
77
|
}
|
|
78
78
|
});
|
|
79
|
+
app.post("/api/workers/:workerId/restart", async (req, res) => {
|
|
80
|
+
try {
|
|
81
|
+
const worker = await orchestrator.restart(req.params.workerId);
|
|
82
|
+
hub.broadcast({ type: "worker-updated", worker });
|
|
83
|
+
res.json(worker);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
(0, errorResponse_1.handleRequestError)(res, error);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
79
89
|
app.patch("/api/workers/:workerId/position", (req, res) => {
|
|
80
90
|
try {
|
|
81
91
|
const x = Number(req.body?.x);
|
|
@@ -102,6 +102,35 @@ class OrchestratorService {
|
|
|
102
102
|
alreadyStopped: !removed
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
|
+
async restart(workerId) {
|
|
106
|
+
const worker = this.requireWorker(workerId);
|
|
107
|
+
try {
|
|
108
|
+
await this.tmux.stop(worker.tmuxRef);
|
|
109
|
+
const tmuxRef = await this.tmux.spawnWorker({
|
|
110
|
+
workerId: worker.id,
|
|
111
|
+
windowName: worker.name,
|
|
112
|
+
projectPath: worker.projectPath,
|
|
113
|
+
command: worker.command,
|
|
114
|
+
projectId: worker.projectId,
|
|
115
|
+
runtimeId: worker.runtimeId,
|
|
116
|
+
runtimeLabel: worker.runtimeLabel
|
|
117
|
+
});
|
|
118
|
+
const restarted = {
|
|
119
|
+
...worker,
|
|
120
|
+
status: "idle",
|
|
121
|
+
activityText: undefined,
|
|
122
|
+
activityTool: undefined,
|
|
123
|
+
activityPath: undefined,
|
|
124
|
+
tmuxRef,
|
|
125
|
+
updatedAt: new Date().toISOString()
|
|
126
|
+
};
|
|
127
|
+
this.workers.saveWorker(restarted);
|
|
128
|
+
return restarted;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
throw (0, appError_1.conflictError)(`Failed to restart agent '${workerId}'.`, "worker_restart_failed");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
105
134
|
updatePosition(workerId, position) {
|
|
106
135
|
const updated = this.workers.updatePosition(workerId, position);
|
|
107
136
|
if (!updated) {
|
|
@@ -112,3 +112,61 @@ function createWorker() {
|
|
|
112
112
|
(0, vitest_1.expect)(workers.deleteWorker).not.toHaveBeenCalled();
|
|
113
113
|
});
|
|
114
114
|
});
|
|
115
|
+
(0, vitest_1.describe)("OrchestratorService.restart", () => {
|
|
116
|
+
(0, vitest_1.it)("restarts tmux in place and preserves the worker record", async () => {
|
|
117
|
+
const worker = createWorker();
|
|
118
|
+
const workers = {
|
|
119
|
+
getWorker: vitest_1.vi.fn(() => worker),
|
|
120
|
+
saveWorker: vitest_1.vi.fn()
|
|
121
|
+
};
|
|
122
|
+
const nextTmuxRef = { session: "arcane-agents", window: "worker-1", pane: "%7" };
|
|
123
|
+
const tmux = {
|
|
124
|
+
stop: vitest_1.vi.fn(async () => undefined),
|
|
125
|
+
spawnWorker: vitest_1.vi.fn(async () => nextTmuxRef)
|
|
126
|
+
};
|
|
127
|
+
const service = new orchestratorService_1.OrchestratorService(createConfig(), workers, tmux);
|
|
128
|
+
const result = await service.restart(worker.id);
|
|
129
|
+
(0, vitest_1.expect)(tmux.stop).toHaveBeenCalledWith(worker.tmuxRef);
|
|
130
|
+
(0, vitest_1.expect)(tmux.spawnWorker).toHaveBeenCalledWith({
|
|
131
|
+
workerId: worker.id,
|
|
132
|
+
windowName: worker.name,
|
|
133
|
+
projectPath: worker.projectPath,
|
|
134
|
+
command: worker.command,
|
|
135
|
+
projectId: worker.projectId,
|
|
136
|
+
runtimeId: worker.runtimeId,
|
|
137
|
+
runtimeLabel: worker.runtimeLabel
|
|
138
|
+
});
|
|
139
|
+
(0, vitest_1.expect)(result).toMatchObject({
|
|
140
|
+
...worker,
|
|
141
|
+
status: "idle",
|
|
142
|
+
tmuxRef: nextTmuxRef,
|
|
143
|
+
activityText: undefined,
|
|
144
|
+
activityTool: undefined,
|
|
145
|
+
activityPath: undefined,
|
|
146
|
+
updatedAt: vitest_1.expect.any(String)
|
|
147
|
+
});
|
|
148
|
+
(0, vitest_1.expect)(workers.saveWorker).toHaveBeenCalledWith(result);
|
|
149
|
+
const stopCallOrder = tmux.stop.mock.invocationCallOrder[0] ?? 0;
|
|
150
|
+
const spawnCallOrder = tmux.spawnWorker.mock.invocationCallOrder[0] ?? 0;
|
|
151
|
+
(0, vitest_1.expect)(stopCallOrder).toBeLessThan(spawnCallOrder);
|
|
152
|
+
});
|
|
153
|
+
(0, vitest_1.it)("surfaces a conflict when restart fails", async () => {
|
|
154
|
+
const worker = createWorker();
|
|
155
|
+
const workers = {
|
|
156
|
+
getWorker: vitest_1.vi.fn(() => worker),
|
|
157
|
+
saveWorker: vitest_1.vi.fn()
|
|
158
|
+
};
|
|
159
|
+
const tmux = {
|
|
160
|
+
stop: vitest_1.vi.fn(async () => {
|
|
161
|
+
throw new Error("tmux failure");
|
|
162
|
+
}),
|
|
163
|
+
spawnWorker: vitest_1.vi.fn()
|
|
164
|
+
};
|
|
165
|
+
const service = new orchestratorService_1.OrchestratorService(createConfig(), workers, tmux);
|
|
166
|
+
await (0, vitest_1.expect)(service.restart(worker.id)).rejects.toMatchObject({
|
|
167
|
+
status: 409,
|
|
168
|
+
code: "worker_restart_failed"
|
|
169
|
+
});
|
|
170
|
+
(0, vitest_1.expect)(workers.saveWorker).not.toHaveBeenCalled();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
@@ -12,7 +12,7 @@ const toolMatchers = [
|
|
|
12
12
|
{ tool: "task", label: "Subtask", regex: /\b(Task|subagent|agent)\b/i },
|
|
13
13
|
{ tool: "todo", label: "Planning", regex: /\b(TodoWrite|todo\b)\b/i },
|
|
14
14
|
{ tool: "web", label: "Fetching", regex: /\b(WebFetch|http|https)\b/i },
|
|
15
|
-
{ tool: "terminal", label: "Terminal", regex: /\b(claude|terminal|tmux)\b/i }
|
|
15
|
+
{ tool: "terminal", label: "Terminal", regex: /\b(claude|codex|terminal|tmux)\b/i }
|
|
16
16
|
];
|
|
17
17
|
const inputPromptLineMatchers = [
|
|
18
18
|
/\[(?:Y\/n|y\/N|y\/n|N\/y)\]\s*$/i,
|
|
@@ -3,17 +3,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.buildWorkerStatusSignalContext = buildWorkerStatusSignalContext;
|
|
4
4
|
const activityParser_1 = require("../activityParser");
|
|
5
5
|
const runtimeSignals_1 = require("../runtimeSignals");
|
|
6
|
-
function buildWorkerStatusSignalContext({ worker, currentCommand, output, observation, transcriptSnapshot, nowMs, interactiveCommands }) {
|
|
6
|
+
function buildWorkerStatusSignalContext({ worker, currentCommand, output, observation, transcriptSnapshot, runtimeProcess, nowMs, interactiveCommands }) {
|
|
7
7
|
const parsed = (0, activityParser_1.parseActivity)(currentCommand, output);
|
|
8
8
|
const commandLower = currentCommand.toLowerCase();
|
|
9
|
-
const
|
|
9
|
+
const wrappedRuntime = runtimeProcess?.runtime;
|
|
10
|
+
const isClaude = wrappedRuntime === "claude" || (0, runtimeSignals_1.isLikelyClaudeSession)(worker, commandLower);
|
|
11
|
+
const claudeSignals = (0, runtimeSignals_1.detectClaudeSignals)(output);
|
|
10
12
|
const openCodeSignals = (0, runtimeSignals_1.detectOpenCodeSignals)(output);
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
const codexSignals = (0, runtimeSignals_1.detectCodexSignals)(output);
|
|
14
|
+
const isOpenCode = wrappedRuntime === "opencode" || (0, runtimeSignals_1.isLikelyOpenCodeSession)(worker, commandLower) || openCodeSignals.prompt || openCodeSignals.active;
|
|
15
|
+
const isCodex = wrappedRuntime === "codex" || (0, runtimeSignals_1.isLikelyCodexSession)(worker, commandLower) || codexSignals.prompt || codexSignals.active;
|
|
16
|
+
const runtimeActivityText = (0, runtimeSignals_1.extractRuntimeActivityText)(output, { isClaude, isOpenCode, isCodex });
|
|
17
|
+
const activeClaudeTask = isClaude ? (0, runtimeSignals_1.extractClaudeActiveTask)(output) : undefined;
|
|
18
|
+
const hasClaudePromptSignal = isClaude && claudeSignals.prompt;
|
|
19
|
+
const hasClaudeProgressSignal = isClaude && claudeSignals.active;
|
|
15
20
|
const openCodePromptSignal = isOpenCode && openCodeSignals.prompt;
|
|
16
21
|
const openCodeActiveSignal = isOpenCode && openCodeSignals.active;
|
|
22
|
+
const codexPromptSignal = isCodex && codexSignals.prompt;
|
|
23
|
+
const codexActiveSignal = isCodex && codexSignals.active;
|
|
17
24
|
const outputQuietForMs = Math.max(0, nowMs - observation.lastOutputChangeAtMs);
|
|
18
25
|
const commandQuietForMs = Math.max(0, nowMs - observation.lastCommandChangeAtMs);
|
|
19
26
|
const createdAtMs = Date.parse(worker.createdAt);
|
|
@@ -29,11 +36,16 @@ function buildWorkerStatusSignalContext({ worker, currentCommand, output, observ
|
|
|
29
36
|
parsed,
|
|
30
37
|
runtimeActivityText,
|
|
31
38
|
activeClaudeTask,
|
|
39
|
+
activeRuntimeProcess: runtimeProcess,
|
|
40
|
+
hasClaudePromptSignal,
|
|
32
41
|
hasClaudeProgressSignal,
|
|
33
42
|
hasOpenCodePromptSignal: openCodePromptSignal,
|
|
34
43
|
hasOpenCodeActiveSignal: openCodeActiveSignal,
|
|
44
|
+
hasCodexPromptSignal: codexPromptSignal,
|
|
45
|
+
hasCodexActiveSignal: codexActiveSignal,
|
|
35
46
|
isClaudeSession: isClaude,
|
|
36
47
|
isOpenCodeSession: isOpenCode,
|
|
48
|
+
isCodexSession: isCodex,
|
|
37
49
|
outputQuietForMs,
|
|
38
50
|
commandQuietForMs,
|
|
39
51
|
workerAgeMs,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.recoverableToolErrorMatchers = exports.fatalRuntimeErrorMatchers = exports.openCodeWorkingFreshWindowMs = exports.claudeWorkingFreshWindowMs = exports.genericWorkingFreshWindowMs = exports.openCodeSpawnGraceMs = exports.claudeSpawnGraceMs = exports.cachedActivityWindowMs = exports.stickyWorkingWindowMs = exports.commandWarmupWindowMs = exports.recentErrorSignalWindowMs = exports.parsedStrongEvidenceWindowMs = void 0;
|
|
3
|
+
exports.recoverableToolErrorMatchers = exports.fatalRuntimeErrorMatchers = exports.codexWorkingFreshWindowMs = exports.openCodeWorkingFreshWindowMs = exports.claudeWorkingFreshWindowMs = exports.genericWorkingFreshWindowMs = exports.codexSpawnGraceMs = exports.openCodeSpawnGraceMs = exports.claudeSpawnGraceMs = exports.cachedActivityWindowMs = exports.stickyWorkingWindowMs = exports.commandWarmupWindowMs = exports.recentErrorSignalWindowMs = exports.parsedStrongEvidenceWindowMs = void 0;
|
|
4
4
|
const parsedStrongEvidenceWindowMs = 8_000;
|
|
5
5
|
exports.parsedStrongEvidenceWindowMs = parsedStrongEvidenceWindowMs;
|
|
6
6
|
const recentErrorSignalWindowMs = 15_000;
|
|
@@ -15,12 +15,16 @@ const claudeSpawnGraceMs = 5_000;
|
|
|
15
15
|
exports.claudeSpawnGraceMs = claudeSpawnGraceMs;
|
|
16
16
|
const openCodeSpawnGraceMs = 5_000;
|
|
17
17
|
exports.openCodeSpawnGraceMs = openCodeSpawnGraceMs;
|
|
18
|
+
const codexSpawnGraceMs = 5_000;
|
|
19
|
+
exports.codexSpawnGraceMs = codexSpawnGraceMs;
|
|
18
20
|
const genericWorkingFreshWindowMs = 12_000;
|
|
19
21
|
exports.genericWorkingFreshWindowMs = genericWorkingFreshWindowMs;
|
|
20
22
|
const claudeWorkingFreshWindowMs = 10_000;
|
|
21
23
|
exports.claudeWorkingFreshWindowMs = claudeWorkingFreshWindowMs;
|
|
22
24
|
const openCodeWorkingFreshWindowMs = 12_000;
|
|
23
25
|
exports.openCodeWorkingFreshWindowMs = openCodeWorkingFreshWindowMs;
|
|
26
|
+
const codexWorkingFreshWindowMs = 10_000;
|
|
27
|
+
exports.codexWorkingFreshWindowMs = codexWorkingFreshWindowMs;
|
|
24
28
|
const fatalRuntimeErrorMatchers = [
|
|
25
29
|
/^traceback\b/i,
|
|
26
30
|
/^unhandled(?:\s+\w+)?\s+exception\b/i,
|
|
@@ -26,6 +26,18 @@ function deriveWorkerStatusDecision(context) {
|
|
|
26
26
|
parsedStrongSignal: false
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
|
+
if (context.hasCodexPromptSignal && !context.hasCodexActiveSignal && !(0, helpers_1.isInteractiveCommand)(context)) {
|
|
30
|
+
pushReason({ code: "codex-approval-prompt", message: "Codex is waiting on an approval or question response." });
|
|
31
|
+
return finalizeDecision(context, {
|
|
32
|
+
status: "attention",
|
|
33
|
+
activityText: context.runtimeActivityText ?? context.parsed.activity.text ?? "Waiting for approval",
|
|
34
|
+
activityTool: "terminal",
|
|
35
|
+
activityPath: undefined,
|
|
36
|
+
confidence: 0.94,
|
|
37
|
+
reasons,
|
|
38
|
+
parsedStrongSignal: false
|
|
39
|
+
});
|
|
40
|
+
}
|
|
29
41
|
if (context.parsed.activity.needsInput && !(0, helpers_1.isInteractiveCommand)(context)) {
|
|
30
42
|
pushReason({ code: "parser-input-prompt", message: "Terminal output indicates input is required." });
|
|
31
43
|
return finalizeDecision(context, {
|
|
@@ -200,10 +212,15 @@ function finalizeDecision(context, partial) {
|
|
|
200
212
|
workerAgeMs: context.workerAgeMs,
|
|
201
213
|
isClaudeSession: context.isClaudeSession,
|
|
202
214
|
isOpenCodeSession: context.isOpenCodeSession,
|
|
215
|
+
isCodexSession: context.isCodexSession,
|
|
216
|
+
hasClaudePromptSignal: context.hasClaudePromptSignal,
|
|
203
217
|
hasOpenCodePromptSignal: context.hasOpenCodePromptSignal,
|
|
204
218
|
hasOpenCodeActiveSignal: context.hasOpenCodeActiveSignal,
|
|
219
|
+
hasCodexPromptSignal: context.hasCodexPromptSignal,
|
|
220
|
+
hasCodexActiveSignal: context.hasCodexActiveSignal,
|
|
205
221
|
hasClaudeProgressSignal: context.hasClaudeProgressSignal,
|
|
206
222
|
hasActiveClaudeTask: Boolean(context.activeClaudeTask),
|
|
223
|
+
hasActiveRuntimeProcess: Boolean(context.activeRuntimeProcess),
|
|
207
224
|
hasRuntimeActivityText: Boolean(context.runtimeActivityText),
|
|
208
225
|
hasParsedStrongSignal: partial.parsedStrongSignal,
|
|
209
226
|
hasParsedNeedsInput: context.parsed.activity.needsInput,
|