scream-code 0.15.1 → 0.15.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-C3aR5fdI.mjs → app-Bqc3MVGh.mjs} +641 -29
- package/dist/{dispatch-Doe-DlpL.mjs → dispatch-CweYoXh3.mjs} +1 -1
- package/dist/{dispatch-ATQMQq1p.mjs → dispatch-DGeogib-.mjs} +270 -93
- package/dist/main.mjs +1 -1
- package/dist/public/assets/{index-BfVIY6UW.css → index-BJaeSGtC.css} +1 -1
- package/dist/public/assets/index-DDVdau1l.js +93 -0
- package/dist/public/assets/index-DDVdau1l.js.map +1 -0
- package/dist/public/index.html +2 -2
- package/package.json +2 -2
- package/dist/public/assets/index-CYmwuyd8.js +0 -93
- package/dist/public/assets/index-CYmwuyd8.js.map +0 -1
|
@@ -4,7 +4,7 @@ import { dirname as __cjsShimDirname } from 'node:path';
|
|
|
4
4
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
5
5
|
const __dirname = __cjsShimDirname(__filename);
|
|
6
6
|
import "./suppress-sqlite-warning-C2VB0doZ.mjs";
|
|
7
|
-
import { $t as isTodoItemShape, A as renderDiffLinesClustered, An as getInputHistoryFile, At as ENABLE_TERMINAL_FOCUS_REPORTING, B as BackgroundAgentStatusComponent, Bn as ScreamHarness, Bt as TERMINAL_THEME_LIGHT, Cn as PULSE_WAVE_FRAMES, Ct as createThemeStyles, D as toggleEmptySessionHint, Dn as saveTuiConfig, Dt as parseOsc11BackgroundTheme, E as isTurnElapsedEnabled, En as loadTuiConfig, Et as detectTerminalTheme, F as estimateTokens, Fn as CLI_USER_AGENT_PRODUCT, Ft as OSC11_RESPONSE_PREFIX_NO_ESC, G as getBreathingFrame, Gn as log, H as AgentGroupComponent, Hn as resolveScreamHome, Ht as isStreaming, I as getSharedSpeedTracker, In as PRODUCT_NAME, It as QUERY_TERMINAL_THEME, J as refineGoal, Jn as isOrphanedToolCallError, Jt as printableChar, K as resetBreathingClock, Kn as resolveGlobalLogPath, L as SkillActivationComponent, Ln as DEFAULT_CATALOG_URL, Lt as TERMINAL_FOCUS_IN, M as langFromPath, Mn as detectInstallSource, Mt as OSC11_QUERY, N as CachedContainer, Nn as CLI_COMMAND_NAME, Nt as OSC11_RESPONSE, O as ToolCallComponent, On as detectShellEnvironment, Ot as DISABLE_TERMINAL_FOCUS_REPORTING, P as ThinkingComponent, Pn as CLI_UI_MODE, Pt as OSC11_RESPONSE_PREFIX, Qt as formatErrorMessage, R as ReadGroupComponent, Rn as fetchCatalog, Rt as TERMINAL_FOCUS_OUT, S as getDaemonInstructions, Sn as PIXEL_PULSE_FRAMES, St as createMarkdownTheme, T as isEmptySessionHintDismissed, Tn as TuiLikePreferencesSchema, Tt as getColorPalette, U as WelcomeComponent, Un as MemoryMemoStore, Ut as FooterComponent, V as AssistantMessageComponent, Vt as isBusy, W as BREATHE_CYCLE_MS, Wn as flushDiagnosticLogs, Xn as SCREAM_ERROR_INFO, Xt as appendStreamingArgsPreview, Yn as ErrorCodes, Yt as STATUS_BULLET, Zt as argsRecord, _ as refreshUpdateCache, _n as isExperimentalFlagEnabled, _t as clearInfoPanelState, an as EMPTY_SESSION_HINT_URL, b as readJsonlFile, bn as isEmbeddingModelCached, bt as resolveThemeSync, c as hasDispose, cn as SESSION_TIPS, ct as handlePlanCommand, d as formatMemoryMemoForInjection, dn as getCtrlDHint, en as parseStreamingArgs, et as toTerminalHyperlink, fn as getLlmNotSetMessage, g as selectUpdateTarget, gn as sortSlashCommands, gt as supportsBalance, hn as BUILTIN_SLASH_COMMANDS, ht as refreshProviderBalance, i as buildRoleAdditionalText, in as CHARS_PER_TOKEN, j as highlightLines, jn as getLogDir, jt as ENABLE_TERMINAL_THEME_REPORTING, k as renderDiffLines, kn as getDataDir, kt as DISABLE_TERMINAL_THEME_REPORTING, l as isPlanExpandable, ln as TIP_ROTATION_INTERVAL_MS, mn as buildSkillSlashCommands, n as clearEvalPanelState, nn as stringValue, nt as getModelCycleLevel, on as EXIT_CONFIRM_WINDOW_MS, ot as handleFusionPlanCommand, pn as getNoActiveSessionMessage, q as clearGoalState, qn as isScreamError, r as openUrl, rn as truncateErrorMessage, s as disposeChildren, sn as MAIN_AGENT_ID, t as dispatchInput, tn as serializeToolResultOutput, tt as changeThinkingLevel, u as MoonLoader, un as getCtrlCHint, v as readUpdateCache, vn as setExperimentalFlags, w as UserMessageComponent, wn as TuiConfigParseError, wt as contrastTextHex, xn as startManualEmbeddingDownload, xt as createEditorTheme, y as appendJsonlLine, yn as getKnowledgeStore, z as parseReadGroupOutput, zn as saveCatalogCache, zt as TERMINAL_THEME_DARK } from "./dispatch-
|
|
7
|
+
import { $t as isTodoItemShape, A as renderDiffLinesClustered, An as getInputHistoryFile, At as ENABLE_TERMINAL_FOCUS_REPORTING, B as BackgroundAgentStatusComponent, Bn as ScreamHarness, Bt as TERMINAL_THEME_LIGHT, Cn as PULSE_WAVE_FRAMES, Ct as createThemeStyles, D as toggleEmptySessionHint, Dn as saveTuiConfig, Dt as parseOsc11BackgroundTheme, E as isTurnElapsedEnabled, En as loadTuiConfig, Et as detectTerminalTheme, F as estimateTokens, Fn as CLI_USER_AGENT_PRODUCT, Ft as OSC11_RESPONSE_PREFIX_NO_ESC, G as getBreathingFrame, Gn as log, H as AgentGroupComponent, Hn as resolveScreamHome, Ht as isStreaming, I as getSharedSpeedTracker, In as PRODUCT_NAME, It as QUERY_TERMINAL_THEME, J as refineGoal, Jn as isOrphanedToolCallError, Jt as printableChar, K as resetBreathingClock, Kn as resolveGlobalLogPath, L as SkillActivationComponent, Ln as DEFAULT_CATALOG_URL, Lt as TERMINAL_FOCUS_IN, M as langFromPath, Mn as detectInstallSource, Mt as OSC11_QUERY, N as CachedContainer, Nn as CLI_COMMAND_NAME, Nt as OSC11_RESPONSE, O as ToolCallComponent, On as detectShellEnvironment, Ot as DISABLE_TERMINAL_FOCUS_REPORTING, P as ThinkingComponent, Pn as CLI_UI_MODE, Pt as OSC11_RESPONSE_PREFIX, Qt as formatErrorMessage, R as ReadGroupComponent, Rn as fetchCatalog, Rt as TERMINAL_FOCUS_OUT, S as getDaemonInstructions, Sn as PIXEL_PULSE_FRAMES, St as createMarkdownTheme, T as isEmptySessionHintDismissed, Tn as TuiLikePreferencesSchema, Tt as getColorPalette, U as WelcomeComponent, Un as MemoryMemoStore, Ut as FooterComponent, V as AssistantMessageComponent, Vt as isBusy, W as BREATHE_CYCLE_MS, Wn as flushDiagnosticLogs, Xn as SCREAM_ERROR_INFO, Xt as appendStreamingArgsPreview, Yn as ErrorCodes, Yt as STATUS_BULLET, Zt as argsRecord, _ as refreshUpdateCache, _n as isExperimentalFlagEnabled, _t as clearInfoPanelState, an as EMPTY_SESSION_HINT_URL, b as readJsonlFile, bn as isEmbeddingModelCached, bt as resolveThemeSync, c as hasDispose, cn as SESSION_TIPS, ct as handlePlanCommand, d as formatMemoryMemoForInjection, dn as getCtrlDHint, en as parseStreamingArgs, et as toTerminalHyperlink, fn as getLlmNotSetMessage, g as selectUpdateTarget, gn as sortSlashCommands, gt as supportsBalance, hn as BUILTIN_SLASH_COMMANDS, ht as refreshProviderBalance, i as buildRoleAdditionalText, in as CHARS_PER_TOKEN, j as highlightLines, jn as getLogDir, jt as ENABLE_TERMINAL_THEME_REPORTING, k as renderDiffLines, kn as getDataDir, kt as DISABLE_TERMINAL_THEME_REPORTING, l as isPlanExpandable, ln as TIP_ROTATION_INTERVAL_MS, mn as buildSkillSlashCommands, n as clearEvalPanelState, nn as stringValue, nt as getModelCycleLevel, on as EXIT_CONFIRM_WINDOW_MS, ot as handleFusionPlanCommand, pn as getNoActiveSessionMessage, q as clearGoalState, qn as isScreamError, r as openUrl, rn as truncateErrorMessage, s as disposeChildren, sn as MAIN_AGENT_ID, t as dispatchInput, tn as serializeToolResultOutput, tt as changeThinkingLevel, u as MoonLoader, un as getCtrlCHint, v as readUpdateCache, vn as setExperimentalFlags, w as UserMessageComponent, wn as TuiConfigParseError, wt as contrastTextHex, xn as startManualEmbeddingDownload, xt as createEditorTheme, y as appendJsonlLine, yn as getKnowledgeStore, z as parseReadGroupOutput, zn as saveCatalogCache, zt as TERMINAL_THEME_DARK } from "./dispatch-DGeogib-.mjs";
|
|
8
8
|
import { a as setLocale, o as t } from "./text-input-dialog-BUXAKycx.mjs";
|
|
9
9
|
import { createRequire } from "node:module";
|
|
10
10
|
import { randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
|
|
@@ -32,7 +32,7 @@ function optionalBuildString(value) {
|
|
|
32
32
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
33
33
|
}
|
|
34
34
|
const SCREAM_BUILD_INFO = {
|
|
35
|
-
version: optionalBuildString("0.15.
|
|
35
|
+
version: optionalBuildString("0.15.3"),
|
|
36
36
|
channel: optionalBuildString(""),
|
|
37
37
|
commit: optionalBuildString(""),
|
|
38
38
|
buildTarget: optionalBuildString("darwin-arm64")
|
|
@@ -6553,27 +6553,34 @@ function installTerminalThemeTracking(state, onTheme) {
|
|
|
6553
6553
|
}
|
|
6554
6554
|
//#endregion
|
|
6555
6555
|
//#region src/tui/components/chrome/pulse-wave-loader.ts
|
|
6556
|
-
|
|
6557
|
-
|
|
6556
|
+
/** Leading/trailing glyph: U+2588 (Block Elements — guaranteed single-column
|
|
6557
|
+
* width). The remaining stage cells render muted middle dots (U+00B7). */
|
|
6558
|
+
const LEADING_BOX = "■";
|
|
6558
6559
|
/**
|
|
6559
|
-
*
|
|
6560
|
+
* 8-box flowing pulse-wave loading indicator.
|
|
6560
6561
|
*
|
|
6561
|
-
*
|
|
6562
|
-
*
|
|
6562
|
+
* A leading box (full primary colour) sweeps across an 8-cell stage, trailed
|
|
6563
|
+
* by a dim box, over a row of muted middle dots:
|
|
6564
|
+
* ■ 淡 ■ · · · · · · → ■ ■ 淡 ■ · · · · → … → (bounce back)
|
|
6565
|
+
* (淡 shown here only to mark the trailing box; it renders as primary+dim.)
|
|
6563
6566
|
*
|
|
6564
|
-
*
|
|
6565
|
-
*
|
|
6566
|
-
*
|
|
6567
|
-
*
|
|
6567
|
+
* 14-frame ping-pong loop @ 80 ms — the leading edge never teleports, it
|
|
6568
|
+
* walks cell by cell.
|
|
6569
|
+
*
|
|
6570
|
+
* All glyphs are width-safe in terminal fonts (U+2588 Block Elements and
|
|
6571
|
+
* U+00B7 Latin-1) — the earlier ■/⬝ mix reflowed the whole line on every
|
|
6572
|
+
* frame because U+2B1D is not single-column guaranteed.
|
|
6568
6573
|
*
|
|
6569
6574
|
* The component auto-starts on construction. Call `stop()` to tear
|
|
6570
|
-
* down the
|
|
6575
|
+
* down the timer chain.
|
|
6571
6576
|
*/
|
|
6572
6577
|
var PulseWaveLoader = class extends Text {
|
|
6573
6578
|
currentFrame = 0;
|
|
6574
6579
|
intervalId = null;
|
|
6575
6580
|
ui;
|
|
6576
6581
|
colorHex;
|
|
6582
|
+
/** Wall-clock time of the last frame advance, for drift-free scheduling. */
|
|
6583
|
+
lastTickAt = 0;
|
|
6577
6584
|
/** Raw frame text without Text's full-width render padding, exposed so
|
|
6578
6585
|
* callers can compose the wave inline with trailing content (e.g. the
|
|
6579
6586
|
* reconnect label in the status bar). */
|
|
@@ -6589,39 +6596,60 @@ var PulseWaveLoader = class extends Text {
|
|
|
6589
6596
|
this.start();
|
|
6590
6597
|
}
|
|
6591
6598
|
start() {
|
|
6599
|
+
this.lastTickAt = performance.now();
|
|
6592
6600
|
this.updateDisplay();
|
|
6593
|
-
this.
|
|
6594
|
-
this.currentFrame = (this.currentFrame + 1) % PULSE_WAVE_FRAMES.length;
|
|
6595
|
-
this.updateDisplay();
|
|
6596
|
-
}, 120);
|
|
6601
|
+
this.scheduleTick(80, 80);
|
|
6597
6602
|
}
|
|
6598
6603
|
stop() {
|
|
6599
6604
|
if (this.intervalId) {
|
|
6600
|
-
|
|
6605
|
+
clearTimeout(this.intervalId);
|
|
6601
6606
|
this.intervalId = null;
|
|
6602
6607
|
}
|
|
6603
6608
|
}
|
|
6609
|
+
dispose() {
|
|
6610
|
+
this.stop();
|
|
6611
|
+
}
|
|
6612
|
+
/**
|
|
6613
|
+
* setTimeout self-rescheduling chain with drift-free frame advancement and
|
|
6614
|
+
* paint-cost backpressure (same pattern as the pi-tui Loader): a slow tick
|
|
6615
|
+
* defers the next one by `max(0, interval - cost, cost × 9)` instead of
|
|
6616
|
+
* stacking intervals, keeping the wave ≤ ~10% CPU under slow writes.
|
|
6617
|
+
*/
|
|
6618
|
+
scheduleTick(intervalMs, delayMs) {
|
|
6619
|
+
const timer = setTimeout(() => {
|
|
6620
|
+
if (this.intervalId !== timer) return;
|
|
6621
|
+
const startedAt = performance.now();
|
|
6622
|
+
const elapsed = startedAt - this.lastTickAt;
|
|
6623
|
+
if (elapsed >= intervalMs) {
|
|
6624
|
+
const steps = Math.floor(elapsed / intervalMs);
|
|
6625
|
+
this.currentFrame = (this.currentFrame + steps) % PULSE_WAVE_FRAMES.length;
|
|
6626
|
+
this.lastTickAt += steps * intervalMs;
|
|
6627
|
+
this.updateDisplay();
|
|
6628
|
+
}
|
|
6629
|
+
const frameCostMs = performance.now() - startedAt;
|
|
6630
|
+
if (this.intervalId !== timer) return;
|
|
6631
|
+
const cadenceDelayMs = Math.max(0, intervalMs - frameCostMs);
|
|
6632
|
+
const backpressureDelayMs = frameCostMs * 9;
|
|
6633
|
+
this.scheduleTick(intervalMs, Math.max(cadenceDelayMs, backpressureDelayMs));
|
|
6634
|
+
}, delayMs);
|
|
6635
|
+
this.intervalId = timer;
|
|
6636
|
+
}
|
|
6604
6637
|
setColorHex(colorHex) {
|
|
6605
6638
|
this.colorHex = colorHex;
|
|
6606
6639
|
this.updateDisplay();
|
|
6607
6640
|
}
|
|
6608
6641
|
updateDisplay() {
|
|
6609
6642
|
const step = PULSE_WAVE_FRAMES[this.currentFrame] ?? PULSE_WAVE_FRAMES[0];
|
|
6610
|
-
const cells =
|
|
6611
|
-
|
|
6612
|
-
1,
|
|
6613
|
-
2
|
|
6614
|
-
].map((idx) => this.renderCell(idx, step.active, step.forward));
|
|
6615
|
-
this.frameText = cells.join(" ");
|
|
6643
|
+
const cells = Array.from({ length: 8 }, (_, idx) => this.renderCell(idx, step.active, step.forward));
|
|
6644
|
+
this.frameText = cells.join("");
|
|
6616
6645
|
this.setText(this.frameText);
|
|
6617
6646
|
this.ui.requestRender();
|
|
6618
6647
|
}
|
|
6619
6648
|
renderCell(index, active, forward) {
|
|
6620
6649
|
const distance = forward ? active - index : index - active;
|
|
6621
|
-
|
|
6622
|
-
if (distance ===
|
|
6623
|
-
|
|
6624
|
-
return chalk.dim(glyph);
|
|
6650
|
+
if (distance === 0) return chalk.hex(this.colorHex)(LEADING_BOX);
|
|
6651
|
+
if (distance === 1) return chalk.hex(this.colorHex).dim(LEADING_BOX);
|
|
6652
|
+
return chalk.dim("·");
|
|
6625
6653
|
}
|
|
6626
6654
|
};
|
|
6627
6655
|
//#endregion
|
|
@@ -12397,7 +12425,7 @@ var ScreamTUI = class {
|
|
|
12397
12425
|
const card = new OnboardingCardComponent({
|
|
12398
12426
|
colors: this.state.theme.colors,
|
|
12399
12427
|
onConfigure: () => {
|
|
12400
|
-
import("./dispatch-
|
|
12428
|
+
import("./dispatch-CweYoXh3.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
|
|
12401
12429
|
this.restoreEditor();
|
|
12402
12430
|
onDone?.();
|
|
12403
12431
|
});
|
|
@@ -14527,7 +14555,14 @@ function toHttpError(error) {
|
|
|
14527
14555
|
if (isScreamError(error)) switch (error.code) {
|
|
14528
14556
|
case ErrorCodes.GOAL_OBJECTIVE_EMPTY:
|
|
14529
14557
|
case ErrorCodes.GOAL_OBJECTIVE_TOO_LONG:
|
|
14530
|
-
case ErrorCodes.REQUEST_INVALID:
|
|
14558
|
+
case ErrorCodes.REQUEST_INVALID:
|
|
14559
|
+
case ErrorCodes.SESSION_PERMISSION_MODE_INVALID:
|
|
14560
|
+
case ErrorCodes.SESSION_PLAN_MODE_INVALID:
|
|
14561
|
+
case ErrorCodes.SESSION_WOLFPACK_MODE_INVALID:
|
|
14562
|
+
case ErrorCodes.SESSION_MODEL_EMPTY:
|
|
14563
|
+
case ErrorCodes.SESSION_THINKING_EMPTY:
|
|
14564
|
+
case ErrorCodes.SKILL_NAME_EMPTY:
|
|
14565
|
+
case ErrorCodes.BACKGROUND_TASK_ID_EMPTY: return new HttpError(400, error.message, error.code);
|
|
14531
14566
|
case ErrorCodes.GOAL_NOT_FOUND:
|
|
14532
14567
|
case ErrorCodes.SESSION_NOT_FOUND: return new HttpError(404, error.message, error.code);
|
|
14533
14568
|
case ErrorCodes.GOAL_ALREADY_EXISTS:
|
|
@@ -15696,6 +15731,49 @@ var WebSession = class {
|
|
|
15696
15731
|
status: this.cachedStatus
|
|
15697
15732
|
}, false);
|
|
15698
15733
|
}
|
|
15734
|
+
/** Switch permission mode, then sync status + metadata (TUI /auto,/yes parity). */
|
|
15735
|
+
async switchPermission(mode) {
|
|
15736
|
+
if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换权限模式。");
|
|
15737
|
+
await this.session.setPermission(mode);
|
|
15738
|
+
this.permission = mode;
|
|
15739
|
+
await this.refreshStatus();
|
|
15740
|
+
this.broadcast({
|
|
15741
|
+
type: "status",
|
|
15742
|
+
status: this.cachedStatus
|
|
15743
|
+
}, false);
|
|
15744
|
+
if (this.homeDir) await saveMetadata(this.homeDir, this.getMetadata());
|
|
15745
|
+
}
|
|
15746
|
+
/** Toggle plan mode, then sync status (TUI /plan parity). */
|
|
15747
|
+
async switchPlanMode(enabled, strategy) {
|
|
15748
|
+
if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换计划模式。");
|
|
15749
|
+
await this.session.setPlanMode(enabled, strategy);
|
|
15750
|
+
await this.refreshStatus();
|
|
15751
|
+
this.broadcast({
|
|
15752
|
+
type: "status",
|
|
15753
|
+
status: this.cachedStatus
|
|
15754
|
+
}, false);
|
|
15755
|
+
}
|
|
15756
|
+
/** Toggle wolfpack mode, then sync status. */
|
|
15757
|
+
async switchWolfpackMode(enabled) {
|
|
15758
|
+
if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换 Wolfpack 模式。");
|
|
15759
|
+
await this.session.setWolfpackMode(enabled);
|
|
15760
|
+
await this.refreshStatus();
|
|
15761
|
+
this.broadcast({
|
|
15762
|
+
type: "status",
|
|
15763
|
+
status: this.cachedStatus
|
|
15764
|
+
}, false);
|
|
15765
|
+
}
|
|
15766
|
+
/** Toggle RLM persistent-python mode (+ optional max depth), then sync status. */
|
|
15767
|
+
async switchRlm(enabled, maxDepth) {
|
|
15768
|
+
if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换 RLM 模式。");
|
|
15769
|
+
await this.session.setRlmEnabled(enabled);
|
|
15770
|
+
if (maxDepth !== void 0) await this.session.setRlmMaxDepth(maxDepth);
|
|
15771
|
+
await this.refreshStatus();
|
|
15772
|
+
this.broadcast({
|
|
15773
|
+
type: "status",
|
|
15774
|
+
status: this.cachedStatus
|
|
15775
|
+
}, false);
|
|
15776
|
+
}
|
|
15699
15777
|
getCoreSessionId() {
|
|
15700
15778
|
return this.coreSessionId;
|
|
15701
15779
|
}
|
|
@@ -15758,6 +15836,15 @@ var WebSession = class {
|
|
|
15758
15836
|
await session.cancelGoal();
|
|
15759
15837
|
}, true);
|
|
15760
15838
|
}
|
|
15839
|
+
/**
|
|
15840
|
+
* Public handle to the live core Session for REST RPC forwarding.
|
|
15841
|
+
* Throws 409 for archived (read-only) sessions so handlers never call into a
|
|
15842
|
+
* closed core session instead of silently creating one.
|
|
15843
|
+
*/
|
|
15844
|
+
requireLiveSession() {
|
|
15845
|
+
if (!this.session) throw new HttpError(409, "Session is archived (read-only)", ErrorCodes.SESSION_CLOSED);
|
|
15846
|
+
return this.session;
|
|
15847
|
+
}
|
|
15761
15848
|
requireGoalSession(allowBusy = false) {
|
|
15762
15849
|
if (!this.session) throw new HttpError(409, "Session is archived (read-only)", ErrorCodes.SESSION_CLOSED);
|
|
15763
15850
|
if (!allowBusy && this.busy) throw new HttpError(409, "Session is busy", ErrorCodes.TURN_AGENT_BUSY);
|
|
@@ -16246,7 +16333,529 @@ var SessionManager = class {
|
|
|
16246
16333
|
for (const [, ws] of this.sessions) await ws.close();
|
|
16247
16334
|
this.sessions.clear();
|
|
16248
16335
|
}
|
|
16336
|
+
/** Resolve a live session or throw 404/409; the single guard for all handlers. */
|
|
16337
|
+
getLiveSession(sessionId) {
|
|
16338
|
+
const ws = this.sessions.get(sessionId);
|
|
16339
|
+
if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
|
|
16340
|
+
return ws.requireLiveSession();
|
|
16341
|
+
}
|
|
16342
|
+
async getSessionStatus(sessionId) {
|
|
16343
|
+
return this.getLiveSession(sessionId).getStatus();
|
|
16344
|
+
}
|
|
16345
|
+
async getSessionUsage(sessionId) {
|
|
16346
|
+
return this.getLiveSession(sessionId).getUsage();
|
|
16347
|
+
}
|
|
16348
|
+
async getSessionContext(sessionId) {
|
|
16349
|
+
return this.getLiveSession(sessionId).getContext();
|
|
16350
|
+
}
|
|
16351
|
+
async getSessionPlan(sessionId) {
|
|
16352
|
+
return this.getLiveSession(sessionId).getPlan();
|
|
16353
|
+
}
|
|
16354
|
+
async setPermission(sessionId, mode) {
|
|
16355
|
+
const ws = this.sessions.get(sessionId);
|
|
16356
|
+
if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
|
|
16357
|
+
await ws.switchPermission(mode);
|
|
16358
|
+
}
|
|
16359
|
+
async setPlanMode(sessionId, enabled, strategy) {
|
|
16360
|
+
const ws = this.sessions.get(sessionId);
|
|
16361
|
+
if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
|
|
16362
|
+
await ws.switchPlanMode(enabled, strategy);
|
|
16363
|
+
}
|
|
16364
|
+
async clearPlan(sessionId) {
|
|
16365
|
+
await this.getLiveSession(sessionId).clearPlan();
|
|
16366
|
+
}
|
|
16367
|
+
async setWolfpackMode(sessionId, enabled) {
|
|
16368
|
+
const ws = this.sessions.get(sessionId);
|
|
16369
|
+
if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
|
|
16370
|
+
await ws.switchWolfpackMode(enabled);
|
|
16371
|
+
}
|
|
16372
|
+
async setRlm(sessionId, enabled, maxDepth) {
|
|
16373
|
+
const ws = this.sessions.get(sessionId);
|
|
16374
|
+
if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
|
|
16375
|
+
await ws.switchRlm(enabled, maxDepth);
|
|
16376
|
+
}
|
|
16377
|
+
async undoHistory(sessionId, count) {
|
|
16378
|
+
await this.getLiveSession(sessionId).undoHistory(count);
|
|
16379
|
+
}
|
|
16380
|
+
async compact(sessionId, instruction) {
|
|
16381
|
+
await this.getLiveSession(sessionId).compact(instruction ? { instruction } : {});
|
|
16382
|
+
}
|
|
16383
|
+
async listSkills(sessionId) {
|
|
16384
|
+
return this.getLiveSession(sessionId).listSkills();
|
|
16385
|
+
}
|
|
16386
|
+
async activateSkill(sessionId, name, args) {
|
|
16387
|
+
await this.getLiveSession(sessionId).activateSkill(name, args);
|
|
16388
|
+
}
|
|
16389
|
+
async removeSkill(sessionId, name) {
|
|
16390
|
+
await this.getLiveSession(sessionId).removeSkill(name);
|
|
16391
|
+
}
|
|
16392
|
+
async listPlugins(sessionId) {
|
|
16393
|
+
return this.getLiveSession(sessionId).listPlugins();
|
|
16394
|
+
}
|
|
16395
|
+
async getPluginInfo(sessionId, id) {
|
|
16396
|
+
return this.getLiveSession(sessionId).getPluginInfo(id);
|
|
16397
|
+
}
|
|
16398
|
+
async installPlugin(sessionId, source) {
|
|
16399
|
+
return this.getLiveSession(sessionId).installPlugin(source);
|
|
16400
|
+
}
|
|
16401
|
+
async setPluginEnabled(sessionId, id, enabled) {
|
|
16402
|
+
await this.getLiveSession(sessionId).setPluginEnabled(id, enabled);
|
|
16403
|
+
}
|
|
16404
|
+
async setPluginMcpServerEnabled(sessionId, id, server, enabled) {
|
|
16405
|
+
await this.getLiveSession(sessionId).setPluginMcpServerEnabled(id, server, enabled);
|
|
16406
|
+
}
|
|
16407
|
+
async removePlugin(sessionId, id) {
|
|
16408
|
+
await this.getLiveSession(sessionId).removePlugin(id);
|
|
16409
|
+
}
|
|
16410
|
+
async reloadPlugins(sessionId) {
|
|
16411
|
+
return this.getLiveSession(sessionId).reloadPlugins();
|
|
16412
|
+
}
|
|
16413
|
+
async activatePlugin(sessionId, id) {
|
|
16414
|
+
await this.getLiveSession(sessionId).activatePlugin(id);
|
|
16415
|
+
}
|
|
16416
|
+
async deactivatePlugin(sessionId, id) {
|
|
16417
|
+
await this.getLiveSession(sessionId).deactivatePlugin(id);
|
|
16418
|
+
}
|
|
16419
|
+
async injectPlugin(sessionId, id) {
|
|
16420
|
+
await this.getLiveSession(sessionId).injectPlugin(id);
|
|
16421
|
+
}
|
|
16422
|
+
async listMcpServers(sessionId) {
|
|
16423
|
+
return this.getLiveSession(sessionId).listMcpServers();
|
|
16424
|
+
}
|
|
16425
|
+
async getMcpStartupMetrics(sessionId) {
|
|
16426
|
+
return this.getLiveSession(sessionId).getMcpStartupMetrics();
|
|
16427
|
+
}
|
|
16428
|
+
async addMcpServer(sessionId, name, config) {
|
|
16429
|
+
await this.getLiveSession(sessionId).addMcpServer(name, config);
|
|
16430
|
+
}
|
|
16431
|
+
async reconnectMcpServer(sessionId, name) {
|
|
16432
|
+
await this.getLiveSession(sessionId).reconnectMcpServer(name);
|
|
16433
|
+
}
|
|
16434
|
+
async stopMcpServer(sessionId, name) {
|
|
16435
|
+
await this.getLiveSession(sessionId).stopMcpServer(name);
|
|
16436
|
+
}
|
|
16437
|
+
async removeMcpServer(sessionId, name) {
|
|
16438
|
+
await this.getLiveSession(sessionId).removeMcpServer(name);
|
|
16439
|
+
}
|
|
16440
|
+
async listBackgroundTasks(sessionId, activeOnly, limit) {
|
|
16441
|
+
return this.getLiveSession(sessionId).listBackgroundTasks({
|
|
16442
|
+
activeOnly,
|
|
16443
|
+
limit
|
|
16444
|
+
});
|
|
16445
|
+
}
|
|
16446
|
+
async getBackgroundTaskOutput(sessionId, taskId, tail) {
|
|
16447
|
+
return this.getLiveSession(sessionId).getBackgroundTaskOutput(taskId, tail !== void 0 ? { tail } : {});
|
|
16448
|
+
}
|
|
16449
|
+
async stopBackgroundTask(sessionId, taskId, reason) {
|
|
16450
|
+
await this.getLiveSession(sessionId).stopBackgroundTask(taskId, reason !== void 0 ? { reason } : {});
|
|
16451
|
+
}
|
|
16452
|
+
getConfig(options) {
|
|
16453
|
+
return this.harness.getConfig(options);
|
|
16454
|
+
}
|
|
16455
|
+
setConfig(patch) {
|
|
16456
|
+
return this.harness.setConfig(patch);
|
|
16457
|
+
}
|
|
16458
|
+
removeProvider(providerId) {
|
|
16459
|
+
return this.harness.removeProvider(providerId);
|
|
16460
|
+
}
|
|
16461
|
+
getExperimentalFlags() {
|
|
16462
|
+
return this.harness.getExperimentalFlags();
|
|
16463
|
+
}
|
|
16464
|
+
preflight() {
|
|
16465
|
+
return this.harness.preflight();
|
|
16466
|
+
}
|
|
16249
16467
|
};
|
|
16468
|
+
async function handleSessionControlRoutes(req, res, url, method, manager) {
|
|
16469
|
+
const statusMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/status$`).exec(url);
|
|
16470
|
+
if (statusMatch && method === "GET") {
|
|
16471
|
+
try {
|
|
16472
|
+
sendJson(res, 200, await manager.getSessionStatus(decodeURIComponent(statusMatch[1])));
|
|
16473
|
+
} catch (error) {
|
|
16474
|
+
sendHttpError(res, error);
|
|
16475
|
+
}
|
|
16476
|
+
return true;
|
|
16477
|
+
}
|
|
16478
|
+
const usageMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/usage$`).exec(url);
|
|
16479
|
+
if (usageMatch && method === "GET") {
|
|
16480
|
+
try {
|
|
16481
|
+
sendJson(res, 200, await manager.getSessionUsage(decodeURIComponent(usageMatch[1])));
|
|
16482
|
+
} catch (error) {
|
|
16483
|
+
sendHttpError(res, error);
|
|
16484
|
+
}
|
|
16485
|
+
return true;
|
|
16486
|
+
}
|
|
16487
|
+
const contextMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/context$`).exec(url);
|
|
16488
|
+
if (contextMatch && method === "GET") {
|
|
16489
|
+
try {
|
|
16490
|
+
sendJson(res, 200, await manager.getSessionContext(decodeURIComponent(contextMatch[1])));
|
|
16491
|
+
} catch (error) {
|
|
16492
|
+
sendHttpError(res, error);
|
|
16493
|
+
}
|
|
16494
|
+
return true;
|
|
16495
|
+
}
|
|
16496
|
+
const planMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plan$`).exec(url);
|
|
16497
|
+
if (planMatch && method === "GET") {
|
|
16498
|
+
try {
|
|
16499
|
+
sendJson(res, 200, await manager.getSessionPlan(decodeURIComponent(planMatch[1])));
|
|
16500
|
+
} catch (error) {
|
|
16501
|
+
sendHttpError(res, error);
|
|
16502
|
+
}
|
|
16503
|
+
return true;
|
|
16504
|
+
}
|
|
16505
|
+
const permissionMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/permission$`).exec(url);
|
|
16506
|
+
if (permissionMatch && method === "POST") {
|
|
16507
|
+
try {
|
|
16508
|
+
const mode = requiredString(await readJsonBody(req), "mode");
|
|
16509
|
+
await manager.setPermission(decodeURIComponent(permissionMatch[1]), mode);
|
|
16510
|
+
sendJson(res, 200, { ok: true });
|
|
16511
|
+
} catch (error) {
|
|
16512
|
+
sendHttpError(res, error);
|
|
16513
|
+
}
|
|
16514
|
+
return true;
|
|
16515
|
+
}
|
|
16516
|
+
const planModeMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plan$`).exec(url);
|
|
16517
|
+
if (planModeMatch && method === "POST") {
|
|
16518
|
+
try {
|
|
16519
|
+
const body = await readJsonBody(req);
|
|
16520
|
+
const enabled = optionalBoolean(body, "enabled", true);
|
|
16521
|
+
const strategy = optionalString(body, "strategy");
|
|
16522
|
+
await manager.setPlanMode(decodeURIComponent(planModeMatch[1]), enabled, strategy);
|
|
16523
|
+
sendJson(res, 200, { ok: true });
|
|
16524
|
+
} catch (error) {
|
|
16525
|
+
sendHttpError(res, error);
|
|
16526
|
+
}
|
|
16527
|
+
return true;
|
|
16528
|
+
}
|
|
16529
|
+
const clearPlanMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plan/clear$`).exec(url);
|
|
16530
|
+
if (clearPlanMatch && method === "POST") {
|
|
16531
|
+
try {
|
|
16532
|
+
await manager.clearPlan(decodeURIComponent(clearPlanMatch[1]));
|
|
16533
|
+
sendJson(res, 200, { ok: true });
|
|
16534
|
+
} catch (error) {
|
|
16535
|
+
sendHttpError(res, error);
|
|
16536
|
+
}
|
|
16537
|
+
return true;
|
|
16538
|
+
}
|
|
16539
|
+
const wolfpackMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/wolfpack$`).exec(url);
|
|
16540
|
+
if (wolfpackMatch && method === "POST") {
|
|
16541
|
+
try {
|
|
16542
|
+
const enabled = optionalBoolean(await readJsonBody(req), "enabled", true);
|
|
16543
|
+
await manager.setWolfpackMode(decodeURIComponent(wolfpackMatch[1]), enabled);
|
|
16544
|
+
sendJson(res, 200, { ok: true });
|
|
16545
|
+
} catch (error) {
|
|
16546
|
+
sendHttpError(res, error);
|
|
16547
|
+
}
|
|
16548
|
+
return true;
|
|
16549
|
+
}
|
|
16550
|
+
const rlmMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/rlm$`).exec(url);
|
|
16551
|
+
if (rlmMatch && method === "POST") {
|
|
16552
|
+
try {
|
|
16553
|
+
const body = await readJsonBody(req);
|
|
16554
|
+
const enabled = optionalBoolean(body, "enabled", true);
|
|
16555
|
+
const maxDepth = typeof body["maxDepth"] === "number" ? Number(body["maxDepth"]) : void 0;
|
|
16556
|
+
await manager.setRlm(decodeURIComponent(rlmMatch[1]), enabled, maxDepth);
|
|
16557
|
+
sendJson(res, 200, { ok: true });
|
|
16558
|
+
} catch (error) {
|
|
16559
|
+
sendHttpError(res, error);
|
|
16560
|
+
}
|
|
16561
|
+
return true;
|
|
16562
|
+
}
|
|
16563
|
+
const undoMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/undo$`).exec(url);
|
|
16564
|
+
if (undoMatch && method === "POST") {
|
|
16565
|
+
try {
|
|
16566
|
+
const body = await readJsonBody(req);
|
|
16567
|
+
const count = typeof body["count"] === "number" ? Number(body["count"]) : 1;
|
|
16568
|
+
await manager.undoHistory(decodeURIComponent(undoMatch[1]), count);
|
|
16569
|
+
sendJson(res, 200, { ok: true });
|
|
16570
|
+
} catch (error) {
|
|
16571
|
+
sendHttpError(res, error);
|
|
16572
|
+
}
|
|
16573
|
+
return true;
|
|
16574
|
+
}
|
|
16575
|
+
const compactMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/compact$`).exec(url);
|
|
16576
|
+
if (compactMatch && method === "POST") {
|
|
16577
|
+
try {
|
|
16578
|
+
const body = await readJsonBody(req);
|
|
16579
|
+
await manager.compact(decodeURIComponent(compactMatch[1]), optionalString(body, "instruction"));
|
|
16580
|
+
sendJson(res, 200, { ok: true });
|
|
16581
|
+
} catch (error) {
|
|
16582
|
+
sendHttpError(res, error);
|
|
16583
|
+
}
|
|
16584
|
+
return true;
|
|
16585
|
+
}
|
|
16586
|
+
return false;
|
|
16587
|
+
}
|
|
16588
|
+
async function handleResourceRoutes(req, res, url, method, manager) {
|
|
16589
|
+
const skillsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills$`).exec(url);
|
|
16590
|
+
if (skillsMatch && method === "GET") {
|
|
16591
|
+
try {
|
|
16592
|
+
sendJson(res, 200, await manager.listSkills(decodeURIComponent(skillsMatch[1])));
|
|
16593
|
+
} catch (error) {
|
|
16594
|
+
sendHttpError(res, error);
|
|
16595
|
+
}
|
|
16596
|
+
return true;
|
|
16597
|
+
}
|
|
16598
|
+
const skillActivateMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills/([^/]+)/activate$`).exec(url);
|
|
16599
|
+
if (skillActivateMatch && method === "POST") {
|
|
16600
|
+
try {
|
|
16601
|
+
const body = await readJsonBody(req);
|
|
16602
|
+
await manager.activateSkill(decodeURIComponent(skillActivateMatch[1]), decodeURIComponent(skillActivateMatch[2]), optionalString(body, "args"));
|
|
16603
|
+
sendJson(res, 200, { ok: true });
|
|
16604
|
+
} catch (error) {
|
|
16605
|
+
sendHttpError(res, error);
|
|
16606
|
+
}
|
|
16607
|
+
return true;
|
|
16608
|
+
}
|
|
16609
|
+
const skillRemoveMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills/([^/]+)$`).exec(url);
|
|
16610
|
+
if (skillRemoveMatch && method === "DELETE") {
|
|
16611
|
+
try {
|
|
16612
|
+
await manager.removeSkill(decodeURIComponent(skillRemoveMatch[1]), decodeURIComponent(skillRemoveMatch[2]));
|
|
16613
|
+
sendJson(res, 200, { ok: true });
|
|
16614
|
+
} catch (error) {
|
|
16615
|
+
sendHttpError(res, error);
|
|
16616
|
+
}
|
|
16617
|
+
return true;
|
|
16618
|
+
}
|
|
16619
|
+
const pluginsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins$`).exec(url);
|
|
16620
|
+
if (pluginsMatch && method === "GET") {
|
|
16621
|
+
try {
|
|
16622
|
+
sendJson(res, 200, await manager.listPlugins(decodeURIComponent(pluginsMatch[1])));
|
|
16623
|
+
} catch (error) {
|
|
16624
|
+
sendHttpError(res, error);
|
|
16625
|
+
}
|
|
16626
|
+
return true;
|
|
16627
|
+
}
|
|
16628
|
+
const pluginInstallMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/install$`).exec(url);
|
|
16629
|
+
if (pluginInstallMatch && method === "POST") {
|
|
16630
|
+
try {
|
|
16631
|
+
const body = await readJsonBody(req);
|
|
16632
|
+
sendJson(res, 200, await manager.installPlugin(decodeURIComponent(pluginInstallMatch[1]), requiredString(body, "source")));
|
|
16633
|
+
} catch (error) {
|
|
16634
|
+
sendHttpError(res, error);
|
|
16635
|
+
}
|
|
16636
|
+
return true;
|
|
16637
|
+
}
|
|
16638
|
+
const pluginReloadMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/reload$`).exec(url);
|
|
16639
|
+
if (pluginReloadMatch && method === "POST") {
|
|
16640
|
+
try {
|
|
16641
|
+
sendJson(res, 200, await manager.reloadPlugins(decodeURIComponent(pluginReloadMatch[1])));
|
|
16642
|
+
} catch (error) {
|
|
16643
|
+
sendHttpError(res, error);
|
|
16644
|
+
}
|
|
16645
|
+
return true;
|
|
16646
|
+
}
|
|
16647
|
+
const pluginEnableMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/enable$`).exec(url);
|
|
16648
|
+
if (pluginEnableMatch && method === "POST") {
|
|
16649
|
+
try {
|
|
16650
|
+
const body = await readJsonBody(req);
|
|
16651
|
+
await manager.setPluginEnabled(decodeURIComponent(pluginEnableMatch[1]), decodeURIComponent(pluginEnableMatch[2]), optionalBoolean(body, "enabled", true));
|
|
16652
|
+
sendJson(res, 200, { ok: true });
|
|
16653
|
+
} catch (error) {
|
|
16654
|
+
sendHttpError(res, error);
|
|
16655
|
+
}
|
|
16656
|
+
return true;
|
|
16657
|
+
}
|
|
16658
|
+
const pluginMcpEnableMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/mcp/([^/]+)/enable$`).exec(url);
|
|
16659
|
+
if (pluginMcpEnableMatch && method === "POST") {
|
|
16660
|
+
try {
|
|
16661
|
+
const body = await readJsonBody(req);
|
|
16662
|
+
await manager.setPluginMcpServerEnabled(decodeURIComponent(pluginMcpEnableMatch[1]), decodeURIComponent(pluginMcpEnableMatch[2]), decodeURIComponent(pluginMcpEnableMatch[3]), optionalBoolean(body, "enabled", true));
|
|
16663
|
+
sendJson(res, 200, { ok: true });
|
|
16664
|
+
} catch (error) {
|
|
16665
|
+
sendHttpError(res, error);
|
|
16666
|
+
}
|
|
16667
|
+
return true;
|
|
16668
|
+
}
|
|
16669
|
+
const pluginActivateMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/activate$`).exec(url);
|
|
16670
|
+
if (pluginActivateMatch && method === "POST") {
|
|
16671
|
+
try {
|
|
16672
|
+
await manager.activatePlugin(decodeURIComponent(pluginActivateMatch[1]), decodeURIComponent(pluginActivateMatch[2]));
|
|
16673
|
+
sendJson(res, 200, { ok: true });
|
|
16674
|
+
} catch (error) {
|
|
16675
|
+
sendHttpError(res, error);
|
|
16676
|
+
}
|
|
16677
|
+
return true;
|
|
16678
|
+
}
|
|
16679
|
+
const pluginDeactivateMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/deactivate$`).exec(url);
|
|
16680
|
+
if (pluginDeactivateMatch && method === "POST") {
|
|
16681
|
+
try {
|
|
16682
|
+
await manager.deactivatePlugin(decodeURIComponent(pluginDeactivateMatch[1]), decodeURIComponent(pluginDeactivateMatch[2]));
|
|
16683
|
+
sendJson(res, 200, { ok: true });
|
|
16684
|
+
} catch (error) {
|
|
16685
|
+
sendHttpError(res, error);
|
|
16686
|
+
}
|
|
16687
|
+
return true;
|
|
16688
|
+
}
|
|
16689
|
+
const pluginInjectMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/inject$`).exec(url);
|
|
16690
|
+
if (pluginInjectMatch && method === "POST") {
|
|
16691
|
+
try {
|
|
16692
|
+
await manager.injectPlugin(decodeURIComponent(pluginInjectMatch[1]), decodeURIComponent(pluginInjectMatch[2]));
|
|
16693
|
+
sendJson(res, 200, { ok: true });
|
|
16694
|
+
} catch (error) {
|
|
16695
|
+
sendHttpError(res, error);
|
|
16696
|
+
}
|
|
16697
|
+
return true;
|
|
16698
|
+
}
|
|
16699
|
+
const pluginInfoMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/(?!(?:install|reload)$)([^/]+)$`).exec(url);
|
|
16700
|
+
if (pluginInfoMatch && method === "GET") {
|
|
16701
|
+
try {
|
|
16702
|
+
sendJson(res, 200, await manager.getPluginInfo(decodeURIComponent(pluginInfoMatch[1]), decodeURIComponent(pluginInfoMatch[2])));
|
|
16703
|
+
} catch (error) {
|
|
16704
|
+
sendHttpError(res, error);
|
|
16705
|
+
}
|
|
16706
|
+
return true;
|
|
16707
|
+
}
|
|
16708
|
+
if (pluginInfoMatch && method === "DELETE") {
|
|
16709
|
+
try {
|
|
16710
|
+
await manager.removePlugin(decodeURIComponent(pluginInfoMatch[1]), decodeURIComponent(pluginInfoMatch[2]));
|
|
16711
|
+
sendJson(res, 200, { ok: true });
|
|
16712
|
+
} catch (error) {
|
|
16713
|
+
sendHttpError(res, error);
|
|
16714
|
+
}
|
|
16715
|
+
return true;
|
|
16716
|
+
}
|
|
16717
|
+
const mcpMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp$`).exec(url);
|
|
16718
|
+
if (mcpMatch && method === "GET") {
|
|
16719
|
+
try {
|
|
16720
|
+
sendJson(res, 200, await manager.listMcpServers(decodeURIComponent(mcpMatch[1])));
|
|
16721
|
+
} catch (error) {
|
|
16722
|
+
sendHttpError(res, error);
|
|
16723
|
+
}
|
|
16724
|
+
return true;
|
|
16725
|
+
}
|
|
16726
|
+
const mcpMetricsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/startup-metrics$`).exec(url);
|
|
16727
|
+
if (mcpMetricsMatch && method === "GET") {
|
|
16728
|
+
try {
|
|
16729
|
+
sendJson(res, 200, await manager.getMcpStartupMetrics(decodeURIComponent(mcpMetricsMatch[1])));
|
|
16730
|
+
} catch (error) {
|
|
16731
|
+
sendHttpError(res, error);
|
|
16732
|
+
}
|
|
16733
|
+
return true;
|
|
16734
|
+
}
|
|
16735
|
+
const mcpAddMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/add$`).exec(url);
|
|
16736
|
+
if (mcpAddMatch && method === "POST") {
|
|
16737
|
+
try {
|
|
16738
|
+
const body = await readJsonBody(req);
|
|
16739
|
+
await manager.addMcpServer(decodeURIComponent(mcpAddMatch[1]), requiredString(body, "name"), body["config"] ?? {});
|
|
16740
|
+
sendJson(res, 200, { ok: true });
|
|
16741
|
+
} catch (error) {
|
|
16742
|
+
sendHttpError(res, error);
|
|
16743
|
+
}
|
|
16744
|
+
return true;
|
|
16745
|
+
}
|
|
16746
|
+
const mcpReconnectMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/([^/]+)/reconnect$`).exec(url);
|
|
16747
|
+
if (mcpReconnectMatch && method === "POST") {
|
|
16748
|
+
try {
|
|
16749
|
+
await manager.reconnectMcpServer(decodeURIComponent(mcpReconnectMatch[1]), decodeURIComponent(mcpReconnectMatch[2]));
|
|
16750
|
+
sendJson(res, 200, { ok: true });
|
|
16751
|
+
} catch (error) {
|
|
16752
|
+
sendHttpError(res, error);
|
|
16753
|
+
}
|
|
16754
|
+
return true;
|
|
16755
|
+
}
|
|
16756
|
+
const mcpStopMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/([^/]+)/stop$`).exec(url);
|
|
16757
|
+
if (mcpStopMatch && method === "POST") {
|
|
16758
|
+
try {
|
|
16759
|
+
await manager.stopMcpServer(decodeURIComponent(mcpStopMatch[1]), decodeURIComponent(mcpStopMatch[2]));
|
|
16760
|
+
sendJson(res, 200, { ok: true });
|
|
16761
|
+
} catch (error) {
|
|
16762
|
+
sendHttpError(res, error);
|
|
16763
|
+
}
|
|
16764
|
+
return true;
|
|
16765
|
+
}
|
|
16766
|
+
const mcpRemoveMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/(?!(?:add|reconnect|stop|startup-metrics)$)([^/]+)$`).exec(url);
|
|
16767
|
+
if (mcpRemoveMatch && method === "DELETE") {
|
|
16768
|
+
try {
|
|
16769
|
+
await manager.removeMcpServer(decodeURIComponent(mcpRemoveMatch[1]), decodeURIComponent(mcpRemoveMatch[2]));
|
|
16770
|
+
sendJson(res, 200, { ok: true });
|
|
16771
|
+
} catch (error) {
|
|
16772
|
+
sendHttpError(res, error);
|
|
16773
|
+
}
|
|
16774
|
+
return true;
|
|
16775
|
+
}
|
|
16776
|
+
const tasksMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks$`).exec(url);
|
|
16777
|
+
if (tasksMatch && method === "GET") {
|
|
16778
|
+
try {
|
|
16779
|
+
const query = new URLSearchParams(url.split("?")[1] ?? "");
|
|
16780
|
+
const activeOnly = query.get("activeOnly") === "true" ? true : query.get("activeOnly") === "false" ? false : void 0;
|
|
16781
|
+
const limit = query.get("limit") ? Number(query.get("limit")) : void 0;
|
|
16782
|
+
sendJson(res, 200, await manager.listBackgroundTasks(decodeURIComponent(tasksMatch[1]), activeOnly, limit));
|
|
16783
|
+
} catch (error) {
|
|
16784
|
+
sendHttpError(res, error);
|
|
16785
|
+
}
|
|
16786
|
+
return true;
|
|
16787
|
+
}
|
|
16788
|
+
const taskOutputMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks/([^/]+)/output$`).exec(url);
|
|
16789
|
+
if (taskOutputMatch && method === "GET") {
|
|
16790
|
+
try {
|
|
16791
|
+
const query = new URLSearchParams(url.split("?")[1] ?? "");
|
|
16792
|
+
const tail = query.get("tail") ? Number(query.get("tail")) : void 0;
|
|
16793
|
+
sendJson(res, 200, { output: await manager.getBackgroundTaskOutput(decodeURIComponent(taskOutputMatch[1]), decodeURIComponent(taskOutputMatch[2]), tail) });
|
|
16794
|
+
} catch (error) {
|
|
16795
|
+
sendHttpError(res, error);
|
|
16796
|
+
}
|
|
16797
|
+
return true;
|
|
16798
|
+
}
|
|
16799
|
+
const taskStopMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks/([^/]+)/stop$`).exec(url);
|
|
16800
|
+
if (taskStopMatch && method === "POST") {
|
|
16801
|
+
try {
|
|
16802
|
+
const body = await readJsonBody(req);
|
|
16803
|
+
await manager.stopBackgroundTask(decodeURIComponent(taskStopMatch[1]), decodeURIComponent(taskStopMatch[2]), optionalString(body, "reason"));
|
|
16804
|
+
sendJson(res, 200, { ok: true });
|
|
16805
|
+
} catch (error) {
|
|
16806
|
+
sendHttpError(res, error);
|
|
16807
|
+
}
|
|
16808
|
+
return true;
|
|
16809
|
+
}
|
|
16810
|
+
return false;
|
|
16811
|
+
}
|
|
16812
|
+
async function handleGlobalRoutes(req, res, url, method, manager) {
|
|
16813
|
+
if (url === `${API_PREFIX}/config` && method === "GET") {
|
|
16814
|
+
try {
|
|
16815
|
+
sendJson(res, 200, await manager.getConfig());
|
|
16816
|
+
} catch (error) {
|
|
16817
|
+
sendHttpError(res, error);
|
|
16818
|
+
}
|
|
16819
|
+
return true;
|
|
16820
|
+
}
|
|
16821
|
+
if (url === `${API_PREFIX}/config` && method === "POST") {
|
|
16822
|
+
try {
|
|
16823
|
+
const patch = (await readJsonBody(req))["patch"];
|
|
16824
|
+
if (patch === null || typeof patch !== "object" || Array.isArray(patch)) throw new HttpError(400, "Missing or invalid patch field", "request.invalid");
|
|
16825
|
+
sendJson(res, 200, await manager.setConfig(patch));
|
|
16826
|
+
} catch (error) {
|
|
16827
|
+
sendHttpError(res, error);
|
|
16828
|
+
}
|
|
16829
|
+
return true;
|
|
16830
|
+
}
|
|
16831
|
+
const providerMatch = new RegExp(`^${API_PREFIX}/config/providers/([^/]+)$`).exec(url);
|
|
16832
|
+
if (providerMatch && method === "DELETE") {
|
|
16833
|
+
try {
|
|
16834
|
+
sendJson(res, 200, await manager.removeProvider(decodeURIComponent(providerMatch[1])));
|
|
16835
|
+
} catch (error) {
|
|
16836
|
+
sendHttpError(res, error);
|
|
16837
|
+
}
|
|
16838
|
+
return true;
|
|
16839
|
+
}
|
|
16840
|
+
if (url === `${API_PREFIX}/experimental-flags` && method === "GET") {
|
|
16841
|
+
try {
|
|
16842
|
+
sendJson(res, 200, await manager.getExperimentalFlags());
|
|
16843
|
+
} catch (error) {
|
|
16844
|
+
sendHttpError(res, error);
|
|
16845
|
+
}
|
|
16846
|
+
return true;
|
|
16847
|
+
}
|
|
16848
|
+
if (url === `${API_PREFIX}/preflight` && method === "GET") {
|
|
16849
|
+
try {
|
|
16850
|
+
await manager.preflight();
|
|
16851
|
+
sendJson(res, 200, { ok: true });
|
|
16852
|
+
} catch (error) {
|
|
16853
|
+
sendHttpError(res, error);
|
|
16854
|
+
}
|
|
16855
|
+
return true;
|
|
16856
|
+
}
|
|
16857
|
+
return false;
|
|
16858
|
+
}
|
|
16250
16859
|
async function runWebServer(opts) {
|
|
16251
16860
|
const homeDir = resolveScreamHome();
|
|
16252
16861
|
const workDir = opts.workDir;
|
|
@@ -16356,6 +16965,9 @@ async function runWebServer(opts) {
|
|
|
16356
16965
|
return;
|
|
16357
16966
|
}
|
|
16358
16967
|
if (await handleGoalRoute(req, res, url, (sessionId) => manager.get(sessionId))) return;
|
|
16968
|
+
if (await handleSessionControlRoutes(req, res, url, method, manager)) return;
|
|
16969
|
+
if (await handleResourceRoutes(req, res, url, method, manager)) return;
|
|
16970
|
+
if (await handleGlobalRoutes(req, res, url, method, manager)) return;
|
|
16359
16971
|
const snapshotMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/snapshot(\\?|$)`).exec(url);
|
|
16360
16972
|
if (snapshotMatch && method === "GET") {
|
|
16361
16973
|
const sessionId = decodeURIComponent(snapshotMatch[1]);
|