scream-code 0.15.0 → 0.15.2
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-CPu6R7nd.mjs → app-6Hcxs2tP.mjs} +607 -9
- package/dist/{dispatch-D8ovLbmr.mjs → dispatch-B_ViYZer.mjs} +113 -10
- package/dist/{dispatch-BPcn43Vw.mjs → dispatch-Dur5jHVw.mjs} +1 -1
- package/dist/main.mjs +1 -1
- package/dist/public/assets/index-B9I4PIWR.js +93 -0
- package/dist/public/assets/index-B9I4PIWR.js.map +1 -0
- package/dist/public/index.html +1 -1
- package/package.json +1 -1
- 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-B_ViYZer.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.2"),
|
|
36
36
|
channel: optionalBuildString(""),
|
|
37
37
|
commit: optionalBuildString(""),
|
|
38
38
|
buildTarget: optionalBuildString("darwin-arm64")
|
|
@@ -12397,7 +12397,7 @@ var ScreamTUI = class {
|
|
|
12397
12397
|
const card = new OnboardingCardComponent({
|
|
12398
12398
|
colors: this.state.theme.colors,
|
|
12399
12399
|
onConfigure: () => {
|
|
12400
|
-
import("./dispatch-
|
|
12400
|
+
import("./dispatch-Dur5jHVw.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
|
|
12401
12401
|
this.restoreEditor();
|
|
12402
12402
|
onDone?.();
|
|
12403
12403
|
});
|
|
@@ -14527,7 +14527,14 @@ function toHttpError(error) {
|
|
|
14527
14527
|
if (isScreamError(error)) switch (error.code) {
|
|
14528
14528
|
case ErrorCodes.GOAL_OBJECTIVE_EMPTY:
|
|
14529
14529
|
case ErrorCodes.GOAL_OBJECTIVE_TOO_LONG:
|
|
14530
|
-
case ErrorCodes.REQUEST_INVALID:
|
|
14530
|
+
case ErrorCodes.REQUEST_INVALID:
|
|
14531
|
+
case ErrorCodes.SESSION_PERMISSION_MODE_INVALID:
|
|
14532
|
+
case ErrorCodes.SESSION_PLAN_MODE_INVALID:
|
|
14533
|
+
case ErrorCodes.SESSION_WOLFPACK_MODE_INVALID:
|
|
14534
|
+
case ErrorCodes.SESSION_MODEL_EMPTY:
|
|
14535
|
+
case ErrorCodes.SESSION_THINKING_EMPTY:
|
|
14536
|
+
case ErrorCodes.SKILL_NAME_EMPTY:
|
|
14537
|
+
case ErrorCodes.BACKGROUND_TASK_ID_EMPTY: return new HttpError(400, error.message, error.code);
|
|
14531
14538
|
case ErrorCodes.GOAL_NOT_FOUND:
|
|
14532
14539
|
case ErrorCodes.SESSION_NOT_FOUND: return new HttpError(404, error.message, error.code);
|
|
14533
14540
|
case ErrorCodes.GOAL_ALREADY_EXISTS:
|
|
@@ -14629,9 +14636,13 @@ async function appendJournalLine(homeDir, sessionId, line) {
|
|
|
14629
14636
|
});
|
|
14630
14637
|
}
|
|
14631
14638
|
}
|
|
14639
|
+
let metaWriteSeq = 0;
|
|
14632
14640
|
async function saveMetadata(homeDir, meta) {
|
|
14633
14641
|
try {
|
|
14634
|
-
|
|
14642
|
+
const path = getMetaPath(homeDir, meta.sessionId);
|
|
14643
|
+
const tmp = `${path}.${process.pid}.${metaWriteSeq++}.tmp`;
|
|
14644
|
+
await writeFile(tmp, JSON.stringify(meta, null, 2));
|
|
14645
|
+
await rename(tmp, path);
|
|
14635
14646
|
} catch (error) {
|
|
14636
14647
|
log.warn("web: failed to save metadata", {
|
|
14637
14648
|
sessionId: meta.sessionId,
|
|
@@ -15032,6 +15043,15 @@ var WebSession = class {
|
|
|
15032
15043
|
});
|
|
15033
15044
|
await appendJournalLine(this.homeDir, this.sessionId, line);
|
|
15034
15045
|
}
|
|
15046
|
+
/** In-flight fire-and-forget persistence writes, drained by close(). */
|
|
15047
|
+
pendingWrites = /* @__PURE__ */ new Set();
|
|
15048
|
+
/** Register a background persistence promise so close() can await it. */
|
|
15049
|
+
trackPending(promise) {
|
|
15050
|
+
const tracked = promise.catch(() => {}).then(() => {
|
|
15051
|
+
this.pendingWrites.delete(tracked);
|
|
15052
|
+
});
|
|
15053
|
+
this.pendingWrites.add(tracked);
|
|
15054
|
+
}
|
|
15035
15055
|
loadFromPersisted(entries) {
|
|
15036
15056
|
for (const entry of entries) if ("type" in entry && entry.type === "user_message") {
|
|
15037
15057
|
const um = entry;
|
|
@@ -15082,7 +15102,7 @@ var WebSession = class {
|
|
|
15082
15102
|
payload
|
|
15083
15103
|
};
|
|
15084
15104
|
this.journal.push(entry);
|
|
15085
|
-
if (!entry.volatile) this.persistEntry(entry);
|
|
15105
|
+
if (!entry.volatile) this.trackPending(this.persistEntry(entry));
|
|
15086
15106
|
return entry;
|
|
15087
15107
|
}
|
|
15088
15108
|
/**
|
|
@@ -15098,7 +15118,7 @@ var WebSession = class {
|
|
|
15098
15118
|
payload
|
|
15099
15119
|
};
|
|
15100
15120
|
this.journal.push(entry);
|
|
15101
|
-
this.persistEntry(entry);
|
|
15121
|
+
this.trackPending(this.persistEntry(entry));
|
|
15102
15122
|
return entry;
|
|
15103
15123
|
}
|
|
15104
15124
|
subscribeEvents() {
|
|
@@ -15148,7 +15168,7 @@ var WebSession = class {
|
|
|
15148
15168
|
});
|
|
15149
15169
|
}
|
|
15150
15170
|
this.liveAssistant = null;
|
|
15151
|
-
this.updateTitle();
|
|
15171
|
+
this.trackPending(this.updateTitle());
|
|
15152
15172
|
} else if (event.type === "assistant.delta") {
|
|
15153
15173
|
const live = this.liveAssistant;
|
|
15154
15174
|
if (live) live.content += event.delta;
|
|
@@ -15336,7 +15356,7 @@ var WebSession = class {
|
|
|
15336
15356
|
return;
|
|
15337
15357
|
}
|
|
15338
15358
|
this.busy = true;
|
|
15339
|
-
this.persistUserMessage(text, this.nextSeq, clientMessageId);
|
|
15359
|
+
this.trackPending(this.persistUserMessage(text, this.nextSeq, clientMessageId));
|
|
15340
15360
|
this.broadcast({
|
|
15341
15361
|
type: "user_message",
|
|
15342
15362
|
clientMessageId,
|
|
@@ -15683,6 +15703,49 @@ var WebSession = class {
|
|
|
15683
15703
|
status: this.cachedStatus
|
|
15684
15704
|
}, false);
|
|
15685
15705
|
}
|
|
15706
|
+
/** Switch permission mode, then sync status + metadata (TUI /auto,/yes parity). */
|
|
15707
|
+
async switchPermission(mode) {
|
|
15708
|
+
if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换权限模式。");
|
|
15709
|
+
await this.session.setPermission(mode);
|
|
15710
|
+
this.permission = mode;
|
|
15711
|
+
await this.refreshStatus();
|
|
15712
|
+
this.broadcast({
|
|
15713
|
+
type: "status",
|
|
15714
|
+
status: this.cachedStatus
|
|
15715
|
+
}, false);
|
|
15716
|
+
if (this.homeDir) await saveMetadata(this.homeDir, this.getMetadata());
|
|
15717
|
+
}
|
|
15718
|
+
/** Toggle plan mode, then sync status (TUI /plan parity). */
|
|
15719
|
+
async switchPlanMode(enabled, strategy) {
|
|
15720
|
+
if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换计划模式。");
|
|
15721
|
+
await this.session.setPlanMode(enabled, strategy);
|
|
15722
|
+
await this.refreshStatus();
|
|
15723
|
+
this.broadcast({
|
|
15724
|
+
type: "status",
|
|
15725
|
+
status: this.cachedStatus
|
|
15726
|
+
}, false);
|
|
15727
|
+
}
|
|
15728
|
+
/** Toggle wolfpack mode, then sync status. */
|
|
15729
|
+
async switchWolfpackMode(enabled) {
|
|
15730
|
+
if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换 Wolfpack 模式。");
|
|
15731
|
+
await this.session.setWolfpackMode(enabled);
|
|
15732
|
+
await this.refreshStatus();
|
|
15733
|
+
this.broadcast({
|
|
15734
|
+
type: "status",
|
|
15735
|
+
status: this.cachedStatus
|
|
15736
|
+
}, false);
|
|
15737
|
+
}
|
|
15738
|
+
/** Toggle RLM persistent-python mode (+ optional max depth), then sync status. */
|
|
15739
|
+
async switchRlm(enabled, maxDepth) {
|
|
15740
|
+
if (!this.session) throw new HttpError(409, "会话已归档(只读),无法切换 RLM 模式。");
|
|
15741
|
+
await this.session.setRlmEnabled(enabled);
|
|
15742
|
+
if (maxDepth !== void 0) await this.session.setRlmMaxDepth(maxDepth);
|
|
15743
|
+
await this.refreshStatus();
|
|
15744
|
+
this.broadcast({
|
|
15745
|
+
type: "status",
|
|
15746
|
+
status: this.cachedStatus
|
|
15747
|
+
}, false);
|
|
15748
|
+
}
|
|
15686
15749
|
getCoreSessionId() {
|
|
15687
15750
|
return this.coreSessionId;
|
|
15688
15751
|
}
|
|
@@ -15745,6 +15808,15 @@ var WebSession = class {
|
|
|
15745
15808
|
await session.cancelGoal();
|
|
15746
15809
|
}, true);
|
|
15747
15810
|
}
|
|
15811
|
+
/**
|
|
15812
|
+
* Public handle to the live core Session for REST RPC forwarding.
|
|
15813
|
+
* Throws 409 for archived (read-only) sessions so handlers never call into a
|
|
15814
|
+
* closed core session instead of silently creating one.
|
|
15815
|
+
*/
|
|
15816
|
+
requireLiveSession() {
|
|
15817
|
+
if (!this.session) throw new HttpError(409, "Session is archived (read-only)", ErrorCodes.SESSION_CLOSED);
|
|
15818
|
+
return this.session;
|
|
15819
|
+
}
|
|
15748
15820
|
requireGoalSession(allowBusy = false) {
|
|
15749
15821
|
if (!this.session) throw new HttpError(409, "Session is archived (read-only)", ErrorCodes.SESSION_CLOSED);
|
|
15750
15822
|
if (!allowBusy && this.busy) throw new HttpError(409, "Session is busy", ErrorCodes.TURN_AGENT_BUSY);
|
|
@@ -15910,6 +15982,7 @@ var WebSession = class {
|
|
|
15910
15982
|
});
|
|
15911
15983
|
this.pendingApprovals.clear();
|
|
15912
15984
|
this.unsubscribe?.();
|
|
15985
|
+
await Promise.allSettled(this.pendingWrites);
|
|
15913
15986
|
for (const [ws] of this.connections) ws.terminate();
|
|
15914
15987
|
this.connections.clear();
|
|
15915
15988
|
if (this.session) {
|
|
@@ -16232,7 +16305,529 @@ var SessionManager = class {
|
|
|
16232
16305
|
for (const [, ws] of this.sessions) await ws.close();
|
|
16233
16306
|
this.sessions.clear();
|
|
16234
16307
|
}
|
|
16308
|
+
/** Resolve a live session or throw 404/409; the single guard for all handlers. */
|
|
16309
|
+
getLiveSession(sessionId) {
|
|
16310
|
+
const ws = this.sessions.get(sessionId);
|
|
16311
|
+
if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
|
|
16312
|
+
return ws.requireLiveSession();
|
|
16313
|
+
}
|
|
16314
|
+
async getSessionStatus(sessionId) {
|
|
16315
|
+
return this.getLiveSession(sessionId).getStatus();
|
|
16316
|
+
}
|
|
16317
|
+
async getSessionUsage(sessionId) {
|
|
16318
|
+
return this.getLiveSession(sessionId).getUsage();
|
|
16319
|
+
}
|
|
16320
|
+
async getSessionContext(sessionId) {
|
|
16321
|
+
return this.getLiveSession(sessionId).getContext();
|
|
16322
|
+
}
|
|
16323
|
+
async getSessionPlan(sessionId) {
|
|
16324
|
+
return this.getLiveSession(sessionId).getPlan();
|
|
16325
|
+
}
|
|
16326
|
+
async setPermission(sessionId, mode) {
|
|
16327
|
+
const ws = this.sessions.get(sessionId);
|
|
16328
|
+
if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
|
|
16329
|
+
await ws.switchPermission(mode);
|
|
16330
|
+
}
|
|
16331
|
+
async setPlanMode(sessionId, enabled, strategy) {
|
|
16332
|
+
const ws = this.sessions.get(sessionId);
|
|
16333
|
+
if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
|
|
16334
|
+
await ws.switchPlanMode(enabled, strategy);
|
|
16335
|
+
}
|
|
16336
|
+
async clearPlan(sessionId) {
|
|
16337
|
+
await this.getLiveSession(sessionId).clearPlan();
|
|
16338
|
+
}
|
|
16339
|
+
async setWolfpackMode(sessionId, enabled) {
|
|
16340
|
+
const ws = this.sessions.get(sessionId);
|
|
16341
|
+
if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
|
|
16342
|
+
await ws.switchWolfpackMode(enabled);
|
|
16343
|
+
}
|
|
16344
|
+
async setRlm(sessionId, enabled, maxDepth) {
|
|
16345
|
+
const ws = this.sessions.get(sessionId);
|
|
16346
|
+
if (!ws) throw new HttpError(404, "Session not found", ErrorCodes.SESSION_NOT_FOUND);
|
|
16347
|
+
await ws.switchRlm(enabled, maxDepth);
|
|
16348
|
+
}
|
|
16349
|
+
async undoHistory(sessionId, count) {
|
|
16350
|
+
await this.getLiveSession(sessionId).undoHistory(count);
|
|
16351
|
+
}
|
|
16352
|
+
async compact(sessionId, instruction) {
|
|
16353
|
+
await this.getLiveSession(sessionId).compact(instruction ? { instruction } : {});
|
|
16354
|
+
}
|
|
16355
|
+
async listSkills(sessionId) {
|
|
16356
|
+
return this.getLiveSession(sessionId).listSkills();
|
|
16357
|
+
}
|
|
16358
|
+
async activateSkill(sessionId, name, args) {
|
|
16359
|
+
await this.getLiveSession(sessionId).activateSkill(name, args);
|
|
16360
|
+
}
|
|
16361
|
+
async removeSkill(sessionId, name) {
|
|
16362
|
+
await this.getLiveSession(sessionId).removeSkill(name);
|
|
16363
|
+
}
|
|
16364
|
+
async listPlugins(sessionId) {
|
|
16365
|
+
return this.getLiveSession(sessionId).listPlugins();
|
|
16366
|
+
}
|
|
16367
|
+
async getPluginInfo(sessionId, id) {
|
|
16368
|
+
return this.getLiveSession(sessionId).getPluginInfo(id);
|
|
16369
|
+
}
|
|
16370
|
+
async installPlugin(sessionId, source) {
|
|
16371
|
+
return this.getLiveSession(sessionId).installPlugin(source);
|
|
16372
|
+
}
|
|
16373
|
+
async setPluginEnabled(sessionId, id, enabled) {
|
|
16374
|
+
await this.getLiveSession(sessionId).setPluginEnabled(id, enabled);
|
|
16375
|
+
}
|
|
16376
|
+
async setPluginMcpServerEnabled(sessionId, id, server, enabled) {
|
|
16377
|
+
await this.getLiveSession(sessionId).setPluginMcpServerEnabled(id, server, enabled);
|
|
16378
|
+
}
|
|
16379
|
+
async removePlugin(sessionId, id) {
|
|
16380
|
+
await this.getLiveSession(sessionId).removePlugin(id);
|
|
16381
|
+
}
|
|
16382
|
+
async reloadPlugins(sessionId) {
|
|
16383
|
+
return this.getLiveSession(sessionId).reloadPlugins();
|
|
16384
|
+
}
|
|
16385
|
+
async activatePlugin(sessionId, id) {
|
|
16386
|
+
await this.getLiveSession(sessionId).activatePlugin(id);
|
|
16387
|
+
}
|
|
16388
|
+
async deactivatePlugin(sessionId, id) {
|
|
16389
|
+
await this.getLiveSession(sessionId).deactivatePlugin(id);
|
|
16390
|
+
}
|
|
16391
|
+
async injectPlugin(sessionId, id) {
|
|
16392
|
+
await this.getLiveSession(sessionId).injectPlugin(id);
|
|
16393
|
+
}
|
|
16394
|
+
async listMcpServers(sessionId) {
|
|
16395
|
+
return this.getLiveSession(sessionId).listMcpServers();
|
|
16396
|
+
}
|
|
16397
|
+
async getMcpStartupMetrics(sessionId) {
|
|
16398
|
+
return this.getLiveSession(sessionId).getMcpStartupMetrics();
|
|
16399
|
+
}
|
|
16400
|
+
async addMcpServer(sessionId, name, config) {
|
|
16401
|
+
await this.getLiveSession(sessionId).addMcpServer(name, config);
|
|
16402
|
+
}
|
|
16403
|
+
async reconnectMcpServer(sessionId, name) {
|
|
16404
|
+
await this.getLiveSession(sessionId).reconnectMcpServer(name);
|
|
16405
|
+
}
|
|
16406
|
+
async stopMcpServer(sessionId, name) {
|
|
16407
|
+
await this.getLiveSession(sessionId).stopMcpServer(name);
|
|
16408
|
+
}
|
|
16409
|
+
async removeMcpServer(sessionId, name) {
|
|
16410
|
+
await this.getLiveSession(sessionId).removeMcpServer(name);
|
|
16411
|
+
}
|
|
16412
|
+
async listBackgroundTasks(sessionId, activeOnly, limit) {
|
|
16413
|
+
return this.getLiveSession(sessionId).listBackgroundTasks({
|
|
16414
|
+
activeOnly,
|
|
16415
|
+
limit
|
|
16416
|
+
});
|
|
16417
|
+
}
|
|
16418
|
+
async getBackgroundTaskOutput(sessionId, taskId, tail) {
|
|
16419
|
+
return this.getLiveSession(sessionId).getBackgroundTaskOutput(taskId, tail !== void 0 ? { tail } : {});
|
|
16420
|
+
}
|
|
16421
|
+
async stopBackgroundTask(sessionId, taskId, reason) {
|
|
16422
|
+
await this.getLiveSession(sessionId).stopBackgroundTask(taskId, reason !== void 0 ? { reason } : {});
|
|
16423
|
+
}
|
|
16424
|
+
getConfig(options) {
|
|
16425
|
+
return this.harness.getConfig(options);
|
|
16426
|
+
}
|
|
16427
|
+
setConfig(patch) {
|
|
16428
|
+
return this.harness.setConfig(patch);
|
|
16429
|
+
}
|
|
16430
|
+
removeProvider(providerId) {
|
|
16431
|
+
return this.harness.removeProvider(providerId);
|
|
16432
|
+
}
|
|
16433
|
+
getExperimentalFlags() {
|
|
16434
|
+
return this.harness.getExperimentalFlags();
|
|
16435
|
+
}
|
|
16436
|
+
preflight() {
|
|
16437
|
+
return this.harness.preflight();
|
|
16438
|
+
}
|
|
16235
16439
|
};
|
|
16440
|
+
async function handleSessionControlRoutes(req, res, url, method, manager) {
|
|
16441
|
+
const statusMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/status$`).exec(url);
|
|
16442
|
+
if (statusMatch && method === "GET") {
|
|
16443
|
+
try {
|
|
16444
|
+
sendJson(res, 200, await manager.getSessionStatus(decodeURIComponent(statusMatch[1])));
|
|
16445
|
+
} catch (error) {
|
|
16446
|
+
sendHttpError(res, error);
|
|
16447
|
+
}
|
|
16448
|
+
return true;
|
|
16449
|
+
}
|
|
16450
|
+
const usageMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/usage$`).exec(url);
|
|
16451
|
+
if (usageMatch && method === "GET") {
|
|
16452
|
+
try {
|
|
16453
|
+
sendJson(res, 200, await manager.getSessionUsage(decodeURIComponent(usageMatch[1])));
|
|
16454
|
+
} catch (error) {
|
|
16455
|
+
sendHttpError(res, error);
|
|
16456
|
+
}
|
|
16457
|
+
return true;
|
|
16458
|
+
}
|
|
16459
|
+
const contextMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/context$`).exec(url);
|
|
16460
|
+
if (contextMatch && method === "GET") {
|
|
16461
|
+
try {
|
|
16462
|
+
sendJson(res, 200, await manager.getSessionContext(decodeURIComponent(contextMatch[1])));
|
|
16463
|
+
} catch (error) {
|
|
16464
|
+
sendHttpError(res, error);
|
|
16465
|
+
}
|
|
16466
|
+
return true;
|
|
16467
|
+
}
|
|
16468
|
+
const planMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plan$`).exec(url);
|
|
16469
|
+
if (planMatch && method === "GET") {
|
|
16470
|
+
try {
|
|
16471
|
+
sendJson(res, 200, await manager.getSessionPlan(decodeURIComponent(planMatch[1])));
|
|
16472
|
+
} catch (error) {
|
|
16473
|
+
sendHttpError(res, error);
|
|
16474
|
+
}
|
|
16475
|
+
return true;
|
|
16476
|
+
}
|
|
16477
|
+
const permissionMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/permission$`).exec(url);
|
|
16478
|
+
if (permissionMatch && method === "POST") {
|
|
16479
|
+
try {
|
|
16480
|
+
const mode = requiredString(await readJsonBody(req), "mode");
|
|
16481
|
+
await manager.setPermission(decodeURIComponent(permissionMatch[1]), mode);
|
|
16482
|
+
sendJson(res, 200, { ok: true });
|
|
16483
|
+
} catch (error) {
|
|
16484
|
+
sendHttpError(res, error);
|
|
16485
|
+
}
|
|
16486
|
+
return true;
|
|
16487
|
+
}
|
|
16488
|
+
const planModeMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plan$`).exec(url);
|
|
16489
|
+
if (planModeMatch && method === "POST") {
|
|
16490
|
+
try {
|
|
16491
|
+
const body = await readJsonBody(req);
|
|
16492
|
+
const enabled = optionalBoolean(body, "enabled", true);
|
|
16493
|
+
const strategy = optionalString(body, "strategy");
|
|
16494
|
+
await manager.setPlanMode(decodeURIComponent(planModeMatch[1]), enabled, strategy);
|
|
16495
|
+
sendJson(res, 200, { ok: true });
|
|
16496
|
+
} catch (error) {
|
|
16497
|
+
sendHttpError(res, error);
|
|
16498
|
+
}
|
|
16499
|
+
return true;
|
|
16500
|
+
}
|
|
16501
|
+
const clearPlanMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plan/clear$`).exec(url);
|
|
16502
|
+
if (clearPlanMatch && method === "POST") {
|
|
16503
|
+
try {
|
|
16504
|
+
await manager.clearPlan(decodeURIComponent(clearPlanMatch[1]));
|
|
16505
|
+
sendJson(res, 200, { ok: true });
|
|
16506
|
+
} catch (error) {
|
|
16507
|
+
sendHttpError(res, error);
|
|
16508
|
+
}
|
|
16509
|
+
return true;
|
|
16510
|
+
}
|
|
16511
|
+
const wolfpackMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/wolfpack$`).exec(url);
|
|
16512
|
+
if (wolfpackMatch && method === "POST") {
|
|
16513
|
+
try {
|
|
16514
|
+
const enabled = optionalBoolean(await readJsonBody(req), "enabled", true);
|
|
16515
|
+
await manager.setWolfpackMode(decodeURIComponent(wolfpackMatch[1]), enabled);
|
|
16516
|
+
sendJson(res, 200, { ok: true });
|
|
16517
|
+
} catch (error) {
|
|
16518
|
+
sendHttpError(res, error);
|
|
16519
|
+
}
|
|
16520
|
+
return true;
|
|
16521
|
+
}
|
|
16522
|
+
const rlmMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/rlm$`).exec(url);
|
|
16523
|
+
if (rlmMatch && method === "POST") {
|
|
16524
|
+
try {
|
|
16525
|
+
const body = await readJsonBody(req);
|
|
16526
|
+
const enabled = optionalBoolean(body, "enabled", true);
|
|
16527
|
+
const maxDepth = typeof body["maxDepth"] === "number" ? Number(body["maxDepth"]) : void 0;
|
|
16528
|
+
await manager.setRlm(decodeURIComponent(rlmMatch[1]), enabled, maxDepth);
|
|
16529
|
+
sendJson(res, 200, { ok: true });
|
|
16530
|
+
} catch (error) {
|
|
16531
|
+
sendHttpError(res, error);
|
|
16532
|
+
}
|
|
16533
|
+
return true;
|
|
16534
|
+
}
|
|
16535
|
+
const undoMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/undo$`).exec(url);
|
|
16536
|
+
if (undoMatch && method === "POST") {
|
|
16537
|
+
try {
|
|
16538
|
+
const body = await readJsonBody(req);
|
|
16539
|
+
const count = typeof body["count"] === "number" ? Number(body["count"]) : 1;
|
|
16540
|
+
await manager.undoHistory(decodeURIComponent(undoMatch[1]), count);
|
|
16541
|
+
sendJson(res, 200, { ok: true });
|
|
16542
|
+
} catch (error) {
|
|
16543
|
+
sendHttpError(res, error);
|
|
16544
|
+
}
|
|
16545
|
+
return true;
|
|
16546
|
+
}
|
|
16547
|
+
const compactMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/compact$`).exec(url);
|
|
16548
|
+
if (compactMatch && method === "POST") {
|
|
16549
|
+
try {
|
|
16550
|
+
const body = await readJsonBody(req);
|
|
16551
|
+
await manager.compact(decodeURIComponent(compactMatch[1]), optionalString(body, "instruction"));
|
|
16552
|
+
sendJson(res, 200, { ok: true });
|
|
16553
|
+
} catch (error) {
|
|
16554
|
+
sendHttpError(res, error);
|
|
16555
|
+
}
|
|
16556
|
+
return true;
|
|
16557
|
+
}
|
|
16558
|
+
return false;
|
|
16559
|
+
}
|
|
16560
|
+
async function handleResourceRoutes(req, res, url, method, manager) {
|
|
16561
|
+
const skillsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills$`).exec(url);
|
|
16562
|
+
if (skillsMatch && method === "GET") {
|
|
16563
|
+
try {
|
|
16564
|
+
sendJson(res, 200, await manager.listSkills(decodeURIComponent(skillsMatch[1])));
|
|
16565
|
+
} catch (error) {
|
|
16566
|
+
sendHttpError(res, error);
|
|
16567
|
+
}
|
|
16568
|
+
return true;
|
|
16569
|
+
}
|
|
16570
|
+
const skillActivateMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills/([^/]+)/activate$`).exec(url);
|
|
16571
|
+
if (skillActivateMatch && method === "POST") {
|
|
16572
|
+
try {
|
|
16573
|
+
const body = await readJsonBody(req);
|
|
16574
|
+
await manager.activateSkill(decodeURIComponent(skillActivateMatch[1]), decodeURIComponent(skillActivateMatch[2]), optionalString(body, "args"));
|
|
16575
|
+
sendJson(res, 200, { ok: true });
|
|
16576
|
+
} catch (error) {
|
|
16577
|
+
sendHttpError(res, error);
|
|
16578
|
+
}
|
|
16579
|
+
return true;
|
|
16580
|
+
}
|
|
16581
|
+
const skillRemoveMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/skills/([^/]+)$`).exec(url);
|
|
16582
|
+
if (skillRemoveMatch && method === "DELETE") {
|
|
16583
|
+
try {
|
|
16584
|
+
await manager.removeSkill(decodeURIComponent(skillRemoveMatch[1]), decodeURIComponent(skillRemoveMatch[2]));
|
|
16585
|
+
sendJson(res, 200, { ok: true });
|
|
16586
|
+
} catch (error) {
|
|
16587
|
+
sendHttpError(res, error);
|
|
16588
|
+
}
|
|
16589
|
+
return true;
|
|
16590
|
+
}
|
|
16591
|
+
const pluginsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins$`).exec(url);
|
|
16592
|
+
if (pluginsMatch && method === "GET") {
|
|
16593
|
+
try {
|
|
16594
|
+
sendJson(res, 200, await manager.listPlugins(decodeURIComponent(pluginsMatch[1])));
|
|
16595
|
+
} catch (error) {
|
|
16596
|
+
sendHttpError(res, error);
|
|
16597
|
+
}
|
|
16598
|
+
return true;
|
|
16599
|
+
}
|
|
16600
|
+
const pluginInstallMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/install$`).exec(url);
|
|
16601
|
+
if (pluginInstallMatch && method === "POST") {
|
|
16602
|
+
try {
|
|
16603
|
+
const body = await readJsonBody(req);
|
|
16604
|
+
sendJson(res, 200, await manager.installPlugin(decodeURIComponent(pluginInstallMatch[1]), requiredString(body, "source")));
|
|
16605
|
+
} catch (error) {
|
|
16606
|
+
sendHttpError(res, error);
|
|
16607
|
+
}
|
|
16608
|
+
return true;
|
|
16609
|
+
}
|
|
16610
|
+
const pluginReloadMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/reload$`).exec(url);
|
|
16611
|
+
if (pluginReloadMatch && method === "POST") {
|
|
16612
|
+
try {
|
|
16613
|
+
sendJson(res, 200, await manager.reloadPlugins(decodeURIComponent(pluginReloadMatch[1])));
|
|
16614
|
+
} catch (error) {
|
|
16615
|
+
sendHttpError(res, error);
|
|
16616
|
+
}
|
|
16617
|
+
return true;
|
|
16618
|
+
}
|
|
16619
|
+
const pluginEnableMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/enable$`).exec(url);
|
|
16620
|
+
if (pluginEnableMatch && method === "POST") {
|
|
16621
|
+
try {
|
|
16622
|
+
const body = await readJsonBody(req);
|
|
16623
|
+
await manager.setPluginEnabled(decodeURIComponent(pluginEnableMatch[1]), decodeURIComponent(pluginEnableMatch[2]), optionalBoolean(body, "enabled", true));
|
|
16624
|
+
sendJson(res, 200, { ok: true });
|
|
16625
|
+
} catch (error) {
|
|
16626
|
+
sendHttpError(res, error);
|
|
16627
|
+
}
|
|
16628
|
+
return true;
|
|
16629
|
+
}
|
|
16630
|
+
const pluginMcpEnableMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/mcp/([^/]+)/enable$`).exec(url);
|
|
16631
|
+
if (pluginMcpEnableMatch && method === "POST") {
|
|
16632
|
+
try {
|
|
16633
|
+
const body = await readJsonBody(req);
|
|
16634
|
+
await manager.setPluginMcpServerEnabled(decodeURIComponent(pluginMcpEnableMatch[1]), decodeURIComponent(pluginMcpEnableMatch[2]), decodeURIComponent(pluginMcpEnableMatch[3]), optionalBoolean(body, "enabled", true));
|
|
16635
|
+
sendJson(res, 200, { ok: true });
|
|
16636
|
+
} catch (error) {
|
|
16637
|
+
sendHttpError(res, error);
|
|
16638
|
+
}
|
|
16639
|
+
return true;
|
|
16640
|
+
}
|
|
16641
|
+
const pluginActivateMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/activate$`).exec(url);
|
|
16642
|
+
if (pluginActivateMatch && method === "POST") {
|
|
16643
|
+
try {
|
|
16644
|
+
await manager.activatePlugin(decodeURIComponent(pluginActivateMatch[1]), decodeURIComponent(pluginActivateMatch[2]));
|
|
16645
|
+
sendJson(res, 200, { ok: true });
|
|
16646
|
+
} catch (error) {
|
|
16647
|
+
sendHttpError(res, error);
|
|
16648
|
+
}
|
|
16649
|
+
return true;
|
|
16650
|
+
}
|
|
16651
|
+
const pluginDeactivateMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/deactivate$`).exec(url);
|
|
16652
|
+
if (pluginDeactivateMatch && method === "POST") {
|
|
16653
|
+
try {
|
|
16654
|
+
await manager.deactivatePlugin(decodeURIComponent(pluginDeactivateMatch[1]), decodeURIComponent(pluginDeactivateMatch[2]));
|
|
16655
|
+
sendJson(res, 200, { ok: true });
|
|
16656
|
+
} catch (error) {
|
|
16657
|
+
sendHttpError(res, error);
|
|
16658
|
+
}
|
|
16659
|
+
return true;
|
|
16660
|
+
}
|
|
16661
|
+
const pluginInjectMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/([^/]+)/inject$`).exec(url);
|
|
16662
|
+
if (pluginInjectMatch && method === "POST") {
|
|
16663
|
+
try {
|
|
16664
|
+
await manager.injectPlugin(decodeURIComponent(pluginInjectMatch[1]), decodeURIComponent(pluginInjectMatch[2]));
|
|
16665
|
+
sendJson(res, 200, { ok: true });
|
|
16666
|
+
} catch (error) {
|
|
16667
|
+
sendHttpError(res, error);
|
|
16668
|
+
}
|
|
16669
|
+
return true;
|
|
16670
|
+
}
|
|
16671
|
+
const pluginInfoMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/plugins/(?!(?:install|reload)$)([^/]+)$`).exec(url);
|
|
16672
|
+
if (pluginInfoMatch && method === "GET") {
|
|
16673
|
+
try {
|
|
16674
|
+
sendJson(res, 200, await manager.getPluginInfo(decodeURIComponent(pluginInfoMatch[1]), decodeURIComponent(pluginInfoMatch[2])));
|
|
16675
|
+
} catch (error) {
|
|
16676
|
+
sendHttpError(res, error);
|
|
16677
|
+
}
|
|
16678
|
+
return true;
|
|
16679
|
+
}
|
|
16680
|
+
if (pluginInfoMatch && method === "DELETE") {
|
|
16681
|
+
try {
|
|
16682
|
+
await manager.removePlugin(decodeURIComponent(pluginInfoMatch[1]), decodeURIComponent(pluginInfoMatch[2]));
|
|
16683
|
+
sendJson(res, 200, { ok: true });
|
|
16684
|
+
} catch (error) {
|
|
16685
|
+
sendHttpError(res, error);
|
|
16686
|
+
}
|
|
16687
|
+
return true;
|
|
16688
|
+
}
|
|
16689
|
+
const mcpMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp$`).exec(url);
|
|
16690
|
+
if (mcpMatch && method === "GET") {
|
|
16691
|
+
try {
|
|
16692
|
+
sendJson(res, 200, await manager.listMcpServers(decodeURIComponent(mcpMatch[1])));
|
|
16693
|
+
} catch (error) {
|
|
16694
|
+
sendHttpError(res, error);
|
|
16695
|
+
}
|
|
16696
|
+
return true;
|
|
16697
|
+
}
|
|
16698
|
+
const mcpMetricsMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/startup-metrics$`).exec(url);
|
|
16699
|
+
if (mcpMetricsMatch && method === "GET") {
|
|
16700
|
+
try {
|
|
16701
|
+
sendJson(res, 200, await manager.getMcpStartupMetrics(decodeURIComponent(mcpMetricsMatch[1])));
|
|
16702
|
+
} catch (error) {
|
|
16703
|
+
sendHttpError(res, error);
|
|
16704
|
+
}
|
|
16705
|
+
return true;
|
|
16706
|
+
}
|
|
16707
|
+
const mcpAddMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/add$`).exec(url);
|
|
16708
|
+
if (mcpAddMatch && method === "POST") {
|
|
16709
|
+
try {
|
|
16710
|
+
const body = await readJsonBody(req);
|
|
16711
|
+
await manager.addMcpServer(decodeURIComponent(mcpAddMatch[1]), requiredString(body, "name"), body["config"] ?? {});
|
|
16712
|
+
sendJson(res, 200, { ok: true });
|
|
16713
|
+
} catch (error) {
|
|
16714
|
+
sendHttpError(res, error);
|
|
16715
|
+
}
|
|
16716
|
+
return true;
|
|
16717
|
+
}
|
|
16718
|
+
const mcpReconnectMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/([^/]+)/reconnect$`).exec(url);
|
|
16719
|
+
if (mcpReconnectMatch && method === "POST") {
|
|
16720
|
+
try {
|
|
16721
|
+
await manager.reconnectMcpServer(decodeURIComponent(mcpReconnectMatch[1]), decodeURIComponent(mcpReconnectMatch[2]));
|
|
16722
|
+
sendJson(res, 200, { ok: true });
|
|
16723
|
+
} catch (error) {
|
|
16724
|
+
sendHttpError(res, error);
|
|
16725
|
+
}
|
|
16726
|
+
return true;
|
|
16727
|
+
}
|
|
16728
|
+
const mcpStopMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/([^/]+)/stop$`).exec(url);
|
|
16729
|
+
if (mcpStopMatch && method === "POST") {
|
|
16730
|
+
try {
|
|
16731
|
+
await manager.stopMcpServer(decodeURIComponent(mcpStopMatch[1]), decodeURIComponent(mcpStopMatch[2]));
|
|
16732
|
+
sendJson(res, 200, { ok: true });
|
|
16733
|
+
} catch (error) {
|
|
16734
|
+
sendHttpError(res, error);
|
|
16735
|
+
}
|
|
16736
|
+
return true;
|
|
16737
|
+
}
|
|
16738
|
+
const mcpRemoveMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/mcp/(?!(?:add|reconnect|stop|startup-metrics)$)([^/]+)$`).exec(url);
|
|
16739
|
+
if (mcpRemoveMatch && method === "DELETE") {
|
|
16740
|
+
try {
|
|
16741
|
+
await manager.removeMcpServer(decodeURIComponent(mcpRemoveMatch[1]), decodeURIComponent(mcpRemoveMatch[2]));
|
|
16742
|
+
sendJson(res, 200, { ok: true });
|
|
16743
|
+
} catch (error) {
|
|
16744
|
+
sendHttpError(res, error);
|
|
16745
|
+
}
|
|
16746
|
+
return true;
|
|
16747
|
+
}
|
|
16748
|
+
const tasksMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks$`).exec(url);
|
|
16749
|
+
if (tasksMatch && method === "GET") {
|
|
16750
|
+
try {
|
|
16751
|
+
const query = new URLSearchParams(url.split("?")[1] ?? "");
|
|
16752
|
+
const activeOnly = query.get("activeOnly") === "true" ? true : query.get("activeOnly") === "false" ? false : void 0;
|
|
16753
|
+
const limit = query.get("limit") ? Number(query.get("limit")) : void 0;
|
|
16754
|
+
sendJson(res, 200, await manager.listBackgroundTasks(decodeURIComponent(tasksMatch[1]), activeOnly, limit));
|
|
16755
|
+
} catch (error) {
|
|
16756
|
+
sendHttpError(res, error);
|
|
16757
|
+
}
|
|
16758
|
+
return true;
|
|
16759
|
+
}
|
|
16760
|
+
const taskOutputMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks/([^/]+)/output$`).exec(url);
|
|
16761
|
+
if (taskOutputMatch && method === "GET") {
|
|
16762
|
+
try {
|
|
16763
|
+
const query = new URLSearchParams(url.split("?")[1] ?? "");
|
|
16764
|
+
const tail = query.get("tail") ? Number(query.get("tail")) : void 0;
|
|
16765
|
+
sendJson(res, 200, { output: await manager.getBackgroundTaskOutput(decodeURIComponent(taskOutputMatch[1]), decodeURIComponent(taskOutputMatch[2]), tail) });
|
|
16766
|
+
} catch (error) {
|
|
16767
|
+
sendHttpError(res, error);
|
|
16768
|
+
}
|
|
16769
|
+
return true;
|
|
16770
|
+
}
|
|
16771
|
+
const taskStopMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/tasks/([^/]+)/stop$`).exec(url);
|
|
16772
|
+
if (taskStopMatch && method === "POST") {
|
|
16773
|
+
try {
|
|
16774
|
+
const body = await readJsonBody(req);
|
|
16775
|
+
await manager.stopBackgroundTask(decodeURIComponent(taskStopMatch[1]), decodeURIComponent(taskStopMatch[2]), optionalString(body, "reason"));
|
|
16776
|
+
sendJson(res, 200, { ok: true });
|
|
16777
|
+
} catch (error) {
|
|
16778
|
+
sendHttpError(res, error);
|
|
16779
|
+
}
|
|
16780
|
+
return true;
|
|
16781
|
+
}
|
|
16782
|
+
return false;
|
|
16783
|
+
}
|
|
16784
|
+
async function handleGlobalRoutes(req, res, url, method, manager) {
|
|
16785
|
+
if (url === `${API_PREFIX}/config` && method === "GET") {
|
|
16786
|
+
try {
|
|
16787
|
+
sendJson(res, 200, await manager.getConfig());
|
|
16788
|
+
} catch (error) {
|
|
16789
|
+
sendHttpError(res, error);
|
|
16790
|
+
}
|
|
16791
|
+
return true;
|
|
16792
|
+
}
|
|
16793
|
+
if (url === `${API_PREFIX}/config` && method === "POST") {
|
|
16794
|
+
try {
|
|
16795
|
+
const patch = (await readJsonBody(req))["patch"];
|
|
16796
|
+
if (patch === null || typeof patch !== "object" || Array.isArray(patch)) throw new HttpError(400, "Missing or invalid patch field", "request.invalid");
|
|
16797
|
+
sendJson(res, 200, await manager.setConfig(patch));
|
|
16798
|
+
} catch (error) {
|
|
16799
|
+
sendHttpError(res, error);
|
|
16800
|
+
}
|
|
16801
|
+
return true;
|
|
16802
|
+
}
|
|
16803
|
+
const providerMatch = new RegExp(`^${API_PREFIX}/config/providers/([^/]+)$`).exec(url);
|
|
16804
|
+
if (providerMatch && method === "DELETE") {
|
|
16805
|
+
try {
|
|
16806
|
+
sendJson(res, 200, await manager.removeProvider(decodeURIComponent(providerMatch[1])));
|
|
16807
|
+
} catch (error) {
|
|
16808
|
+
sendHttpError(res, error);
|
|
16809
|
+
}
|
|
16810
|
+
return true;
|
|
16811
|
+
}
|
|
16812
|
+
if (url === `${API_PREFIX}/experimental-flags` && method === "GET") {
|
|
16813
|
+
try {
|
|
16814
|
+
sendJson(res, 200, await manager.getExperimentalFlags());
|
|
16815
|
+
} catch (error) {
|
|
16816
|
+
sendHttpError(res, error);
|
|
16817
|
+
}
|
|
16818
|
+
return true;
|
|
16819
|
+
}
|
|
16820
|
+
if (url === `${API_PREFIX}/preflight` && method === "GET") {
|
|
16821
|
+
try {
|
|
16822
|
+
await manager.preflight();
|
|
16823
|
+
sendJson(res, 200, { ok: true });
|
|
16824
|
+
} catch (error) {
|
|
16825
|
+
sendHttpError(res, error);
|
|
16826
|
+
}
|
|
16827
|
+
return true;
|
|
16828
|
+
}
|
|
16829
|
+
return false;
|
|
16830
|
+
}
|
|
16236
16831
|
async function runWebServer(opts) {
|
|
16237
16832
|
const homeDir = resolveScreamHome();
|
|
16238
16833
|
const workDir = opts.workDir;
|
|
@@ -16342,6 +16937,9 @@ async function runWebServer(opts) {
|
|
|
16342
16937
|
return;
|
|
16343
16938
|
}
|
|
16344
16939
|
if (await handleGoalRoute(req, res, url, (sessionId) => manager.get(sessionId))) return;
|
|
16940
|
+
if (await handleSessionControlRoutes(req, res, url, method, manager)) return;
|
|
16941
|
+
if (await handleResourceRoutes(req, res, url, method, manager)) return;
|
|
16942
|
+
if (await handleGlobalRoutes(req, res, url, method, manager)) return;
|
|
16345
16943
|
const snapshotMatch = new RegExp(`^${API_PREFIX}/sessions/([^/]+)/snapshot(\\?|$)`).exec(url);
|
|
16346
16944
|
if (snapshotMatch && method === "GET") {
|
|
16347
16945
|
const sessionId = decodeURIComponent(snapshotMatch[1]);
|