@oddessentials/agent-guild 0.1.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/LICENSE +21 -0
- package/README.md +162 -0
- package/bin/agent-guild-report.mjs +134 -0
- package/bin/agent-guild.mjs +240 -0
- package/config/providers.default.json +124 -0
- package/examples/claude-code-settings.json +48 -0
- package/examples/codex-hooks.json +45 -0
- package/examples/gemini-settings.json +61 -0
- package/examples/grok-hooks.json +55 -0
- package/examples/providers.json +27 -0
- package/node_modules/@xterm/addon-fit/LICENSE +19 -0
- package/node_modules/@xterm/addon-fit/README.md +24 -0
- package/node_modules/@xterm/addon-fit/lib/addon-fit.js +2 -0
- package/node_modules/@xterm/addon-fit/lib/addon-fit.js.map +1 -0
- package/node_modules/@xterm/addon-fit/lib/addon-fit.mjs +18 -0
- package/node_modules/@xterm/addon-fit/lib/addon-fit.mjs.map +7 -0
- package/node_modules/@xterm/addon-fit/package.json +26 -0
- package/node_modules/@xterm/addon-fit/src/FitAddon.ts +92 -0
- package/node_modules/@xterm/addon-fit/typings/addon-fit.d.ts +55 -0
- package/node_modules/@xterm/addon-serialize/README.md +42 -0
- package/node_modules/@xterm/addon-serialize/lib/addon-serialize.js +2 -0
- package/node_modules/@xterm/addon-serialize/lib/addon-serialize.js.map +1 -0
- package/node_modules/@xterm/addon-serialize/lib/addon-serialize.mjs +19 -0
- package/node_modules/@xterm/addon-serialize/lib/addon-serialize.mjs.map +7 -0
- package/node_modules/@xterm/addon-serialize/package.json +29 -0
- package/node_modules/@xterm/addon-serialize/src/SerializeAddon.ts +695 -0
- package/node_modules/@xterm/addon-serialize/typings/addon-serialize.d.ts +116 -0
- package/node_modules/@xterm/addon-web-links/LICENSE +19 -0
- package/node_modules/@xterm/addon-web-links/README.md +21 -0
- package/node_modules/@xterm/addon-web-links/lib/addon-web-links.js +2 -0
- package/node_modules/@xterm/addon-web-links/lib/addon-web-links.js.map +1 -0
- package/node_modules/@xterm/addon-web-links/lib/addon-web-links.mjs +18 -0
- package/node_modules/@xterm/addon-web-links/lib/addon-web-links.mjs.map +7 -0
- package/node_modules/@xterm/addon-web-links/package.json +26 -0
- package/node_modules/@xterm/addon-web-links/src/WebLinkProvider.ts +199 -0
- package/node_modules/@xterm/addon-web-links/src/WebLinksAddon.ts +58 -0
- package/node_modules/@xterm/addon-web-links/typings/addon-web-links.d.ts +57 -0
- package/node_modules/@xterm/headless/README.md +31 -0
- package/node_modules/@xterm/headless/lib-headless/xterm-headless.js +2 -0
- package/node_modules/@xterm/headless/lib-headless/xterm-headless.js.map +1 -0
- package/node_modules/@xterm/headless/lib-headless/xterm-headless.mjs +46 -0
- package/node_modules/@xterm/headless/lib-headless/xterm-headless.mjs.map +7 -0
- package/node_modules/@xterm/headless/package.json +29 -0
- package/node_modules/@xterm/headless/typings/xterm-headless.d.ts +1385 -0
- package/node_modules/@xterm/xterm/LICENSE +21 -0
- package/node_modules/@xterm/xterm/README.md +243 -0
- package/node_modules/@xterm/xterm/css/xterm.css +285 -0
- package/node_modules/@xterm/xterm/lib/xterm.js +2 -0
- package/node_modules/@xterm/xterm/lib/xterm.js.map +1 -0
- package/node_modules/@xterm/xterm/lib/xterm.mjs +53 -0
- package/node_modules/@xterm/xterm/lib/xterm.mjs.map +7 -0
- package/node_modules/@xterm/xterm/package.json +111 -0
- package/node_modules/@xterm/xterm/src/browser/AccessibilityManager.ts +435 -0
- package/node_modules/@xterm/xterm/src/browser/Clipboard.ts +93 -0
- package/node_modules/@xterm/xterm/src/browser/ColorContrastCache.ts +34 -0
- package/node_modules/@xterm/xterm/src/browser/CoreBrowserTerminal.ts +1339 -0
- package/node_modules/@xterm/xterm/src/browser/Linkifier.ts +403 -0
- package/node_modules/@xterm/xterm/src/browser/LocalizableStrings.ts +23 -0
- package/node_modules/@xterm/xterm/src/browser/OscLinkProvider.ts +129 -0
- package/node_modules/@xterm/xterm/src/browser/RenderDebouncer.ts +84 -0
- package/node_modules/@xterm/xterm/src/browser/TimeBasedDebouncer.ts +86 -0
- package/node_modules/@xterm/xterm/src/browser/Types.ts +226 -0
- package/node_modules/@xterm/xterm/src/browser/Viewport.ts +192 -0
- package/node_modules/@xterm/xterm/src/browser/decorations/BufferDecorationRenderer.ts +139 -0
- package/node_modules/@xterm/xterm/src/browser/decorations/ColorZoneStore.ts +117 -0
- package/node_modules/@xterm/xterm/src/browser/decorations/OverviewRulerRenderer.ts +214 -0
- package/node_modules/@xterm/xterm/src/browser/input/CompositionHelper.ts +248 -0
- package/node_modules/@xterm/xterm/src/browser/input/Mouse.ts +54 -0
- package/node_modules/@xterm/xterm/src/browser/input/MoveToCell.ts +251 -0
- package/node_modules/@xterm/xterm/src/browser/public/Terminal.ts +275 -0
- package/node_modules/@xterm/xterm/src/browser/renderer/dom/DomRenderer.ts +542 -0
- package/node_modules/@xterm/xterm/src/browser/renderer/dom/DomRendererRowFactory.ts +546 -0
- package/node_modules/@xterm/xterm/src/browser/renderer/dom/WidthCache.ts +167 -0
- package/node_modules/@xterm/xterm/src/browser/renderer/shared/Constants.ts +6 -0
- package/node_modules/@xterm/xterm/src/browser/renderer/shared/RendererUtils.ts +95 -0
- package/node_modules/@xterm/xterm/src/browser/renderer/shared/SelectionRenderModel.ts +93 -0
- package/node_modules/@xterm/xterm/src/browser/renderer/shared/Types.ts +84 -0
- package/node_modules/@xterm/xterm/src/browser/selection/SelectionModel.ts +144 -0
- package/node_modules/@xterm/xterm/src/browser/selection/Types.ts +15 -0
- package/node_modules/@xterm/xterm/src/browser/services/CharSizeService.ts +127 -0
- package/node_modules/@xterm/xterm/src/browser/services/CharacterJoinerService.ts +339 -0
- package/node_modules/@xterm/xterm/src/browser/services/CoreBrowserService.ts +137 -0
- package/node_modules/@xterm/xterm/src/browser/services/LinkProviderService.ts +28 -0
- package/node_modules/@xterm/xterm/src/browser/services/MouseService.ts +46 -0
- package/node_modules/@xterm/xterm/src/browser/services/RenderService.ts +376 -0
- package/node_modules/@xterm/xterm/src/browser/services/SelectionService.ts +1039 -0
- package/node_modules/@xterm/xterm/src/browser/services/Services.ts +158 -0
- package/node_modules/@xterm/xterm/src/browser/services/ThemeService.ts +198 -0
- package/node_modules/@xterm/xterm/src/browser/shared/Constants.ts +8 -0
- package/node_modules/@xterm/xterm/src/common/CircularList.ts +241 -0
- package/node_modules/@xterm/xterm/src/common/Clone.ts +23 -0
- package/node_modules/@xterm/xterm/src/common/Color.ts +376 -0
- package/node_modules/@xterm/xterm/src/common/CoreTerminal.ts +283 -0
- package/node_modules/@xterm/xterm/src/common/InputHandler.ts +3495 -0
- package/node_modules/@xterm/xterm/src/common/MultiKeyMap.ts +42 -0
- package/node_modules/@xterm/xterm/src/common/Platform.ts +44 -0
- package/node_modules/@xterm/xterm/src/common/SortedList.ts +194 -0
- package/node_modules/@xterm/xterm/src/common/TaskQueue.ts +166 -0
- package/node_modules/@xterm/xterm/src/common/TypedArrayUtils.ts +17 -0
- package/node_modules/@xterm/xterm/src/common/Types.ts +552 -0
- package/node_modules/@xterm/xterm/src/common/WindowsMode.ts +27 -0
- package/node_modules/@xterm/xterm/src/common/buffer/AttributeData.ts +211 -0
- package/node_modules/@xterm/xterm/src/common/buffer/Buffer.ts +662 -0
- package/node_modules/@xterm/xterm/src/common/buffer/BufferLine.ts +551 -0
- package/node_modules/@xterm/xterm/src/common/buffer/BufferRange.ts +13 -0
- package/node_modules/@xterm/xterm/src/common/buffer/BufferReflow.ts +226 -0
- package/node_modules/@xterm/xterm/src/common/buffer/BufferSet.ts +134 -0
- package/node_modules/@xterm/xterm/src/common/buffer/CellData.ts +94 -0
- package/node_modules/@xterm/xterm/src/common/buffer/Constants.ts +157 -0
- package/node_modules/@xterm/xterm/src/common/buffer/Marker.ts +43 -0
- package/node_modules/@xterm/xterm/src/common/buffer/Types.ts +52 -0
- package/node_modules/@xterm/xterm/src/common/data/Charsets.ts +256 -0
- package/node_modules/@xterm/xterm/src/common/data/EscapeSequences.ts +153 -0
- package/node_modules/@xterm/xterm/src/common/input/Keyboard.ts +373 -0
- package/node_modules/@xterm/xterm/src/common/input/TextDecoder.ts +346 -0
- package/node_modules/@xterm/xterm/src/common/input/UnicodeV6.ts +145 -0
- package/node_modules/@xterm/xterm/src/common/input/WriteBuffer.ts +247 -0
- package/node_modules/@xterm/xterm/src/common/input/XParseColor.ts +80 -0
- package/node_modules/@xterm/xterm/src/common/parser/Constants.ts +58 -0
- package/node_modules/@xterm/xterm/src/common/parser/DcsParser.ts +192 -0
- package/node_modules/@xterm/xterm/src/common/parser/EscapeSequenceParser.ts +792 -0
- package/node_modules/@xterm/xterm/src/common/parser/OscParser.ts +238 -0
- package/node_modules/@xterm/xterm/src/common/parser/Params.ts +229 -0
- package/node_modules/@xterm/xterm/src/common/parser/Types.ts +275 -0
- package/node_modules/@xterm/xterm/src/common/public/AddonManager.ts +53 -0
- package/node_modules/@xterm/xterm/src/common/public/BufferApiView.ts +35 -0
- package/node_modules/@xterm/xterm/src/common/public/BufferLineApiView.ts +29 -0
- package/node_modules/@xterm/xterm/src/common/public/BufferNamespaceApi.ts +36 -0
- package/node_modules/@xterm/xterm/src/common/public/ParserApi.ts +37 -0
- package/node_modules/@xterm/xterm/src/common/public/UnicodeApi.ts +27 -0
- package/node_modules/@xterm/xterm/src/common/services/BufferService.ts +154 -0
- package/node_modules/@xterm/xterm/src/common/services/CharsetService.ts +34 -0
- package/node_modules/@xterm/xterm/src/common/services/CoreMouseService.ts +365 -0
- package/node_modules/@xterm/xterm/src/common/services/CoreService.ts +92 -0
- package/node_modules/@xterm/xterm/src/common/services/DecorationService.ts +140 -0
- package/node_modules/@xterm/xterm/src/common/services/InstantiationService.ts +85 -0
- package/node_modules/@xterm/xterm/src/common/services/LogService.ts +124 -0
- package/node_modules/@xterm/xterm/src/common/services/OptionsService.ts +212 -0
- package/node_modules/@xterm/xterm/src/common/services/OscLinkService.ts +115 -0
- package/node_modules/@xterm/xterm/src/common/services/ServiceRegistry.ts +49 -0
- package/node_modules/@xterm/xterm/src/common/services/Services.ts +396 -0
- package/node_modules/@xterm/xterm/src/common/services/UnicodeService.ts +111 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/browser.ts +141 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/canIUse.ts +49 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/dom.ts +2369 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/fastDomNode.ts +316 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/globalPointerMoveMonitor.ts +112 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/iframe.ts +135 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/keyboardEvent.ts +213 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/mouseEvent.ts +229 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/touch.ts +372 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +303 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +114 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +720 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +165 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +114 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +243 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +118 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +116 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/ui/widget.ts +57 -0
- package/node_modules/@xterm/xterm/src/vs/base/browser/window.ts +14 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/arrays.ts +887 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/arraysFind.ts +202 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/assert.ts +71 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/async.ts +1992 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/cancellation.ts +148 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/charCode.ts +450 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/collections.ts +140 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/decorators.ts +130 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/equals.ts +146 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/errors.ts +303 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/event.ts +1778 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/functional.ts +32 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/hash.ts +316 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/iterator.ts +159 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/keyCodes.ts +526 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/keybindings.ts +284 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/lazy.ts +47 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/lifecycle.ts +801 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/linkedList.ts +142 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/map.ts +202 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/numbers.ts +98 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/observable.ts +76 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/api.ts +31 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/autorun.ts +281 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/base.ts +489 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/debugName.ts +145 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/derived.ts +428 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/lazyObservableValue.ts +146 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/logging.ts +328 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/promise.ts +209 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/observableInternal/utils.ts +610 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/platform.ts +281 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/scrollable.ts +522 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/sequence.ts +34 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/stopwatch.ts +43 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/strings.ts +557 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/symbols.ts +9 -0
- package/node_modules/@xterm/xterm/src/vs/base/common/uint.ts +59 -0
- package/node_modules/@xterm/xterm/src/vs/patches/nls.ts +90 -0
- package/node_modules/@xterm/xterm/src/vs/typings/base-common.d.ts +20 -0
- package/node_modules/@xterm/xterm/src/vs/typings/require.d.ts +42 -0
- package/node_modules/@xterm/xterm/src/vs/typings/vscode-globals-nls.d.ts +36 -0
- package/node_modules/@xterm/xterm/src/vs/typings/vscode-globals-product.d.ts +33 -0
- package/node_modules/@xterm/xterm/typings/xterm.d.ts +1957 -0
- package/node_modules/node-addon-api/LICENSE.md +9 -0
- package/node_modules/node-addon-api/README.md +319 -0
- package/node_modules/node-addon-api/common.gypi +20 -0
- package/node_modules/node-addon-api/except.gypi +25 -0
- package/node_modules/node-addon-api/index.js +12 -0
- package/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
- package/node_modules/node-addon-api/napi-inl.h +6607 -0
- package/node_modules/node-addon-api/napi.h +3201 -0
- package/node_modules/node-addon-api/node_addon_api.gyp +32 -0
- package/node_modules/node-addon-api/node_api.gyp +9 -0
- package/node_modules/node-addon-api/noexcept.gypi +26 -0
- package/node_modules/node-addon-api/nothing.c +0 -0
- package/node_modules/node-addon-api/package-support.json +21 -0
- package/node_modules/node-addon-api/package.json +480 -0
- package/node_modules/node-addon-api/tools/README.md +73 -0
- package/node_modules/node-addon-api/tools/check-napi.js +99 -0
- package/node_modules/node-addon-api/tools/clang-format.js +71 -0
- package/node_modules/node-addon-api/tools/conversion.js +301 -0
- package/node_modules/node-addon-api/tools/eslint-format.js +79 -0
- package/node_modules/node-pty/LICENSE +69 -0
- package/node_modules/node-pty/README.md +164 -0
- package/node_modules/node-pty/binding.gyp +150 -0
- package/node_modules/node-pty/lib/conpty_console_list_agent.js +25 -0
- package/node_modules/node-pty/lib/eventEmitter2.js +47 -0
- package/node_modules/node-pty/lib/index.js +52 -0
- package/node_modules/node-pty/lib/interfaces.js +7 -0
- package/node_modules/node-pty/lib/shared/conout.js +10 -0
- package/node_modules/node-pty/lib/terminal.js +190 -0
- package/node_modules/node-pty/lib/types.js +7 -0
- package/node_modules/node-pty/lib/unixTerminal.js +351 -0
- package/node_modules/node-pty/lib/utils.js +38 -0
- package/node_modules/node-pty/lib/windowsConoutConnection.js +131 -0
- package/node_modules/node-pty/lib/windowsPtyAgent.js +344 -0
- package/node_modules/node-pty/lib/windowsTerminal.js +209 -0
- package/node_modules/node-pty/lib/worker/conoutSocketWorker.js +22 -0
- package/node_modules/node-pty/package.json +66 -0
- package/node_modules/node-pty/prebuilds/darwin-arm64/pty.node +0 -0
- package/node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper +0 -0
- package/node_modules/node-pty/prebuilds/darwin-x64/pty.node +0 -0
- package/node_modules/node-pty/prebuilds/darwin-x64/spawn-helper +0 -0
- package/node_modules/node-pty/prebuilds/linux-arm64/pty.node +0 -0
- package/node_modules/node-pty/prebuilds/linux-x64/pty.node +0 -0
- package/node_modules/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
- package/node_modules/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
- package/node_modules/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
- package/node_modules/node-pty/prebuilds/win32-arm64/conpty.pdb +0 -0
- package/node_modules/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
- package/node_modules/node-pty/prebuilds/win32-arm64/conpty_console_list.pdb +0 -0
- package/node_modules/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
- package/node_modules/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
- package/node_modules/node-pty/prebuilds/win32-x64/conpty.node +0 -0
- package/node_modules/node-pty/prebuilds/win32-x64/conpty.pdb +0 -0
- package/node_modules/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
- package/node_modules/node-pty/prebuilds/win32-x64/conpty_console_list.pdb +0 -0
- package/node_modules/node-pty/scripts/post-install.js +76 -0
- package/node_modules/node-pty/scripts/prebuild.js +34 -0
- package/node_modules/node-pty/src/unix/pty.cc +876 -0
- package/node_modules/node-pty/src/unix/spawn-helper.cc +23 -0
- package/node_modules/node-pty/src/win/conpty.cc +616 -0
- package/node_modules/node-pty/src/win/conpty.h +41 -0
- package/node_modules/node-pty/src/win/conpty_console_list.cc +44 -0
- package/node_modules/node-pty/src/win/path_util.cc +95 -0
- package/node_modules/node-pty/src/win/path_util.h +26 -0
- package/node_modules/node-pty/third_party/conpty/1.25.260303002/win10-arm64/OpenConsole.exe +0 -0
- package/node_modules/node-pty/third_party/conpty/1.25.260303002/win10-arm64/conpty.dll +0 -0
- package/node_modules/node-pty/third_party/conpty/1.25.260303002/win10-x64/OpenConsole.exe +0 -0
- package/node_modules/node-pty/third_party/conpty/1.25.260303002/win10-x64/conpty.dll +0 -0
- package/node_modules/node-pty/typings/node-pty.d.ts +215 -0
- package/node_modules/ws/LICENSE +20 -0
- package/node_modules/ws/README.md +548 -0
- package/node_modules/ws/browser.js +8 -0
- package/node_modules/ws/index.js +22 -0
- package/node_modules/ws/lib/buffer-util.js +131 -0
- package/node_modules/ws/lib/constants.js +19 -0
- package/node_modules/ws/lib/event-target.js +292 -0
- package/node_modules/ws/lib/extension.js +203 -0
- package/node_modules/ws/lib/limiter.js +55 -0
- package/node_modules/ws/lib/permessage-deflate.js +530 -0
- package/node_modules/ws/lib/receiver.js +743 -0
- package/node_modules/ws/lib/sender.js +607 -0
- package/node_modules/ws/lib/stream.js +161 -0
- package/node_modules/ws/lib/subprotocol.js +62 -0
- package/node_modules/ws/lib/validation.js +152 -0
- package/node_modules/ws/lib/websocket-server.js +562 -0
- package/node_modules/ws/lib/websocket.js +1421 -0
- package/node_modules/ws/package.json +74 -0
- package/node_modules/ws/wrapper.mjs +21 -0
- package/package.json +52 -0
- package/src/manager/command-resolver.mjs +140 -0
- package/src/manager/config.mjs +94 -0
- package/src/manager/install-channels.mjs +268 -0
- package/src/manager/main.mjs +136 -0
- package/src/manager/providers.mjs +601 -0
- package/src/manager/report-shims.mjs +104 -0
- package/src/manager/server.mjs +421 -0
- package/src/manager/session-manager.mjs +306 -0
- package/src/manager/session.mjs +531 -0
- package/src/manager/shell-env.mjs +150 -0
- package/src/manager/usage.mjs +535 -0
- package/src/manager/versions.mjs +62 -0
- package/src/report/hooks.mjs +150 -0
- package/web/app.js +916 -0
- package/web/art/art.css +186 -0
- package/web/art/characters/anthropic/idle.png +0 -0
- package/web/art/characters/anthropic/locked.png +0 -0
- package/web/art/characters/anthropic/working.png +0 -0
- package/web/art/characters/google/idle.png +0 -0
- package/web/art/characters/google/locked.png +0 -0
- package/web/art/characters/google/working.png +0 -0
- package/web/art/characters/openai/idle.png +0 -0
- package/web/art/characters/openai/locked.png +0 -0
- package/web/art/characters/openai/working.png +0 -0
- package/web/art/characters/shell/idle.png +0 -0
- package/web/art/characters/shell/locked.png +0 -0
- package/web/art/characters/shell/working.png +0 -0
- package/web/art/characters/xai/idle.png +0 -0
- package/web/art/characters/xai/locked.png +0 -0
- package/web/art/characters/xai/working.png +0 -0
- package/web/art/familiars/aether.png +0 -0
- package/web/art/familiars/flame.png +0 -0
- package/web/art/familiars/leaf.png +0 -0
- package/web/art/familiars/night.png +0 -0
- package/web/art/icons/anthropic.png +0 -0
- package/web/art/icons/google.png +0 -0
- package/web/art/icons/openai.png +0 -0
- package/web/art/icons/shell.png +0 -0
- package/web/art/icons/xai.png +0 -0
- package/web/art/page.png +0 -0
- package/web/art/ui/frame.png +0 -0
- package/web/art/ui/gem-mana.png +0 -0
- package/web/art/ui/gem-vitality.png +0 -0
- package/web/art/ui/guild-crest.png +0 -0
- package/web/art/ui/level-medallion.png +0 -0
- package/web/art/ui/padlock.png +0 -0
- package/web/icons/README.md +8 -0
- package/web/index.html +154 -0
- package/web/styles.css +226 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
// One terminal session: a node-pty process, a headless terminal that mirrors
|
|
2
|
+
// its screen (so a reconnecting client gets the current screen, not a raw
|
|
3
|
+
// byte replay), and the sub-agents and model the coding tool has reported.
|
|
4
|
+
|
|
5
|
+
import { EventEmitter } from 'node:events';
|
|
6
|
+
import { execFile } from 'node:child_process';
|
|
7
|
+
import crypto from 'node:crypto';
|
|
8
|
+
import pty from 'node-pty';
|
|
9
|
+
import headless from '@xterm/headless';
|
|
10
|
+
import serializeAddon from '@xterm/addon-serialize';
|
|
11
|
+
|
|
12
|
+
const { Terminal } = headless;
|
|
13
|
+
const { SerializeAddon } = serializeAddon;
|
|
14
|
+
|
|
15
|
+
export const OSC_AGENT_CODE = 7777;
|
|
16
|
+
export const OSC_AGENT_PREFIX = 'agent-guild;';
|
|
17
|
+
const AGENT_STATUSES = new Set(['working', 'waiting', 'idle', 'done']);
|
|
18
|
+
const MODEL_SOURCE_RANK = { args: 0, screen: 1, report: 2 };
|
|
19
|
+
const SCREEN_SCAN_DELAY_MS = 400;
|
|
20
|
+
const SCREEN_SCAN_MAX_DELAY_MS = 2000;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Colours reported to programs that query them (OSC 10/11/12), matching the
|
|
24
|
+
* web page's terminal theme. Tools use these to choose light or dark output.
|
|
25
|
+
*/
|
|
26
|
+
export const REPORTED_COLORS = { 10: '#e6e9ef', 11: '#0f1115', 12: '#e6e9ef' };
|
|
27
|
+
|
|
28
|
+
function oscColor(hex) {
|
|
29
|
+
const [r, g, b] = [1, 3, 5].map((i) => hex.slice(i, i + 2));
|
|
30
|
+
return `rgb:${r}${r}/${g}${g}/${b}${b}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Clean a user-supplied session name: a trimmed string of at most 80 chars. */
|
|
34
|
+
export function cleanName(name) {
|
|
35
|
+
return typeof name === 'string' ? name.trim().slice(0, 80) : '';
|
|
36
|
+
}
|
|
37
|
+
const MAX_AGENTS = 64;
|
|
38
|
+
|
|
39
|
+
export function clampDimension(value, fallback, min, max) {
|
|
40
|
+
const n = Math.floor(Number(value));
|
|
41
|
+
if (!Number.isFinite(n)) return fallback;
|
|
42
|
+
return Math.min(max, Math.max(min, n));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class Session extends EventEmitter {
|
|
46
|
+
/**
|
|
47
|
+
* @param {object} opts
|
|
48
|
+
* @param {string} opts.id
|
|
49
|
+
* @param {object} opts.provider provider description (id, vendor, tool, color, ...)
|
|
50
|
+
* @param {{file: string, args: string[]|string}} opts.spawnSpec
|
|
51
|
+
* @param {string} opts.cwd
|
|
52
|
+
* @param {object} opts.env
|
|
53
|
+
* @param {number} opts.cols
|
|
54
|
+
* @param {number} opts.rows
|
|
55
|
+
* @param {string} [opts.name]
|
|
56
|
+
* @param {string|null} [opts.resume] id of the tool's own session being resumed
|
|
57
|
+
* @param {string|null} [opts.task] "install" for a package install, else null
|
|
58
|
+
* @param {string} opts.reportToken
|
|
59
|
+
* @param {number} [opts.scrollback]
|
|
60
|
+
* @param {number} [opts.activityIdleMs]
|
|
61
|
+
* @param {number} [opts.doneAgentLingerMs]
|
|
62
|
+
* @param {number} [opts.killGraceMs] time between hang-up and force kill
|
|
63
|
+
*/
|
|
64
|
+
constructor(opts) {
|
|
65
|
+
super();
|
|
66
|
+
this.id = opts.id;
|
|
67
|
+
this.provider = opts.provider;
|
|
68
|
+
this.name = cleanName(opts.name) || opts.provider.tool;
|
|
69
|
+
this.resume = opts.resume ?? null;
|
|
70
|
+
this.task = opts.task ?? null;
|
|
71
|
+
this.cwd = opts.cwd;
|
|
72
|
+
this.cols = opts.cols;
|
|
73
|
+
this.rows = opts.rows;
|
|
74
|
+
this.reportToken = opts.reportToken;
|
|
75
|
+
this.scrollback = opts.scrollback ?? 5000;
|
|
76
|
+
this.activityIdleMs = opts.activityIdleMs ?? 2500;
|
|
77
|
+
this.doneAgentLingerMs = opts.doneAgentLingerMs ?? 15000;
|
|
78
|
+
this.killGraceMs = opts.killGraceMs ?? 4000;
|
|
79
|
+
this.createdAt = new Date().toISOString();
|
|
80
|
+
this.exitedAt = null;
|
|
81
|
+
this.status = 'running';
|
|
82
|
+
this.exitCode = null;
|
|
83
|
+
this.signal = null;
|
|
84
|
+
this.activity = 'quiet';
|
|
85
|
+
this.lastOutputAt = null;
|
|
86
|
+
this.agents = new Map();
|
|
87
|
+
this.model = null;
|
|
88
|
+
this.modelRegex = null;
|
|
89
|
+
if (opts.provider.modelPattern && this.task === null) {
|
|
90
|
+
try {
|
|
91
|
+
this.modelRegex = new RegExp(opts.provider.modelPattern, 'gi');
|
|
92
|
+
} catch (err) {
|
|
93
|
+
queueMicrotask(() => this.emit('warning', `ignoring modelPattern: ${err.message}`));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
this.subscribers = new Set();
|
|
97
|
+
this._activityTimer = null;
|
|
98
|
+
this._agentTimers = new Map();
|
|
99
|
+
this._killTimer = null;
|
|
100
|
+
this._scanTimer = null;
|
|
101
|
+
this._scanDeadline = null;
|
|
102
|
+
/** Resolves when the process has exited, even after dispose(). */
|
|
103
|
+
this.exited = new Promise((resolve) => { this._resolveExited = resolve; });
|
|
104
|
+
|
|
105
|
+
this.term = new Terminal({
|
|
106
|
+
cols: this.cols,
|
|
107
|
+
rows: this.rows,
|
|
108
|
+
scrollback: this.scrollback,
|
|
109
|
+
allowProposedApi: true,
|
|
110
|
+
});
|
|
111
|
+
this.serializer = new SerializeAddon();
|
|
112
|
+
this.term.loadAddon(this.serializer);
|
|
113
|
+
this.term.parser.registerOscHandler(OSC_AGENT_CODE, (payload) => {
|
|
114
|
+
this._handleOscReport(payload);
|
|
115
|
+
return true;
|
|
116
|
+
});
|
|
117
|
+
// The mirror is the terminal of record: it answers the program's queries
|
|
118
|
+
// (cursor position, device attributes, modes, colours) exactly once,
|
|
119
|
+
// whether zero or several clients are attached. Clients must not answer.
|
|
120
|
+
this.term.onData((reply) => this._reply(reply));
|
|
121
|
+
for (const code of Object.keys(REPORTED_COLORS)) {
|
|
122
|
+
this.term.parser.registerOscHandler(Number(code), (payload) => {
|
|
123
|
+
if (payload !== '?') return false;
|
|
124
|
+
this._reply(`\x1b]${code};${oscColor(REPORTED_COLORS[code])}\x1b\\`);
|
|
125
|
+
return true;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
this.disposed = false;
|
|
130
|
+
try {
|
|
131
|
+
this.pty = pty.spawn(opts.spawnSpec.file, opts.spawnSpec.args, {
|
|
132
|
+
name: 'xterm-256color',
|
|
133
|
+
cols: this.cols,
|
|
134
|
+
rows: this.rows,
|
|
135
|
+
cwd: this.cwd,
|
|
136
|
+
env: opts.env,
|
|
137
|
+
useConpty: true,
|
|
138
|
+
});
|
|
139
|
+
} catch (err) {
|
|
140
|
+
this.term.dispose();
|
|
141
|
+
throw err;
|
|
142
|
+
}
|
|
143
|
+
this.pty.onData((data) => this._onData(data));
|
|
144
|
+
this.pty.onExit(({ exitCode, signal }) => this._onExit(exitCode, signal));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Read live: on Windows node-pty connects the console asynchronously and
|
|
149
|
+
* reports pid 0 until then. The first output announces the session again.
|
|
150
|
+
*/
|
|
151
|
+
get pid() {
|
|
152
|
+
return this.status === 'running' && !this.disposed ? this.pty.pid || null : null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ---- terminal I/O ------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
_onData(data) {
|
|
158
|
+
if (this.disposed) return;
|
|
159
|
+
this.term.write(data);
|
|
160
|
+
this._broadcast({ type: 'data', data });
|
|
161
|
+
this.lastOutputAt = Date.now();
|
|
162
|
+
if (this.modelRegex && this.model?.source !== 'report') this._scheduleModelScan();
|
|
163
|
+
if (this.activity !== 'active') {
|
|
164
|
+
this.activity = 'active';
|
|
165
|
+
this._changed();
|
|
166
|
+
}
|
|
167
|
+
clearTimeout(this._activityTimer);
|
|
168
|
+
this._activityTimer = setTimeout(() => {
|
|
169
|
+
this.activity = 'quiet';
|
|
170
|
+
this._changed();
|
|
171
|
+
}, this.activityIdleMs);
|
|
172
|
+
this._activityTimer.unref?.();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
_onExit(exitCode, signal) {
|
|
176
|
+
clearTimeout(this._killTimer);
|
|
177
|
+
this._resolveExited();
|
|
178
|
+
if (this.disposed) return;
|
|
179
|
+
this.exitedAt ??= new Date().toISOString();
|
|
180
|
+
this.status = 'exited';
|
|
181
|
+
this.exitCode = exitCode ?? null;
|
|
182
|
+
this.signal = signal || null;
|
|
183
|
+
this.activity = 'quiet';
|
|
184
|
+
clearTimeout(this._activityTimer);
|
|
185
|
+
clearTimeout(this._killTimer);
|
|
186
|
+
clearTimeout(this._scanTimer);
|
|
187
|
+
this._clearAgents();
|
|
188
|
+
// Let the headless terminal finish parsing before announcing the exit so
|
|
189
|
+
// any client attaching afterwards still sees the final screen.
|
|
190
|
+
this.term.write('', () => {
|
|
191
|
+
this._broadcast({ type: 'exit', exitCode: this.exitCode, signal: this.signal });
|
|
192
|
+
this._changed();
|
|
193
|
+
this.emit('exit', this);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
_broadcast(message) {
|
|
198
|
+
for (const sub of this.subscribers) {
|
|
199
|
+
if (sub.pending) sub.pending.push(message);
|
|
200
|
+
else sub.send(message);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Attach a client. `send` receives protocol messages. The first message is
|
|
206
|
+
* always a `snapshot` of the current screen; output produced while the
|
|
207
|
+
* snapshot is being built is queued and delivered right after it.
|
|
208
|
+
* Returns a detach function.
|
|
209
|
+
*/
|
|
210
|
+
attach(send) {
|
|
211
|
+
const sub = { send, pending: [] };
|
|
212
|
+
this.subscribers.add(sub);
|
|
213
|
+
// Everything written to the mirror before this marker is in the snapshot;
|
|
214
|
+
// everything after it is queued in sub.pending.
|
|
215
|
+
this.term.write('', () => {
|
|
216
|
+
if (!this.subscribers.has(sub)) return;
|
|
217
|
+
send({
|
|
218
|
+
type: 'snapshot',
|
|
219
|
+
data: this._serializeScreen(),
|
|
220
|
+
cols: this.term.cols,
|
|
221
|
+
rows: this.term.rows,
|
|
222
|
+
session: this.toJSON(),
|
|
223
|
+
});
|
|
224
|
+
const queued = sub.pending;
|
|
225
|
+
sub.pending = null;
|
|
226
|
+
for (const message of queued) send(message);
|
|
227
|
+
if (this.status === 'exited' && !queued.some((m) => m.type === 'exit')) {
|
|
228
|
+
send({ type: 'exit', exitCode: this.exitCode, signal: this.signal });
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
this._changed();
|
|
232
|
+
return () => {
|
|
233
|
+
if (this.subscribers.delete(sub)) this._changed();
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The current screen as a VT stream. The serialize addon restores the
|
|
239
|
+
* buffers and most modes; cursor visibility and the mouse encoding are
|
|
240
|
+
* added here because it omits them.
|
|
241
|
+
*/
|
|
242
|
+
_serializeScreen() {
|
|
243
|
+
let data = this.serializer.serialize({ scrollback: this.scrollback });
|
|
244
|
+
const core = this.term._core;
|
|
245
|
+
if (core?.coreService?.isCursorHidden) data += '\x1b[?25l';
|
|
246
|
+
const encoding = core?.coreMouseService?.activeEncoding;
|
|
247
|
+
if (encoding === 'SGR') data += '\x1b[?1006h';
|
|
248
|
+
else if (encoding === 'SGR_PIXELS') data += '\x1b[?1016h';
|
|
249
|
+
return data;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
write(data) {
|
|
253
|
+
if (this.disposed || this.status !== 'running' || typeof data !== 'string' || data.length === 0) return;
|
|
254
|
+
try { this.pty.write(data); } catch { /* process is exiting */ }
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
_reply(data) {
|
|
258
|
+
this.write(data);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
resize(cols, rows) {
|
|
262
|
+
const c = clampDimension(cols, this.cols, 2, 1000);
|
|
263
|
+
const r = clampDimension(rows, this.rows, 1, 500);
|
|
264
|
+
if (c === this.cols && r === this.rows) return;
|
|
265
|
+
this.cols = c;
|
|
266
|
+
this.rows = r;
|
|
267
|
+
this.term.resize(c, r);
|
|
268
|
+
if (this.status === 'running') {
|
|
269
|
+
try { this.pty.resize(c, r); } catch { /* process may be exiting */ }
|
|
270
|
+
}
|
|
271
|
+
this._broadcast({ type: 'resize', cols: c, rows: r });
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* End the process. On macOS and Linux: hang-up first, force after a grace
|
|
276
|
+
* period. On Windows: end the whole process tree at once. node-pty's own
|
|
277
|
+
* Windows kill first asks a helper process for the console's process list,
|
|
278
|
+
* and when that helper fails it waits a fixed five seconds.
|
|
279
|
+
*/
|
|
280
|
+
kill({ graceMs = this.killGraceMs } = {}) {
|
|
281
|
+
if (this.status !== 'running') return;
|
|
282
|
+
if (process.platform === 'win32') {
|
|
283
|
+
this._killWindowsTree();
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const force = () => { try { this.pty.kill('SIGKILL'); } catch { /* gone */ } };
|
|
287
|
+
try { this.pty.kill('SIGHUP'); } catch { force(); }
|
|
288
|
+
clearTimeout(this._killTimer);
|
|
289
|
+
this._killTimer = setTimeout(force, graceMs);
|
|
290
|
+
this._killTimer.unref?.();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
_killWindowsTree() {
|
|
294
|
+
const fallback = () => {
|
|
295
|
+
if (this.status === 'running') { try { this.pty.kill(); } catch { /* gone */ } }
|
|
296
|
+
};
|
|
297
|
+
const pid = this.pty.pid;
|
|
298
|
+
if (!pid) return fallback();
|
|
299
|
+
execFile('taskkill', ['/PID', String(pid), '/T', '/F'], { windowsHide: true, timeout: 5000 }, (err) => {
|
|
300
|
+
if (err) fallback();
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Detach everything. A still-running process gets the normal hang-up and
|
|
306
|
+
* is force-killed after the grace period if it ignores it.
|
|
307
|
+
*/
|
|
308
|
+
dispose({ graceMs } = {}) {
|
|
309
|
+
if (this.disposed) return;
|
|
310
|
+
this.kill(graceMs === undefined ? {} : { graceMs });
|
|
311
|
+
this.disposed = true;
|
|
312
|
+
clearTimeout(this._activityTimer);
|
|
313
|
+
clearTimeout(this._scanTimer);
|
|
314
|
+
for (const t of this._agentTimers.values()) clearTimeout(t);
|
|
315
|
+
this.subscribers.clear();
|
|
316
|
+
this.term.dispose();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
rename(name) {
|
|
320
|
+
const clean = cleanName(name);
|
|
321
|
+
if (!clean) return;
|
|
322
|
+
this.name = clean;
|
|
323
|
+
this._changed();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// ---- sub-agents --------------------------------------------------------
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Record a report about an agent working inside this session. `status`
|
|
330
|
+
* "done" (or `remove: true`) removes it after a short linger so the UI can
|
|
331
|
+
* show it finishing. Returns the stored agent or null when removed.
|
|
332
|
+
*/
|
|
333
|
+
reportAgent(report, source = 'api') {
|
|
334
|
+
if (!report || typeof report !== 'object') throw badRequest('agent report must be an object');
|
|
335
|
+
if (this.status !== 'running') throw Object.assign(new Error('session has exited'), { status: 409 });
|
|
336
|
+
if (report.finishForeground === true && report.agentId === undefined) {
|
|
337
|
+
// The tool is between turns, so no foreground agent can still be
|
|
338
|
+
// running; one that is never got its end event.
|
|
339
|
+
for (const agent of [...this.agents.values()]) {
|
|
340
|
+
if (agent.foreground && agent.status === 'working') this.reportAgent({ agentId: agent.id, status: 'done' }, source);
|
|
341
|
+
}
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
const id = String(report.agentId ?? report.agent ?? report.id ?? '').trim().slice(0, 128);
|
|
345
|
+
if (!id) throw badRequest('agentId is required');
|
|
346
|
+
|
|
347
|
+
if (report.remove === true) {
|
|
348
|
+
this._removeAgent(id);
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
const status = report.status === undefined ? 'working' : String(report.status);
|
|
352
|
+
if (!AGENT_STATUSES.has(status)) {
|
|
353
|
+
throw badRequest(`status must be one of ${[...AGENT_STATUSES].join(', ')}`);
|
|
354
|
+
}
|
|
355
|
+
const now = new Date().toISOString();
|
|
356
|
+
const existing = this.agents.get(id);
|
|
357
|
+
// A first report that already says done would only flash an icon:
|
|
358
|
+
// Claude Code's internal helpers (prompt suggestions, side questions)
|
|
359
|
+
// stop without ever having started here.
|
|
360
|
+
if (!existing && status === 'done') return null;
|
|
361
|
+
// A repeated done (Grok Build ends a sub-agent's session after its
|
|
362
|
+
// turn) must not restart the linger.
|
|
363
|
+
if (existing?.status === 'done' && status === 'done') return existing;
|
|
364
|
+
if (!existing && this.agents.size >= MAX_AGENTS && !this._evictDoneAgent()) {
|
|
365
|
+
throw badRequest(`too many agents (max ${MAX_AGENTS})`);
|
|
366
|
+
}
|
|
367
|
+
const agent = {
|
|
368
|
+
id,
|
|
369
|
+
name: String(report.name ?? existing?.name ?? id).slice(0, 80),
|
|
370
|
+
kind: String(report.kind ?? existing?.kind ?? 'agent').slice(0, 40),
|
|
371
|
+
status,
|
|
372
|
+
detail: report.detail === undefined ? existing?.detail ?? '' : String(report.detail).slice(0, 200),
|
|
373
|
+
// The parent waits for a foreground agent, so a model reported while
|
|
374
|
+
// it works belongs to the agent, not to the session.
|
|
375
|
+
foreground: report.foreground === undefined ? existing?.foreground ?? false : report.foreground === true,
|
|
376
|
+
startedAt: existing?.startedAt ?? now,
|
|
377
|
+
updatedAt: now,
|
|
378
|
+
source,
|
|
379
|
+
};
|
|
380
|
+
this.agents.set(id, agent);
|
|
381
|
+
clearTimeout(this._agentTimers.get(id));
|
|
382
|
+
this._agentTimers.delete(id);
|
|
383
|
+
if (status === 'done') {
|
|
384
|
+
const timer = setTimeout(() => this._removeAgent(id), this.doneAgentLingerMs);
|
|
385
|
+
timer.unref?.();
|
|
386
|
+
this._agentTimers.set(id, timer);
|
|
387
|
+
}
|
|
388
|
+
this._changed();
|
|
389
|
+
return agent;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
_removeAgent(id) {
|
|
393
|
+
clearTimeout(this._agentTimers.get(id));
|
|
394
|
+
this._agentTimers.delete(id);
|
|
395
|
+
if (this.agents.delete(id)) this._changed();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** Drop the done agent that has lingered longest, to make room. */
|
|
399
|
+
_evictDoneAgent() {
|
|
400
|
+
let oldest = null;
|
|
401
|
+
for (const agent of this.agents.values()) {
|
|
402
|
+
if (agent.status === 'done' && (!oldest || agent.updatedAt < oldest.updatedAt)) oldest = agent;
|
|
403
|
+
}
|
|
404
|
+
if (!oldest) return false;
|
|
405
|
+
this._removeAgent(oldest.id);
|
|
406
|
+
return true;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
_clearAgents() {
|
|
410
|
+
for (const t of this._agentTimers.values()) clearTimeout(t);
|
|
411
|
+
this._agentTimers.clear();
|
|
412
|
+
this.agents.clear();
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
_handleOscReport(payload) {
|
|
416
|
+
if (!payload.startsWith(OSC_AGENT_PREFIX)) return;
|
|
417
|
+
try {
|
|
418
|
+
const report = JSON.parse(payload.slice(OSC_AGENT_PREFIX.length));
|
|
419
|
+
const isAgent = report && typeof report === 'object' &&
|
|
420
|
+
((report.agentId ?? report.agent ?? report.id) !== undefined || report.finishForeground === true);
|
|
421
|
+
if (isAgent) this.reportAgent(report, 'terminal');
|
|
422
|
+
else this.reportModel(report);
|
|
423
|
+
} catch (err) {
|
|
424
|
+
this.emit('warning', `ignored in-band report: ${err.message}`);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// ---- model -------------------------------------------------------------
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* The main model the tool says it is using. Explicit reports win over
|
|
432
|
+
* text seen on screen, which wins over a --model argument.
|
|
433
|
+
*/
|
|
434
|
+
setModel({ name, displayName = null }, source) {
|
|
435
|
+
if (this.model && MODEL_SOURCE_RANK[this.model.source] > MODEL_SOURCE_RANK[source]) return;
|
|
436
|
+
const current = this.model;
|
|
437
|
+
if (current && current.name === name && current.displayName === displayName && current.source === source) return;
|
|
438
|
+
this.model = { name, displayName, source };
|
|
439
|
+
this._changed();
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
reportModel(report) {
|
|
443
|
+
if (!report || typeof report !== 'object') throw badRequest('model report must be an object');
|
|
444
|
+
if (this.status !== 'running') throw Object.assign(new Error('session has exited'), { status: 409 });
|
|
445
|
+
const name = String(report.model ?? '').trim().slice(0, 120);
|
|
446
|
+
if (!name) throw badRequest('model is required');
|
|
447
|
+
const displayName = report.displayName === undefined || report.displayName === null ? null : String(report.displayName).trim().slice(0, 80) || null;
|
|
448
|
+
// Gemini CLI fires BeforeModel for a sub-agent's requests too, with the
|
|
449
|
+
// sub-agent's model and nothing to tell them apart. While a foreground
|
|
450
|
+
// agent works its parent makes no request, so the report is the agent's.
|
|
451
|
+
if (this._foregroundAgentWorking()) return this.model;
|
|
452
|
+
this.setModel({ name, displayName }, 'report');
|
|
453
|
+
return this.model;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
_foregroundAgentWorking() {
|
|
457
|
+
for (const agent of this.agents.values()) {
|
|
458
|
+
if (agent.foreground && agent.status === 'working') return true;
|
|
459
|
+
}
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Scan shortly after output pauses, and at least every couple of seconds
|
|
465
|
+
* while output keeps coming, so a busy tool still gets scanned.
|
|
466
|
+
*/
|
|
467
|
+
_scheduleModelScan() {
|
|
468
|
+
const now = Date.now();
|
|
469
|
+
this._scanDeadline ??= now + SCREEN_SCAN_MAX_DELAY_MS;
|
|
470
|
+
clearTimeout(this._scanTimer);
|
|
471
|
+
const delay = Math.max(0, Math.min(SCREEN_SCAN_DELAY_MS, this._scanDeadline - now));
|
|
472
|
+
this._scanTimer = setTimeout(() => this._scanScreenForModel(), delay);
|
|
473
|
+
this._scanTimer.unref?.();
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
_scanScreenForModel() {
|
|
477
|
+
this._scanDeadline = null;
|
|
478
|
+
if (this.disposed || !this.modelRegex || this.model?.source === 'report') return;
|
|
479
|
+
const buffer = this.term.buffer.active;
|
|
480
|
+
const lines = [];
|
|
481
|
+
for (let y = 0; y < this.term.rows; y++) lines.push(buffer.getLine(buffer.baseY + y)?.translateToString(true) ?? '');
|
|
482
|
+
let last = null;
|
|
483
|
+
for (const match of lines.join('\n').matchAll(this.modelRegex)) last = match[0];
|
|
484
|
+
if (last) this.setModel({ name: last.replace(/[.,;:)]+$/, '') }, 'screen');
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// ---- state -------------------------------------------------------------
|
|
488
|
+
|
|
489
|
+
_changed() {
|
|
490
|
+
this.emit('changed', this);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
toJSON() {
|
|
494
|
+
return {
|
|
495
|
+
id: this.id,
|
|
496
|
+
name: this.name,
|
|
497
|
+
provider: {
|
|
498
|
+
id: this.provider.id,
|
|
499
|
+
vendor: this.provider.vendor,
|
|
500
|
+
tool: this.provider.tool,
|
|
501
|
+
color: this.provider.color,
|
|
502
|
+
monogram: this.provider.monogram,
|
|
503
|
+
iconUrl: this.provider.iconUrl,
|
|
504
|
+
},
|
|
505
|
+
cwd: this.cwd,
|
|
506
|
+
resume: this.resume,
|
|
507
|
+
task: this.task,
|
|
508
|
+
pid: this.pid,
|
|
509
|
+
status: this.status,
|
|
510
|
+
exitCode: this.exitCode,
|
|
511
|
+
signal: this.signal,
|
|
512
|
+
activity: this.activity,
|
|
513
|
+
lastOutputAt: this.lastOutputAt ? new Date(this.lastOutputAt).toISOString() : null,
|
|
514
|
+
createdAt: this.createdAt,
|
|
515
|
+
exitedAt: this.exitedAt,
|
|
516
|
+
cols: this.cols,
|
|
517
|
+
rows: this.rows,
|
|
518
|
+
attachedClients: this.subscribers.size,
|
|
519
|
+
model: this.model,
|
|
520
|
+
agents: [...this.agents.values()],
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function badRequest(message) {
|
|
526
|
+
return Object.assign(new Error(message), { status: 400 });
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
export function newId(bytes = 6) {
|
|
530
|
+
return crypto.randomBytes(bytes).toString('hex');
|
|
531
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// Recover the user's real PATH on macOS and Linux.
|
|
2
|
+
//
|
|
3
|
+
// An app started from Finder, the Dock, or a login item does not inherit the
|
|
4
|
+
// PATH that the user's shell profile builds (Homebrew, nvm, ~/.local/bin, ...),
|
|
5
|
+
// which is exactly where CLI coding tools usually live. Like VS Code, we ask
|
|
6
|
+
// the user's login shell for its PATH and merge it in.
|
|
7
|
+
|
|
8
|
+
import { execFile, spawn, spawnSync } from 'node:child_process';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
|
|
11
|
+
const START = '__AGENT_GUILD_PATH_START__';
|
|
12
|
+
const END = '__AGENT_GUILD_PATH_END__';
|
|
13
|
+
|
|
14
|
+
export function mergePathLists(primary, secondary, delimiter = path.delimiter) {
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const list of [primary, secondary]) {
|
|
18
|
+
for (const entry of (list || '').split(delimiter)) {
|
|
19
|
+
if (!entry || seen.has(entry)) continue;
|
|
20
|
+
seen.add(entry);
|
|
21
|
+
out.push(entry);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return out.join(delimiter);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Pull PATH out of `env` output captured between the two markers. */
|
|
28
|
+
export function parsePathFromEnvOutput(stdout) {
|
|
29
|
+
if (typeof stdout !== 'string') return null;
|
|
30
|
+
const start = stdout.indexOf(START);
|
|
31
|
+
const end = stdout.indexOf(END, start);
|
|
32
|
+
if (start === -1 || end === -1) return null;
|
|
33
|
+
const line = stdout.slice(start + START.length, end).split(/\r?\n/).find((l) => l.startsWith('PATH='));
|
|
34
|
+
return line ? line.slice('PATH='.length) : null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function loginShellPath({ shell = process.env.SHELL, timeoutMs = 8000 } = {}) {
|
|
38
|
+
if (process.platform === 'win32' || !shell) return null;
|
|
39
|
+
// Read the exported PATH from `env` rather than expanding $PATH: fish, for
|
|
40
|
+
// one, expands "$PATH" to a space-separated list.
|
|
41
|
+
const result = spawnSync(shell, ['-l', '-i', '-c', `printf '%s' ${START}; command env; printf '%s' ${END}`], {
|
|
42
|
+
encoding: 'utf8',
|
|
43
|
+
timeout: timeoutMs,
|
|
44
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
45
|
+
env: { ...process.env, AGENT_GUILD_RESOLVING_ENV: '1' },
|
|
46
|
+
});
|
|
47
|
+
if (result.error) return null;
|
|
48
|
+
return parsePathFromEnvOutput(result.stdout);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Returns a copy of process.env whose PATH also contains the login shell's
|
|
53
|
+
* PATH entries. Set AGENT_GUILD_SKIP_SHELL_ENV=1 to disable the lookup.
|
|
54
|
+
*/
|
|
55
|
+
export function trimPathExt(env, platform = process.platform) {
|
|
56
|
+
if (platform !== 'win32') return env;
|
|
57
|
+
const key = Object.keys(env).find((k) => k.toUpperCase() === 'PATHEXT');
|
|
58
|
+
if (key) env[key] = env[key].split(';').map((ext) => ext.trim()).filter(Boolean).join(';');
|
|
59
|
+
return env;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function resolveBaseEnv() {
|
|
63
|
+
const env = trimPathExt({ ...process.env });
|
|
64
|
+
if (process.env.AGENT_GUILD_SKIP_SHELL_ENV === '1') return env;
|
|
65
|
+
const shellPath = loginShellPath();
|
|
66
|
+
if (shellPath) env.PATH = mergePathLists(shellPath, env.PATH);
|
|
67
|
+
return env;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function weavePaths(current, discovered, { delimiter = path.delimiter, caseInsensitive = process.platform === 'win32' } = {}) {
|
|
71
|
+
const id = (entry) => (caseInsensitive ? entry.toLowerCase() : entry).replace(/(?<=.)[\\/]+$/, '');
|
|
72
|
+
const unique = (list) => {
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
return (list || '').split(delimiter).filter((entry) => entry && !seen.has(id(entry)) && seen.add(id(entry)));
|
|
75
|
+
};
|
|
76
|
+
const out = unique(current);
|
|
77
|
+
const fresh = unique(discovered);
|
|
78
|
+
const indexOf = (entry) => out.findIndex((e) => id(e) === id(entry));
|
|
79
|
+
fresh.forEach((entry, i) => {
|
|
80
|
+
if (indexOf(entry) !== -1) return;
|
|
81
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
82
|
+
const at = indexOf(fresh[j]);
|
|
83
|
+
if (at !== -1) return void out.splice(at + 1, 0, entry);
|
|
84
|
+
}
|
|
85
|
+
for (let j = i + 1; j < fresh.length; j++) {
|
|
86
|
+
const at = indexOf(fresh[j]);
|
|
87
|
+
if (at !== -1) return void out.splice(at, 0, entry);
|
|
88
|
+
}
|
|
89
|
+
out.push(entry);
|
|
90
|
+
});
|
|
91
|
+
return out.join(delimiter);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function parseRegValue(stdout, name = 'Path') {
|
|
95
|
+
const pattern = new RegExp(`^\\s+${name}\\s+REG_(?:EXPAND_)?SZ\\s+(.*)$`, 'i');
|
|
96
|
+
for (const line of String(stdout || '').split(/\r?\n/)) {
|
|
97
|
+
const match = line.match(pattern);
|
|
98
|
+
if (match) return match[1].trim();
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function expandWindowsVars(value, env) {
|
|
104
|
+
return value.replace(/%([^%;]+)%/g, (whole, name) => {
|
|
105
|
+
const key = Object.keys(env).find((k) => k.toUpperCase() === name.toUpperCase());
|
|
106
|
+
return key ? env[key] : whole;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function queryRegistry(key, env, timeoutMs) {
|
|
111
|
+
const system32 = path.win32.join(env.SystemRoot || env.SYSTEMROOT || 'C:\\Windows', 'System32');
|
|
112
|
+
return new Promise((resolve) => {
|
|
113
|
+
execFile(path.win32.join(system32, 'reg.exe'), ['query', key, '/v', 'Path'], { timeout: timeoutMs, windowsHide: true }, (err, stdout) => {
|
|
114
|
+
resolve(err ? null : stdout);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function readWindowsPath({ env = process.env, timeoutMs = 5000, query = queryRegistry } = {}) {
|
|
120
|
+
const machine = parseRegValue(await query('HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment', env, timeoutMs));
|
|
121
|
+
if (!machine) return null;
|
|
122
|
+
const user = parseRegValue(await query('HKCU\\Environment', env, timeoutMs));
|
|
123
|
+
return [machine, user].filter(Boolean).map((value) => expandWindowsVars(value, env)).join(';');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function readLoginShellPath({ shell = process.env.SHELL, timeoutMs = 8000 } = {}) {
|
|
127
|
+
if (!shell) return Promise.resolve(null);
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
let stdout = '';
|
|
130
|
+
let child;
|
|
131
|
+
try {
|
|
132
|
+
child = spawn(shell, ['-l', '-i', '-c', `printf '%s' ${START}; command env; printf '%s' ${END}`], {
|
|
133
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
134
|
+
env: { ...process.env, AGENT_GUILD_RESOLVING_ENV: '1' },
|
|
135
|
+
});
|
|
136
|
+
} catch {
|
|
137
|
+
return resolve(null);
|
|
138
|
+
}
|
|
139
|
+
const timer = setTimeout(() => { child.kill(); resolve(null); }, timeoutMs);
|
|
140
|
+
child.stdout.setEncoding('utf8');
|
|
141
|
+
child.stdout.on('data', (chunk) => { stdout += chunk; });
|
|
142
|
+
child.on('error', () => { clearTimeout(timer); resolve(null); });
|
|
143
|
+
child.on('close', () => { clearTimeout(timer); resolve(parsePathFromEnvOutput(stdout)); });
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function pathReader(platform = process.platform, env = process.env) {
|
|
148
|
+
if (env.AGENT_GUILD_SKIP_SHELL_ENV === '1') return null;
|
|
149
|
+
return platform === 'win32' ? () => readWindowsPath({ env }) : () => readLoginShellPath({ shell: env.SHELL });
|
|
150
|
+
}
|