@epoch-agent/server 0.8.1 → 0.9.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/dist/index.d.ts +75 -13
- package/dist/index.js +61 -12
- package/dist/web/assets/index-B0ky2TY1.css +1 -0
- package/dist/web/assets/index-C8ef1aOs.js +376 -0
- package/dist/web/index.html +2 -2
- package/package.json +3 -3
- package/dist/web/assets/index-Cl7UCWRQ.css +0 -1
- package/dist/web/assets/index-De1G68kR.js +0 -376
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Lang, WireSkillImportFailure, WireSkillImportForm, WireSkillRemoveFailure, WireCapabilitiesResponse, WireSkillBodyResponse, EpochUserContent, AgentEvent, WireEnvelope, CustomCommandDef, ExpandedCommand, ModelRef, SetSelectionResult, WireCommandExpansion, WireCustomCommand, WireGoalPhase, WireGoalRefusal, WireTurnState, WireSessionFinish, ApprovalOutcome, QuestionAnswer, SessionSurfaceView, WireSessionReference, WireFileReference, ProviderType, ModelSelectionOrigin, WireModelCaveat, WireModelRejection, ContextBreakdown, PermissionLevel, WirePermissionRememberFailure, WirePluginSourceType, WirePluginVisibility, WirePluginRefuseReason, WirePluginEntry, WireModelSource, WireModelProbeStatus, ScheduleDefinition, ScheduleRun, ScheduleBackendKind, OperationType, ScheduleTrigger, WireScheduleIssue, ScheduleRunStatus, TrustLevel, WireSandboxReason, WireAuditOutcome, WireAuditCode, WireCachedApproval, WireCachedDecision, WireToolVerdict, WireToolGateAxis, WireSecurityResponse, WireRewindScope, WireRewindRequest, WireCheckpointSummary, WireRewindPreview, WireRewindResponse, ActiveAgentRole, WireSettingValue, WireSettingLayer, WireSettingWriteLayer, WireSettingFutile, WireSettingApply, WireSettingValueKind, WireSettingsResponse, EpochConfig, TokenUsage, WireCompressionLine, WireProviderSummary, WireToolSummary, Diagnostic, WireReplayMessage, WireFileChange, WireAuthGuard, WireWorkspaceDiffResponse, BackgroundTaskInfo, WireTasksResponse, WireBackgroundTask, WireScheduleRow } from '@epoch-agent/protocol';
|
|
1
|
+
import { Lang, WireSkillImportFailure, WireSkillImportForm, WireSkillRemoveFailure, WireCapabilitiesResponse, WireSkillBodyResponse, EpochUserContent, AgentEvent, WireEnvelope, CustomCommandDef, ExpandedCommand, ModelRef, SetSelectionResult, WireCommandExpansion, WireCustomCommand, WireGoalPhase, WireGoalRefusal, WireTurnState, WireSessionFinish, ApprovalOutcome, QuestionAnswer, SessionSurfaceView, WireSessionReference, WireFileReference, ProviderType, ModelSelectionOrigin, WireModelCaveat, WireModelRejection, ContextBreakdown, PermissionLevel, WirePermissionRememberFailure, WirePluginSourceType, WirePluginVisibility, WirePluginStatus, WirePluginRefuseReason, WirePluginUploadFieldName, WirePluginUploadFields, WirePluginEntry, WireModelSource, WireModelProbeStatus, ScheduleDefinition, ScheduleRun, ScheduleBackendKind, OperationType, ScheduleTrigger, WireScheduleIssue, ScheduleRunStatus, TrustLevel, WireSandboxReason, WireAuditOutcome, WireAuditCode, WireCachedApproval, WireCachedDecision, WireToolVerdict, WireToolGateAxis, WireSecurityResponse, WireRewindScope, WireRewindRequest, WireCheckpointSummary, WireRewindPreview, WireRewindResponse, ActiveAgentRole, WireSettingValue, WireSettingLayer, WireSettingWriteLayer, WireSettingFutile, WireSettingApply, WireSettingValueKind, WireSettingsResponse, EpochConfig, TokenUsage, WireCompressionLine, WireProviderSummary, WireToolSummary, Diagnostic, WireReplayMessage, WireFileChange, WireAuthGuard, WireWorkspaceDiffResponse, BackgroundTaskInfo, WireTasksResponse, WireBackgroundTask, WireScheduleRow } from '@epoch-agent/protocol';
|
|
2
2
|
import { SerializableApprovalRequest, SerializableQuestionRequest, ApprovalRevokeResult, LevelChangeResult, WorkspaceFilesView } from '@epoch-agent/runtime';
|
|
3
3
|
import { ServerResponse } from 'node:http';
|
|
4
4
|
|
|
@@ -1510,6 +1510,34 @@ declare class SessionHub {
|
|
|
1510
1510
|
* 调用方回 404,同审批。
|
|
1511
1511
|
*/
|
|
1512
1512
|
respondQuestion(requestId: string, answer: QuestionAnswer): boolean;
|
|
1513
|
+
/**
|
|
1514
|
+
* 广播「这个会话的后台任务清单变了」(方案 36 PR-3)。
|
|
1515
|
+
*
|
|
1516
|
+
* 唯一的调用方是 `index.ts` 里那条对作业表的订阅 —— Hub 自己不认识任务表
|
|
1517
|
+
* (那是 runtime 转出来的一张进程级的表),它在这条链上只负责发帧。
|
|
1518
|
+
*
|
|
1519
|
+
* ## ⚠️ **不查 `registry` 有没有这个会话**
|
|
1520
|
+
*
|
|
1521
|
+
* 作业表是**进程级**的,而 Hub 那张只装此刻活跃的那几段。两者对不上有两种
|
|
1522
|
+
* 真实情况,各自都该发帧:
|
|
1523
|
+
*
|
|
1524
|
+
* - **被冷却的会话**(`onCooled` 把它从 registry 里摘了)它的 `pnpm dev`
|
|
1525
|
+
* 照旧在跑,而正看着它的那个标签页照旧要知道它跑完了没有;
|
|
1526
|
+
* - **不经过 Hub 的会话**(定时任务、嵌入宿主自己起的那一段)—— 那些帧没有
|
|
1527
|
+
* 客户端认领,`reducer.ts` 的 `parkFrame` 会把它们折进 `parked` 里,
|
|
1528
|
+
* 代价是一格没人读的对象。
|
|
1529
|
+
*
|
|
1530
|
+
* 查一下会便宜一点,但那条判据是错的:**它把「Hub 认不认识」当成了「值不值得
|
|
1531
|
+
* 广播」,而这一帧的宾语是任务表不是 Hub**。收窄的代价是第一种情况静默失效,
|
|
1532
|
+
* 而那正好是这一帧最该管的那一档(冷却的会话没有回合状态跃迁,
|
|
1533
|
+
* 它的任务数除了这一帧之外没有任何东西会推)。
|
|
1534
|
+
*
|
|
1535
|
+
* ⚠️ 代价如实记在这儿:每一帧占一个全局序号、进那 512 格的环形缓冲。
|
|
1536
|
+
* 这一族事件很稀(起一格 / 结束一格,**不含输出**,判据在 `infra` 的
|
|
1537
|
+
* `notifyJobs` 上),所以它挤不动重连游标 —— 那条前提一旦破了(比如有人
|
|
1538
|
+
* 把这条通道接到 `append` 上),先回来看这一段。
|
|
1539
|
+
*/
|
|
1540
|
+
notifyTasksChanged(sessionId: string): void;
|
|
1513
1541
|
/**
|
|
1514
1542
|
* 收摊:撤掉倒计时、放弃所有挂起的审批、中止所有在跑的回合。
|
|
1515
1543
|
*
|
|
@@ -2263,9 +2291,9 @@ interface RuntimeModel {
|
|
|
2263
2291
|
}
|
|
2264
2292
|
|
|
2265
2293
|
/**
|
|
2266
|
-
*
|
|
2294
|
+
* 插件那一页的十三条端点(方案 59 §六 E2,2026-08-21;本地源与市场管理 2026-09-04
|
|
2267
2295
|
* 补,判据在 protocol 的 `wire-plugin.ts` 文件头第一节那次**翻案**;上传 2026-09-08
|
|
2268
|
-
*
|
|
2296
|
+
* 补,判据在那份文件头第一节之二;刷新 2026-09-10 补):
|
|
2269
2297
|
*
|
|
2270
2298
|
* ```
|
|
2271
2299
|
* GET /api/plugins 装着的 + 市场里有什么 + 已加的市场 + pendingRestart + uploadEnabled
|
|
@@ -2280,6 +2308,7 @@ interface RuntimeModel {
|
|
|
2280
2308
|
* POST /api/plugins/marketplaces/remove {name} → 移除一个市场
|
|
2281
2309
|
* POST /api/plugins/upload/preview {path} → 会打包哪些文件,一个字节都不发
|
|
2282
2310
|
* POST /api/plugins/upload {path, token} → 打包 + 交给宿主
|
|
2311
|
+
* POST /api/plugins/refresh (不读正文) → 让宿主重拉目录 + 回一份新列表
|
|
2283
2312
|
* ```
|
|
2284
2313
|
*
|
|
2285
2314
|
* ## ⚠️ 这一层**一条策略判断都没有**
|
|
@@ -2410,6 +2439,15 @@ interface PluginHitView {
|
|
|
2410
2439
|
size?: number;
|
|
2411
2440
|
homepage?: string;
|
|
2412
2441
|
visibility?: WirePluginVisibility;
|
|
2442
|
+
/**
|
|
2443
|
+
* 这一条在市场那一侧的档口(2026-09-10)。可选 → 网线上 `null`。
|
|
2444
|
+
*
|
|
2445
|
+
* ⚠️ **缺席有两种成因,而这一层不分**:这个市场压根不报这个,或者它报了、
|
|
2446
|
+
* 但在自己那份目录的 `display.fields` 里没选它(判据在 core 的
|
|
2447
|
+
* `projectEntryForDisplay` 上 —— 投影在「市场里有什么」那个唯一出口做完了)。
|
|
2448
|
+
* 两者在屏幕上是同一句话(那一格不画),所以这儿不需要第二格去分。
|
|
2449
|
+
*/
|
|
2450
|
+
status?: WirePluginStatus;
|
|
2413
2451
|
};
|
|
2414
2452
|
sourceType?: WirePluginSourceType;
|
|
2415
2453
|
installable: boolean;
|
|
@@ -2566,23 +2604,43 @@ interface PluginControlView {
|
|
|
2566
2604
|
*/
|
|
2567
2605
|
previewUpload(path: string, lang?: Lang): UploadPreviewOutcomeView;
|
|
2568
2606
|
upload(path: string, token: string,
|
|
2569
|
-
/**
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2607
|
+
/**
|
|
2608
|
+
* 用户在那一屏上填的(2026-09-10 从两格放宽到七格)。
|
|
2609
|
+
*
|
|
2610
|
+
* ⚠️ **两道收窄都由下一层做,这一层一格都不过滤**:宿主没声明要问的丢掉、
|
|
2611
|
+
* 宿主锁死的(`editable: false`)也丢掉换成它自己的值 —— 判据在 runtime 的
|
|
2612
|
+
* `PluginUploadForm` 上。这一层只把请求体收窄成**形状**(见 {@link uploadFormOf})。
|
|
2613
|
+
*/
|
|
2614
|
+
form: UploadFormView, lang?: Lang): Promise<UploadOutcomeView>;
|
|
2574
2615
|
readonly pendingRestart: boolean;
|
|
2575
|
-
/**
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2616
|
+
/**
|
|
2617
|
+
* 上传表单多问哪几格、每一格长什么样。**七个键恒有,不画的是 `null`** ——
|
|
2618
|
+
* runtime 那一层拍平好的(2026-09-10 从两个布尔换成形态表)。
|
|
2619
|
+
*
|
|
2620
|
+
* ⚠️ 这一层**原样投影,一格都不重判**:「哪几档写法等价」整个在 runtime 的
|
|
2621
|
+
* `normalizeUploadField` 里,在这儿再判一次就是给同一个问题第二个答案。
|
|
2622
|
+
*/
|
|
2623
|
+
readonly uploadOptions: WirePluginUploadFields;
|
|
2580
2624
|
/**
|
|
2581
2625
|
* 宿主给了上传出口吗。**runtime 算好的结论**,这一层原样投影上网线 ——
|
|
2582
2626
|
* 在这儿重判一次(比如看那个回调在不在)就是给同一个问题第二个答案,
|
|
2583
2627
|
* 判据同文件头那句「这一层一条策略判断都没有」。
|
|
2584
2628
|
*/
|
|
2585
2629
|
readonly uploadEnabled: boolean;
|
|
2630
|
+
/**
|
|
2631
|
+
* 去把目录重拉一遍(2026-09-10)。两段(宿主取新目录 → 我们吃进
|
|
2632
|
+
* `marketplaces.json`)都在 runtime 那一层,这儿只转一发。
|
|
2633
|
+
*
|
|
2634
|
+
* ⚠️ 回执里**没有列表** —— 刷完之后那一份由这一层自己 `pluginsPayload()`
|
|
2635
|
+
* 现读一次。让 runtime 顺手把列表捎回来的话,「首屏那一份长什么样」
|
|
2636
|
+
* 就有了第二个产地,判据在 `pluginsPayload` 上。
|
|
2637
|
+
*/
|
|
2638
|
+
refresh(lang?: Lang): Promise<ActionOutcomeView>;
|
|
2639
|
+
/**
|
|
2640
|
+
* 宿主给了刷新出口吗。**runtime 算好的结论**,这一层原样投影上网线 ——
|
|
2641
|
+
* 口径逐字同 {@link uploadEnabled}。
|
|
2642
|
+
*/
|
|
2643
|
+
readonly refreshEnabled: boolean;
|
|
2586
2644
|
}
|
|
2587
2645
|
/**
|
|
2588
2646
|
* 装着的一条 → 网线上那一行。
|
|
@@ -2593,6 +2651,10 @@ interface PluginControlView {
|
|
|
2593
2651
|
* 「什么都不带」。
|
|
2594
2652
|
*/
|
|
2595
2653
|
declare function toWirePluginEntry(entry: PluginEntryView): WirePluginEntry;
|
|
2654
|
+
/** `PluginUploadForm`(runtime)的镜像 —— 上传那一屏上用户填的七格 */
|
|
2655
|
+
type UploadFormView = {
|
|
2656
|
+
visibility?: WirePluginVisibility;
|
|
2657
|
+
} & Partial<Record<Exclude<WirePluginUploadFieldName, 'visibility'>, string>>;
|
|
2596
2658
|
|
|
2597
2659
|
/**
|
|
2598
2660
|
* 「我们支持哪几家、这一家有哪些模型、给这一家配一把 key」 —— `GET /api/providers`、
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createServer } from 'http';
|
|
2
|
-
import { t, ApprovalRelay, QuestionRelay, serializeStream, withRoleScope, withModelScope, withToolScope, nextRunAt, skillIndexDescription, SANDBOX_EXCLUDES, SANDBOX_COVERS, isSystemOpenable, backgroundTaskOutput, listBackgroundTasks, providerLabel, MAX_REASON_CHARS, MAX_OBJECTIVE_CHARS, MAX_GOAL_MAX_ROUNDS, DEFAULT_GOAL_MAX_ROUNDS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_DEFAULTS, coalesceRecording, readRecording } from '@epoch-agent/runtime';
|
|
2
|
+
import { t, ApprovalRelay, QuestionRelay, serializeStream, withRoleScope, withModelScope, withToolScope, nextRunAt, observeBackgroundTasks, skillIndexDescription, SANDBOX_EXCLUDES, SANDBOX_COVERS, isSystemOpenable, backgroundTaskOutput, listBackgroundTasks, providerLabel, MAX_REASON_CHARS, MAX_OBJECTIVE_CHARS, MAX_GOAL_MAX_ROUNDS, DEFAULT_GOAL_MAX_ROUNDS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_DEFAULTS, coalesceRecording, readRecording } from '@epoch-agent/runtime';
|
|
3
3
|
import { timingSafeEqual, randomBytes } from 'crypto';
|
|
4
|
-
import { PLAN_OUTCOMES, WIRE_AUTH_COOKIE, WIRE_AUTH_COOKIE_ATTRS, WIRE_AUTH_TOKEN_PARAM, extractAllMentions, MAX_ATTACH_FILES, utf8ByteLength, MAX_SESSION_REFS, attachSessionSurface, isWireRewindScope, WIRE_REWIND_SCOPES, MAX_ATTACH_TOTAL_BYTES, MAX_ATTACH_BYTES, WIRE_LANG_PARAM, isLang, isQuestionAnswerMap, isProviderType, apiKeyEnvVar, WIRE_FILE_STAT_MAX, WIRE_SETTING_WRITE_LAYERS, isWirePlanAction, WIRE_PLAN_ACTIONS, isPermissionLevel, PERMISSION_LEVELS, collectPendingApprovals, unfixedRules, isWireSettingWriteLayer, parseModelRef, WIRE_GOAL_ACTIONS, OPERATION_TYPES, isOperationType } from '@epoch-agent/protocol';
|
|
4
|
+
import { PLAN_OUTCOMES, WIRE_AUTH_COOKIE, WIRE_AUTH_COOKIE_ATTRS, WIRE_AUTH_TOKEN_PARAM, extractAllMentions, MAX_ATTACH_FILES, utf8ByteLength, MAX_SESSION_REFS, attachSessionSurface, isWireRewindScope, WIRE_REWIND_SCOPES, MAX_ATTACH_TOTAL_BYTES, MAX_ATTACH_BYTES, WIRE_LANG_PARAM, isLang, isQuestionAnswerMap, isProviderType, apiKeyEnvVar, WIRE_FILE_STAT_MAX, WIRE_SETTING_WRITE_LAYERS, isWirePlanAction, WIRE_PLAN_ACTIONS, isPermissionLevel, PERMISSION_LEVELS, collectPendingApprovals, unfixedRules, isWireSettingWriteLayer, parseModelRef, WIRE_GOAL_ACTIONS, OPERATION_TYPES, isOperationType, WIRE_PLUGIN_UPLOAD_FORM_FIELDS } from '@epoch-agent/protocol';
|
|
5
5
|
import { resolve, join, relative, isAbsolute, sep, dirname, basename, extname } from 'path';
|
|
6
6
|
import { existsSync, realpathSync, statSync, mkdirSync, createReadStream, readdirSync, accessSync, constants } from 'fs';
|
|
7
7
|
import { homedir } from 'os';
|
|
@@ -554,6 +554,10 @@ var SessionHub = class {
|
|
|
554
554
|
this.syncApprovalState(entry);
|
|
555
555
|
return true;
|
|
556
556
|
}
|
|
557
|
+
notifyTasksChanged(sessionId) {
|
|
558
|
+
if (this.closed) return;
|
|
559
|
+
this.publish(sessionId, { type: "tasks-changed" });
|
|
560
|
+
}
|
|
557
561
|
shutdown() {
|
|
558
562
|
if (this.closed) return;
|
|
559
563
|
this.closed = true;
|
|
@@ -2470,6 +2474,7 @@ function toWirePluginHit(hit, installed) {
|
|
|
2470
2474
|
size: hit.entry.size ?? null,
|
|
2471
2475
|
homepage: hit.entry.homepage ?? null,
|
|
2472
2476
|
visibility: hit.entry.visibility ?? null,
|
|
2477
|
+
status: hit.entry.status ?? null,
|
|
2473
2478
|
updateAvailable: mine !== void 0 && !mine.linked && hit.entry.version !== void 0 && hit.entry.version !== mine.version
|
|
2474
2479
|
};
|
|
2475
2480
|
}
|
|
@@ -2526,21 +2531,38 @@ function fieldOf(payload, key) {
|
|
|
2526
2531
|
const value = payload[key];
|
|
2527
2532
|
return typeof value === "string" ? value.trim() : "";
|
|
2528
2533
|
}
|
|
2534
|
+
function uploadFormOf(payload) {
|
|
2535
|
+
const form = {};
|
|
2536
|
+
for (const name of WIRE_PLUGIN_UPLOAD_FORM_FIELDS) {
|
|
2537
|
+
const raw = fieldOf(payload, name);
|
|
2538
|
+
if (raw === "") continue;
|
|
2539
|
+
if (name === "visibility") {
|
|
2540
|
+
if (raw === "private" || raw === "public") form[name] = raw;
|
|
2541
|
+
continue;
|
|
2542
|
+
}
|
|
2543
|
+
form[name] = raw;
|
|
2544
|
+
}
|
|
2545
|
+
return form;
|
|
2546
|
+
}
|
|
2529
2547
|
function listPlugins(ctx, res, lang) {
|
|
2530
2548
|
const control = controlOf(ctx, res, lang);
|
|
2531
2549
|
if (!control) return;
|
|
2550
|
+
sendJson(res, 200, pluginsPayload(control));
|
|
2551
|
+
}
|
|
2552
|
+
function pluginsPayload(control) {
|
|
2532
2553
|
const installed = control.list();
|
|
2533
2554
|
const installedMap = new Map(
|
|
2534
2555
|
installed.map((p) => [p.name, { version: p.version, linked: p.linked }])
|
|
2535
2556
|
);
|
|
2536
|
-
|
|
2557
|
+
return {
|
|
2537
2558
|
installed: installed.map(toWirePluginEntry),
|
|
2538
2559
|
hits: control.search().map((hit) => toWirePluginHit(hit, installedMap)),
|
|
2539
2560
|
marketplaces: control.marketplaces().map(toWireMarketplace),
|
|
2540
2561
|
pendingRestart: control.pendingRestart,
|
|
2541
2562
|
uploadEnabled: control.uploadEnabled,
|
|
2542
|
-
uploadOptions: control.uploadOptions
|
|
2543
|
-
|
|
2563
|
+
uploadOptions: control.uploadOptions,
|
|
2564
|
+
refreshEnabled: control.refreshEnabled
|
|
2565
|
+
};
|
|
2544
2566
|
}
|
|
2545
2567
|
function sendPreview(res, outcome) {
|
|
2546
2568
|
if (!outcome.ok) {
|
|
@@ -2686,10 +2708,7 @@ function uploadPlugin(ctx, req, res, lang) {
|
|
|
2686
2708
|
await control.upload(
|
|
2687
2709
|
fieldOf(payload, "path"),
|
|
2688
2710
|
fieldOf(payload, "token"),
|
|
2689
|
-
|
|
2690
|
-
...fieldOf(payload, "visibility") === "private" ? { visibility: "private" } : fieldOf(payload, "visibility") === "public" ? { visibility: "public" } : {},
|
|
2691
|
-
...fieldOf(payload, "changelog") ? { changelog: fieldOf(payload, "changelog") } : {}
|
|
2692
|
-
},
|
|
2711
|
+
uploadFormOf(payload),
|
|
2693
2712
|
lang
|
|
2694
2713
|
)
|
|
2695
2714
|
);
|
|
@@ -2739,6 +2758,26 @@ function sendUpload(res, outcome) {
|
|
|
2739
2758
|
reason: outcome.ok ? null : outcome.reason
|
|
2740
2759
|
});
|
|
2741
2760
|
}
|
|
2761
|
+
async function refreshPlugins(ctx, res, lang) {
|
|
2762
|
+
if (refusedByLan(ctx, res, lang)) return;
|
|
2763
|
+
const control = controlOf(ctx, res, lang);
|
|
2764
|
+
if (!control) return;
|
|
2765
|
+
const outcome = await control.refresh(lang);
|
|
2766
|
+
if (!outcome.ok) {
|
|
2767
|
+
return sendJson(res, 200, {
|
|
2768
|
+
ok: false,
|
|
2769
|
+
plugins: null,
|
|
2770
|
+
reason: outcome.reason,
|
|
2771
|
+
detail: outcome.detail
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
2774
|
+
sendJson(res, 200, {
|
|
2775
|
+
ok: true,
|
|
2776
|
+
plugins: pluginsPayload(control),
|
|
2777
|
+
reason: null,
|
|
2778
|
+
detail: outcome.detail === "" ? null : outcome.detail
|
|
2779
|
+
});
|
|
2780
|
+
}
|
|
2742
2781
|
function toWireProvider(option, lang) {
|
|
2743
2782
|
return {
|
|
2744
2783
|
type: option.type,
|
|
@@ -4457,6 +4496,9 @@ async function handleApi(opts, req, res, method, segments, lang) {
|
|
|
4457
4496
|
if (resource === "plugins" && first === "upload" && second === void 0 && method === "POST") {
|
|
4458
4497
|
return uploadPlugin(ctx, req, res, lang);
|
|
4459
4498
|
}
|
|
4499
|
+
if (resource === "plugins" && first === "refresh" && second === void 0 && method === "POST") {
|
|
4500
|
+
return refreshPlugins(ctx, res, lang);
|
|
4501
|
+
}
|
|
4460
4502
|
if (resource === "providers" && first === void 0 && isRead(method)) {
|
|
4461
4503
|
return listProviders(ctx, res, lang);
|
|
4462
4504
|
}
|
|
@@ -4686,6 +4728,9 @@ async function createWebServer(opts) {
|
|
|
4686
4728
|
onCooled: (sessionId) => opts.runtime.sessionFactory.release(sessionId)
|
|
4687
4729
|
});
|
|
4688
4730
|
if (opts.runtime.session) hub.register(opts.runtime.session);
|
|
4731
|
+
const unsubscribeTasks = observeBackgroundTasks((sessionId) => {
|
|
4732
|
+
hub.notifyTasksChanged(sessionId);
|
|
4733
|
+
});
|
|
4689
4734
|
let handle = null;
|
|
4690
4735
|
const server = createServer((req, res) => {
|
|
4691
4736
|
if (handle) return handle(req, res);
|
|
@@ -4694,7 +4739,10 @@ async function createWebServer(opts) {
|
|
|
4694
4739
|
});
|
|
4695
4740
|
const webRoot = opts.webRoot ?? defaultWebRoot();
|
|
4696
4741
|
const listened = await listen(server, binding.host, binding.port);
|
|
4697
|
-
if (!listened.ok)
|
|
4742
|
+
if (!listened.ok) {
|
|
4743
|
+
unsubscribeTasks();
|
|
4744
|
+
return listened;
|
|
4745
|
+
}
|
|
4698
4746
|
handle = buildHandler(opts, hub, binding, listened.port, webRoot);
|
|
4699
4747
|
return {
|
|
4700
4748
|
ok: true,
|
|
@@ -4706,7 +4754,7 @@ async function createWebServer(opts) {
|
|
|
4706
4754
|
...webRoot === void 0 ? {} : { webRoot },
|
|
4707
4755
|
observe: (sink) => hub.observe(sink),
|
|
4708
4756
|
snapshot: () => hub.listSessions(),
|
|
4709
|
-
close: makeCloser(server, hub, opts.runtime)
|
|
4757
|
+
close: makeCloser(server, hub, opts.runtime, unsubscribeTasks)
|
|
4710
4758
|
}
|
|
4711
4759
|
};
|
|
4712
4760
|
}
|
|
@@ -4757,10 +4805,11 @@ function describeListenError(err, host, port) {
|
|
|
4757
4805
|
}
|
|
4758
4806
|
return t("web.listen_failed", { host, port, reason: err.message }, void 0);
|
|
4759
4807
|
}
|
|
4760
|
-
function makeCloser(server, hub, runtime) {
|
|
4808
|
+
function makeCloser(server, hub, runtime, unsubscribeTasks) {
|
|
4761
4809
|
let closing = null;
|
|
4762
4810
|
return () => {
|
|
4763
4811
|
closing ??= (async () => {
|
|
4812
|
+
unsubscribeTasks();
|
|
4764
4813
|
hub.shutdown();
|
|
4765
4814
|
server.closeAllConnections();
|
|
4766
4815
|
await new Promise((resolve4) => server.close(() => resolve4()));
|