@jingyi0605/codingns4dsh 0.1.7-rc.3 → 0.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +695 -0
- package/README.en.md +23 -9
- package/README.md +23 -9
- package/data/build/dist/bootstrap/dsh-connection-adapter.d.ts +1 -1
- package/data/build/dist/bootstrap/index.d.ts +1 -1
- package/data/build/dist/client/bundle.js +1200 -170
- package/data/build/dist/client/context-breakdown-dom.d.ts +53 -0
- package/data/build/dist/client/context-breakdown-dom.js +171 -0
- package/data/build/dist/client/dsh-h5-bootstrap.js +7 -1
- package/data/build/dist/client/features/cli-adapters.js +11 -0
- package/data/build/dist/client/features/index.d.ts +1 -0
- package/data/build/dist/client/features/index.js +1 -0
- package/data/build/dist/client/features/subscription-usage-panel.d.ts +15 -0
- package/data/build/dist/client/features/subscription-usage-panel.js +117 -0
- package/data/build/dist/client/features/workspace-session-enhancement-panel.js +22 -5
- package/data/build/dist/client/features/workspace-session-enhancement.js +17 -2
- package/data/build/dist/client/git-management.js +80 -10
- package/data/build/dist/client/index.d.ts +1 -0
- package/data/build/dist/client/index.js +16 -49
- package/data/build/dist/client/locale.js +20 -2
- package/data/build/dist/client/remote-web-context.js +26 -2
- package/data/build/dist/client/settings-bridge.d.ts +20 -5
- package/data/build/dist/client/settings-bridge.js +30 -7
- package/data/build/dist/client/subscription-slot.d.ts +3 -1
- package/data/build/dist/client/subscription-slot.js +31 -6
- package/data/build/dist/client/terminal/ui.js +1 -1
- package/data/build/dist/client/terminal/xterm-view.js +1 -1
- package/data/build/dist/dsh-capabilities/client/config-forms-adapter.d.ts +10 -0
- package/data/build/dist/dsh-capabilities/client/config-forms-adapter.js +22 -0
- package/data/build/dist/dsh-capabilities/client/primitives-adapter.d.ts +5 -2
- package/data/build/dist/dsh-capabilities/client/primitives-adapter.js +3 -2
- package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.d.ts +2 -2
- package/data/build/dist/dsh-capabilities/host/config-forms-adapter.d.ts +22 -1
- package/data/build/dist/dsh-capabilities/host/config-forms-adapter.js +29 -3
- package/data/build/dist/dsh-capabilities/host/settings-scope-adapter.d.ts +2 -2
- package/data/build/dist/dsh-capabilities/matrix.js +31 -1
- package/data/build/dist/dsh-capabilities/routes.d.ts +8 -1
- package/data/build/dist/dsh-capabilities/routes.js +173 -5
- package/data/build/dist/dsh-capabilities/types.d.ts +1 -1
- package/data/build/dist/features/registry.d.ts +7 -0
- package/data/build/dist/features/registry.js +20 -2
- package/data/build/dist/host/cli-adapters/feature.js +24 -2
- package/data/build/dist/host/cli-adapters/native-team-proxy.d.ts +36 -0
- package/data/build/dist/host/cli-adapters/native-team-proxy.js +108 -0
- package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +35 -0
- package/data/build/dist/host/cli-adapters/provider-subscription.js +405 -5
- package/data/build/dist/host/cli-adapters/registry.d.ts +9 -3
- package/data/build/dist/host/cli-adapters/registry.js +30 -0
- package/data/build/dist/host/cli-adapters/session-store.d.ts +8 -2
- package/data/build/dist/host/cli-adapters/session-store.js +19 -0
- package/data/build/dist/host/features/git-management.js +41 -18
- package/data/build/dist/host/features/types.d.ts +6 -3
- package/data/build/dist/host/index-injection.d.ts +12 -5
- package/data/build/dist/host/index-injection.js +32 -11
- package/data/build/dist/host/index.js +15 -4
- package/data/build/dist/host/lan-access-dsh.d.ts +2 -2
- package/data/build/dist/host/native-session-bridge.d.ts +4 -0
- package/data/build/dist/host/native-session-bridge.js +51 -9
- package/data/build/dist/host/relay-tunnel-runtime.js +10 -1
- package/data/build/dist/host/remote-web-runtime.js +40 -17
- package/data/build/dist/host/rpc.d.ts +24 -5
- package/data/build/dist/host/rpc.js +38 -9
- package/data/build/dist/host/session-migration-repair.js +14 -0
- package/data/build/dist/host/settings.d.ts +2 -2
- package/data/build/dist/host/settings.js +11 -1
- package/data/build/dist/host/terminal/startup.d.ts +2 -2
- package/data/build/dist/shared/contracts/cli-adapter.d.ts +22 -1
- package/data/build/dist/shared/contracts/config.d.ts +21 -0
- package/data/build/dist/shared/contracts/config.js +17 -0
- package/data/build/dist/shared/contracts/errors.d.ts +2 -2
- package/data/build/dist/shared/contracts/subscription.d.ts +7 -0
- package/data/build/dist/shared/contracts/subscription.js +14 -1
- package/data/build/dist/shared/contracts/version.d.ts +4 -4
- package/data/build/dist/shared/contracts/version.js +3 -3
- package/data/build/dist/shared/index.d.ts +3 -2
- package/data/build/dist/shared/index.js +2 -1
- package/data/build/dist/transport/carrier.d.ts +11 -1
- package/data/build/dist/transport/carrier.js +77 -26
- package/data/build/dist/transport/debug.d.ts +5 -0
- package/data/build/dist/transport/debug.js +17 -2
- package/data/build/dist/transport/dsh-session.d.ts +18 -1
- package/data/build/dist/transport/dsh-session.js +48 -13
- package/data/build/dist/transport/index.d.ts +1 -1
- package/data/build/dist/transport/index.js +1 -1
- package/package.json +23 -19
- package/profile/README.md +2 -2
- package/profile/package.json +4 -3
- package/profile/version.json +3 -3
- package/version.json +3 -3
|
@@ -44,9 +44,9 @@ window.__ModuleLoader__.load({
|
|
|
44
44
|
enumerable: true
|
|
45
45
|
}) : target, mod));
|
|
46
46
|
//#endregion
|
|
47
|
-
let react = require("react");
|
|
48
47
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
49
48
|
_deepseek_ai_dsh_client_ui_primitives = __toESM(_deepseek_ai_dsh_client_ui_primitives, 1);
|
|
49
|
+
let react = require("react");
|
|
50
50
|
//#region assets/provider-icons/claude-code.png
|
|
51
51
|
var claude_code_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAF+0lEQVRYha2XbXBUZxXHf+e5u4SkCZjg0NIWnQLJLgRF5UWnL75MP9gMjqPT2eyC0qlMiYxOUAR206LOyhiSTVscp52O0xamWgu7WTp0qLUz6kht6Qed1L7M5GUTsAoDTRSIJJos2b33+GFfWMJuNqmcT/c595z/+d3nOc+9zxXKWE9Li7tm0fjyqmT6H0t/Fp8sFz9XM+UCauouH8Rx+ifmWWdP7Q0sLRbT98g3lySC/mcSQd+GGwrQt2vzx1G2ZEa6KJ3S1mJxlpOKIDyEmEM3FGCk5vw5YCw3FmFbb9hXfV2gcm/26vINBfhS+LU0qgcKXB9xT1hbr6kdDhtgcYaDxHSNwVBg92Ao0DMY8n97zgAAZnK8U2AoX1D0++rzWfkC44k6wAVghP7C3EQo8Iiijyq61oEDiV2bPjojwKk234pEsPnh/lDgzpyv/olXrwi6qyDsjsQy2ZQXmJdefPWWvp0v3uZ/CLQ9NxZINpxJj84IYDvmOUT2G/T1DD0CUB/pflnhd1eT5Mcnwl90Adi23JzPl9TbAEN7fF9AeapQW5AOicftGQFEOJ+9tEDbB9v8x99r21wLYBlnJ5AGUKhfklz8QEbY3J71nVnZceziUPAbtzvGxAB3wdPH6yPRx6cXvw4gbbm/B5zIO5SvVKjdMxAMrKvviPcpHMyLqvyoN+ybh7AUwIi+M9TaVOGI/SJwc4Fsf6rS2SqgxQBkukPDYTM40RdCZB/Z5gKmEGmzjStq2alBoDqbvF2hEWgF9oEuAvlugdx/Deaz9ZEjvcWKFwXIWaLNv14cDqmwugDvFRVOi8qOrOMdhT6BzQhvoNxzjbjItxo6o8+VqjEjAMBQa1OFXbXwh4KGuLqmDleXToHzwG1FpLs9kah/uvf98IPz08nkSuZbp+vDL4zNCJAHaQt82lH9JfCJ2cQrTFjq8hiZGrfVtQGcDQhrFNYAywELuFw15SwRgN6dvrrahcnkreGXJ0pCtDZVOFULwsCerEBJExhVGAY8FH/Z2cBvPJHY1ySx13cbaXMaqACSwCVRLqlhVNBRVTOm4lxCOSdqPkB0FdA2m5kosGGEHpC3UP2z6uRJb9fx8SwsJEL+nwBNwEKgWqBSoXaORQqtH+VNhDdErJMNnYf/ViqwbBNO3lRbVZFSS9yyIG1jucRuVuGnM6TZZL4d/wS9KEgawIH/gF5EzAWBvzecto9KPG7PqgkBTu0NLHVs7VBlcxnwS8ALgtyp6Kco3S9/OF850lQWYCD41RpMZUiUH5BZmgkRDqmyVaCqeJYeS1dqwJq4UoG56S5w7hb4PMp6YH426LJR1+qSAOrzWYPLZCvIPuAWAIHXxGGXY3geWAV6DOTrJRR+r5q8P9dskNtJ1etBVl4R19FPdh4eLQowEPR5RKxfgebOeGMqGvLMX/V0YrK/W+B+4ATCYZRnEJ5E+Q5gFM4AFwQ+I8hbLks2Ltt/ZKTUg163Rwfamrcj5q/54spvLZes9nZ2/yIx0b8jW/wCLmeLOLoCAEdeAjmYnaWPGXga9Jiia1O28+bAbt8dswJQEFF5IrO2chGVBzxdsY0r2qNnBx5uvkuELkAFedDTHj+nIl4AIzJsjL2XTAOisB+XtqryKLBcLHNyKLSpsSyAgKrKJoRtbksaPV3R5yFz7BaHOOBGOdAQib6SRV4L4KQZqe+I/wvYmZWqE1ue8nbFggjbgDoH/VOxY3vZXdDT0uKuqR37I+jdIH9JV9r3NIbjU717fLe4jPkASDVEYhW5730i2PwqIvdlZkK3eCPdv06E/GuAbmCBJxJbUnIGill13b8fyxRnRG070BiOTwG4MeuyszBSeNiw3KZFYDTzdPLzU7u3LPZEYu+qTq5Tlev+K2YESAQD94rKjqzgl72Pxd/P3VNLsgAyXJizoj161kG3Z4d1aZP6HIC36/i4tyt6dE4AasQNDIhxNnoisXevvZndJcLw9DxvpLsb5XGE9yzD2ZlqzPpVPN0SIf854FaUZz1dsW0fVqdsD5Q05Qhgg7z+oTX+Xyv2pzNX+x+eTlGviPgh3AAAAABJRU5ErkJggg==";
|
|
52
52
|
//#endregion
|
|
@@ -353,8 +353,27 @@ window.__ModuleLoader__.load({
|
|
|
353
353
|
reason: item.reason
|
|
354
354
|
}))
|
|
355
355
|
});
|
|
356
|
-
|
|
357
|
-
for (const name of this.records.keys()) if (
|
|
356
|
+
const failures = [];
|
|
357
|
+
for (const name of this.records.keys()) if (desired.has(name)) await this.step(name, () => this.start(name), failures);
|
|
358
|
+
for (const name of this.records.keys()) if (!desired.has(name)) await this.step(name, () => this.disable(name), failures);
|
|
359
|
+
if (failures.length > 0) throw failures[0];
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* 执行单个模块的启停,并把失败留给调用方汇总。
|
|
363
|
+
*
|
|
364
|
+
* 模块失败(例如必需能力缺失)只影响它自己:同一轮同步里其余模块照常启停,
|
|
365
|
+
* 失败原因通过模块状态和 trace 暴露,最后只把第一个错误抛给宿主记录。
|
|
366
|
+
*/
|
|
367
|
+
async step(name, operation, failures) {
|
|
368
|
+
try {
|
|
369
|
+
await operation();
|
|
370
|
+
} catch (error) {
|
|
371
|
+
failures.push(error);
|
|
372
|
+
traceFeature("reconcile.failed", {
|
|
373
|
+
feature: name,
|
|
374
|
+
error: serializeError(error)
|
|
375
|
+
});
|
|
376
|
+
}
|
|
358
377
|
}
|
|
359
378
|
async drain(name) {
|
|
360
379
|
return this.enqueue(name, async () => {
|
|
@@ -682,7 +701,7 @@ window.__ModuleLoader__.load({
|
|
|
682
701
|
"feature.reverseProxy.label": "Relay access service",
|
|
683
702
|
"feature.reverseProxy.description": "Access a DSH Host through the isolated Codingns4DSH relay.",
|
|
684
703
|
"feature.cliAdapters.label": "External Agent integration",
|
|
685
|
-
"feature.cliAdapters.description": "View installed external Agents, versions, command paths, available models, and enable them independently.",
|
|
704
|
+
"feature.cliAdapters.description": "View installed external Agents, versions, command paths, available models, and enable them independently. External Agent context panels keep only the occupancy percentage and totals.",
|
|
686
705
|
"feature.workspaceSession.label": "Workspace session enhancement",
|
|
687
706
|
"feature.workspaceSession.description": "Show Agent logos, archived-session entries, workspace hiding controls, subscription or usage information, local quick sessions, and an optional remembered conversation/right-sidebar width ratio.",
|
|
688
707
|
"feature.terminal.label": "Terminal enhancements",
|
|
@@ -870,6 +889,15 @@ window.__ModuleLoader__.load({
|
|
|
870
889
|
"workspace.workspaceHidingDescription": "Add a Hide workspace action to each workspace menu and show hidden workspaces through the workspace filter.",
|
|
871
890
|
"workspace.showSubscriptionUsage": "Show subscription and usage",
|
|
872
891
|
"workspace.subscriptionUsageDescription": "Detect and display provider subscription balances and upstream usage in the conversation footer.",
|
|
892
|
+
"workspace.subscriptionUsageSettings": "Settings",
|
|
893
|
+
"subscriptionUsage.dialogTitle": "Usage query settings",
|
|
894
|
+
"subscriptionUsage.dialogHint": "The timeout applies to every adapter. The refresh interval drives the footer usage query; 0 disables automatic refresh, and a result is reused while it is still fresh.",
|
|
895
|
+
"subscriptionUsage.timeoutSecs": "Query timeout (seconds)",
|
|
896
|
+
"subscriptionUsage.refreshInterval": "Auto refresh interval (minutes)",
|
|
897
|
+
"subscriptionUsage.refreshIntervalHint": "0 disables automatic refresh.",
|
|
898
|
+
"subscriptionUsage.cancel": "Cancel",
|
|
899
|
+
"subscriptionUsage.save": "Save",
|
|
900
|
+
"subscriptionUsage.saved": "Usage query settings saved",
|
|
873
901
|
"workspace.showQuickPhrases": "Show quick sessions",
|
|
874
902
|
"workspace.quickPhrasesDescription": "Keep reusable session instructions locally in Codingns4DSH and show the quick sessions button inside the composer.",
|
|
875
903
|
"workspace.rememberConversationRightbarRatio": "Remember conversation and right sidebar width ratio",
|
|
@@ -911,7 +939,7 @@ window.__ModuleLoader__.load({
|
|
|
911
939
|
"feature.reverseProxy.label": "中转访问服务",
|
|
912
940
|
"feature.reverseProxy.description": "通过隔离的 Codingns4DSH 中继访问 DSH Host。",
|
|
913
941
|
"feature.cliAdapters.label": "外部Agent集成",
|
|
914
|
-
"feature.cliAdapters.description": "查看外部 Agent
|
|
942
|
+
"feature.cliAdapters.description": "查看外部 Agent 的安装状态、版本、命令路径和可用模型,并单独启用或停用。外部 Agent 的上下文面板只保留占用百分比与总量。",
|
|
915
943
|
"feature.workspaceSession.label": "工作区会话增强",
|
|
916
944
|
"feature.workspaceSession.description": "在原生工作区会话行显示 Agent Logo、归档会话入口、工作区隐藏功能和订阅/用量信息,提供本地快捷会话,并可记忆对话窗口与右侧栏宽度比例。",
|
|
917
945
|
"feature.terminal.label": "终端增强",
|
|
@@ -1099,6 +1127,15 @@ window.__ModuleLoader__.load({
|
|
|
1099
1127
|
"workspace.workspaceHidingDescription": "在工作区菜单中加入“隐藏工作区”,通过工作区筛选菜单显示隐藏的工作区并支持恢复。",
|
|
1100
1128
|
"workspace.showSubscriptionUsage": "显示订阅/用量检测",
|
|
1101
1129
|
"workspace.subscriptionUsageDescription": "在对话底部检测并显示 Agent 订阅余额和上游用量。",
|
|
1130
|
+
"workspace.subscriptionUsageSettings": "设置",
|
|
1131
|
+
"subscriptionUsage.dialogTitle": "用量查询设置",
|
|
1132
|
+
"subscriptionUsage.dialogHint": "超时时间作用于所有适配器;自动查询间隔作用于对话底部的用量查询,0 表示不自动查询,间隔内的查询结果会直接复用。",
|
|
1133
|
+
"subscriptionUsage.timeoutSecs": "超时时间(秒)",
|
|
1134
|
+
"subscriptionUsage.refreshInterval": "自动查询间隔(分钟)",
|
|
1135
|
+
"subscriptionUsage.refreshIntervalHint": "0 表示不自动查询。",
|
|
1136
|
+
"subscriptionUsage.cancel": "取消",
|
|
1137
|
+
"subscriptionUsage.save": "保存",
|
|
1138
|
+
"subscriptionUsage.saved": "用量查询设置已保存",
|
|
1102
1139
|
"workspace.showQuickPhrases": "显示快捷会话",
|
|
1103
1140
|
"workspace.quickPhrasesDescription": "在 Codingns4DSH 内本地保存可复用的会话指令,并将快捷会话按钮显示在输入工具区。",
|
|
1104
1141
|
"workspace.rememberConversationRightbarRatio": "记忆对话窗口与右侧栏的宽度比例",
|
|
@@ -1125,9 +1162,9 @@ window.__ModuleLoader__.load({
|
|
|
1125
1162
|
//#region src/shared/contracts/version.ts
|
|
1126
1163
|
init_locale();
|
|
1127
1164
|
/** 当前经过完整验证的 DSH 版本;源文件由根目录 version.json 同步。 */
|
|
1128
|
-
const DSH_VERSION = "0.
|
|
1165
|
+
const DSH_VERSION = "0.2.0-rc.1";
|
|
1129
1166
|
/** 插件支持的 DSH 版本范围;插件版本与宿主版本独立发布。 */
|
|
1130
|
-
const DSH_COMPATIBILITY = ">=0.
|
|
1167
|
+
const DSH_COMPATIBILITY = ">=0.2.0-rc.1 <=0.2.0-rc.1";
|
|
1131
1168
|
/** Host 注入到浏览器页面的真实 DSH 版本全局字段。 */
|
|
1132
1169
|
const CODINGNS_DSH_VERSION_GLOBAL = "__CODINGNS_DSH_VERSION__";
|
|
1133
1170
|
/** 识别没有 alpha2 Sidebar 扩展 API 的 DSH 旧版运行时。 */
|
|
@@ -1135,7 +1172,7 @@ window.__ModuleLoader__.load({
|
|
|
1135
1172
|
return /^0\.1\.5(?:-|$)/u.test(version);
|
|
1136
1173
|
}
|
|
1137
1174
|
/** Codingns4DSH 插件自身的 npm 版本。 */
|
|
1138
|
-
const CODINGNS_VERSION = "0.
|
|
1175
|
+
const CODINGNS_VERSION = "0.2.0-beta.1";
|
|
1139
1176
|
/** 判断宿主版本是否落在当前插件声明的 DSH 兼容范围内。 */
|
|
1140
1177
|
function isDshVersionCompatible(version) {
|
|
1141
1178
|
const match = /^>=([^ ]+) <=([^ ]+)$/u.exec(DSH_COMPATIBILITY);
|
|
@@ -1184,6 +1221,30 @@ window.__ModuleLoader__.load({
|
|
|
1184
1221
|
}
|
|
1185
1222
|
//#endregion
|
|
1186
1223
|
//#region src/shared/contracts/config.ts
|
|
1224
|
+
const DEFAULT_SUBSCRIPTION_USAGE_SETTINGS = {
|
|
1225
|
+
timeoutSecs: 10,
|
|
1226
|
+
refreshIntervalMins: 5
|
|
1227
|
+
};
|
|
1228
|
+
const SUBSCRIPTION_USAGE_TIMEOUT_SECS_LIMITS = {
|
|
1229
|
+
min: 3,
|
|
1230
|
+
max: 120
|
|
1231
|
+
};
|
|
1232
|
+
const SUBSCRIPTION_USAGE_REFRESH_INTERVAL_MINS_LIMITS = {
|
|
1233
|
+
min: 0,
|
|
1234
|
+
max: 1440
|
|
1235
|
+
};
|
|
1236
|
+
/** 归一化用量查询设置:缺省字段回填默认值,越界值收敛到允许范围。 */
|
|
1237
|
+
function normalizeSubscriptionUsageSettings(value) {
|
|
1238
|
+
const record = typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
1239
|
+
return {
|
|
1240
|
+
timeoutSecs: clampSettingsInteger(record.timeoutSecs, SUBSCRIPTION_USAGE_TIMEOUT_SECS_LIMITS, DEFAULT_SUBSCRIPTION_USAGE_SETTINGS.timeoutSecs),
|
|
1241
|
+
refreshIntervalMins: clampSettingsInteger(record.refreshIntervalMins, SUBSCRIPTION_USAGE_REFRESH_INTERVAL_MINS_LIMITS, DEFAULT_SUBSCRIPTION_USAGE_SETTINGS.refreshIntervalMins)
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
function clampSettingsInteger(value, limits, fallback) {
|
|
1245
|
+
const numeric = typeof value === "number" && Number.isFinite(value) ? Math.round(value) : fallback;
|
|
1246
|
+
return Math.min(limits.max, Math.max(limits.min, numeric));
|
|
1247
|
+
}
|
|
1187
1248
|
const DEFAULT_FILE_MANAGEMENT_SETTINGS = {
|
|
1188
1249
|
menuEnhancement: true,
|
|
1189
1250
|
fileEditor: true,
|
|
@@ -1221,6 +1282,7 @@ window.__ModuleLoader__.load({
|
|
|
1221
1282
|
const CODINGNS_TERMINAL_ENHANCEMENT_FIELD = "terminalEnhancement";
|
|
1222
1283
|
const CODINGNS_WORKSPACE_SESSION_ENHANCEMENT_FIELD = "workspaceSessionEnhancement";
|
|
1223
1284
|
const CODINGNS_FILE_MANAGEMENT_FIELD = "fileManagement";
|
|
1285
|
+
const CODINGNS_SUBSCRIPTION_USAGE_FIELD = "subscriptionUsage";
|
|
1224
1286
|
const DEFAULT_CODINGNS_CONTROL_BASE_URLS = ["https://channel.codingns.com:1443"];
|
|
1225
1287
|
/** 独立 H5 登录页面地址;登录控制站后可从设置页复制给其他设备。 */
|
|
1226
1288
|
const CODINGNS_H5_LOGIN_URL = "https://dsh.codingns.com";
|
|
@@ -2534,6 +2596,20 @@ window.__ModuleLoader__.load({
|
|
|
2534
2596
|
if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`${name} 大小限制无效`);
|
|
2535
2597
|
}
|
|
2536
2598
|
//#endregion
|
|
2599
|
+
//#region src/shared/contracts/subscription.ts
|
|
2600
|
+
/**
|
|
2601
|
+
* 判断上一次用量查询结果是否仍在可复用窗口内。
|
|
2602
|
+
*
|
|
2603
|
+
* 间隔 ≤ 0 表示不自动查询:此时不复用旧结果,每次挂载都重新查询一次。
|
|
2604
|
+
* 该判定由 Client 侧底部用量入口使用,避免每次进入会话都请求上游。
|
|
2605
|
+
*/
|
|
2606
|
+
function isSubscriptionUsageFresh(capturedAtMs, nowMs, refreshIntervalMins) {
|
|
2607
|
+
if (!Number.isFinite(refreshIntervalMins) || refreshIntervalMins <= 0) return false;
|
|
2608
|
+
if (!Number.isFinite(capturedAtMs) || !Number.isFinite(nowMs)) return false;
|
|
2609
|
+
const age = nowMs - capturedAtMs;
|
|
2610
|
+
return age >= 0 && age < refreshIntervalMins * 6e4;
|
|
2611
|
+
}
|
|
2612
|
+
//#endregion
|
|
2537
2613
|
//#region src/transport/debug.ts
|
|
2538
2614
|
init_debug();
|
|
2539
2615
|
/** 创建一个可注入测试 sink 的调试 logger。默认开关由当前运行环境决定。 */
|
|
@@ -2561,6 +2637,11 @@ window.__ModuleLoader__.load({
|
|
|
2561
2637
|
/**
|
|
2562
2638
|
* 调试字段采用正向白名单。Transport 可能同时处理 token、URL、命令和正文,
|
|
2563
2639
|
* 因此不能依赖调用方自觉脱敏;未知字段和错误正文直接丢弃。
|
|
2640
|
+
*
|
|
2641
|
+
* `path` / `method` / `status` / `errorCode` 是 DSH Web 请求的路由元数据:
|
|
2642
|
+
* 没有它们就无法区分「请求没到达 Host」和「请求到达但返回空结果」,
|
|
2643
|
+
* 中继设置页空白这类问题只能靠日志猜。这些字段只能是 HTTP 路径与状态,
|
|
2644
|
+
* 禁止把查询串、Cookie、请求/响应正文塞进来。
|
|
2564
2645
|
*/
|
|
2565
2646
|
function sanitizeDshTransportDebugFields(fields) {
|
|
2566
2647
|
const allowed = /* @__PURE__ */ new Set([
|
|
@@ -2598,15 +2679,29 @@ window.__ModuleLoader__.load({
|
|
|
2598
2679
|
"lowWaterMark",
|
|
2599
2680
|
"dataType",
|
|
2600
2681
|
"valueType",
|
|
2601
|
-
"trafficRemainingBytes"
|
|
2682
|
+
"trafficRemainingBytes",
|
|
2683
|
+
"path",
|
|
2684
|
+
"method",
|
|
2685
|
+
"status",
|
|
2686
|
+
"errorCode"
|
|
2602
2687
|
]);
|
|
2603
2688
|
const result = {};
|
|
2604
2689
|
for (const [key, value] of Object.entries(fields)) {
|
|
2605
2690
|
if (!allowed.has(key)) continue;
|
|
2606
|
-
if (typeof value === "string"
|
|
2691
|
+
if (typeof value === "string") {
|
|
2692
|
+
result[key] = key === "path" ? sanitizeDebugPath(value) : value;
|
|
2693
|
+
continue;
|
|
2694
|
+
}
|
|
2695
|
+
if (typeof value === "number" || typeof value === "boolean" || value === null) result[key] = value;
|
|
2607
2696
|
}
|
|
2608
2697
|
return result;
|
|
2609
2698
|
}
|
|
2699
|
+
/** 路径只保留 pathname,去掉查询串与可能带凭据的片段。 */
|
|
2700
|
+
function sanitizeDebugPath(value) {
|
|
2701
|
+
const query = value.indexOf("?");
|
|
2702
|
+
const trimmed = query === -1 ? value : value.slice(0, query);
|
|
2703
|
+
return trimmed.length > 200 ? `${trimmed.slice(0, 200)}…` : trimmed;
|
|
2704
|
+
}
|
|
2610
2705
|
/** 解析统一的 Codingns4DSH 调试开关,保留旧隧道变量作为兼容别名。 */
|
|
2611
2706
|
function resolveDshTransportDebugEnabled() {
|
|
2612
2707
|
return resolveCodingNsDebugEnabled();
|
|
@@ -3244,7 +3339,7 @@ window.__ModuleLoader__.load({
|
|
|
3244
3339
|
}
|
|
3245
3340
|
//#endregion
|
|
3246
3341
|
//#region src/transport/dsh-session.ts
|
|
3247
|
-
/** 负责 DSH hello/ready
|
|
3342
|
+
/** 负责 DSH hello/ready、协议与能力协商和心跳,不执行任何业务。 */
|
|
3248
3343
|
var DshSession = class {
|
|
3249
3344
|
options;
|
|
3250
3345
|
stateValue = "idle";
|
|
@@ -3327,19 +3422,33 @@ window.__ModuleLoader__.load({
|
|
|
3327
3422
|
sendHello() {
|
|
3328
3423
|
this.send(this.createEnvelope("session.hello", "session", {
|
|
3329
3424
|
protocol: this.options.protocol ?? "dsh-transport-v1",
|
|
3330
|
-
dshVersion: this.options.dshVersion ?? "0.
|
|
3425
|
+
dshVersion: this.options.dshVersion ?? "0.2.0-rc.1",
|
|
3331
3426
|
capabilities: [...this.options.capabilities ?? []]
|
|
3332
3427
|
}));
|
|
3333
3428
|
}
|
|
3334
|
-
sendReady(capabilities) {
|
|
3429
|
+
sendReady(capabilities, peerDshVersion) {
|
|
3335
3430
|
this.send(this.createEnvelope("session.ready", "session", {
|
|
3336
3431
|
protocol: this.options.protocol ?? "dsh-transport-v1",
|
|
3337
|
-
dshVersion: this.options.dshVersion ?? "0.
|
|
3432
|
+
dshVersion: peerDshVersion ?? this.options.dshVersion ?? "0.2.0-rc.1",
|
|
3433
|
+
hostDshVersion: this.options.dshVersion ?? "0.2.0-rc.1",
|
|
3338
3434
|
capabilities: [...capabilities],
|
|
3339
3435
|
byteCredit: 65536,
|
|
3340
3436
|
messageCredit: 32
|
|
3341
3437
|
}));
|
|
3342
3438
|
}
|
|
3439
|
+
/**
|
|
3440
|
+
* 握手只校验隧道协议版本:对端缺省不带协议时按 v1 处理。
|
|
3441
|
+
* DSH 应用版本不再参与握手门槛;只有插件升级 DSH_ENVELOPE_PROTOCOL
|
|
3442
|
+
* (对应不再兼容的 WebRTC 变更)时,才会在此拒绝旧协议对端。
|
|
3443
|
+
*/
|
|
3444
|
+
handshakeProtocolMatches(envelope) {
|
|
3445
|
+
return (envelope.meta.protocol ?? "dsh-transport-v1") === (this.options.protocol ?? "dsh-transport-v1");
|
|
3446
|
+
}
|
|
3447
|
+
/** 协议不兼容时先回发 session.close 让对端拿到明确原因,再收敛本地会话。 */
|
|
3448
|
+
rejectHandshake(reason) {
|
|
3449
|
+
this.send(this.createEnvelope("session.close", "session", { reason }));
|
|
3450
|
+
this.fail(new Error(reason));
|
|
3451
|
+
}
|
|
3343
3452
|
receive(data) {
|
|
3344
3453
|
if (this.stateValue === "closed") return;
|
|
3345
3454
|
let envelope;
|
|
@@ -3374,12 +3483,11 @@ window.__ModuleLoader__.load({
|
|
|
3374
3483
|
this.fail(/* @__PURE__ */ new Error("非法 session.hello"));
|
|
3375
3484
|
return;
|
|
3376
3485
|
}
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
if (protocol !== (this.options.protocol ?? "dsh-transport-v1") || typeof dshVersion !== "string" || !isDshVersionCompatible(dshVersion)) {
|
|
3380
|
-
this.fail(/* @__PURE__ */ new Error("PROTOCOL_VERSION_UNSUPPORTED"));
|
|
3486
|
+
if (!this.handshakeProtocolMatches(envelope)) {
|
|
3487
|
+
this.rejectHandshake("PROTOCOL_VERSION_UNSUPPORTED");
|
|
3381
3488
|
return;
|
|
3382
3489
|
}
|
|
3490
|
+
const peerDshVersion = typeof envelope.meta.dshVersion === "string" ? envelope.meta.dshVersion : void 0;
|
|
3383
3491
|
const offered = readCapabilities(envelope.meta.capabilities);
|
|
3384
3492
|
const allowed = new Set(this.options.capabilities ?? offered);
|
|
3385
3493
|
this.remoteCapabilities = offered.filter((capability) => allowed.has(capability));
|
|
@@ -3388,7 +3496,7 @@ window.__ModuleLoader__.load({
|
|
|
3388
3496
|
role: this.options.role,
|
|
3389
3497
|
capabilities: this.remoteCapabilities
|
|
3390
3498
|
});
|
|
3391
|
-
this.sendReady(this.remoteCapabilities);
|
|
3499
|
+
this.sendReady(this.remoteCapabilities, peerDshVersion);
|
|
3392
3500
|
this.readyResolve?.();
|
|
3393
3501
|
this.options.onReady?.(this);
|
|
3394
3502
|
return;
|
|
@@ -3398,10 +3506,8 @@ window.__ModuleLoader__.load({
|
|
|
3398
3506
|
this.fail(/* @__PURE__ */ new Error("非法 session.ready"));
|
|
3399
3507
|
return;
|
|
3400
3508
|
}
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
if (protocol !== (this.options.protocol ?? "dsh-transport-v1") || typeof dshVersion !== "string" || !isDshVersionCompatible(dshVersion)) {
|
|
3404
|
-
this.fail(/* @__PURE__ */ new Error("PROTOCOL_VERSION_UNSUPPORTED"));
|
|
3509
|
+
if (!this.handshakeProtocolMatches(envelope)) {
|
|
3510
|
+
this.rejectHandshake("PROTOCOL_VERSION_UNSUPPORTED");
|
|
3405
3511
|
return;
|
|
3406
3512
|
}
|
|
3407
3513
|
this.remoteCapabilities = readCapabilities(envelope.meta.capabilities);
|
|
@@ -3462,11 +3568,29 @@ window.__ModuleLoader__.load({
|
|
|
3462
3568
|
}
|
|
3463
3569
|
}, interval);
|
|
3464
3570
|
}
|
|
3571
|
+
/**
|
|
3572
|
+
* 会话失败必须同时收敛物理线路。
|
|
3573
|
+
*
|
|
3574
|
+
* 早期实现只把本地状态改成 `degraded`、拒绝 `waitReady`:对端既收不到
|
|
3575
|
+
* `session.close`,DataChannel 也不会关闭,于是一次发送失败(例如浏览器宣告的
|
|
3576
|
+
* max-message-size 太小、分片发送被拒、背压超时)之后,对端会在一个已经死掉的
|
|
3577
|
+
* 线路上等一个永远不回来的响应。中继页面的原生设置 `settings/describe` 正是这样
|
|
3578
|
+
* 永久停在 loading:页面空白、没有任何报错。
|
|
3579
|
+
*/
|
|
3465
3580
|
fail(error) {
|
|
3581
|
+
if (this.stateValue === "closed" || this.stateValue === "degraded") return;
|
|
3582
|
+
const wasReady = this.stateValue === "ready";
|
|
3466
3583
|
this.stateValue = "degraded";
|
|
3467
3584
|
this.debug.log("session.error", { error: error.message });
|
|
3468
3585
|
this.readyReject?.(error);
|
|
3469
3586
|
this.options.onError?.(error);
|
|
3587
|
+
if (wasReady) try {
|
|
3588
|
+
this.send(this.createEnvelope("session.close", "session", { reason: error.message }));
|
|
3589
|
+
} catch {}
|
|
3590
|
+
const carrier = this.options.carrier;
|
|
3591
|
+
setTimeout(() => {
|
|
3592
|
+
carrier.close(`DSH Session 失败:${error.message}`);
|
|
3593
|
+
}, 0);
|
|
3470
3594
|
}
|
|
3471
3595
|
};
|
|
3472
3596
|
function envelopeDebugFields(envelope) {
|
|
@@ -3590,7 +3714,7 @@ window.__ModuleLoader__.load({
|
|
|
3590
3714
|
//#endregion
|
|
3591
3715
|
//#region src/transport/carrier.ts
|
|
3592
3716
|
const TUNNEL_DATA_CHANNEL_LABEL = "codingns-tunnel";
|
|
3593
|
-
/**
|
|
3717
|
+
/** 单个分片的正文上限;整帧为正文加上分片头。 */
|
|
3594
3718
|
const DATA_CHANNEL_FRAGMENT_PAYLOAD_BYTES = 65536;
|
|
3595
3719
|
const DATA_CHANNEL_FRAGMENT_MAGIC = new Uint8Array([
|
|
3596
3720
|
68,
|
|
@@ -3638,6 +3762,22 @@ window.__ModuleLoader__.load({
|
|
|
3638
3762
|
notifyClosed("DataChannel closed");
|
|
3639
3763
|
listeners.clear();
|
|
3640
3764
|
};
|
|
3765
|
+
/**
|
|
3766
|
+
* 物理线路已经不可继续使用时统一收敛:分片缺帧、非法帧和背压超时都必须让上层
|
|
3767
|
+
* 立刻失败。早期实现只在重组超时时删掉半成品分片,请求因此永远得不到回应,
|
|
3768
|
+
* 表现成「设置页一直空白但没有任何报错」。
|
|
3769
|
+
*/
|
|
3770
|
+
const failCarrier = (error) => {
|
|
3771
|
+
if (state === "closed") return;
|
|
3772
|
+
logger.log("carrier.failed", { error: error.message });
|
|
3773
|
+
state = "closed";
|
|
3774
|
+
clearFragments();
|
|
3775
|
+
notifyClosed(error.message);
|
|
3776
|
+
listeners.clear();
|
|
3777
|
+
try {
|
|
3778
|
+
channel.close();
|
|
3779
|
+
} catch {}
|
|
3780
|
+
};
|
|
3641
3781
|
const processBytes = (bytes, value) => {
|
|
3642
3782
|
if (!bytes) {
|
|
3643
3783
|
logger.log("carrier.receive.invalid", {
|
|
@@ -3661,10 +3801,7 @@ window.__ModuleLoader__.load({
|
|
|
3661
3801
|
prefix: bytesToHex(bytes.subarray(0, 8)),
|
|
3662
3802
|
error: error instanceof Error ? error.message : String(error)
|
|
3663
3803
|
});
|
|
3664
|
-
|
|
3665
|
-
clearFragments();
|
|
3666
|
-
listeners.clear();
|
|
3667
|
-
channel.close();
|
|
3804
|
+
failCarrier(error instanceof Error ? error : new Error(String(error)));
|
|
3668
3805
|
}
|
|
3669
3806
|
};
|
|
3670
3807
|
const onMessage = (event) => {
|
|
@@ -3769,7 +3906,10 @@ window.__ModuleLoader__.load({
|
|
|
3769
3906
|
const parsed = decodeFragment(data, maxReassemblyBytes);
|
|
3770
3907
|
let assembly = fragments.get(parsed.fragmentId);
|
|
3771
3908
|
if (!assembly) {
|
|
3772
|
-
const timer = setTimeout(() =>
|
|
3909
|
+
const timer = setTimeout(() => {
|
|
3910
|
+
fragments.delete(parsed.fragmentId);
|
|
3911
|
+
failCarrier(/* @__PURE__ */ new Error(`DataChannel 分片重组超时(${String(parsed.chunkCount)} 片,共 ${String(parsed.totalBytes)} 字节)`));
|
|
3912
|
+
}, reassemblyTimeoutMs);
|
|
3773
3913
|
assembly = {
|
|
3774
3914
|
totalBytes: parsed.totalBytes,
|
|
3775
3915
|
chunkCount: parsed.chunkCount,
|
|
@@ -3791,15 +3931,7 @@ window.__ModuleLoader__.load({
|
|
|
3791
3931
|
if (assembly.chunks.size !== assembly.chunkCount) return null;
|
|
3792
3932
|
clearTimeout(assembly.timer);
|
|
3793
3933
|
fragments.delete(parsed.fragmentId);
|
|
3794
|
-
const result =
|
|
3795
|
-
let offset = 0;
|
|
3796
|
-
for (let index = 0; index < assembly.chunkCount; index += 1) {
|
|
3797
|
-
const chunk = assembly.chunks.get(index);
|
|
3798
|
-
if (!chunk) throw new Error("DataChannel 分片缺失");
|
|
3799
|
-
result.set(chunk, offset);
|
|
3800
|
-
offset += chunk.byteLength;
|
|
3801
|
-
}
|
|
3802
|
-
if (offset !== assembly.totalBytes) throw new Error("DataChannel 分片总长度不一致");
|
|
3934
|
+
const result = assembleChunks(assembly);
|
|
3803
3935
|
logger.log("carrier.fragment.complete", {
|
|
3804
3936
|
fragmentId: parsed.fragmentId,
|
|
3805
3937
|
chunks: assembly.chunkCount,
|
|
@@ -3813,8 +3945,9 @@ window.__ModuleLoader__.load({
|
|
|
3813
3945
|
},
|
|
3814
3946
|
send(data) {
|
|
3815
3947
|
if (!(data instanceof Uint8Array)) return Promise.reject(/* @__PURE__ */ new TypeError("Carrier 只接受 Uint8Array"));
|
|
3816
|
-
|
|
3817
|
-
|
|
3948
|
+
const result = chain.then(() => sendLogical(data));
|
|
3949
|
+
chain = result.then(() => void 0, () => void 0);
|
|
3950
|
+
return result;
|
|
3818
3951
|
},
|
|
3819
3952
|
subscribe(listener) {
|
|
3820
3953
|
listeners.add(listener);
|
|
@@ -3855,18 +3988,23 @@ window.__ModuleLoader__.load({
|
|
|
3855
3988
|
result.set(body, 20);
|
|
3856
3989
|
return result;
|
|
3857
3990
|
}
|
|
3991
|
+
/**
|
|
3992
|
+
* 按分片头校验单个分片,不假设发送端的固定正文长度。
|
|
3993
|
+
*
|
|
3994
|
+
* 旧版本(以及线上已部署的 H5 运行时)以 64 KiB 正文分片,只有最后一片更短;
|
|
3995
|
+
* 分片长度由发送端决定,接收端必须按「除最后一片外长度一致」来校验,否则任何
|
|
3996
|
+
* 分片长度调整都会让新旧两端无法互通。单帧正文仍限制在
|
|
3997
|
+
* `DATA_CHANNEL_FRAGMENT_PAYLOAD_BYTES` 以内,避免一次分配过大内存。
|
|
3998
|
+
*/
|
|
3858
3999
|
function decodeFragment(data, maxReassemblyBytes) {
|
|
3859
4000
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
3860
4001
|
const fragmentId = view.getUint32(4);
|
|
3861
4002
|
const index = view.getUint32(8);
|
|
3862
4003
|
const chunkCount = view.getUint32(12);
|
|
3863
4004
|
const totalBytes = view.getUint32(16);
|
|
3864
|
-
if (chunkCount
|
|
3865
|
-
if (chunkCount !== Math.ceil(totalBytes / 65536)) throw new Error("DataChannel 分片数量无效");
|
|
3866
|
-
const offset = index * DATA_CHANNEL_FRAGMENT_PAYLOAD_BYTES;
|
|
3867
|
-
const expectedBytes = Math.min(DATA_CHANNEL_FRAGMENT_PAYLOAD_BYTES, totalBytes - offset);
|
|
4005
|
+
if (chunkCount < 2 || totalBytes < 2 || totalBytes > maxReassemblyBytes || index >= chunkCount) throw new Error("DataChannel 分片头无效");
|
|
3868
4006
|
const body = data.subarray(20);
|
|
3869
|
-
if (body.byteLength
|
|
4007
|
+
if (body.byteLength === 0 || body.byteLength > 65536) throw new Error("DataChannel 分片长度无效");
|
|
3870
4008
|
return {
|
|
3871
4009
|
fragmentId,
|
|
3872
4010
|
index,
|
|
@@ -3875,6 +4013,27 @@ window.__ModuleLoader__.load({
|
|
|
3875
4013
|
body: body.slice()
|
|
3876
4014
|
};
|
|
3877
4015
|
}
|
|
4016
|
+
/** 收齐全部索引后按发送端的分片长度重组,并校验长度自洽。 */
|
|
4017
|
+
function assembleChunks(assembly) {
|
|
4018
|
+
const first = assembly.chunks.get(0);
|
|
4019
|
+
if (first === void 0) throw new Error("DataChannel 分片缺失");
|
|
4020
|
+
const chunkBytes = first.byteLength;
|
|
4021
|
+
if (chunkBytes === 0 || chunkBytes > 65536) throw new Error("DataChannel 分片长度无效");
|
|
4022
|
+
if (assembly.chunkCount !== Math.ceil(assembly.totalBytes / chunkBytes)) throw new Error("DataChannel 分片数量无效");
|
|
4023
|
+
const lastBytes = assembly.totalBytes - chunkBytes * (assembly.chunkCount - 1);
|
|
4024
|
+
if (lastBytes < 1 || lastBytes > chunkBytes) throw new Error("DataChannel 分片长度无效");
|
|
4025
|
+
const result = new Uint8Array(assembly.totalBytes);
|
|
4026
|
+
let offset = 0;
|
|
4027
|
+
for (let index = 0; index < assembly.chunkCount; index += 1) {
|
|
4028
|
+
const chunk = assembly.chunks.get(index);
|
|
4029
|
+
if (!chunk) throw new Error("DataChannel 分片缺失");
|
|
4030
|
+
if (chunk.byteLength !== (index === assembly.chunkCount - 1 ? lastBytes : chunkBytes)) throw new Error("DataChannel 分片长度不一致");
|
|
4031
|
+
result.set(chunk, offset);
|
|
4032
|
+
offset += chunk.byteLength;
|
|
4033
|
+
}
|
|
4034
|
+
if (offset !== assembly.totalBytes) throw new Error("DataChannel 分片总长度不一致");
|
|
4035
|
+
return result;
|
|
4036
|
+
}
|
|
3878
4037
|
function toBytes$1(value) {
|
|
3879
4038
|
if (value instanceof Uint8Array) return new Uint8Array(value);
|
|
3880
4039
|
if (value instanceof ArrayBuffer) return new Uint8Array(value.slice(0));
|
|
@@ -4602,12 +4761,34 @@ window.__ModuleLoader__.load({
|
|
|
4602
4761
|
const pending = new Map();
|
|
4603
4762
|
// RPC、WebSocket 和远程 stream 必须使用各自的 ID 空间;资源 fetch
|
|
4604
4763
|
// 不能改变 WebSocket 的 ID,否则 ws.open 与 ws.send 会指向不同 key。
|
|
4764
|
+
//
|
|
4765
|
+
// 每个 ID 还必须带 kind 前缀:早期实现把 fetch/script 计数器和 ws.open 计数器
|
|
4766
|
+
// 当成裸数字放进同一个 pending Map,两个计数器各自从 1 开始,页面启动时
|
|
4767
|
+
// 「插件包 fetch」与「/api/remote.mux ws.open」几乎同时发生,后到的
|
|
4768
|
+
// pending.set 会覆盖先到的在途请求。被覆盖的那条 Promise 永远不会落地:
|
|
4769
|
+
// 如果它正好是原生设置页的 settings/describe,DSH 的设置镜像会一直停在
|
|
4770
|
+
// loading,中继下的「模型」页就永久空白且没有任何报错。
|
|
4605
4771
|
let nextCallId = 0;
|
|
4606
4772
|
let nextSocketId = 0;
|
|
4607
4773
|
let nextStreamId = 0;
|
|
4608
4774
|
let remoteLoadChain = Promise.resolve();
|
|
4775
|
+
const allocateId = (prefix, counter) => {
|
|
4776
|
+
let value = counter + 1;
|
|
4777
|
+
let id = prefix + String(value);
|
|
4778
|
+
// 绝不覆盖未完成的请求:万一同前缀 ID 仍在使用就继续往后取。
|
|
4779
|
+
while (pending.has(id)) {
|
|
4780
|
+
value += 1;
|
|
4781
|
+
id = prefix + String(value);
|
|
4782
|
+
}
|
|
4783
|
+
return { id, counter: value };
|
|
4784
|
+
};
|
|
4609
4785
|
const call = (kind, input, body, explicitId) => new Promise((resolve, reject) => {
|
|
4610
|
-
|
|
4786
|
+
let id = explicitId;
|
|
4787
|
+
if (id === undefined) {
|
|
4788
|
+
const allocated = allocateId('req:', nextCallId);
|
|
4789
|
+
nextCallId = allocated.counter;
|
|
4790
|
+
id = allocated.id;
|
|
4791
|
+
}
|
|
4611
4792
|
pending.set(id, { resolve, reject });
|
|
4612
4793
|
bridgeLog('bridge.call', { kind, id, path: input && typeof input.path === 'string' ? input.path : undefined });
|
|
4613
4794
|
parent.postMessage({ kind, id, input, body }, '*');
|
|
@@ -4939,7 +5120,9 @@ window.__ModuleLoader__.load({
|
|
|
4939
5120
|
this.url = String(url);
|
|
4940
5121
|
this.readyState = RemoteWebSocket.CONNECTING;
|
|
4941
5122
|
this._listeners = new Map();
|
|
4942
|
-
|
|
5123
|
+
const allocated = allocateId('ws:', nextSocketId);
|
|
5124
|
+
nextSocketId = allocated.counter;
|
|
5125
|
+
window.__dshRemoteSockets.set(this._id = allocated.id, this);
|
|
4943
5126
|
bridgeLog('bridge.ws.open', { id: this._id, path: new URL(this.url, resourceBase()).pathname });
|
|
4944
5127
|
call('ws.open', { path: new URL(this.url, resourceBase()).pathname }, undefined, this._id).then(() => {
|
|
4945
5128
|
if (this.readyState !== RemoteWebSocket.CONNECTING) return;
|
|
@@ -5447,10 +5630,13 @@ window.__ModuleLoader__.load({
|
|
|
5447
5630
|
const attachConnectionClose = (current) => {
|
|
5448
5631
|
current.onClosed((error) => {
|
|
5449
5632
|
if (stopped || current !== connection) return;
|
|
5633
|
+
const reason = error ?? /* @__PURE__ */ new Error("WebRTC connection closed");
|
|
5450
5634
|
debug.log("bootstrap.connection.closed", {
|
|
5451
5635
|
generation,
|
|
5452
|
-
error:
|
|
5636
|
+
error: reason.message
|
|
5453
5637
|
});
|
|
5638
|
+
transport.invalidateConnection(reason);
|
|
5639
|
+
session?.close(reason.message);
|
|
5454
5640
|
reconnectTimer = setTimeout(() => {
|
|
5455
5641
|
reconnectTimer = void 0;
|
|
5456
5642
|
reconnect();
|
|
@@ -5796,14 +5982,33 @@ window.__ModuleLoader__.load({
|
|
|
5796
5982
|
return result.value;
|
|
5797
5983
|
}
|
|
5798
5984
|
/** 设置 RPC 只能传 JSON;在浏览器边界尽早拒绝函数、循环引用等无效值。 */
|
|
5799
|
-
function toJsonValue(value) {
|
|
5985
|
+
function toJsonValue(value, visiting = /* @__PURE__ */ new WeakSet()) {
|
|
5800
5986
|
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
5801
5987
|
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
5802
|
-
if (Array.isArray(value))
|
|
5803
|
-
|
|
5988
|
+
if (Array.isArray(value)) {
|
|
5989
|
+
if (visiting.has(value)) throw new TypeError("设置值不能包含循环引用");
|
|
5990
|
+
visiting.add(value);
|
|
5991
|
+
try {
|
|
5992
|
+
return value.map((entry) => toJsonValue(entry, visiting));
|
|
5993
|
+
} finally {
|
|
5994
|
+
visiting.delete(value);
|
|
5995
|
+
}
|
|
5996
|
+
}
|
|
5997
|
+
if (typeof value !== "object" || value === void 0) throw new TypeError("设置值必须是可序列化的 JSON");
|
|
5998
|
+
const prototype = Object.getPrototypeOf(value);
|
|
5999
|
+
if (prototype !== Object.prototype && prototype !== null) throw new TypeError("设置值只能包含 JSON 对象、数组和标量");
|
|
6000
|
+
if (visiting.has(value)) throw new TypeError("设置值不能包含循环引用");
|
|
6001
|
+
visiting.add(value);
|
|
5804
6002
|
const result = {};
|
|
5805
|
-
|
|
5806
|
-
|
|
6003
|
+
try {
|
|
6004
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
6005
|
+
if (entry === void 0) continue;
|
|
6006
|
+
result[key] = toJsonValue(entry, visiting);
|
|
6007
|
+
}
|
|
6008
|
+
return result;
|
|
6009
|
+
} finally {
|
|
6010
|
+
visiting.delete(value);
|
|
6011
|
+
}
|
|
5807
6012
|
}
|
|
5808
6013
|
function createCodingNsSettingsBridge(local, rpc) {
|
|
5809
6014
|
return new CodingNsSettingsBridge(local, rpc);
|
|
@@ -6527,6 +6732,126 @@ window.__ModuleLoader__.load({
|
|
|
6527
6732
|
listeners = /* @__PURE__ */ new Set();
|
|
6528
6733
|
}));
|
|
6529
6734
|
//#endregion
|
|
6735
|
+
//#region src/client/context-breakdown-dom.ts
|
|
6736
|
+
init_session_adapter_cache();
|
|
6737
|
+
/** 命中会话的上下文面板会被打上该属性,插件样式据此隐藏构成部分。 */
|
|
6738
|
+
const CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE = "data-codingns-context-breakdown-hidden";
|
|
6739
|
+
/** 插件样式标签的幂等键。 */
|
|
6740
|
+
const CONTEXT_BREAKDOWN_STYLE_ID = "codingns4dsh-context-breakdown-style";
|
|
6741
|
+
/** 对话根节点上承载当前会话 id 的 DSH 属性。 */
|
|
6742
|
+
const CONTEXT_BREAKDOWN_CONVERSATION_ATTRIBUTE = "data-conversation-session";
|
|
6743
|
+
/** 默认隐藏构成明细的适配器:它们自己组装请求,启发式估算与真实上下文不同源。 */
|
|
6744
|
+
const DEFAULT_CONTEXT_BREAKDOWN_HIDDEN_ADAPTERS = ["codex"];
|
|
6745
|
+
/** 适配器未知时请求刷新映射的最小间隔,避免面板长时间停留时反复查询。 */
|
|
6746
|
+
const ADAPTER_REFRESH_INTERVAL_MS = 5e3;
|
|
6747
|
+
const CONTEXT_BREAKDOWN_STYLE_TEXT = `[${CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE}]>dl{display:none!important}[${CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE}]>div:nth-of-type(2){display:none!important}`;
|
|
6748
|
+
/**
|
|
6749
|
+
* 为上下文面板安装构成明细隐藏器。
|
|
6750
|
+
*
|
|
6751
|
+
* 打开面板时才需要判定所属会话,因此只观察 `body` 的直接子节点(portal 插入),
|
|
6752
|
+
* 不跟踪面板内部重渲染;隐藏通过面板属性加插件样式实现,React 复用面板节点时
|
|
6753
|
+
* 属性仍然有效。
|
|
6754
|
+
*/
|
|
6755
|
+
function startContextBreakdownDom(options = {}) {
|
|
6756
|
+
const dom = options.document ?? (typeof document === "undefined" ? void 0 : document);
|
|
6757
|
+
const Observer = options.MutationObserver ?? (typeof MutationObserver === "undefined" ? void 0 : MutationObserver);
|
|
6758
|
+
const adapterIdForSession = options.adapterIdForSession ?? sessionAdapterId;
|
|
6759
|
+
const hiddenAdapterIds = new Set(options.hiddenAdapterIds ?? DEFAULT_CONTEXT_BREAKDOWN_HIDDEN_ADAPTERS);
|
|
6760
|
+
const refreshAdapters = options.refreshAdapters;
|
|
6761
|
+
const adapterRefreshIntervalMs = options.adapterRefreshIntervalMs ?? ADAPTER_REFRESH_INTERVAL_MS;
|
|
6762
|
+
const now = options.now ?? (() => Date.now());
|
|
6763
|
+
let disposed = false;
|
|
6764
|
+
let scanQueued = false;
|
|
6765
|
+
let lastAdapterRefresh = Number.NEGATIVE_INFINITY;
|
|
6766
|
+
const scan = () => {
|
|
6767
|
+
if (disposed || dom === void 0) return;
|
|
6768
|
+
const sessionId = openContextMeterSessionId(dom);
|
|
6769
|
+
const adapterId = sessionId === void 0 ? void 0 : adapterIdForSession(sessionId);
|
|
6770
|
+
const hideComposition = adapterId !== void 0 && hiddenAdapterIds.has(adapterId);
|
|
6771
|
+
for (const panel of contextMeterPanels(dom)) if (hideComposition) panel.setAttribute(CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE, "");
|
|
6772
|
+
else panel.removeAttribute(CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE);
|
|
6773
|
+
if (sessionId !== void 0 && adapterId === void 0) scheduleAdapterRefresh();
|
|
6774
|
+
};
|
|
6775
|
+
const scheduleScan = () => {
|
|
6776
|
+
if (disposed || scanQueued) return;
|
|
6777
|
+
scanQueued = true;
|
|
6778
|
+
queueMicrotask(() => {
|
|
6779
|
+
scanQueued = false;
|
|
6780
|
+
scan();
|
|
6781
|
+
});
|
|
6782
|
+
};
|
|
6783
|
+
const scheduleAdapterRefresh = () => {
|
|
6784
|
+
if (disposed || refreshAdapters === void 0) return;
|
|
6785
|
+
const timestamp = now();
|
|
6786
|
+
if (timestamp - lastAdapterRefresh < adapterRefreshIntervalMs) return;
|
|
6787
|
+
lastAdapterRefresh = timestamp;
|
|
6788
|
+
(async () => {
|
|
6789
|
+
try {
|
|
6790
|
+
await refreshAdapters();
|
|
6791
|
+
} catch {}
|
|
6792
|
+
if (!disposed) scheduleScan();
|
|
6793
|
+
})();
|
|
6794
|
+
};
|
|
6795
|
+
installContextBreakdownStyles(dom);
|
|
6796
|
+
const observer = dom === void 0 || Observer === void 0 ? void 0 : new Observer(scheduleScan);
|
|
6797
|
+
const observeTarget = dom?.body ?? dom?.documentElement;
|
|
6798
|
+
if (observer !== void 0 && observeTarget !== void 0 && observeTarget !== null) observer.observe(observeTarget, { childList: true });
|
|
6799
|
+
const unsubscribe = subscribeSessionAdapters(scheduleScan);
|
|
6800
|
+
scheduleScan();
|
|
6801
|
+
return {
|
|
6802
|
+
refresh: scheduleScan,
|
|
6803
|
+
dispose() {
|
|
6804
|
+
if (disposed) return;
|
|
6805
|
+
disposed = true;
|
|
6806
|
+
observer?.disconnect();
|
|
6807
|
+
unsubscribe();
|
|
6808
|
+
if (dom === void 0) return;
|
|
6809
|
+
for (const panel of contextMeterPanels(dom)) panel.removeAttribute(CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE);
|
|
6810
|
+
}
|
|
6811
|
+
};
|
|
6812
|
+
}
|
|
6813
|
+
/** 返回当前展开着上下文面板的会话 id;没有展开的面板时返回 undefined。 */
|
|
6814
|
+
function openContextMeterSessionId(dom) {
|
|
6815
|
+
for (const conversation of Array.from(dom.querySelectorAll(`[${CONTEXT_BREAKDOWN_CONVERSATION_ATTRIBUTE}]`))) {
|
|
6816
|
+
const sessionId = conversation.getAttribute(CONTEXT_BREAKDOWN_CONVERSATION_ATTRIBUTE)?.trim();
|
|
6817
|
+
if (sessionId === void 0 || sessionId === "") continue;
|
|
6818
|
+
for (const button of Array.from(conversation.querySelectorAll("button"))) if (button.getAttribute("aria-haspopup") === "dialog" && button.getAttribute("aria-expanded") === "true") return sessionId;
|
|
6819
|
+
}
|
|
6820
|
+
}
|
|
6821
|
+
/** 找出挂在 body 下、带构成明细的上下文面板。 */
|
|
6822
|
+
function contextMeterPanels(dom) {
|
|
6823
|
+
const body = dom.body;
|
|
6824
|
+
if (body === void 0 || body === null) return [];
|
|
6825
|
+
const panels = [];
|
|
6826
|
+
for (const child of Array.from(body.children)) {
|
|
6827
|
+
if (child.getAttribute("role") !== "dialog") continue;
|
|
6828
|
+
if (!hasCompositionDetails(child)) continue;
|
|
6829
|
+
panels.push(child);
|
|
6830
|
+
}
|
|
6831
|
+
return panels;
|
|
6832
|
+
}
|
|
6833
|
+
/** 面板的明细是 `dl > div > dt + dd`;普通 `dl` 不会包一层 div。 */
|
|
6834
|
+
function hasCompositionDetails(panel) {
|
|
6835
|
+
for (const child of Array.from(panel.children)) {
|
|
6836
|
+
if (child.tagName !== "DL") continue;
|
|
6837
|
+
for (const row of Array.from(child.children)) {
|
|
6838
|
+
if (row.tagName !== "DIV") continue;
|
|
6839
|
+
if (row.querySelector("dt") !== null && row.querySelector("dd") !== null) return true;
|
|
6840
|
+
}
|
|
6841
|
+
}
|
|
6842
|
+
return false;
|
|
6843
|
+
}
|
|
6844
|
+
/** 按 quick-phrase 的既有模式幂等注入插件样式。 */
|
|
6845
|
+
function installContextBreakdownStyles(dom) {
|
|
6846
|
+
if (dom === void 0 || dom.head === void 0 || dom.head === null) return;
|
|
6847
|
+
if (Array.from(dom.head.children).some((child) => child.tagName === "STYLE" && child.dataset.pluginCss === "codingns4dsh-context-breakdown-style")) return;
|
|
6848
|
+
const style = dom.createElement("style");
|
|
6849
|
+
style.dataset.plugin = "codingns4dsh";
|
|
6850
|
+
style.dataset.pluginCss = CONTEXT_BREAKDOWN_STYLE_ID;
|
|
6851
|
+
style.textContent = CONTEXT_BREAKDOWN_STYLE_TEXT;
|
|
6852
|
+
dom.head.appendChild(style);
|
|
6853
|
+
}
|
|
6854
|
+
//#endregion
|
|
6530
6855
|
//#region src/client/cli-slots.ts
|
|
6531
6856
|
var cli_slots_exports = /* @__PURE__ */ __exportAll({
|
|
6532
6857
|
AgentSlot: () => AgentSlot,
|
|
@@ -7299,6 +7624,7 @@ window.__ModuleLoader__.load({
|
|
|
7299
7624
|
init_cli_catalog();
|
|
7300
7625
|
init_theme();
|
|
7301
7626
|
init_locale();
|
|
7627
|
+
init_session_adapter_cache();
|
|
7302
7628
|
/** 外部 Agent 集成模块。Agent 进程在 Host 运行,浏览器只读取目录和状态。 */
|
|
7303
7629
|
const cliAdaptersFeature = {
|
|
7304
7630
|
descriptor: {
|
|
@@ -7317,6 +7643,13 @@ window.__ModuleLoader__.load({
|
|
|
7317
7643
|
}
|
|
7318
7644
|
},
|
|
7319
7645
|
start: async (context) => {
|
|
7646
|
+
const contextBreakdownDom = startContextBreakdownDom({
|
|
7647
|
+
adapterIdForSession: sessionAdapterId,
|
|
7648
|
+
refreshAdapters: async () => {
|
|
7649
|
+
replaceSessionAdapters(await fetchSessionAdapters(context.services.rpc));
|
|
7650
|
+
}
|
|
7651
|
+
});
|
|
7652
|
+
context.resources.add(() => contextBreakdownDom.dispose());
|
|
7320
7653
|
const slots = context.services.slots;
|
|
7321
7654
|
if (slots === void 0) return;
|
|
7322
7655
|
context.resources.add(registerExternalToolStreamUi(context.services));
|
|
@@ -9290,12 +9623,173 @@ window.__ModuleLoader__.load({
|
|
|
9290
9623
|
borderTop: "1px solid var(--dsw-alias-border-l3, rgba(128,128,128,.18))"
|
|
9291
9624
|
};
|
|
9292
9625
|
//#endregion
|
|
9626
|
+
//#region src/client/features/subscription-usage-panel.ts
|
|
9627
|
+
init_theme();
|
|
9628
|
+
init_locale();
|
|
9629
|
+
/**
|
|
9630
|
+
* 用量查询设置对话框:单次查询超时与自动查询间隔。
|
|
9631
|
+
*
|
|
9632
|
+
* 两项设置由 Host 统一下发给所有适配器读取器;Client 侧所有 Agent 共用同一个
|
|
9633
|
+
* 自动刷新定时器与同一份结果缓存,不区分具体适配器。
|
|
9634
|
+
*/
|
|
9635
|
+
function SubscriptionUsageSettingsDialog({ services, snapshot, notify, onClose }) {
|
|
9636
|
+
const t = useCodingNsTranslator(services.locale);
|
|
9637
|
+
const value = normalizeSubscriptionUsageSettings(snapshot.value?.subscriptionUsage);
|
|
9638
|
+
const disabled = snapshot.status === "loading" || !snapshot.writable;
|
|
9639
|
+
const [timeoutText, setTimeoutText] = (0, react.useState)(() => String(value.timeoutSecs));
|
|
9640
|
+
const [intervalText, setIntervalText] = (0, react.useState)(() => String(value.refreshIntervalMins));
|
|
9641
|
+
(0, react.useEffect)(() => {
|
|
9642
|
+
const closeOnEscape = (event) => {
|
|
9643
|
+
if (event.key === "Escape") onClose();
|
|
9644
|
+
};
|
|
9645
|
+
document.addEventListener("keydown", closeOnEscape);
|
|
9646
|
+
return () => document.removeEventListener("keydown", closeOnEscape);
|
|
9647
|
+
}, [onClose]);
|
|
9648
|
+
const save = () => {
|
|
9649
|
+
const next = normalizeSubscriptionUsageSettings({
|
|
9650
|
+
timeoutSecs: Number(timeoutText),
|
|
9651
|
+
refreshIntervalMins: Number(intervalText)
|
|
9652
|
+
});
|
|
9653
|
+
services.settings.mutate([{
|
|
9654
|
+
op: "set",
|
|
9655
|
+
path: [CODINGNS_SUBSCRIPTION_USAGE_FIELD, "timeoutSecs"],
|
|
9656
|
+
value: next.timeoutSecs
|
|
9657
|
+
}, {
|
|
9658
|
+
op: "set",
|
|
9659
|
+
path: [CODINGNS_SUBSCRIPTION_USAGE_FIELD, "refreshIntervalMins"],
|
|
9660
|
+
value: next.refreshIntervalMins
|
|
9661
|
+
}]).then((accepted) => {
|
|
9662
|
+
if (!accepted) {
|
|
9663
|
+
notify({
|
|
9664
|
+
kind: "error",
|
|
9665
|
+
message: t("settings.moduleWriteRejected")
|
|
9666
|
+
});
|
|
9667
|
+
return;
|
|
9668
|
+
}
|
|
9669
|
+
notify({
|
|
9670
|
+
kind: "success",
|
|
9671
|
+
message: t("subscriptionUsage.saved")
|
|
9672
|
+
});
|
|
9673
|
+
onClose();
|
|
9674
|
+
}).catch((cause) => {
|
|
9675
|
+
notify({
|
|
9676
|
+
kind: "error",
|
|
9677
|
+
message: cause instanceof Error ? cause.message : String(cause)
|
|
9678
|
+
});
|
|
9679
|
+
});
|
|
9680
|
+
};
|
|
9681
|
+
return (0, react.createElement)("div", {
|
|
9682
|
+
className: "codingns4dsh-usage-settings-overlay",
|
|
9683
|
+
role: "presentation",
|
|
9684
|
+
onPointerDown: onClose,
|
|
9685
|
+
style: usageSettingsOverlayStyle
|
|
9686
|
+
}, (0, react.createElement)("div", {
|
|
9687
|
+
role: "dialog",
|
|
9688
|
+
"aria-modal": true,
|
|
9689
|
+
"aria-label": t("subscriptionUsage.dialogTitle"),
|
|
9690
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
9691
|
+
style: usageSettingsDialogStyle
|
|
9692
|
+
}, (0, react.createElement)("div", { style: {
|
|
9693
|
+
display: "grid",
|
|
9694
|
+
gap: 4
|
|
9695
|
+
} }, (0, react.createElement)("strong", { style: {
|
|
9696
|
+
fontSize: 14,
|
|
9697
|
+
lineHeight: 1.4
|
|
9698
|
+
} }, t("subscriptionUsage.dialogTitle")), (0, react.createElement)("span", { style: dshSettingsHelpStyle }, t("subscriptionUsage.dialogHint"))), (0, react.createElement)(UsageQueryNumberField, {
|
|
9699
|
+
label: t("subscriptionUsage.timeoutSecs"),
|
|
9700
|
+
value: timeoutText,
|
|
9701
|
+
min: SUBSCRIPTION_USAGE_TIMEOUT_SECS_LIMITS.min,
|
|
9702
|
+
max: SUBSCRIPTION_USAGE_TIMEOUT_SECS_LIMITS.max,
|
|
9703
|
+
disabled,
|
|
9704
|
+
onChange: setTimeoutText
|
|
9705
|
+
}), (0, react.createElement)(UsageQueryNumberField, {
|
|
9706
|
+
label: t("subscriptionUsage.refreshInterval"),
|
|
9707
|
+
help: t("subscriptionUsage.refreshIntervalHint"),
|
|
9708
|
+
value: intervalText,
|
|
9709
|
+
min: SUBSCRIPTION_USAGE_REFRESH_INTERVAL_MINS_LIMITS.min,
|
|
9710
|
+
max: SUBSCRIPTION_USAGE_REFRESH_INTERVAL_MINS_LIMITS.max,
|
|
9711
|
+
disabled,
|
|
9712
|
+
onChange: setIntervalText
|
|
9713
|
+
}), (0, react.createElement)("div", { style: {
|
|
9714
|
+
display: "flex",
|
|
9715
|
+
justifyContent: "flex-end",
|
|
9716
|
+
gap: 8,
|
|
9717
|
+
paddingTop: 4
|
|
9718
|
+
} }, (0, react.createElement)("button", {
|
|
9719
|
+
type: "button",
|
|
9720
|
+
onClick: onClose,
|
|
9721
|
+
style: dshSettingsButtonStyle
|
|
9722
|
+
}, t("subscriptionUsage.cancel")), (0, react.createElement)("button", {
|
|
9723
|
+
type: "button",
|
|
9724
|
+
disabled,
|
|
9725
|
+
onClick: save,
|
|
9726
|
+
style: dshSettingsPrimaryButtonStyle
|
|
9727
|
+
}, t("subscriptionUsage.save")))));
|
|
9728
|
+
}
|
|
9729
|
+
/** 整数输入行:文本受控,保存时统一夹取到合法区间。 */
|
|
9730
|
+
function UsageQueryNumberField({ label, help, value, min, max, disabled, onChange }) {
|
|
9731
|
+
return (0, react.createElement)("div", { style: usageQueryNumberRowStyle }, (0, react.createElement)("span", { style: { minWidth: 0 } }, (0, react.createElement)("strong", { style: {
|
|
9732
|
+
display: "block",
|
|
9733
|
+
fontSize: 13,
|
|
9734
|
+
lineHeight: 1.4
|
|
9735
|
+
} }, label), help === void 0 ? null : (0, react.createElement)("span", { style: {
|
|
9736
|
+
display: "block",
|
|
9737
|
+
marginTop: 3,
|
|
9738
|
+
...dshSettingsHelpStyle
|
|
9739
|
+
} }, help)), (0, react.createElement)("input", {
|
|
9740
|
+
type: "number",
|
|
9741
|
+
min,
|
|
9742
|
+
max,
|
|
9743
|
+
step: 1,
|
|
9744
|
+
"aria-label": label,
|
|
9745
|
+
value,
|
|
9746
|
+
disabled,
|
|
9747
|
+
onChange: (event) => onChange(event.currentTarget.value),
|
|
9748
|
+
style: usageQueryNumberInputStyle
|
|
9749
|
+
}));
|
|
9750
|
+
}
|
|
9751
|
+
const usageSettingsOverlayStyle = {
|
|
9752
|
+
position: "fixed",
|
|
9753
|
+
inset: 0,
|
|
9754
|
+
zIndex: 1400,
|
|
9755
|
+
display: "flex",
|
|
9756
|
+
alignItems: "center",
|
|
9757
|
+
justifyContent: "center",
|
|
9758
|
+
padding: 20,
|
|
9759
|
+
boxSizing: "border-box",
|
|
9760
|
+
background: "rgba(0, 0, 0, 0.35)"
|
|
9761
|
+
};
|
|
9762
|
+
const usageSettingsDialogStyle = {
|
|
9763
|
+
...dshPopupSurfaceStyle,
|
|
9764
|
+
width: "min(100%, 420px)",
|
|
9765
|
+
boxSizing: "border-box",
|
|
9766
|
+
padding: 16,
|
|
9767
|
+
borderRadius: 12,
|
|
9768
|
+
display: "grid",
|
|
9769
|
+
gap: 12
|
|
9770
|
+
};
|
|
9771
|
+
const usageQueryNumberRowStyle = {
|
|
9772
|
+
...dshSettingsListRowStyle,
|
|
9773
|
+
justifyContent: "space-between"
|
|
9774
|
+
};
|
|
9775
|
+
const usageQueryNumberInputStyle = {
|
|
9776
|
+
...dshFieldStyle,
|
|
9777
|
+
flex: "0 0 auto",
|
|
9778
|
+
width: 110,
|
|
9779
|
+
minHeight: 32,
|
|
9780
|
+
boxSizing: "border-box",
|
|
9781
|
+
padding: "5px 8px",
|
|
9782
|
+
borderRadius: 6,
|
|
9783
|
+
fontSize: 13
|
|
9784
|
+
};
|
|
9785
|
+
//#endregion
|
|
9293
9786
|
//#region src/client/features/workspace-session-enhancement-panel.ts
|
|
9294
9787
|
init_theme();
|
|
9295
9788
|
init_locale();
|
|
9296
9789
|
/** 工作区会话增强的单列设置面板。 */
|
|
9297
9790
|
function WorkspaceSessionEnhancementPanel({ services, enabled, snapshot, notify }) {
|
|
9298
9791
|
const t = useCodingNsTranslator(services.locale);
|
|
9792
|
+
const [usageSettingsOpen, setUsageSettingsOpen] = (0, react.useState)(false);
|
|
9299
9793
|
const value = snapshot.value?.workspaceSessionEnhancement ?? DEFAULT_WORKSPACE_SESSION_ENHANCEMENT_SETTINGS;
|
|
9300
9794
|
const disabled = !enabled || snapshot.status === "loading" || !snapshot.writable;
|
|
9301
9795
|
const updateSetting = (field, nextValue) => {
|
|
@@ -9370,7 +9864,14 @@ window.__ModuleLoader__.load({
|
|
|
9370
9864
|
flex: "0 0 auto",
|
|
9371
9865
|
accentColor: dshThemeColor.accent
|
|
9372
9866
|
}
|
|
9373
|
-
})), (0, react.createElement)("
|
|
9867
|
+
})), (0, react.createElement)("div", { style: dshSettingsListRowStyle }, (0, react.createElement)("label", { style: {
|
|
9868
|
+
display: "flex",
|
|
9869
|
+
alignItems: "center",
|
|
9870
|
+
gap: 12,
|
|
9871
|
+
flex: "1 1 auto",
|
|
9872
|
+
minWidth: 0,
|
|
9873
|
+
cursor: "pointer"
|
|
9874
|
+
} }, (0, react.createElement)("span", { style: { minWidth: 0 } }, (0, react.createElement)("strong", { style: {
|
|
9374
9875
|
display: "block",
|
|
9375
9876
|
fontSize: 13,
|
|
9376
9877
|
lineHeight: 1.4
|
|
@@ -9389,7 +9890,15 @@ window.__ModuleLoader__.load({
|
|
|
9389
9890
|
flex: "0 0 auto",
|
|
9390
9891
|
accentColor: dshThemeColor.accent
|
|
9391
9892
|
}
|
|
9392
|
-
})), (0, react.createElement)("
|
|
9893
|
+
})), (0, react.createElement)("button", {
|
|
9894
|
+
type: "button",
|
|
9895
|
+
"aria-haspopup": "dialog",
|
|
9896
|
+
"aria-expanded": usageSettingsOpen,
|
|
9897
|
+
"aria-label": t("workspace.subscriptionUsageSettings"),
|
|
9898
|
+
disabled,
|
|
9899
|
+
onClick: () => setUsageSettingsOpen(true),
|
|
9900
|
+
style: dshSettingsButtonStyle
|
|
9901
|
+
}, t("workspace.subscriptionUsageSettings"))), (0, react.createElement)("label", { style: dshSettingsListRowStyle }, (0, react.createElement)("span", { style: { minWidth: 0 } }, (0, react.createElement)("strong", { style: {
|
|
9393
9902
|
display: "block",
|
|
9394
9903
|
fontSize: 13,
|
|
9395
9904
|
lineHeight: 1.4
|
|
@@ -9446,14 +9955,25 @@ window.__ModuleLoader__.load({
|
|
|
9446
9955
|
flex: "0 0 auto",
|
|
9447
9956
|
accentColor: dshThemeColor.accent
|
|
9448
9957
|
}
|
|
9449
|
-
})))
|
|
9958
|
+
})), usageSettingsOpen && (0, react.createElement)(SubscriptionUsageSettingsDialog, {
|
|
9959
|
+
services,
|
|
9960
|
+
snapshot,
|
|
9961
|
+
notify,
|
|
9962
|
+
onClose: () => setUsageSettingsOpen(false)
|
|
9963
|
+
}));
|
|
9450
9964
|
}
|
|
9451
9965
|
//#endregion
|
|
9452
9966
|
//#region src/client/subscription-slot.ts
|
|
9453
9967
|
init_cli_catalog();
|
|
9454
9968
|
init_provider_icons();
|
|
9969
|
+
init_session_adapter_cache();
|
|
9455
9970
|
init_theme();
|
|
9456
9971
|
const SUBSCRIPTION_STYLE_ID = "codingns4dsh-subscription-responsive-style";
|
|
9972
|
+
/**
|
|
9973
|
+
* 进程内用量结果缓存:同一适配器/提供商在刷新间隔内直接复用上次结果,
|
|
9974
|
+
* 避免每次进入会话或切回同一 Agent 都请求上游;null 结果不缓存,便于刚登录后立刻重试。
|
|
9975
|
+
*/
|
|
9976
|
+
const subscriptionUsageCache = /* @__PURE__ */ new Map();
|
|
9457
9977
|
/** 移动端订阅入口只保留图标,完整数据仍可在点击后的弹层中查看。 */
|
|
9458
9978
|
function installSubscriptionStyles() {
|
|
9459
9979
|
if (typeof document === "undefined" || document.querySelector(`style[data-plugin-css="${SUBSCRIPTION_STYLE_ID}"]`) !== null) return;
|
|
@@ -9464,7 +9984,7 @@ window.__ModuleLoader__.load({
|
|
|
9464
9984
|
document.head.appendChild(style);
|
|
9465
9985
|
}
|
|
9466
9986
|
/** 在 DSH 原生步骤统计左侧显示当前 Agent 的订阅余量。 */
|
|
9467
|
-
function registerSubscriptionSlot(slots, rpc) {
|
|
9987
|
+
function registerSubscriptionSlot(slots, rpc, getRefreshIntervalMins) {
|
|
9468
9988
|
installSubscriptionStyles();
|
|
9469
9989
|
return slots.inject("conversation.composer.dock", () => slots.register({
|
|
9470
9990
|
name: "conversation.composer.dock",
|
|
@@ -9473,7 +9993,8 @@ window.__ModuleLoader__.load({
|
|
|
9473
9993
|
label: "Agent 订阅余量",
|
|
9474
9994
|
inject: (sessionId) => ({
|
|
9475
9995
|
rpc,
|
|
9476
|
-
sessionId
|
|
9996
|
+
sessionId,
|
|
9997
|
+
...getRefreshIntervalMins === void 0 ? {} : { getRefreshIntervalMins }
|
|
9477
9998
|
})
|
|
9478
9999
|
}, CommandCodeSubscriptionSlot));
|
|
9479
10000
|
}
|
|
@@ -9487,6 +10008,10 @@ window.__ModuleLoader__.load({
|
|
|
9487
10008
|
const [clock, setClock] = (0, react.useState)(() => Date.now());
|
|
9488
10009
|
const rootRef = (0, react.useRef)(null);
|
|
9489
10010
|
const modelSelectionRevision = props.useSession?.((value) => JSON.stringify(value.modelSelection));
|
|
10011
|
+
const [adapterRevision, bumpAdapterRevision] = (0, react.useState)(0);
|
|
10012
|
+
(0, react.useEffect)(() => subscribeSessionAdapters(() => {
|
|
10013
|
+
bumpAdapterRevision((value) => value + 1);
|
|
10014
|
+
}), []);
|
|
9490
10015
|
(0, react.useEffect)(() => {
|
|
9491
10016
|
const sessionId = props.sessionId?.trim();
|
|
9492
10017
|
if (!sessionId) {
|
|
@@ -9522,10 +10047,21 @@ window.__ModuleLoader__.load({
|
|
|
9522
10047
|
setAdapterId(adapterId);
|
|
9523
10048
|
setProviderId(selection.providerId ?? null);
|
|
9524
10049
|
}
|
|
10050
|
+
const intervalMins = props.getRefreshIntervalMins?.() ?? DEFAULT_SUBSCRIPTION_USAGE_SETTINGS.refreshIntervalMins;
|
|
10051
|
+
const cacheKey = `${adapterId}|${selection.providerId ?? ""}`;
|
|
10052
|
+
const cached = subscriptionUsageCache.get(cacheKey);
|
|
10053
|
+
if (cached !== void 0 && isSubscriptionUsageFresh(cached.capturedAt, Date.now(), intervalMins)) {
|
|
10054
|
+
if (active) setUsage(cached.usage);
|
|
10055
|
+
return;
|
|
10056
|
+
}
|
|
9525
10057
|
const next = await callCliRpc(props.rpc, "subscription", {
|
|
9526
10058
|
adapterId,
|
|
9527
10059
|
...selection.providerId ? { providerId: selection.providerId } : {}
|
|
9528
10060
|
});
|
|
10061
|
+
if (next !== null) subscriptionUsageCache.set(cacheKey, {
|
|
10062
|
+
usage: next,
|
|
10063
|
+
capturedAt: Date.now()
|
|
10064
|
+
});
|
|
9529
10065
|
if (active) setUsage(next);
|
|
9530
10066
|
} catch {
|
|
9531
10067
|
if (active) {
|
|
@@ -9537,17 +10073,20 @@ window.__ModuleLoader__.load({
|
|
|
9537
10073
|
}
|
|
9538
10074
|
};
|
|
9539
10075
|
refresh();
|
|
9540
|
-
const
|
|
10076
|
+
const intervalMins = props.getRefreshIntervalMins?.() ?? DEFAULT_SUBSCRIPTION_USAGE_SETTINGS.refreshIntervalMins;
|
|
10077
|
+
const timer = intervalMins > 0 ? globalThis.setInterval(() => {
|
|
9541
10078
|
refresh();
|
|
9542
|
-
},
|
|
10079
|
+
}, intervalMins * 6e4) : void 0;
|
|
9543
10080
|
return () => {
|
|
9544
10081
|
active = false;
|
|
9545
|
-
globalThis.clearInterval(timer);
|
|
10082
|
+
if (timer !== void 0) globalThis.clearInterval(timer);
|
|
9546
10083
|
};
|
|
9547
10084
|
}, [
|
|
9548
10085
|
props.rpc,
|
|
9549
10086
|
props.sessionId,
|
|
9550
|
-
|
|
10087
|
+
props.getRefreshIntervalMins,
|
|
10088
|
+
modelSelectionRevision,
|
|
10089
|
+
adapterRevision
|
|
9551
10090
|
]);
|
|
9552
10091
|
(0, react.useEffect)(() => {
|
|
9553
10092
|
if (!eligible || usage === null) return;
|
|
@@ -9749,7 +10288,7 @@ window.__ModuleLoader__.load({
|
|
|
9749
10288
|
return usage.balances.find((balance) => balance.currency.toUpperCase() === "USD") ?? usage.balances[0] ?? null;
|
|
9750
10289
|
}
|
|
9751
10290
|
function isSubscriptionAdapter(adapterId) {
|
|
9752
|
-
return adapterId === "command-code" || adapterId === "codex" || adapterId === "claude-code" || adapterId === "dsh" || adapterId === "grok" || adapterId === "opencode";
|
|
10291
|
+
return adapterId === "command-code" || adapterId === "codex" || adapterId === "claude-code" || adapterId === "dsh" || adapterId === "grok" || adapterId === "kimi" || adapterId === "opencode";
|
|
9753
10292
|
}
|
|
9754
10293
|
function isRemoteWebContext() {
|
|
9755
10294
|
return globalThis.__CODINGNS4DSH_REMOTE_WEB_CONTEXT__ === true;
|
|
@@ -9767,6 +10306,7 @@ window.__ModuleLoader__.load({
|
|
|
9767
10306
|
case "claude-code": return "Claude Code";
|
|
9768
10307
|
case "dsh": return "DSH";
|
|
9769
10308
|
case "grok": return "Grok";
|
|
10309
|
+
case "kimi": return "Kimi Code";
|
|
9770
10310
|
case "opencode": return "OpenCode";
|
|
9771
10311
|
default: return "Agent";
|
|
9772
10312
|
}
|
|
@@ -11010,6 +11550,7 @@ window.__ModuleLoader__.load({
|
|
|
11010
11550
|
let adapterRefreshTimer;
|
|
11011
11551
|
let disposeSubscription;
|
|
11012
11552
|
let disposeQuickPhrases;
|
|
11553
|
+
let lastSubscriptionUsageSignature = null;
|
|
11013
11554
|
const disableLogo = () => {
|
|
11014
11555
|
generation += 1;
|
|
11015
11556
|
logoDom?.dispose();
|
|
@@ -11053,7 +11594,7 @@ window.__ModuleLoader__.load({
|
|
|
11053
11594
|
};
|
|
11054
11595
|
const enableSubscription = () => {
|
|
11055
11596
|
if (disposeSubscription !== void 0 || context.services.slots === void 0) return;
|
|
11056
|
-
disposeSubscription = registerSubscriptionSlot(context.services.slots, context.services.rpc);
|
|
11597
|
+
disposeSubscription = registerSubscriptionSlot(context.services.slots, context.services.rpc, () => normalizeSubscriptionUsageSettings(context.services.settings.getSnapshot().value?.subscriptionUsage).refreshIntervalMins);
|
|
11057
11598
|
};
|
|
11058
11599
|
const disableSubscription = () => {
|
|
11059
11600
|
disposeSubscription?.();
|
|
@@ -11104,12 +11645,22 @@ window.__ModuleLoader__.load({
|
|
|
11104
11645
|
else disableWorkspaceVisibility();
|
|
11105
11646
|
if (workspaceSettings?.showAdapterLogo ?? DEFAULT_WORKSPACE_SESSION_ENHANCEMENT_SETTINGS.showAdapterLogo) enableLogo();
|
|
11106
11647
|
else disableLogo();
|
|
11107
|
-
|
|
11648
|
+
const showSubscriptionUsage = workspaceSettings?.showSubscriptionUsage ?? DEFAULT_WORKSPACE_SESSION_ENHANCEMENT_SETTINGS.showSubscriptionUsage;
|
|
11649
|
+
if (showSubscriptionUsage) enableSubscription();
|
|
11108
11650
|
else disableSubscription();
|
|
11109
11651
|
if (workspaceSettings?.showQuickPhrases ?? DEFAULT_WORKSPACE_SESSION_ENHANCEMENT_SETTINGS.showQuickPhrases) enableQuickPhrases();
|
|
11110
11652
|
else disableQuickPhrases();
|
|
11111
11653
|
if (workspaceSettings?.rememberConversationRightbarRatio ?? DEFAULT_WORKSPACE_SESSION_ENHANCEMENT_SETTINGS.rememberConversationRightbarRatio) enableRightbarMemory();
|
|
11112
11654
|
else disableRightbarMemory();
|
|
11655
|
+
const subscriptionUsageSignature = JSON.stringify(context.services.settings.getSnapshot().value?.subscriptionUsage ?? null);
|
|
11656
|
+
if (lastSubscriptionUsageSignature === null) lastSubscriptionUsageSignature = subscriptionUsageSignature;
|
|
11657
|
+
else if (subscriptionUsageSignature !== lastSubscriptionUsageSignature) {
|
|
11658
|
+
lastSubscriptionUsageSignature = subscriptionUsageSignature;
|
|
11659
|
+
if (disposeSubscription !== void 0) {
|
|
11660
|
+
disableSubscription();
|
|
11661
|
+
if (showSubscriptionUsage) enableSubscription();
|
|
11662
|
+
}
|
|
11663
|
+
}
|
|
11113
11664
|
};
|
|
11114
11665
|
sync();
|
|
11115
11666
|
const unsubscribe = context.services.settings.subscribe(sync);
|
|
@@ -12282,14 +12833,38 @@ window.__ModuleLoader__.load({
|
|
|
12282
12833
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
12283
12834
|
const [toast, setToast] = (0, react.useState)(null);
|
|
12284
12835
|
const [diffView, setDiffView] = (0, react.useState)(null);
|
|
12836
|
+
const [fileDiff, setFileDiff] = (0, react.useState)(null);
|
|
12837
|
+
const [singleColumn, setSingleColumn] = (0, react.useState)(false);
|
|
12838
|
+
const [activeColumn, setActiveColumn] = (0, react.useState)("files");
|
|
12285
12839
|
const [historyTotalCount, setHistoryTotalCount] = (0, react.useState)(0);
|
|
12286
12840
|
const [historyLoadingMore, setHistoryLoadingMore] = (0, react.useState)(false);
|
|
12841
|
+
const contentGridRef = (0, react.useRef)(null);
|
|
12287
12842
|
const historyExpanded = (0, react.useRef)(false);
|
|
12843
|
+
const gridVisible = status !== null && status.snapshot.enabled !== false;
|
|
12288
12844
|
(0, react.useEffect)(() => {
|
|
12289
12845
|
if (toast === null) return;
|
|
12290
12846
|
const timer = globalThis.setTimeout(() => setToast(null), 3200);
|
|
12291
12847
|
return () => globalThis.clearTimeout(timer);
|
|
12292
12848
|
}, [toast]);
|
|
12849
|
+
(0, react.useEffect)(() => {
|
|
12850
|
+
const element = contentGridRef.current;
|
|
12851
|
+
if (element === null) {
|
|
12852
|
+
setSingleColumn(false);
|
|
12853
|
+
return;
|
|
12854
|
+
}
|
|
12855
|
+
const update = () => {
|
|
12856
|
+
const tracks = getComputedStyle(element).gridTemplateColumns.split(" ").filter((track) => track !== "");
|
|
12857
|
+
setSingleColumn(tracks.length <= 1);
|
|
12858
|
+
};
|
|
12859
|
+
update();
|
|
12860
|
+
if (typeof ResizeObserver === "undefined") {
|
|
12861
|
+
window.addEventListener("resize", update);
|
|
12862
|
+
return () => window.removeEventListener("resize", update);
|
|
12863
|
+
}
|
|
12864
|
+
const observer = new ResizeObserver(update);
|
|
12865
|
+
observer.observe(element);
|
|
12866
|
+
return () => observer.disconnect();
|
|
12867
|
+
}, [gridVisible]);
|
|
12293
12868
|
const notify = (kind, message) => setToast({
|
|
12294
12869
|
kind,
|
|
12295
12870
|
message
|
|
@@ -12305,6 +12880,7 @@ window.__ModuleLoader__.load({
|
|
|
12305
12880
|
historyExpanded.current = false;
|
|
12306
12881
|
setBranches(null);
|
|
12307
12882
|
setDiffView(null);
|
|
12883
|
+
setFileDiff(null);
|
|
12308
12884
|
const load = async (resolvedWorkspaceId) => {
|
|
12309
12885
|
const cached = readCache(resolvedWorkspaceId);
|
|
12310
12886
|
if (cached !== null && !historyExpanded.current) {
|
|
@@ -12471,6 +13047,23 @@ window.__ModuleLoader__.load({
|
|
|
12471
13047
|
setToast(null);
|
|
12472
13048
|
}).catch((error) => notify("error", error instanceof Error ? error.message : String(error))).finally(() => setBusy(false));
|
|
12473
13049
|
};
|
|
13050
|
+
const openFileDiff = (path, staged) => {
|
|
13051
|
+
if (workspaceId === void 0) return;
|
|
13052
|
+
setBusy(true);
|
|
13053
|
+
notify("info", "正在读取文件 Diff…");
|
|
13054
|
+
call$1(props.rpc, "git/diff", {
|
|
13055
|
+
workspaceId,
|
|
13056
|
+
path,
|
|
13057
|
+
staged
|
|
13058
|
+
}).then((value) => {
|
|
13059
|
+
setFileDiff({
|
|
13060
|
+
path,
|
|
13061
|
+
staged,
|
|
13062
|
+
diff: value
|
|
13063
|
+
});
|
|
13064
|
+
setToast(null);
|
|
13065
|
+
}).catch((error) => notify("error", error instanceof Error ? error.message : String(error))).finally(() => setBusy(false));
|
|
13066
|
+
};
|
|
12474
13067
|
const loadMoreHistory = () => {
|
|
12475
13068
|
if (workspaceId === void 0 || historyLoadingMore || history.length >= historyTotalCount) return;
|
|
12476
13069
|
setHistoryLoadingMore(true);
|
|
@@ -12506,10 +13099,25 @@ window.__ModuleLoader__.load({
|
|
|
12506
13099
|
return (0, react.createElement)("section", {
|
|
12507
13100
|
style: panelStyle,
|
|
12508
13101
|
"data-git-management-panel": "true"
|
|
12509
|
-
}, (0, react.createElement)("header", { style: headerStyle }, (0, react.createElement)("div", { style: { minWidth: 0 } }, (0, react.createElement)("h1", { style: titleStyle }, "Git"), (0, react.createElement)("div", {
|
|
12510
|
-
style:
|
|
12511
|
-
|
|
12512
|
-
|
|
13102
|
+
}, (0, react.createElement)("header", { style: headerStyle }, (0, react.createElement)("div", { style: { minWidth: 0 } }, (0, react.createElement)("h1", { style: titleStyle }, "Git")), (0, react.createElement)("div", { style: headerActionsStyle }, singleColumn ? (0, react.createElement)("div", {
|
|
13103
|
+
style: columnSwitchStyle,
|
|
13104
|
+
role: "group",
|
|
13105
|
+
"aria-label": "切换文件与版本视图"
|
|
13106
|
+
}, (0, react.createElement)("button", {
|
|
13107
|
+
type: "button",
|
|
13108
|
+
onClick: () => setActiveColumn("files"),
|
|
13109
|
+
style: activeColumn === "files" ? columnSwitchActiveButtonStyle : columnSwitchButtonStyle,
|
|
13110
|
+
title: "切换文件",
|
|
13111
|
+
"aria-label": "切换文件",
|
|
13112
|
+
"aria-pressed": activeColumn === "files"
|
|
13113
|
+
}, "切换文件"), (0, react.createElement)("button", {
|
|
13114
|
+
type: "button",
|
|
13115
|
+
onClick: () => setActiveColumn("history"),
|
|
13116
|
+
style: activeColumn === "history" ? columnSwitchActiveButtonStyle : columnSwitchButtonStyle,
|
|
13117
|
+
title: "切换版本",
|
|
13118
|
+
"aria-label": "切换版本",
|
|
13119
|
+
"aria-pressed": activeColumn === "history"
|
|
13120
|
+
}, "切换版本")) : null, (0, react.createElement)(GitOperationsMenu, {
|
|
12513
13121
|
busy,
|
|
12514
13122
|
hasRemote: Boolean(status?.snapshot.hasRemote || branches?.remote.length),
|
|
12515
13123
|
canUndo: history.length > 0,
|
|
@@ -12542,7 +13150,15 @@ window.__ModuleLoader__.load({
|
|
|
12542
13150
|
}, "初始化 Git")) : null, status !== null ? (0, react.createElement)("div", { style: summaryStyle }, `${staged.length} 个已暂存 · ${unstaged.length} 个未暂存 · ${historyTotalCount} 条提交`) : null, diffView !== null ? (0, react.createElement)(DiffViewer, {
|
|
12543
13151
|
diff: diffView,
|
|
12544
13152
|
onClose: () => setDiffView(null)
|
|
12545
|
-
}) : null,
|
|
13153
|
+
}) : null, fileDiff !== null ? (0, react.createElement)(FileDiffViewer, {
|
|
13154
|
+
path: fileDiff.path,
|
|
13155
|
+
staged: fileDiff.staged,
|
|
13156
|
+
diff: fileDiff.diff,
|
|
13157
|
+
onClose: () => setFileDiff(null)
|
|
13158
|
+
}) : null, status !== null && status.snapshot.enabled !== false ? (0, react.createElement)("div", {
|
|
13159
|
+
ref: contentGridRef,
|
|
13160
|
+
style: contentGridStyle
|
|
13161
|
+
}, singleColumn && activeColumn !== "files" ? null : (0, react.createElement)("div", { style: columnStyle }, (0, react.createElement)("section", { style: commitSectionStyle }, (0, react.createElement)("div", { style: commitEditorRowStyle }, (0, react.createElement)("textarea", {
|
|
12546
13162
|
value: subject,
|
|
12547
13163
|
disabled: busy || workspaceId === void 0,
|
|
12548
13164
|
onChange: (event) => setSubject(event.currentTarget.value),
|
|
@@ -12573,6 +13189,7 @@ window.__ModuleLoader__.load({
|
|
|
12573
13189
|
title: "暂存文件",
|
|
12574
13190
|
items: staged,
|
|
12575
13191
|
busy,
|
|
13192
|
+
onOpenDiff: openFileDiff,
|
|
12576
13193
|
onAction: (action, path) => void run(`git/${action}`, { targets: [path] }),
|
|
12577
13194
|
onBatchAction: (action, paths) => void run(`git/${action}`, { targets: paths }),
|
|
12578
13195
|
onBulkAction: () => void run("git/unstage", { targets: staged.map((item) => item.path) })
|
|
@@ -12580,10 +13197,11 @@ window.__ModuleLoader__.load({
|
|
|
12580
13197
|
title: "未提交文件",
|
|
12581
13198
|
items: unstaged,
|
|
12582
13199
|
busy,
|
|
13200
|
+
onOpenDiff: openFileDiff,
|
|
12583
13201
|
onAction: (action, path) => void run(`git/${action}`, { targets: [path] }),
|
|
12584
13202
|
onBatchAction: (action, paths) => void run(`git/${action}`, { targets: paths }),
|
|
12585
13203
|
onBulkAction: () => void run("git/stage", { targets: unstaged.map((item) => item.path) })
|
|
12586
|
-
}) : null), (0, react.createElement)("div", { style: columnStyle }, (0, react.createElement)(HistorySection, {
|
|
13204
|
+
}) : null), singleColumn && activeColumn !== "history" ? null : (0, react.createElement)("div", { style: columnStyle }, (0, react.createElement)(HistorySection, {
|
|
12587
13205
|
history,
|
|
12588
13206
|
totalCount: historyTotalCount,
|
|
12589
13207
|
hasMore: history.length < historyTotalCount,
|
|
@@ -12601,7 +13219,7 @@ window.__ModuleLoader__.load({
|
|
|
12601
13219
|
onUndo: () => runGitOperation("undo")
|
|
12602
13220
|
}))) : null);
|
|
12603
13221
|
}
|
|
12604
|
-
function ChangeSection({ title, items, busy, onAction, onBatchAction, onBulkAction }) {
|
|
13222
|
+
function ChangeSection({ title, items, busy, onOpenDiff, onAction, onBatchAction, onBulkAction }) {
|
|
12605
13223
|
const [hoveredPath, setHoveredPath] = (0, react.useState)(null);
|
|
12606
13224
|
const nodes = buildChangeTree(items);
|
|
12607
13225
|
const staged = title === "暂存文件";
|
|
@@ -12656,7 +13274,8 @@ window.__ModuleLoader__.load({
|
|
|
12656
13274
|
paddingLeft: 28 + depth * 14
|
|
12657
13275
|
},
|
|
12658
13276
|
onMouseEnter: () => setHoveredPath(node.path),
|
|
12659
|
-
onMouseLeave: () => setHoveredPath(null)
|
|
13277
|
+
onMouseLeave: () => setHoveredPath(null),
|
|
13278
|
+
onDoubleClick: () => onOpenDiff(node.path, staged)
|
|
12660
13279
|
}, (0, react.createElement)("span", { style: fileIconStyle$1 }, fileIcon$1(node.name)), (0, react.createElement)("span", {
|
|
12661
13280
|
title: node.path,
|
|
12662
13281
|
style: fileNameStyle$1
|
|
@@ -12736,7 +13355,6 @@ window.__ModuleLoader__.load({
|
|
|
12736
13355
|
function DiffViewer({ diff, onClose }) {
|
|
12737
13356
|
const providedFiles = diff.files ?? [];
|
|
12738
13357
|
const files = providedFiles.length > 0 ? providedFiles : parseDiffFiles(diff.content);
|
|
12739
|
-
const lines = parseDiffLines(diff.content);
|
|
12740
13358
|
return (0, react.createElement)("div", { style: diffOverlayStyle }, (0, react.createElement)("section", {
|
|
12741
13359
|
role: "dialog",
|
|
12742
13360
|
"aria-modal": true,
|
|
@@ -12754,10 +13372,32 @@ window.__ModuleLoader__.load({
|
|
|
12754
13372
|
}, (0, react.createElement)("span", {
|
|
12755
13373
|
style: diffFileStatusStyle,
|
|
12756
13374
|
"data-status": file.status
|
|
12757
|
-
}, diffFileStatusLabel(file.status)), (0, react.createElement)("div", { style: diffFileNameStyle }, (0, react.createElement)("strong", void 0, file.path), file.oldPath ? (0, react.createElement)("span", { style: diffFileOldPathStyle }, `原路径:${file.oldPath}`) : null), file.binary ? (0, react.createElement)("span", { style: diffBinaryStyle }, "二进制") : null)))), (0, react.createElement)("section", { style: diffDiffSectionStyle }, (0, react.createElement)("div", { style: diffSectionHeaderStyle }, (0, react.createElement)("strong", void 0, "Diff"), diff.truncated ? (0, react.createElement)("span", { style: diffTruncatedStyle }, "内容已截断") : null),
|
|
13375
|
+
}, diffFileStatusLabel(file.status)), (0, react.createElement)("div", { style: diffFileNameStyle }, (0, react.createElement)("strong", void 0, file.path), file.oldPath ? (0, react.createElement)("span", { style: diffFileOldPathStyle }, `原路径:${file.oldPath}`) : null), file.binary ? (0, react.createElement)("span", { style: diffBinaryStyle }, "二进制") : null)))), (0, react.createElement)("section", { style: diffDiffSectionStyle }, (0, react.createElement)("div", { style: diffSectionHeaderStyle }, (0, react.createElement)("strong", void 0, "Diff"), diff.truncated ? (0, react.createElement)("span", { style: diffTruncatedStyle }, "内容已截断") : null), (0, react.createElement)(DiffLines, { content: diff.content })))));
|
|
13376
|
+
}
|
|
13377
|
+
function DiffLines({ content }) {
|
|
13378
|
+
const lines = parseDiffLines(content);
|
|
13379
|
+
if (lines.length === 0) return (0, react.createElement)("div", { style: diffEmptyStyle }, "当前没有可显示的文本差异。");
|
|
13380
|
+
return (0, react.createElement)("div", { style: diffLinesStyle }, ...lines.map((line, index) => (0, react.createElement)("div", {
|
|
12758
13381
|
key: `${index}:${line.kind}:${line.text}`,
|
|
12759
13382
|
style: diffLineStyle$1(line.kind)
|
|
12760
|
-
}, (0, react.createElement)("span", { style: diffLineNumberStyle }, line.oldLineNo === null ? "" : String(line.oldLineNo)), (0, react.createElement)("span", { style: diffLineNumberStyle }, line.newLineNo === null ? "" : String(line.newLineNo)), (0, react.createElement)("code", { style: diffCodeStyle }, `${diffLinePrefix(line.kind)}${line.text}`))))
|
|
13383
|
+
}, (0, react.createElement)("span", { style: diffLineNumberStyle }, line.oldLineNo === null ? "" : String(line.oldLineNo)), (0, react.createElement)("span", { style: diffLineNumberStyle }, line.newLineNo === null ? "" : String(line.newLineNo)), (0, react.createElement)("code", { style: diffCodeStyle }, `${diffLinePrefix(line.kind)}${line.text}`))));
|
|
13384
|
+
}
|
|
13385
|
+
function FileDiffViewer({ path, staged, diff, onClose }) {
|
|
13386
|
+
return (0, react.createElement)("div", { style: diffOverlayStyle }, (0, react.createElement)("section", {
|
|
13387
|
+
role: "dialog",
|
|
13388
|
+
"aria-modal": true,
|
|
13389
|
+
"aria-label": "文件 Diff",
|
|
13390
|
+
style: diffStyle$1
|
|
13391
|
+
}, (0, react.createElement)("div", { style: sectionHeaderStyle }, (0, react.createElement)("strong", {
|
|
13392
|
+
style: fileDiffTitleStyle,
|
|
13393
|
+
title: path
|
|
13394
|
+
}, `文件 Diff · ${path}`), (0, react.createElement)("div", { style: rowActionsStyle }, (0, react.createElement)("span", { style: diffCountStyle }, staged ? "已暂存" : "未暂存"), (0, react.createElement)("button", {
|
|
13395
|
+
type: "button",
|
|
13396
|
+
onClick: onClose,
|
|
13397
|
+
style: iconButtonStyle$1,
|
|
13398
|
+
title: "关闭 Diff",
|
|
13399
|
+
"aria-label": "关闭 Diff"
|
|
13400
|
+
}, "×"))), (0, react.createElement)("div", { style: diffBodyStyle }, (0, react.createElement)("section", { style: diffDiffSectionStyle }, (0, react.createElement)("div", { style: diffSectionHeaderStyle }, (0, react.createElement)("strong", void 0, staged ? "暂存区 Diff(相对 HEAD)" : "工作区 Diff"), diff.binary ? (0, react.createElement)("span", { style: diffBinaryStyle }, "二进制文件") : null, diff.truncated ? (0, react.createElement)("span", { style: diffTruncatedStyle }, "内容已截断") : null), (0, react.createElement)(DiffLines, { content: diff.content })))));
|
|
12761
13401
|
}
|
|
12762
13402
|
function parseDiffLines(content) {
|
|
12763
13403
|
const lines = [];
|
|
@@ -12877,41 +13517,48 @@ window.__ModuleLoader__.load({
|
|
|
12877
13517
|
}, (0, react.createElement)("time", {
|
|
12878
13518
|
dateTime: group.key,
|
|
12879
13519
|
style: historyDateHeaderTextStyle
|
|
12880
|
-
}, group.label)), ...group.items.map(({ item, index, timeLabel }) =>
|
|
12881
|
-
|
|
12882
|
-
|
|
12883
|
-
|
|
12884
|
-
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
12911
|
-
|
|
12912
|
-
|
|
12913
|
-
|
|
12914
|
-
|
|
13520
|
+
}, group.label)), ...group.items.map(({ item, index, timeLabel }) => {
|
|
13521
|
+
const refs = item.refs ?? [];
|
|
13522
|
+
return (0, react.createElement)("div", {
|
|
13523
|
+
key: item.commitHash,
|
|
13524
|
+
style: historyRowStyle
|
|
13525
|
+
}, (0, react.createElement)("div", { style: historyRowMainStyle }, (0, react.createElement)("code", { style: hashStyle }, item.commitHash.slice(0, 8)), (0, react.createElement)("span", {
|
|
13526
|
+
style: fileNameStyle$1,
|
|
13527
|
+
title: item.subject
|
|
13528
|
+
}, item.subject), (0, react.createElement)("time", {
|
|
13529
|
+
style: historyTimeStyle,
|
|
13530
|
+
dateTime: item.authoredAt
|
|
13531
|
+
}, timeLabel), (0, react.createElement)("details", { style: menuStyle }, (0, react.createElement)("summary", {
|
|
13532
|
+
style: menuSummaryStyle,
|
|
13533
|
+
title: "版本操作菜单",
|
|
13534
|
+
"aria-label": "版本操作菜单"
|
|
13535
|
+
}, "⋯"), (0, react.createElement)("div", { style: menuPopupStyle }, (0, react.createElement)("button", {
|
|
13536
|
+
type: "button",
|
|
13537
|
+
onClick: () => onViewDiff(item.commitHash),
|
|
13538
|
+
style: menuButtonStyle$1
|
|
13539
|
+
}, "查看更改文件与 Diff"), (0, react.createElement)("button", {
|
|
13540
|
+
type: "button",
|
|
13541
|
+
onClick: () => onCopy(item.commitHash),
|
|
13542
|
+
style: menuButtonStyle$1
|
|
13543
|
+
}, "复制 Commit Hash"), (0, react.createElement)("button", {
|
|
13544
|
+
type: "button",
|
|
13545
|
+
onClick: () => onCopyMessage(buildCommitMessageText(item.subject, item.body)),
|
|
13546
|
+
style: menuButtonStyle$1
|
|
13547
|
+
}, "复制提交信息"), (0, react.createElement)("button", {
|
|
13548
|
+
type: "button",
|
|
13549
|
+
onClick: () => onCopy(item.commitHash),
|
|
13550
|
+
style: menuButtonStyle$1
|
|
13551
|
+
}, "复制 Git 版本号"), index === 0 ? (0, react.createElement)("button", {
|
|
13552
|
+
type: "button",
|
|
13553
|
+
disabled: busy,
|
|
13554
|
+
onClick: onUndo,
|
|
13555
|
+
style: dangerMenuButtonStyle
|
|
13556
|
+
}, "撤销上次提交") : null))), refs.length === 0 ? null : (0, react.createElement)("div", { style: historyRefListStyle }, ...refs.map((ref) => (0, react.createElement)("span", {
|
|
13557
|
+
key: `${ref.kind}:${ref.name}`,
|
|
13558
|
+
title: ref.name,
|
|
13559
|
+
style: historyRefPillStyle(ref.kind, ref.remoteName)
|
|
13560
|
+
}, ref.name))));
|
|
13561
|
+
})]);
|
|
12915
13562
|
return (0, react.createElement)("section", { style: sectionStyle }, (0, react.createElement)("div", { style: sectionHeaderStyle }, (0, react.createElement)("strong", void 0, `Git 版本 (${totalCount})`), branches === null ? null : (0, react.createElement)("select", {
|
|
12916
13563
|
value: branches.currentBranch,
|
|
12917
13564
|
disabled: busy,
|
|
@@ -13327,8 +13974,48 @@ window.__ModuleLoader__.load({
|
|
|
13327
13974
|
const normalizedBody = body.trim();
|
|
13328
13975
|
return normalizedBody ? `${normalizedSubject}\n\n${normalizedBody}` : normalizedSubject;
|
|
13329
13976
|
}
|
|
13977
|
+
const REMOTE_REF_PALETTE = [
|
|
13978
|
+
"#8b5cf6",
|
|
13979
|
+
"#ef4444",
|
|
13980
|
+
"#10b981",
|
|
13981
|
+
"#f59e0b",
|
|
13982
|
+
"#06b6d4",
|
|
13983
|
+
"#ec4899"
|
|
13984
|
+
];
|
|
13985
|
+
function resolveRemotePaletteIndex(remoteName) {
|
|
13986
|
+
if (remoteName === null) return 0;
|
|
13987
|
+
let hash = 0;
|
|
13988
|
+
for (const character of remoteName) hash = hash * 33 + character.charCodeAt(0) >>> 0;
|
|
13989
|
+
return hash % REMOTE_REF_PALETTE.length;
|
|
13990
|
+
}
|
|
13991
|
+
function historyRefPillStyle(kind, remoteName) {
|
|
13992
|
+
if (kind === "remote") {
|
|
13993
|
+
const color = REMOTE_REF_PALETTE[resolveRemotePaletteIndex(remoteName)];
|
|
13994
|
+
return {
|
|
13995
|
+
...historyRefPillBaseStyle,
|
|
13996
|
+
border: `1px solid color-mix(in srgb, ${color} 34%, transparent)`,
|
|
13997
|
+
background: `color-mix(in srgb, ${color} 14%, transparent)`,
|
|
13998
|
+
color
|
|
13999
|
+
};
|
|
14000
|
+
}
|
|
14001
|
+
const color = kind === "head" ? "#4f9cff" : "#67b0ff";
|
|
14002
|
+
const mix = kind === "head" ? {
|
|
14003
|
+
border: 36,
|
|
14004
|
+
background: 18
|
|
14005
|
+
} : {
|
|
14006
|
+
border: 28,
|
|
14007
|
+
background: 12
|
|
14008
|
+
};
|
|
14009
|
+
return {
|
|
14010
|
+
...historyRefPillBaseStyle,
|
|
14011
|
+
border: `1px solid color-mix(in srgb, ${color} ${mix.border}%, transparent)`,
|
|
14012
|
+
background: `color-mix(in srgb, ${color} ${mix.background}%, transparent)`,
|
|
14013
|
+
color: "#2b74d8"
|
|
14014
|
+
};
|
|
14015
|
+
}
|
|
13330
14016
|
const panelStyle = {
|
|
13331
14017
|
position: "relative",
|
|
14018
|
+
userSelect: "none",
|
|
13332
14019
|
boxSizing: "border-box",
|
|
13333
14020
|
display: "flex",
|
|
13334
14021
|
flexDirection: "column",
|
|
@@ -13364,14 +14051,31 @@ window.__ModuleLoader__.load({
|
|
|
13364
14051
|
alignItems: "center",
|
|
13365
14052
|
gap: 4
|
|
13366
14053
|
};
|
|
13367
|
-
const
|
|
13368
|
-
|
|
13369
|
-
|
|
14054
|
+
const columnSwitchStyle = {
|
|
14055
|
+
display: "inline-flex",
|
|
14056
|
+
alignItems: "center",
|
|
14057
|
+
gap: 2,
|
|
14058
|
+
padding: 2,
|
|
14059
|
+
border: `1px solid ${dshThemeColor.border}`,
|
|
14060
|
+
borderRadius: 5,
|
|
14061
|
+
background: dshThemeColor.pageBackground
|
|
14062
|
+
};
|
|
14063
|
+
const columnSwitchButtonStyle = {
|
|
14064
|
+
border: 0,
|
|
14065
|
+
borderRadius: 3,
|
|
14066
|
+
padding: "3px 6px",
|
|
13370
14067
|
color: dshThemeColor.labelSecondary,
|
|
13371
|
-
|
|
13372
|
-
|
|
14068
|
+
background: "transparent",
|
|
14069
|
+
cursor: "pointer",
|
|
14070
|
+
fontSize: 11,
|
|
13373
14071
|
whiteSpace: "nowrap"
|
|
13374
14072
|
};
|
|
14073
|
+
const columnSwitchActiveButtonStyle = {
|
|
14074
|
+
...columnSwitchButtonStyle,
|
|
14075
|
+
color: dshThemeColor.accent,
|
|
14076
|
+
background: "color-mix(in srgb, currentColor 12%, transparent)",
|
|
14077
|
+
fontWeight: 600
|
|
14078
|
+
};
|
|
13375
14079
|
const summaryStyle = {
|
|
13376
14080
|
color: dshThemeColor.labelSecondary,
|
|
13377
14081
|
fontSize: 12
|
|
@@ -13445,11 +14149,34 @@ window.__ModuleLoader__.load({
|
|
|
13445
14149
|
fontSize: 11
|
|
13446
14150
|
};
|
|
13447
14151
|
const historyRowStyle = {
|
|
14152
|
+
display: "flex",
|
|
14153
|
+
flexDirection: "column",
|
|
14154
|
+
gap: 3,
|
|
14155
|
+
fontSize: 12
|
|
14156
|
+
};
|
|
14157
|
+
const historyRowMainStyle = {
|
|
13448
14158
|
display: "flex",
|
|
13449
14159
|
alignItems: "center",
|
|
13450
14160
|
gap: 8,
|
|
13451
|
-
|
|
13452
|
-
|
|
14161
|
+
minWidth: 0,
|
|
14162
|
+
minHeight: 28
|
|
14163
|
+
};
|
|
14164
|
+
const historyRefListStyle = {
|
|
14165
|
+
display: "flex",
|
|
14166
|
+
flexWrap: "wrap",
|
|
14167
|
+
gap: "4px 6px",
|
|
14168
|
+
paddingLeft: 2
|
|
14169
|
+
};
|
|
14170
|
+
const historyRefPillBaseStyle = {
|
|
14171
|
+
display: "inline-flex",
|
|
14172
|
+
alignItems: "center",
|
|
14173
|
+
justifyContent: "center",
|
|
14174
|
+
minHeight: 18,
|
|
14175
|
+
padding: "0 8px",
|
|
14176
|
+
borderRadius: 999,
|
|
14177
|
+
fontSize: 10,
|
|
14178
|
+
lineHeight: 1,
|
|
14179
|
+
whiteSpace: "nowrap"
|
|
13453
14180
|
};
|
|
13454
14181
|
const historyDateHeaderStyle = {
|
|
13455
14182
|
paddingTop: 6,
|
|
@@ -13598,6 +14325,10 @@ window.__ModuleLoader__.load({
|
|
|
13598
14325
|
color: dshThemeColor.labelTertiary,
|
|
13599
14326
|
fontSize: 11
|
|
13600
14327
|
};
|
|
14328
|
+
const fileDiffTitleStyle = {
|
|
14329
|
+
...fileNameStyle$1,
|
|
14330
|
+
fontSize: 12
|
|
14331
|
+
};
|
|
13601
14332
|
const diffTruncatedStyle = {
|
|
13602
14333
|
color: dshThemeColor.labelTertiary,
|
|
13603
14334
|
fontSize: 11
|
|
@@ -49127,6 +49858,25 @@ window.__ModuleLoader__.load({
|
|
|
49127
49858
|
writable: false,
|
|
49128
49859
|
status: "unavailable"
|
|
49129
49860
|
});
|
|
49861
|
+
/**
|
|
49862
|
+
* 解析 Host 真正下发的 ConfigForm namespace。
|
|
49863
|
+
*
|
|
49864
|
+
* `ConfigForms.get` 对任何 entry 都会造出一份表单:Host 持久模式下它是停在
|
|
49865
|
+
* `loading` 的空表单,memory 模式下才是 `unavailable`。绑定一份 Host 没有下发的
|
|
49866
|
+
* 表单会让设置页永远停在未就绪状态,并把所有模块开关显示为不可操作,因此这里
|
|
49867
|
+
* 只认可共享 mirror 中列出的 entry;判定不出来时返回 undefined,由调用方退回
|
|
49868
|
+
* 插件自己的 Host RPC 设置边界。
|
|
49869
|
+
*/
|
|
49870
|
+
function resolveServedConfigFormNamespace(forms, entryIds) {
|
|
49871
|
+
if (forms === void 0) return void 0;
|
|
49872
|
+
try {
|
|
49873
|
+
const namespaces = forms.describe?.().getSnapshot().view?.namespaces;
|
|
49874
|
+
debugInfo("codingns4dsh: client config form namespaces", { namespaces: namespaces?.map((item) => item.ns) });
|
|
49875
|
+
return entryIds.find((id) => namespaces?.some((item) => item.ns === id));
|
|
49876
|
+
} catch {
|
|
49877
|
+
return;
|
|
49878
|
+
}
|
|
49879
|
+
}
|
|
49130
49880
|
/** 0.1.7 Client ConfigForm 路由适配器。 */
|
|
49131
49881
|
function createConfigFormSettingsStore(forms, namespace, options = {}) {
|
|
49132
49882
|
const form = forms.get(namespace);
|
|
@@ -49236,7 +49986,7 @@ window.__ModuleLoader__.load({
|
|
|
49236
49986
|
return value === void 0 ? void 0 : (({ cliSessions: _cliSessions, ...clientValue }) => clientValue)(value);
|
|
49237
49987
|
}
|
|
49238
49988
|
//#endregion
|
|
49239
|
-
//#region node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.
|
|
49989
|
+
//#region node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.5/node_modules/@deepseek-ai/cosmokit/lib/index.js
|
|
49240
49990
|
/** Return true when a value is `null` or `undefined`. */
|
|
49241
49991
|
function isNullable(value) {
|
|
49242
49992
|
return value === null || value === void 0;
|
|
@@ -49405,7 +50155,7 @@ window.__ModuleLoader__.load({
|
|
|
49405
50155
|
Time.template = template;
|
|
49406
50156
|
})(Time || (Time = {}));
|
|
49407
50157
|
//#endregion
|
|
49408
|
-
//#region node_modules/.pnpm/@deepseek-ai+cordis@4.0.
|
|
50158
|
+
//#region node_modules/.pnpm/@deepseek-ai+cordis@4.0.4_@deepseek-ai+cordis-plugin-include@1.0.9_@deepseek-ai+cordis-plugin-loader@1.0.5/node_modules/@deepseek-ai/cordis/lib/index.js
|
|
49409
50159
|
/** Ordered collection of disposable values with O(1) deletion by value. */
|
|
49410
50160
|
var DisposableList = class {
|
|
49411
50161
|
sn = 0;
|
|
@@ -50014,8 +50764,9 @@ window.__ModuleLoader__.load({
|
|
|
50014
50764
|
*/
|
|
50015
50765
|
exporter(exporter) {
|
|
50016
50766
|
return this.ctx.effect(() => {
|
|
50017
|
-
|
|
50018
|
-
|
|
50767
|
+
const id = ++this._snExporter;
|
|
50768
|
+
this.exporters.set(id, exporter);
|
|
50769
|
+
return () => this.exporters.delete(id);
|
|
50019
50770
|
}, "ctx.logger.exporter()");
|
|
50020
50771
|
}
|
|
50021
50772
|
_resolveConfig() {
|
|
@@ -50818,8 +51569,8 @@ window.__ModuleLoader__.load({
|
|
|
50818
51569
|
*
|
|
50819
51570
|
* @param config — the new raw config; validated before anything restarts.
|
|
50820
51571
|
* @param noSave — hint for persistence hooks not to write the change back.
|
|
50821
|
-
* @returns the
|
|
50822
|
-
* @throws when
|
|
51572
|
+
* @returns nothing; the restart runs behind the `internal/update` waterfall.
|
|
51573
|
+
* @throws {ValidationError} when the new config fails validation.
|
|
50823
51574
|
*/
|
|
50824
51575
|
update(config, noSave = false) {
|
|
50825
51576
|
this.assertActive();
|
|
@@ -50831,7 +51582,7 @@ window.__ModuleLoader__.load({
|
|
|
50831
51582
|
return;
|
|
50832
51583
|
}
|
|
50833
51584
|
config = this._resolveConfig(config);
|
|
50834
|
-
|
|
51585
|
+
this.context.waterfall(this, "internal/update", config, noSave, () => {
|
|
50835
51586
|
this.config = config;
|
|
50836
51587
|
this._error = void 0;
|
|
50837
51588
|
return this.restart();
|
|
@@ -51819,17 +52570,18 @@ window.__ModuleLoader__.load({
|
|
|
51819
52570
|
}
|
|
51820
52571
|
//#endregion
|
|
51821
52572
|
//#region src/dsh-capabilities/client/primitives-adapter.ts
|
|
52573
|
+
const unavailableIcon = () => null;
|
|
51822
52574
|
/**
|
|
51823
52575
|
* 图标导出兼容层。0.1.7 可能改名为 Regular/Medium,运行时优先使用新导出,
|
|
51824
52576
|
* 旧导出仍作为回退;业务组件不再直接判断 DSH 版本。
|
|
51825
52577
|
*/
|
|
51826
52578
|
function resolvePlusIcon() {
|
|
51827
52579
|
const value = _deepseek_ai_dsh_client_ui_primitives;
|
|
51828
|
-
return value.IconPlusOutlineRegular ?? value.IconPlusOutlineMedium ??
|
|
52580
|
+
return value.IconPlusOutlineRegular ?? value.IconPlusOutlineMedium ?? value.IconPlusOutline16 ?? unavailableIcon;
|
|
51829
52581
|
}
|
|
51830
52582
|
function resolveChevronDownIcon() {
|
|
51831
52583
|
const value = _deepseek_ai_dsh_client_ui_primitives;
|
|
51832
|
-
return value.IconChevronDownOutlineRegular ?? value.IconChevronDownOutlineMedium ??
|
|
52584
|
+
return value.IconChevronDownOutlineRegular ?? value.IconChevronDownOutlineMedium ?? value.IconChevronDownOutline14 ?? unavailableIcon;
|
|
51833
52585
|
}
|
|
51834
52586
|
//#endregion
|
|
51835
52587
|
//#region src/client/terminal/recovery.ts
|
|
@@ -66387,11 +67139,11 @@ window.__ModuleLoader__.load({
|
|
|
66387
67139
|
* 探测只检查结构,不读取版本字符串;版本范围由 Registry 统一处理,避免
|
|
66388
67140
|
* 业务模块在运行时继续堆叠 if (version >= ...)。
|
|
66389
67141
|
*/
|
|
66390
|
-
function createDshCapabilityRegistry(dshVersion, runtime, context) {
|
|
67142
|
+
function createDshCapabilityRegistry(dshVersion, runtime, context, facts = {}) {
|
|
66391
67143
|
const registry = new DshCapabilityRegistry(dshVersion, runtime);
|
|
66392
|
-
const value = context;
|
|
66393
67144
|
const rangeLegacy = ">=0.1.5-rc.3 <=0.1.6";
|
|
66394
67145
|
const rangeModern = ">=0.1.7-rc.2 <=0.1.7-rc.2";
|
|
67146
|
+
const range020 = ">=0.2.0-rc.1 <=0.2.0-rc.1";
|
|
66395
67147
|
const add = (route) => registry.register(route);
|
|
66396
67148
|
if (runtime === "host") {
|
|
66397
67149
|
add({
|
|
@@ -66460,6 +67212,7 @@ window.__ModuleLoader__.load({
|
|
|
66460
67212
|
detect: (ctx) => ctx.remote !== void 0,
|
|
66461
67213
|
create: (ctx) => ctx.remote
|
|
66462
67214
|
});
|
|
67215
|
+
addDsh020HostRoutes(add, range020);
|
|
66463
67216
|
addPeerHostHostRoutes(add);
|
|
66464
67217
|
} else {
|
|
66465
67218
|
add({
|
|
@@ -66492,8 +67245,8 @@ window.__ModuleLoader__.load({
|
|
|
66492
67245
|
priority: 10,
|
|
66493
67246
|
status: "supported",
|
|
66494
67247
|
introducedIn: "0.1.5-rc.3",
|
|
66495
|
-
detect: () =>
|
|
66496
|
-
create: () =>
|
|
67248
|
+
detect: () => facts.primitives !== void 0,
|
|
67249
|
+
create: () => facts.primitives
|
|
66497
67250
|
});
|
|
66498
67251
|
add({
|
|
66499
67252
|
id: "locale-runtime",
|
|
@@ -66550,10 +67303,304 @@ window.__ModuleLoader__.load({
|
|
|
66550
67303
|
detect: (ctx) => ctx.remote !== void 0,
|
|
66551
67304
|
create: (ctx) => ctx.remote
|
|
66552
67305
|
});
|
|
67306
|
+
addDsh020ClientRoutes(add, range020, facts);
|
|
66553
67307
|
addPeerHostClientRoutes(add);
|
|
66554
67308
|
}
|
|
66555
67309
|
return registry;
|
|
66556
67310
|
}
|
|
67311
|
+
/** DSH 0.2 Host 结构化能力。检测基于服务形状,不把版本判断散落到业务模块。 */
|
|
67312
|
+
function addDsh020HostRoutes(add, supportedDsh) {
|
|
67313
|
+
add({
|
|
67314
|
+
id: "settings-forms-020",
|
|
67315
|
+
capability: "settings.store",
|
|
67316
|
+
supportedDsh,
|
|
67317
|
+
runtime: "host",
|
|
67318
|
+
priority: 30,
|
|
67319
|
+
status: "supported",
|
|
67320
|
+
introducedIn: "0.2.0-rc.1",
|
|
67321
|
+
detect: (ctx) => hasMethods(read(ctx, "settings"), [
|
|
67322
|
+
"describe",
|
|
67323
|
+
"mutate",
|
|
67324
|
+
"configure"
|
|
67325
|
+
]),
|
|
67326
|
+
create: (ctx) => read(ctx, "settings")
|
|
67327
|
+
});
|
|
67328
|
+
add({
|
|
67329
|
+
id: "connection-rpc-020",
|
|
67330
|
+
capability: "connection.rpc",
|
|
67331
|
+
supportedDsh,
|
|
67332
|
+
runtime: "host",
|
|
67333
|
+
priority: 30,
|
|
67334
|
+
status: "supported",
|
|
67335
|
+
introducedIn: "0.2.0-rc.1",
|
|
67336
|
+
detect: (ctx) => hasMethods(read(ctx, "connection.rpc"), ["handle", "intercept"]) && hasMethods(read(ctx, "connection.fetch"), ["register"]),
|
|
67337
|
+
create: (ctx) => read(ctx, "connection")
|
|
67338
|
+
});
|
|
67339
|
+
add({
|
|
67340
|
+
id: "connection-peer-admission-020",
|
|
67341
|
+
capability: "connection.peer",
|
|
67342
|
+
supportedDsh,
|
|
67343
|
+
runtime: "host",
|
|
67344
|
+
priority: 30,
|
|
67345
|
+
status: "supported",
|
|
67346
|
+
introducedIn: "0.2.0-rc.1",
|
|
67347
|
+
detect: (ctx) => hasMethods(read(ctx, "connection"), ["admit", "requestRejection"]) && read(ctx, "connection.operator") !== void 0,
|
|
67348
|
+
create: (ctx) => read(ctx, "connection")
|
|
67349
|
+
});
|
|
67350
|
+
add({
|
|
67351
|
+
id: "connection-rpc-attachment-020",
|
|
67352
|
+
capability: "connection.attachment",
|
|
67353
|
+
supportedDsh,
|
|
67354
|
+
runtime: "host",
|
|
67355
|
+
priority: 10,
|
|
67356
|
+
status: "supported",
|
|
67357
|
+
introducedIn: "0.2.0-rc.1",
|
|
67358
|
+
detect: (ctx) => hasMethods(read(ctx, "connection.rpc"), ["handle"]) && hasMethods(read(ctx, "connection.fetch"), ["register"]),
|
|
67359
|
+
create: (ctx) => read(ctx, "connection")
|
|
67360
|
+
});
|
|
67361
|
+
add({
|
|
67362
|
+
id: "connection-rpc-uplink-020",
|
|
67363
|
+
capability: "connection.uplink",
|
|
67364
|
+
supportedDsh,
|
|
67365
|
+
runtime: "host",
|
|
67366
|
+
priority: 10,
|
|
67367
|
+
status: "supported",
|
|
67368
|
+
introducedIn: "0.2.0-rc.1",
|
|
67369
|
+
detect: (ctx) => hasMethods(read(ctx, "connection.rpc"), ["handle"]) && read(ctx, "connection.operator") !== void 0,
|
|
67370
|
+
create: (ctx) => read(ctx, "connection")
|
|
67371
|
+
});
|
|
67372
|
+
add({
|
|
67373
|
+
id: "remote-context-stream-020",
|
|
67374
|
+
capability: "typert.remote",
|
|
67375
|
+
supportedDsh,
|
|
67376
|
+
runtime: "host",
|
|
67377
|
+
priority: 30,
|
|
67378
|
+
status: "supported",
|
|
67379
|
+
introducedIn: "0.2.0-rc.1",
|
|
67380
|
+
detect: (ctx) => read(ctx, "typert.remotes") !== void 0 && read(ctx, "typert.local") !== void 0,
|
|
67381
|
+
create: (ctx) => read(ctx, "typert")
|
|
67382
|
+
});
|
|
67383
|
+
add({
|
|
67384
|
+
id: "typert-context-registry-020",
|
|
67385
|
+
capability: "typert.context",
|
|
67386
|
+
supportedDsh,
|
|
67387
|
+
runtime: "host",
|
|
67388
|
+
priority: 10,
|
|
67389
|
+
status: "supported",
|
|
67390
|
+
introducedIn: "0.2.0-rc.1",
|
|
67391
|
+
detect: (ctx) => hasMethods(read(ctx, "typert.contexts"), ["getHost", "getClient"]),
|
|
67392
|
+
create: (ctx) => read(ctx, "typert.contexts")
|
|
67393
|
+
});
|
|
67394
|
+
add({
|
|
67395
|
+
id: "typert-remote-stream-020",
|
|
67396
|
+
capability: "typert.stream",
|
|
67397
|
+
supportedDsh,
|
|
67398
|
+
runtime: "host",
|
|
67399
|
+
priority: 10,
|
|
67400
|
+
status: "supported",
|
|
67401
|
+
introducedIn: "0.2.0-rc.1",
|
|
67402
|
+
detect: (ctx) => hasMethods(read(ctx, "typert.remotes"), ["register", "list"]),
|
|
67403
|
+
create: (ctx) => read(ctx, "typert.remotes")
|
|
67404
|
+
});
|
|
67405
|
+
add({
|
|
67406
|
+
id: "session-format-v4",
|
|
67407
|
+
capability: "session.format-v4",
|
|
67408
|
+
supportedDsh,
|
|
67409
|
+
runtime: "host",
|
|
67410
|
+
priority: 10,
|
|
67411
|
+
status: "supported",
|
|
67412
|
+
introducedIn: "0.2.0-rc.1",
|
|
67413
|
+
detect: (ctx) => hasMethods(read(ctx, "sessions"), ["get", "list"]),
|
|
67414
|
+
create: (ctx) => read(ctx, "sessions")
|
|
67415
|
+
});
|
|
67416
|
+
add({
|
|
67417
|
+
id: "subagent-continuable-020",
|
|
67418
|
+
capability: "subagent.continuable",
|
|
67419
|
+
supportedDsh,
|
|
67420
|
+
runtime: "host",
|
|
67421
|
+
priority: 10,
|
|
67422
|
+
status: "supported",
|
|
67423
|
+
introducedIn: "0.2.0-rc.1",
|
|
67424
|
+
detect: (ctx) => hasMethods(read(ctx, "subagents"), ["startContinuable", "sendMessage"]),
|
|
67425
|
+
create: (ctx) => read(ctx, "subagents")
|
|
67426
|
+
});
|
|
67427
|
+
add({
|
|
67428
|
+
id: "agent-team-native-020",
|
|
67429
|
+
capability: "agent-team.native",
|
|
67430
|
+
supportedDsh,
|
|
67431
|
+
runtime: "host",
|
|
67432
|
+
priority: 10,
|
|
67433
|
+
status: "supported",
|
|
67434
|
+
introducedIn: "0.2.0-rc.1",
|
|
67435
|
+
detect: (ctx) => hasMethods(read(ctx, "agentTeams"), ["listMembers", "spawnTeammate"]) && read(ctx, "agents") !== void 0,
|
|
67436
|
+
create: (ctx) => read(ctx, "agentTeams")
|
|
67437
|
+
});
|
|
67438
|
+
}
|
|
67439
|
+
/** DSH 0.2 Client 结构化能力;图标和 UI 服务允许由新旧导出共同提供。 */
|
|
67440
|
+
function addDsh020ClientRoutes(add, supportedDsh, facts) {
|
|
67441
|
+
add({
|
|
67442
|
+
id: "config-forms-020",
|
|
67443
|
+
capability: "settings.store",
|
|
67444
|
+
supportedDsh,
|
|
67445
|
+
runtime: "client",
|
|
67446
|
+
priority: 30,
|
|
67447
|
+
status: "supported",
|
|
67448
|
+
introducedIn: "0.2.0-rc.1",
|
|
67449
|
+
detect: (ctx) => hasMethods(read(ctx, "settings"), ["describe", "update"]) || hasMethods(read(ctx, "configForms"), ["get"]),
|
|
67450
|
+
create: (ctx) => read(ctx, "settings") ?? read(ctx, "configForms")
|
|
67451
|
+
});
|
|
67452
|
+
add({
|
|
67453
|
+
id: "regular-plus-icon-020",
|
|
67454
|
+
capability: "ui.icon.plus",
|
|
67455
|
+
supportedDsh,
|
|
67456
|
+
runtime: "client",
|
|
67457
|
+
priority: 30,
|
|
67458
|
+
status: "supported",
|
|
67459
|
+
introducedIn: "0.2.0-rc.1",
|
|
67460
|
+
detect: () => hasAny(facts.primitives, ["IconPlusOutlineRegular", "IconPlusOutlineMedium"]),
|
|
67461
|
+
create: () => facts.primitives
|
|
67462
|
+
});
|
|
67463
|
+
add({
|
|
67464
|
+
id: "regular-chevron-icon-020",
|
|
67465
|
+
capability: "ui.icon.chevron",
|
|
67466
|
+
supportedDsh,
|
|
67467
|
+
runtime: "client",
|
|
67468
|
+
priority: 30,
|
|
67469
|
+
status: "supported",
|
|
67470
|
+
introducedIn: "0.2.0-rc.1",
|
|
67471
|
+
detect: () => hasAny(facts.primitives, ["IconChevronDownOutlineRegular", "IconChevronDownOutlineMedium"]),
|
|
67472
|
+
create: () => facts.primitives
|
|
67473
|
+
});
|
|
67474
|
+
add({
|
|
67475
|
+
id: "locale-runtime-020",
|
|
67476
|
+
capability: "locale.runtime",
|
|
67477
|
+
supportedDsh,
|
|
67478
|
+
runtime: "client",
|
|
67479
|
+
priority: 30,
|
|
67480
|
+
status: "supported",
|
|
67481
|
+
introducedIn: "0.2.0-rc.1",
|
|
67482
|
+
detect: (ctx) => read(ctx, "locale") !== void 0,
|
|
67483
|
+
create: (ctx) => read(ctx, "locale")
|
|
67484
|
+
});
|
|
67485
|
+
add({
|
|
67486
|
+
id: "theme-runtime-020",
|
|
67487
|
+
capability: "theme.runtime",
|
|
67488
|
+
supportedDsh,
|
|
67489
|
+
runtime: "client",
|
|
67490
|
+
priority: 30,
|
|
67491
|
+
status: "supported",
|
|
67492
|
+
introducedIn: "0.2.0-rc.1",
|
|
67493
|
+
detect: (ctx) => read(ctx, "theme") !== void 0,
|
|
67494
|
+
create: (ctx) => read(ctx, "theme")
|
|
67495
|
+
});
|
|
67496
|
+
add({
|
|
67497
|
+
id: "conversation-events-020",
|
|
67498
|
+
capability: "conversation.tool-call",
|
|
67499
|
+
supportedDsh,
|
|
67500
|
+
runtime: "client",
|
|
67501
|
+
priority: 30,
|
|
67502
|
+
status: "supported",
|
|
67503
|
+
introducedIn: "0.2.0-rc.1",
|
|
67504
|
+
detect: (ctx) => read(ctx, "uiConversation") !== void 0,
|
|
67505
|
+
create: (ctx) => read(ctx, "uiConversation")
|
|
67506
|
+
});
|
|
67507
|
+
add({
|
|
67508
|
+
id: "sidebar-right-dock-020",
|
|
67509
|
+
capability: "sidebar.right",
|
|
67510
|
+
supportedDsh,
|
|
67511
|
+
runtime: "client",
|
|
67512
|
+
priority: 30,
|
|
67513
|
+
status: "supported",
|
|
67514
|
+
introducedIn: "0.2.0-rc.1",
|
|
67515
|
+
detect: (ctx) => read(ctx, "sidebarRight") !== void 0 || read(ctx, "sidebarRightTabs") !== void 0,
|
|
67516
|
+
create: (ctx) => read(ctx, "sidebarRight") ?? read(ctx, "sidebarRightTabs")
|
|
67517
|
+
});
|
|
67518
|
+
add({
|
|
67519
|
+
id: "remote-context-stream-020-client",
|
|
67520
|
+
capability: "typert.remote",
|
|
67521
|
+
supportedDsh,
|
|
67522
|
+
runtime: "client",
|
|
67523
|
+
priority: 30,
|
|
67524
|
+
status: "supported",
|
|
67525
|
+
introducedIn: "0.2.0-rc.1",
|
|
67526
|
+
detect: (ctx) => hasMethods(read(ctx, "remote"), ["$mount"]),
|
|
67527
|
+
create: (ctx) => read(ctx, "remote")
|
|
67528
|
+
});
|
|
67529
|
+
add({
|
|
67530
|
+
id: "typert-context-registry-020-client",
|
|
67531
|
+
capability: "typert.context",
|
|
67532
|
+
supportedDsh,
|
|
67533
|
+
runtime: "client",
|
|
67534
|
+
priority: 10,
|
|
67535
|
+
status: "supported",
|
|
67536
|
+
introducedIn: "0.2.0-rc.1",
|
|
67537
|
+
detect: (ctx) => hasMethods(read(ctx, "typert.contexts"), ["getHost", "getClient"]),
|
|
67538
|
+
create: (ctx) => read(ctx, "typert.contexts")
|
|
67539
|
+
});
|
|
67540
|
+
add({
|
|
67541
|
+
id: "typert-remote-stream-020-client",
|
|
67542
|
+
capability: "typert.stream",
|
|
67543
|
+
supportedDsh,
|
|
67544
|
+
runtime: "client",
|
|
67545
|
+
priority: 10,
|
|
67546
|
+
status: "supported",
|
|
67547
|
+
introducedIn: "0.2.0-rc.1",
|
|
67548
|
+
detect: (ctx) => hasMethods(read(ctx, "remote"), ["$mount", "$stream"]),
|
|
67549
|
+
create: (ctx) => read(ctx, "remote")
|
|
67550
|
+
});
|
|
67551
|
+
add({
|
|
67552
|
+
id: "client-web-boot-graph-020",
|
|
67553
|
+
capability: "client.boot-graph",
|
|
67554
|
+
supportedDsh,
|
|
67555
|
+
runtime: "client",
|
|
67556
|
+
priority: 10,
|
|
67557
|
+
status: "supported",
|
|
67558
|
+
introducedIn: "0.2.0-rc.1",
|
|
67559
|
+
detect: (ctx) => read(ctx, "modules.version") === "client",
|
|
67560
|
+
create: (ctx) => read(ctx, "modules")
|
|
67561
|
+
});
|
|
67562
|
+
}
|
|
67563
|
+
/**
|
|
67564
|
+
* 读取探测上下文里的服务或结构事实。
|
|
67565
|
+
*
|
|
67566
|
+
* Cordis 的 Context 只在调用方 fiber 的 inject 链(或祖先 fiber 提供的服务)里
|
|
67567
|
+
* 解析属性;直接读未注入的服务会抛 `cannot get property "x" without inject`,
|
|
67568
|
+
* 把 `a || b` 形式的探测短路成失败。因此这里统一先走 `ctx.get(...)`(Cordis 提供
|
|
67569
|
+
* 的免 inject 读取),再回退到普通属性读取,让探测只依据服务形状,不受消费方
|
|
67570
|
+
* inject 列表影响。
|
|
67571
|
+
*/
|
|
67572
|
+
function read(context, path) {
|
|
67573
|
+
const [head, ...rest] = path.split(".");
|
|
67574
|
+
if (head === void 0) return void 0;
|
|
67575
|
+
let value = readKey(context, head);
|
|
67576
|
+
for (const key of rest) value = readProperty(value, key);
|
|
67577
|
+
return value;
|
|
67578
|
+
}
|
|
67579
|
+
/** 读取上下文上的服务(优先 `ctx.get`)或直接挂在上下文上的事实对象。 */
|
|
67580
|
+
function readKey(context, key) {
|
|
67581
|
+
if (typeof context !== "object" || context === null) return void 0;
|
|
67582
|
+
const getter = readProperty(context, "get");
|
|
67583
|
+
if (typeof getter === "function") try {
|
|
67584
|
+
const value = getter.call(context, key);
|
|
67585
|
+
if (value !== void 0) return value;
|
|
67586
|
+
} catch {}
|
|
67587
|
+
return readProperty(context, key);
|
|
67588
|
+
}
|
|
67589
|
+
/** 读取对象属性;Cordis 代理对未注入的服务会抛错,统一按缺失处理。 */
|
|
67590
|
+
function readProperty(target, key) {
|
|
67591
|
+
if (typeof target !== "object" || target === null) return void 0;
|
|
67592
|
+
try {
|
|
67593
|
+
return Reflect.get(target, key);
|
|
67594
|
+
} catch {
|
|
67595
|
+
return;
|
|
67596
|
+
}
|
|
67597
|
+
}
|
|
67598
|
+
function hasMethods(value, methods) {
|
|
67599
|
+
return typeof value === "object" && value !== null && methods.every((method) => typeof Reflect.get(value, method) === "function");
|
|
67600
|
+
}
|
|
67601
|
+
function hasAny(value, keys) {
|
|
67602
|
+
return typeof value === "object" && value !== null && keys.some((key) => Reflect.get(value, key) !== void 0);
|
|
67603
|
+
}
|
|
66557
67604
|
/** PeerHost Host 能力只接受显式注入的适配器,避免把普通 DSH 服务误报为已实现。 */
|
|
66558
67605
|
function addPeerHostHostRoutes(add) {
|
|
66559
67606
|
addPeerHostRoute(add, "peer-host.store", "peer-host-store", "peerHostStore");
|
|
@@ -66562,12 +67609,20 @@ window.__ModuleLoader__.load({
|
|
|
66562
67609
|
addPeerHostRoute(add, "peer-host.ws-proxy", "peer-host-ws-proxy", "peerHostWsProxy");
|
|
66563
67610
|
addPeerHostRoute(add, "peer-host.aggregate", "peer-host-aggregate", "peerHostAggregate");
|
|
66564
67611
|
addPeerHostRoute(add, "peer-host.relay-route", "peer-host-relay-route", "peerHostRelayRoute");
|
|
67612
|
+
addPeerHostRoute(add, "peer-host.store", "peer-host-store-020", "peerHostStore", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", 30);
|
|
67613
|
+
addPeerHostRoute(add, "peer-host.handshake", "peer-host-handshake-020", "peerHostHandshake", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", 30);
|
|
67614
|
+
addPeerHostRoute(add, "peer-host.http-proxy", "peer-host-http-proxy-020", "peerHostHttpProxy", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", 30);
|
|
67615
|
+
addPeerHostRoute(add, "peer-host.ws-proxy", "peer-host-ws-proxy-020", "peerHostWsProxy", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", 30);
|
|
67616
|
+
addPeerHostRoute(add, "peer-host.aggregate", "peer-host-aggregate-020", "peerHostAggregate", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", 30);
|
|
67617
|
+
addPeerHostRoute(add, "peer-host.relay-route", "peer-host-relay-route-020", "peerHostRelayRoute", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", 30);
|
|
66565
67618
|
}
|
|
66566
67619
|
/** PeerHost Client 导航能力由独立 adapter 注入;未注入时由 Feature 诊断降级。 */
|
|
66567
67620
|
function addPeerHostClientRoutes(add) {
|
|
66568
67621
|
addPeerHostRoute(add, "peer-host.native-navigation", "peer-host-native-navigation-legacy", "peerHostNativeNavigation", ">=0.1.5-rc.3 <=0.1.6", "deprecated");
|
|
66569
67622
|
addPeerHostRoute(add, "peer-host.native-navigation", "peer-host-native-navigation-modern", "peerHostNativeNavigation", ">=0.1.7-rc.2 <=0.1.7-rc.2", "supported", 20);
|
|
66570
67623
|
addPeerHostRoute(add, "peer-host.remote-web-context-fallback", "peer-host-remote-web-context-fallback", "peerHostRemoteWebContextFallback");
|
|
67624
|
+
addPeerHostRoute(add, "peer-host.native-navigation", "peer-host-native-navigation-020", "peerHostNativeNavigation", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", 30);
|
|
67625
|
+
addPeerHostRoute(add, "peer-host.remote-web-context-fallback", "peer-host-remote-web-context-fallback-020", "peerHostRemoteWebContextFallback", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", 30);
|
|
66571
67626
|
}
|
|
66572
67627
|
function addPeerHostRoute(add, capability, id, field, supportedDsh = ">=0.1.5-rc.3 <=0.1.7-rc.2", status = "supported", priority = 10) {
|
|
66573
67628
|
add({
|
|
@@ -66583,10 +67638,10 @@ window.__ModuleLoader__.load({
|
|
|
66583
67638
|
});
|
|
66584
67639
|
}
|
|
66585
67640
|
function readPeerHostAdapter(context, field) {
|
|
66586
|
-
if (
|
|
66587
|
-
return context
|
|
67641
|
+
if (Array.isArray(context)) return void 0;
|
|
67642
|
+
return readKey(context, field);
|
|
66588
67643
|
}
|
|
66589
|
-
route("settings.store", "legacy-settings-scope", "host", ">=0.1.5-rc.3 <=0.1.6", "supported", ["host/settings.ts", "host/features/*"]), route("settings.store", "config-forms", "host", ">=0.1.7-rc.2 <=0.1.7-rc.2", "supported", ["host/settings.ts", "client/settings-bridge.ts"]), route("connection.rpc", "legacy-rpc-handler", "host", ">=0.1.5-rc.3 <=0.1.6", "supported", ["host/rpc.ts"]), route("connection.rpc", "peer-aware-rpc-handler", "host", ">=0.1.7-rc.2 <=0.1.7-rc.2", "supported", ["host/rpc.ts"]), route("connection.peer", "
|
|
67644
|
+
route("settings.store", "legacy-settings-scope", "host", ">=0.1.5-rc.3 <=0.1.6", "supported", ["host/settings.ts", "host/features/*"]), route("settings.store", "config-forms", "host", ">=0.1.7-rc.2 <=0.1.7-rc.2", "supported", ["host/settings.ts", "client/settings-bridge.ts"]), route("connection.rpc", "legacy-rpc-handler", "host", ">=0.1.5-rc.3 <=0.1.6", "supported", ["host/rpc.ts"]), route("connection.rpc", "peer-aware-rpc-handler", "host", ">=0.1.7-rc.2 <=0.1.7-rc.2", "supported", ["host/rpc.ts"]), route("connection.peer", "peer-scope", "host", ">=0.1.7-rc.2 <=0.1.7-rc.2", "supported", ["host/rpc.ts"]), route("ui.icon.plus", "fixed-plus-icon", "client", ">=0.1.5-rc.3 <=0.1.6", "supported", ["client/terminal/xterm-view.ts"]), route("ui.icon.plus", "regular-plus-icon", "client", ">=0.1.7-rc.2 <=0.1.7-rc.2", "supported", ["client/terminal/xterm-view.ts"]), route("ui.icon.chevron", "fixed-chevron-icon", "client", ">=0.1.5-rc.3 <=0.1.6", "supported", ["client/terminal/ui.ts"]), route("ui.icon.chevron", "regular-chevron-icon", "client", ">=0.1.7-rc.2 <=0.1.7-rc.2", "supported", ["client/terminal/ui.ts"]), route("locale.runtime", "locale-runtime", "client", ">=0.1.5-rc.3 <=0.1.7-rc.2", "supported", ["client/locale.ts"]), route("theme.runtime", "theme-runtime", "client", ">=0.1.5-rc.3 <=0.1.7-rc.2", "supported", ["client/theme.ts"]), route("conversation.tool-call", "conversation-events", "client", ">=0.1.5-rc.3 <=0.1.7-rc.2", "supported", ["client/external-tool-stream.ts"]), route("sidebar.right", "sidebar-right-tabs", "client", ">=0.1.5-rc.3 <=0.1.7-rc.2", "supported", ["client/terminal/ui.ts"]), route("typert.remote", "remote-result", "host", ">=0.1.5-rc.3 <=0.1.7-rc.2", "supported", ["host/terminal/terminal-controller.ts", "client/terminal/model.ts"]), route("peer-host.store", "peer-host-store", "host", ">=0.1.5-rc.3 <=0.1.7-rc.2", "supported", ["host/features/peer-host.ts"]), route("peer-host.handshake", "peer-host-handshake", "host", ">=0.1.5-rc.3 <=0.1.7-rc.2", "supported", ["host/features/peer-host.ts"]), route("peer-host.http-proxy", "peer-host-http-proxy", "host", ">=0.1.5-rc.3 <=0.1.7-rc.2", "supported", ["host/features/peer-host.ts"]), route("peer-host.ws-proxy", "peer-host-ws-proxy", "host", ">=0.1.5-rc.3 <=0.1.7-rc.2", "supported", ["host/features/peer-host.ts"]), route("peer-host.aggregate", "peer-host-aggregate", "host", ">=0.1.5-rc.3 <=0.1.7-rc.2", "supported", ["host/features/peer-host.ts"]), route("peer-host.relay-route", "peer-host-relay-route", "host", ">=0.1.5-rc.3 <=0.1.7-rc.2", "supported", ["host/features/peer-host.ts"]), route("peer-host.native-navigation", "peer-host-native-navigation-legacy", "client", ">=0.1.5-rc.3 <=0.1.6", "deprecated", ["client/features/peer-host.ts"]), route("peer-host.native-navigation", "peer-host-native-navigation-modern", "client", ">=0.1.7-rc.2 <=0.1.7-rc.2", "supported", ["client/features/peer-host.ts"]), route("peer-host.remote-web-context-fallback", "peer-host-remote-web-context-fallback", "client", ">=0.1.5-rc.3 <=0.1.7-rc.2", "supported", ["client/features/peer-host.ts"]), route("settings.store", "settings-forms-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/settings.ts", "host/rpc.ts"]), route("connection.rpc", "connection-rpc-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/rpc.ts", "transport/dsh-transport.ts"]), route("connection.peer", "connection-peer-admission-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/rpc.ts"]), route("connection.attachment", "connection-rpc-attachment-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/rpc.ts", "transport/dsh-transport.ts"]), route("connection.uplink", "connection-rpc-uplink-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/rpc.ts", "transport/dsh-transport.ts"]), route("typert.remote", "remote-context-stream-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/terminal/terminal-controller.ts", "client/terminal/model.ts"]), route("typert.context", "typert-context-registry-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["typert.host.ts", "host/terminal/terminal-controller.ts"]), route("typert.stream", "typert-remote-stream-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["typert.host.ts", "typert.remote-client.ts"]), route("session.format-v4", "session-format-v4", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/native-session-bridge.ts", "host/session-migration-repair.ts"]), route("subagent.continuable", "subagent-continuable-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/cli-adapters/session-store.ts", "host/cli-adapters/feature.ts"]), route("agent-team.native", "agent-team-native-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/cli-adapters/feature.ts", "host/cli-adapters/native-team-proxy.ts"]), route("ui.icon.plus", "regular-plus-icon-020", "client", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["client/terminal/xterm-view.ts"]), route("ui.icon.chevron", "regular-chevron-icon-020", "client", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["client/terminal/ui.ts"]), route("locale.runtime", "locale-runtime-020", "client", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["client/locale.ts"]), route("theme.runtime", "theme-runtime-020", "client", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["client/theme.ts"]), route("conversation.tool-call", "conversation-events-020", "client", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["client/external-tool-stream.ts"]), route("sidebar.right", "sidebar-right-dock-020", "client", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["client/terminal/ui.ts"]), route("typert.remote", "remote-context-stream-020-client", "client", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["client/terminal/model.ts"]), route("typert.context", "typert-context-registry-020-client", "client", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["typert.remote-client.ts", "client/terminal/model.ts"]), route("typert.stream", "typert-remote-stream-020-client", "client", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["typert.remote-client.ts", "client/terminal/model.ts"]), route("client.boot-graph", "client-web-boot-graph-020", "client", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["client/index.ts", "package.json"]), route("peer-host.native-navigation", "peer-host-native-navigation-020", "client", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["client/features/peer-host.ts"]), route("peer-host.remote-web-context-fallback", "peer-host-remote-web-context-fallback-020", "client", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["client/features/peer-host.ts"]), route("peer-host.store", "peer-host-store-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/features/peer-host.ts"]), route("peer-host.handshake", "peer-host-handshake-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/features/peer-host.ts"]), route("peer-host.http-proxy", "peer-host-http-proxy-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/features/peer-host.ts"]), route("peer-host.ws-proxy", "peer-host-ws-proxy-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/features/peer-host.ts"]), route("peer-host.aggregate", "peer-host-aggregate-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/features/peer-host.ts"]), route("peer-host.relay-route", "peer-host-relay-route-020", "host", ">=0.2.0-rc.1 <=0.2.0-rc.1", "supported", ["host/features/peer-host.ts"]);
|
|
66590
67645
|
function route(capability, routeId, runtime, supportedDsh, status, consumers, replacement, removableAfter) {
|
|
66591
67646
|
return {
|
|
66592
67647
|
capability,
|
|
@@ -72083,7 +73138,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
72083
73138
|
debugInfo("codingns4dsh: client account bar registration begin");
|
|
72084
73139
|
const disposeAccountBar = startCodingNsAccountBar(connection.rpc, void 0, settings);
|
|
72085
73140
|
debugInfo("codingns4dsh: client account bar registration ready");
|
|
72086
|
-
const capabilityProfile = createDshCapabilityRegistry(dshVersion, "client", settingsCtx).getProfile(settingsCtx);
|
|
73141
|
+
const capabilityProfile = createDshCapabilityRegistry(dshVersion, "client", settingsCtx, { primitives: _deepseek_ai_dsh_client_ui_primitives }).getProfile(settingsCtx);
|
|
72087
73142
|
debugInfo("codingns4dsh: client capabilities resolved", {
|
|
72088
73143
|
dshVersion,
|
|
72089
73144
|
capabilities: [...capabilityProfile.capabilities.entries()].map(([capability, resolution]) => ({
|
|
@@ -72173,50 +73228,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
72173
73228
|
return createCodingNsSettingsBridge(scopeBinder.bind({ namespace: CODINGNS_SETTINGS_NAMESPACE }), rpc);
|
|
72174
73229
|
}
|
|
72175
73230
|
const forms = ctx.get("configForms");
|
|
72176
|
-
const
|
|
72177
|
-
if (
|
|
72178
|
-
debugWarn("codingns4dsh: client config form
|
|
73231
|
+
const namespace = resolveServedConfigFormNamespace(forms, CODINGNS_SETTINGS_ENTRY_IDS);
|
|
73232
|
+
if (namespace === void 0 || forms === void 0) {
|
|
73233
|
+
debugWarn("codingns4dsh: client config form not served; falling back to Host settings RPC", { hasConfigForms: forms !== void 0 });
|
|
72179
73234
|
return createCodingNsSettingsBridge(void 0, rpc);
|
|
72180
73235
|
}
|
|
72181
|
-
debugInfo("codingns4dsh: client settings source=configForms", {
|
|
72182
|
-
|
|
72183
|
-
hasForm: true
|
|
72184
|
-
});
|
|
72185
|
-
return createConfigFormSettingsStore({ get: () => form }, CODINGNS_SETTINGS_NAMESPACE, { writeUnfenced: async (ops) => callCodingNsRpc(rpc, "settings/set", { ops }) });
|
|
72186
|
-
}
|
|
72187
|
-
function findConfigForm(forms) {
|
|
72188
|
-
if (forms === void 0) return void 0;
|
|
72189
|
-
try {
|
|
72190
|
-
const namespaces = forms.describe?.().getSnapshot().view?.namespaces;
|
|
72191
|
-
debugInfo("codingns4dsh: client config form namespaces", { namespaces: namespaces?.map((item) => item.ns) });
|
|
72192
|
-
const servedId = CODINGNS_SETTINGS_ENTRY_IDS.find((id) => namespaces?.some((item) => item.ns === id));
|
|
72193
|
-
if (servedId !== void 0) {
|
|
72194
|
-
const form = forms.get(servedId);
|
|
72195
|
-
debugInfo("codingns4dsh: client config form namespace selected", {
|
|
72196
|
-
namespace: servedId,
|
|
72197
|
-
hasForm: form !== void 0
|
|
72198
|
-
});
|
|
72199
|
-
return form;
|
|
72200
|
-
}
|
|
72201
|
-
} catch {}
|
|
72202
|
-
for (const id of CODINGNS_SETTINGS_ENTRY_IDS) try {
|
|
72203
|
-
const form = forms.get(id);
|
|
72204
|
-
if (form !== void 0 && form.getSnapshot().status !== "unavailable") {
|
|
72205
|
-
debugInfo("codingns4dsh: client config form namespace fallback selected", {
|
|
72206
|
-
namespace: id,
|
|
72207
|
-
status: form.getSnapshot().status,
|
|
72208
|
-
revision: form.getSnapshot().revision
|
|
72209
|
-
});
|
|
72210
|
-
return form;
|
|
72211
|
-
}
|
|
72212
|
-
} catch {}
|
|
73236
|
+
debugInfo("codingns4dsh: client settings source=configForms", { namespace });
|
|
73237
|
+
return createConfigFormSettingsStore(forms, namespace, { writeUnfenced: async (ops) => callCodingNsRpc(rpc, "settings/set", { ops }) });
|
|
72213
73238
|
}
|
|
72214
73239
|
//#endregion
|
|
72215
73240
|
exports.CLIENT_FEATURES = CLIENT_FEATURES;
|
|
73241
|
+
exports.CONTEXT_BREAKDOWN_CONVERSATION_ATTRIBUTE = CONTEXT_BREAKDOWN_CONVERSATION_ATTRIBUTE;
|
|
73242
|
+
exports.CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE = CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE;
|
|
73243
|
+
exports.CONTEXT_BREAKDOWN_STYLE_ID = CONTEXT_BREAKDOWN_STYLE_ID;
|
|
72216
73244
|
exports.CodingNsSettingsSection = CodingNsSettingsSection;
|
|
72217
73245
|
exports.CodingNsTerminalView = CodingNsTerminalView;
|
|
72218
73246
|
exports.CodingNsWebTerminals = CodingNsWebTerminals;
|
|
72219
73247
|
exports.CommandCodeSubscriptionSlot = CommandCodeSubscriptionSlot;
|
|
73248
|
+
exports.DEFAULT_CONTEXT_BREAKDOWN_HIDDEN_ADAPTERS = DEFAULT_CONTEXT_BREAKDOWN_HIDDEN_ADAPTERS;
|
|
72220
73249
|
exports.PEER_HOST_NAVIGATION_ATTRIBUTE = PEER_HOST_NAVIGATION_ATTRIBUTE;
|
|
72221
73250
|
exports.PEER_HOST_SESSION_ATTRIBUTE = PEER_HOST_SESSION_ATTRIBUTE;
|
|
72222
73251
|
exports.PEER_HOST_STATUS_ATTRIBUTE = PEER_HOST_STATUS_ATTRIBUTE;
|
|
@@ -72243,6 +73272,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
72243
73272
|
exports.registerSubscriptionSlot = registerSubscriptionSlot;
|
|
72244
73273
|
exports.settingsModules = settingsModules;
|
|
72245
73274
|
exports.startBrowserRelayConnection = startBrowserRelayConnection;
|
|
73275
|
+
exports.startContextBreakdownDom = startContextBreakdownDom;
|
|
72246
73276
|
exports.startDshH5Bootstrap = startDshH5Bootstrap;
|
|
72247
73277
|
exports.startDshH5BrowserBootstrap = startDshH5BrowserBootstrap;
|
|
72248
73278
|
exports.startPeerHostNativeNavigation = startPeerHostNativeNavigation;
|