@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
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH 上下文面板(ContextMeter)的构成明细隐藏器。
|
|
3
|
+
*
|
|
4
|
+
* 面板同时展示两组不同来源的数字:百分比与 `~已用 / 窗口` 来自 Provider 上报的
|
|
5
|
+
* 真实用量,而「系统 / 工具 / 消息」三行明细与分段条来自 token-meter 对本地表面
|
|
6
|
+
* 的启发式估算。外部 Agent(codex 等)自己组装请求,它的系统提示、工具定义和
|
|
7
|
+
* 对话历史都不经过本地表面,两组数字因此天然对不上;`contextBreakdown` 投影又
|
|
8
|
+
* 是按定义全局注册的,无法只对某个会话关闭。这里在浏览器侧按会话隐藏构成部分,
|
|
9
|
+
* 只保留百分比与总量。
|
|
10
|
+
*
|
|
11
|
+
* 依赖的 DSH 0.2.0-rc.1 对话 DOM 契约(任何一条不成立都只是不生效,不影响其他
|
|
12
|
+
* 功能,也不会误改宿主界面):
|
|
13
|
+
* - 对话根节点带 `data-conversation-session`;
|
|
14
|
+
* - 面板触发器是 `button[aria-haspopup="dialog"]`,展开时 `aria-expanded="true"`;
|
|
15
|
+
* - 面板经 portal 直挂 `document.body`,`role="dialog"`,直接子节点依次是 header、
|
|
16
|
+
* 分段条 div 与可选的明细 dl(`div > dt + dd` 结构)。
|
|
17
|
+
*/
|
|
18
|
+
/** 命中会话的上下文面板会被打上该属性,插件样式据此隐藏构成部分。 */
|
|
19
|
+
export declare const CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE = "data-codingns-context-breakdown-hidden";
|
|
20
|
+
/** 插件样式标签的幂等键。 */
|
|
21
|
+
export declare const CONTEXT_BREAKDOWN_STYLE_ID = "codingns4dsh-context-breakdown-style";
|
|
22
|
+
/** 对话根节点上承载当前会话 id 的 DSH 属性。 */
|
|
23
|
+
export declare const CONTEXT_BREAKDOWN_CONVERSATION_ATTRIBUTE = "data-conversation-session";
|
|
24
|
+
/** 默认隐藏构成明细的适配器:它们自己组装请求,启发式估算与真实上下文不同源。 */
|
|
25
|
+
export declare const DEFAULT_CONTEXT_BREAKDOWN_HIDDEN_ADAPTERS: readonly string[];
|
|
26
|
+
export interface ContextBreakdownDomController {
|
|
27
|
+
/** 显式触发一次重扫,例如适配器映射由外部刷新后。 */
|
|
28
|
+
refresh(): void;
|
|
29
|
+
/** 断开观察器、取消订阅并移除本轮标记。 */
|
|
30
|
+
dispose(): void;
|
|
31
|
+
}
|
|
32
|
+
export interface ContextBreakdownDomOptions {
|
|
33
|
+
readonly document?: Document;
|
|
34
|
+
readonly MutationObserver?: typeof MutationObserver;
|
|
35
|
+
/** 会话到适配器的解析;默认读取 Host 映射缓存。 */
|
|
36
|
+
readonly adapterIdForSession?: (sessionId: string) => string | undefined;
|
|
37
|
+
/** 需要隐藏构成明细的适配器,默认只有 codex。 */
|
|
38
|
+
readonly hiddenAdapterIds?: readonly string[];
|
|
39
|
+
/** 会话适配器未知时按需刷新映射;缺省则只依赖已有缓存。 */
|
|
40
|
+
readonly refreshAdapters?: () => void | Promise<void>;
|
|
41
|
+
readonly adapterRefreshIntervalMs?: number;
|
|
42
|
+
/** 时钟注入点,仅用于测试节流。 */
|
|
43
|
+
readonly now?: () => number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 为上下文面板安装构成明细隐藏器。
|
|
47
|
+
*
|
|
48
|
+
* 打开面板时才需要判定所属会话,因此只观察 `body` 的直接子节点(portal 插入),
|
|
49
|
+
* 不跟踪面板内部重渲染;隐藏通过面板属性加插件样式实现,React 复用面板节点时
|
|
50
|
+
* 属性仍然有效。
|
|
51
|
+
*/
|
|
52
|
+
export declare function startContextBreakdownDom(options?: ContextBreakdownDomOptions): ContextBreakdownDomController;
|
|
53
|
+
//# sourceMappingURL=context-breakdown-dom.d.ts.map
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DSH 上下文面板(ContextMeter)的构成明细隐藏器。
|
|
3
|
+
*
|
|
4
|
+
* 面板同时展示两组不同来源的数字:百分比与 `~已用 / 窗口` 来自 Provider 上报的
|
|
5
|
+
* 真实用量,而「系统 / 工具 / 消息」三行明细与分段条来自 token-meter 对本地表面
|
|
6
|
+
* 的启发式估算。外部 Agent(codex 等)自己组装请求,它的系统提示、工具定义和
|
|
7
|
+
* 对话历史都不经过本地表面,两组数字因此天然对不上;`contextBreakdown` 投影又
|
|
8
|
+
* 是按定义全局注册的,无法只对某个会话关闭。这里在浏览器侧按会话隐藏构成部分,
|
|
9
|
+
* 只保留百分比与总量。
|
|
10
|
+
*
|
|
11
|
+
* 依赖的 DSH 0.2.0-rc.1 对话 DOM 契约(任何一条不成立都只是不生效,不影响其他
|
|
12
|
+
* 功能,也不会误改宿主界面):
|
|
13
|
+
* - 对话根节点带 `data-conversation-session`;
|
|
14
|
+
* - 面板触发器是 `button[aria-haspopup="dialog"]`,展开时 `aria-expanded="true"`;
|
|
15
|
+
* - 面板经 portal 直挂 `document.body`,`role="dialog"`,直接子节点依次是 header、
|
|
16
|
+
* 分段条 div 与可选的明细 dl(`div > dt + dd` 结构)。
|
|
17
|
+
*/
|
|
18
|
+
import { sessionAdapterId, subscribeSessionAdapters } from './session-adapter-cache.js';
|
|
19
|
+
/** 命中会话的上下文面板会被打上该属性,插件样式据此隐藏构成部分。 */
|
|
20
|
+
export const CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE = 'data-codingns-context-breakdown-hidden';
|
|
21
|
+
/** 插件样式标签的幂等键。 */
|
|
22
|
+
export const CONTEXT_BREAKDOWN_STYLE_ID = 'codingns4dsh-context-breakdown-style';
|
|
23
|
+
/** 对话根节点上承载当前会话 id 的 DSH 属性。 */
|
|
24
|
+
export const CONTEXT_BREAKDOWN_CONVERSATION_ATTRIBUTE = 'data-conversation-session';
|
|
25
|
+
/** 默认隐藏构成明细的适配器:它们自己组装请求,启发式估算与真实上下文不同源。 */
|
|
26
|
+
export const DEFAULT_CONTEXT_BREAKDOWN_HIDDEN_ADAPTERS = ['codex'];
|
|
27
|
+
/** 适配器未知时请求刷新映射的最小间隔,避免面板长时间停留时反复查询。 */
|
|
28
|
+
const ADAPTER_REFRESH_INTERVAL_MS = 5_000;
|
|
29
|
+
const CONTEXT_BREAKDOWN_STYLE_TEXT = `[${CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE}]>dl{display:none!important}`
|
|
30
|
+
+ `[${CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE}]>div:nth-of-type(2){display:none!important}`;
|
|
31
|
+
/**
|
|
32
|
+
* 为上下文面板安装构成明细隐藏器。
|
|
33
|
+
*
|
|
34
|
+
* 打开面板时才需要判定所属会话,因此只观察 `body` 的直接子节点(portal 插入),
|
|
35
|
+
* 不跟踪面板内部重渲染;隐藏通过面板属性加插件样式实现,React 复用面板节点时
|
|
36
|
+
* 属性仍然有效。
|
|
37
|
+
*/
|
|
38
|
+
export function startContextBreakdownDom(options = {}) {
|
|
39
|
+
const dom = options.document ?? (typeof document === 'undefined' ? undefined : document);
|
|
40
|
+
const Observer = options.MutationObserver
|
|
41
|
+
?? (typeof MutationObserver === 'undefined' ? undefined : MutationObserver);
|
|
42
|
+
const adapterIdForSession = options.adapterIdForSession ?? sessionAdapterId;
|
|
43
|
+
const hiddenAdapterIds = new Set(options.hiddenAdapterIds ?? DEFAULT_CONTEXT_BREAKDOWN_HIDDEN_ADAPTERS);
|
|
44
|
+
const refreshAdapters = options.refreshAdapters;
|
|
45
|
+
const adapterRefreshIntervalMs = options.adapterRefreshIntervalMs ?? ADAPTER_REFRESH_INTERVAL_MS;
|
|
46
|
+
const now = options.now ?? (() => Date.now());
|
|
47
|
+
let disposed = false;
|
|
48
|
+
let scanQueued = false;
|
|
49
|
+
let lastAdapterRefresh = Number.NEGATIVE_INFINITY;
|
|
50
|
+
const scan = () => {
|
|
51
|
+
if (disposed || dom === undefined)
|
|
52
|
+
return;
|
|
53
|
+
const sessionId = openContextMeterSessionId(dom);
|
|
54
|
+
const adapterId = sessionId === undefined ? undefined : adapterIdForSession(sessionId);
|
|
55
|
+
const hideComposition = adapterId !== undefined && hiddenAdapterIds.has(adapterId);
|
|
56
|
+
for (const panel of contextMeterPanels(dom)) {
|
|
57
|
+
if (hideComposition)
|
|
58
|
+
panel.setAttribute(CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE, '');
|
|
59
|
+
else
|
|
60
|
+
panel.removeAttribute(CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE);
|
|
61
|
+
}
|
|
62
|
+
if (sessionId !== undefined && adapterId === undefined)
|
|
63
|
+
scheduleAdapterRefresh();
|
|
64
|
+
};
|
|
65
|
+
const scheduleScan = () => {
|
|
66
|
+
if (disposed || scanQueued)
|
|
67
|
+
return;
|
|
68
|
+
scanQueued = true;
|
|
69
|
+
queueMicrotask(() => {
|
|
70
|
+
scanQueued = false;
|
|
71
|
+
scan();
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
const scheduleAdapterRefresh = () => {
|
|
75
|
+
if (disposed || refreshAdapters === undefined)
|
|
76
|
+
return;
|
|
77
|
+
const timestamp = now();
|
|
78
|
+
if (timestamp - lastAdapterRefresh < adapterRefreshIntervalMs)
|
|
79
|
+
return;
|
|
80
|
+
lastAdapterRefresh = timestamp;
|
|
81
|
+
void (async () => {
|
|
82
|
+
try {
|
|
83
|
+
await refreshAdapters();
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// 映射刷新失败时保持现状;下一次打开面板仍会按缓存判定。
|
|
87
|
+
}
|
|
88
|
+
if (!disposed)
|
|
89
|
+
scheduleScan();
|
|
90
|
+
})();
|
|
91
|
+
};
|
|
92
|
+
installContextBreakdownStyles(dom);
|
|
93
|
+
const observer = dom === undefined || Observer === undefined ? undefined : new Observer(scheduleScan);
|
|
94
|
+
const observeTarget = dom?.body ?? dom?.documentElement;
|
|
95
|
+
if (observer !== undefined && observeTarget !== undefined && observeTarget !== null) {
|
|
96
|
+
observer.observe(observeTarget, { childList: true });
|
|
97
|
+
}
|
|
98
|
+
const unsubscribe = subscribeSessionAdapters(scheduleScan);
|
|
99
|
+
scheduleScan();
|
|
100
|
+
return {
|
|
101
|
+
refresh: scheduleScan,
|
|
102
|
+
dispose() {
|
|
103
|
+
if (disposed)
|
|
104
|
+
return;
|
|
105
|
+
disposed = true;
|
|
106
|
+
observer?.disconnect();
|
|
107
|
+
unsubscribe();
|
|
108
|
+
if (dom === undefined)
|
|
109
|
+
return;
|
|
110
|
+
for (const panel of contextMeterPanels(dom))
|
|
111
|
+
panel.removeAttribute(CONTEXT_BREAKDOWN_HIDDEN_ATTRIBUTE);
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/** 返回当前展开着上下文面板的会话 id;没有展开的面板时返回 undefined。 */
|
|
116
|
+
function openContextMeterSessionId(dom) {
|
|
117
|
+
for (const conversation of Array.from(dom.querySelectorAll(`[${CONTEXT_BREAKDOWN_CONVERSATION_ATTRIBUTE}]`))) {
|
|
118
|
+
const sessionId = conversation.getAttribute(CONTEXT_BREAKDOWN_CONVERSATION_ATTRIBUTE)?.trim();
|
|
119
|
+
if (sessionId === undefined || sessionId === '')
|
|
120
|
+
continue;
|
|
121
|
+
for (const button of Array.from(conversation.querySelectorAll('button'))) {
|
|
122
|
+
if (button.getAttribute('aria-haspopup') === 'dialog'
|
|
123
|
+
&& button.getAttribute('aria-expanded') === 'true')
|
|
124
|
+
return sessionId;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
/** 找出挂在 body 下、带构成明细的上下文面板。 */
|
|
130
|
+
function contextMeterPanels(dom) {
|
|
131
|
+
const body = dom.body;
|
|
132
|
+
if (body === undefined || body === null)
|
|
133
|
+
return [];
|
|
134
|
+
const panels = [];
|
|
135
|
+
for (const child of Array.from(body.children)) {
|
|
136
|
+
if (child.getAttribute('role') !== 'dialog')
|
|
137
|
+
continue;
|
|
138
|
+
if (!hasCompositionDetails(child))
|
|
139
|
+
continue;
|
|
140
|
+
panels.push(child);
|
|
141
|
+
}
|
|
142
|
+
return panels;
|
|
143
|
+
}
|
|
144
|
+
/** 面板的明细是 `dl > div > dt + dd`;普通 `dl` 不会包一层 div。 */
|
|
145
|
+
function hasCompositionDetails(panel) {
|
|
146
|
+
for (const child of Array.from(panel.children)) {
|
|
147
|
+
if (child.tagName !== 'DL')
|
|
148
|
+
continue;
|
|
149
|
+
for (const row of Array.from(child.children)) {
|
|
150
|
+
if (row.tagName !== 'DIV')
|
|
151
|
+
continue;
|
|
152
|
+
if (row.querySelector('dt') !== null && row.querySelector('dd') !== null)
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
/** 按 quick-phrase 的既有模式幂等注入插件样式。 */
|
|
159
|
+
function installContextBreakdownStyles(dom) {
|
|
160
|
+
if (dom === undefined || dom.head === undefined || dom.head === null)
|
|
161
|
+
return;
|
|
162
|
+
const existing = Array.from(dom.head.children).some((child) => (child.tagName === 'STYLE' && child.dataset.pluginCss === CONTEXT_BREAKDOWN_STYLE_ID));
|
|
163
|
+
if (existing)
|
|
164
|
+
return;
|
|
165
|
+
const style = dom.createElement('style');
|
|
166
|
+
style.dataset.plugin = 'codingns4dsh';
|
|
167
|
+
style.dataset.pluginCss = CONTEXT_BREAKDOWN_STYLE_ID;
|
|
168
|
+
style.textContent = CONTEXT_BREAKDOWN_STYLE_TEXT;
|
|
169
|
+
dom.head.appendChild(style);
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=context-breakdown-dom.js.map
|
|
@@ -203,7 +203,13 @@ export async function startDshH5BrowserBootstrap(options) {
|
|
|
203
203
|
current.onClosed((error) => {
|
|
204
204
|
if (stopped || current !== connection)
|
|
205
205
|
return;
|
|
206
|
-
|
|
206
|
+
const reason = error ?? new Error('WebRTC connection closed');
|
|
207
|
+
debug.log('bootstrap.connection.closed', { generation, error: reason.message });
|
|
208
|
+
// 物理线路断开必须立刻让在途请求失败,而不是等重连流程再收尾:
|
|
209
|
+
// 中继页面上的原生设置 describe 只会一直停在 loading,表现成「模型」页
|
|
210
|
+
// 空白且没有任何报错,会话列表却因为重连成功而看起来正常。
|
|
211
|
+
transport.invalidateConnection(reason);
|
|
212
|
+
session?.close(reason.message);
|
|
207
213
|
reconnectTimer = setTimeout(() => { reconnectTimer = undefined; void reconnect(); }, 50);
|
|
208
214
|
});
|
|
209
215
|
};
|
|
@@ -3,6 +3,8 @@ import { archiveCliSession, callCliRpc, errorMessage, listCliSessions, restoreCl
|
|
|
3
3
|
import { dshFormRootStyle, dshPopupSurfaceStyle, dshSettingsButtonStyle, dshSettingsListRowStyle, dshThemeColor } from '../theme.js';
|
|
4
4
|
import { useCodingNsTranslator } from '../locale.js';
|
|
5
5
|
import { registerExternalToolStreamUi } from '../external-tool-stream.js';
|
|
6
|
+
import { startContextBreakdownDom } from '../context-breakdown-dom.js';
|
|
7
|
+
import { fetchSessionAdapters, replaceSessionAdapters, sessionAdapterId } from '../session-adapter-cache.js';
|
|
6
8
|
/** 外部 Agent 集成模块。Agent 进程在 Host 运行,浏览器只读取目录和状态。 */
|
|
7
9
|
export const cliAdaptersFeature = {
|
|
8
10
|
descriptor: {
|
|
@@ -21,6 +23,15 @@ export const cliAdaptersFeature = {
|
|
|
21
23
|
},
|
|
22
24
|
},
|
|
23
25
|
start: async (context) => {
|
|
26
|
+
// 外部 Agent 的上下文由 Agent 自己组装,上下文面板的启发式构成与真实用量
|
|
27
|
+
// 不同源;对这类适配器的会话隐藏构成明细,只保留百分比与总量。
|
|
28
|
+
const contextBreakdownDom = startContextBreakdownDom({
|
|
29
|
+
adapterIdForSession: sessionAdapterId,
|
|
30
|
+
refreshAdapters: async () => {
|
|
31
|
+
replaceSessionAdapters(await fetchSessionAdapters(context.services.rpc));
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
context.resources.add(() => contextBreakdownDom.dispose());
|
|
24
35
|
const slots = context.services.slots;
|
|
25
36
|
if (slots === undefined)
|
|
26
37
|
return;
|
|
@@ -39,5 +39,6 @@ export { ReverseProxyPanel } from './reverse-proxy-panel.js';
|
|
|
39
39
|
export { CliAdaptersPanel } from './cli-adapters.js';
|
|
40
40
|
export { TerminalEnhancementPanel } from './terminal-enhancement-panel.js';
|
|
41
41
|
export { WorkspaceSessionEnhancementPanel } from './workspace-session-enhancement-panel.js';
|
|
42
|
+
export { SubscriptionUsageSettingsDialog } from './subscription-usage-panel.js';
|
|
42
43
|
export type { CodingNsClientFeatureModule, CodingNsClientServices, CodingNsRpcClient, CodingNsRpcResult, FeaturePanelProps, } from './types.js';
|
|
43
44
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -54,4 +54,5 @@ export { ReverseProxyPanel } from './reverse-proxy-panel.js';
|
|
|
54
54
|
export { CliAdaptersPanel } from './cli-adapters.js';
|
|
55
55
|
export { TerminalEnhancementPanel } from './terminal-enhancement-panel.js';
|
|
56
56
|
export { WorkspaceSessionEnhancementPanel } from './workspace-session-enhancement-panel.js';
|
|
57
|
+
export { SubscriptionUsageSettingsDialog } from './subscription-usage-panel.js';
|
|
57
58
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import type { FeaturePanelProps } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* 用量查询设置对话框:单次查询超时与自动查询间隔。
|
|
5
|
+
*
|
|
6
|
+
* 两项设置由 Host 统一下发给所有适配器读取器;Client 侧所有 Agent 共用同一个
|
|
7
|
+
* 自动刷新定时器与同一份结果缓存,不区分具体适配器。
|
|
8
|
+
*/
|
|
9
|
+
export declare function SubscriptionUsageSettingsDialog({ services, snapshot, notify, onClose }: {
|
|
10
|
+
readonly services: FeaturePanelProps['services'];
|
|
11
|
+
readonly snapshot: FeaturePanelProps['snapshot'];
|
|
12
|
+
readonly notify: FeaturePanelProps['notify'];
|
|
13
|
+
readonly onClose: () => void;
|
|
14
|
+
}): ReactElement;
|
|
15
|
+
//# sourceMappingURL=subscription-usage-panel.d.ts.map
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createElement, useEffect, useState } from 'react';
|
|
2
|
+
import { CODINGNS_SUBSCRIPTION_USAGE_FIELD, SUBSCRIPTION_USAGE_REFRESH_INTERVAL_MINS_LIMITS, SUBSCRIPTION_USAGE_TIMEOUT_SECS_LIMITS, normalizeSubscriptionUsageSettings, } from '../../shared/contracts/config.js';
|
|
3
|
+
import { dshFieldStyle, dshPopupSurfaceStyle, dshSettingsButtonStyle, dshSettingsHelpStyle, dshSettingsListRowStyle, dshSettingsPrimaryButtonStyle, } from '../theme.js';
|
|
4
|
+
import { useCodingNsTranslator } from '../locale.js';
|
|
5
|
+
/**
|
|
6
|
+
* 用量查询设置对话框:单次查询超时与自动查询间隔。
|
|
7
|
+
*
|
|
8
|
+
* 两项设置由 Host 统一下发给所有适配器读取器;Client 侧所有 Agent 共用同一个
|
|
9
|
+
* 自动刷新定时器与同一份结果缓存,不区分具体适配器。
|
|
10
|
+
*/
|
|
11
|
+
export function SubscriptionUsageSettingsDialog({ services, snapshot, notify, onClose }) {
|
|
12
|
+
const t = useCodingNsTranslator(services.locale);
|
|
13
|
+
const value = normalizeSubscriptionUsageSettings(snapshot.value?.subscriptionUsage);
|
|
14
|
+
const disabled = snapshot.status === 'loading' || !snapshot.writable;
|
|
15
|
+
const [timeoutText, setTimeoutText] = useState(() => String(value.timeoutSecs));
|
|
16
|
+
const [intervalText, setIntervalText] = useState(() => String(value.refreshIntervalMins));
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const closeOnEscape = (event) => {
|
|
19
|
+
if (event.key === 'Escape')
|
|
20
|
+
onClose();
|
|
21
|
+
};
|
|
22
|
+
document.addEventListener('keydown', closeOnEscape);
|
|
23
|
+
return () => document.removeEventListener('keydown', closeOnEscape);
|
|
24
|
+
}, [onClose]);
|
|
25
|
+
const save = () => {
|
|
26
|
+
const next = normalizeSubscriptionUsageSettings({
|
|
27
|
+
timeoutSecs: Number(timeoutText),
|
|
28
|
+
refreshIntervalMins: Number(intervalText),
|
|
29
|
+
});
|
|
30
|
+
void services.settings.mutate([
|
|
31
|
+
{ op: 'set', path: [CODINGNS_SUBSCRIPTION_USAGE_FIELD, 'timeoutSecs'], value: next.timeoutSecs },
|
|
32
|
+
{ op: 'set', path: [CODINGNS_SUBSCRIPTION_USAGE_FIELD, 'refreshIntervalMins'], value: next.refreshIntervalMins },
|
|
33
|
+
]).then((accepted) => {
|
|
34
|
+
if (!accepted) {
|
|
35
|
+
notify({ kind: 'error', message: t('settings.moduleWriteRejected') });
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
notify({ kind: 'success', message: t('subscriptionUsage.saved') });
|
|
39
|
+
onClose();
|
|
40
|
+
}).catch((cause) => {
|
|
41
|
+
notify({ kind: 'error', message: cause instanceof Error ? cause.message : String(cause) });
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
return createElement('div', {
|
|
45
|
+
className: 'codingns4dsh-usage-settings-overlay',
|
|
46
|
+
role: 'presentation',
|
|
47
|
+
onPointerDown: onClose,
|
|
48
|
+
style: usageSettingsOverlayStyle,
|
|
49
|
+
}, createElement('div', {
|
|
50
|
+
role: 'dialog',
|
|
51
|
+
'aria-modal': true,
|
|
52
|
+
'aria-label': t('subscriptionUsage.dialogTitle'),
|
|
53
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
54
|
+
style: usageSettingsDialogStyle,
|
|
55
|
+
}, createElement('div', { style: { display: 'grid', gap: 4 } }, createElement('strong', { style: { fontSize: 14, lineHeight: 1.4 } }, t('subscriptionUsage.dialogTitle')), createElement('span', { style: dshSettingsHelpStyle }, t('subscriptionUsage.dialogHint'))), createElement(UsageQueryNumberField, {
|
|
56
|
+
label: t('subscriptionUsage.timeoutSecs'),
|
|
57
|
+
value: timeoutText,
|
|
58
|
+
min: SUBSCRIPTION_USAGE_TIMEOUT_SECS_LIMITS.min,
|
|
59
|
+
max: SUBSCRIPTION_USAGE_TIMEOUT_SECS_LIMITS.max,
|
|
60
|
+
disabled,
|
|
61
|
+
onChange: setTimeoutText,
|
|
62
|
+
}), createElement(UsageQueryNumberField, {
|
|
63
|
+
label: t('subscriptionUsage.refreshInterval'),
|
|
64
|
+
help: t('subscriptionUsage.refreshIntervalHint'),
|
|
65
|
+
value: intervalText,
|
|
66
|
+
min: SUBSCRIPTION_USAGE_REFRESH_INTERVAL_MINS_LIMITS.min,
|
|
67
|
+
max: SUBSCRIPTION_USAGE_REFRESH_INTERVAL_MINS_LIMITS.max,
|
|
68
|
+
disabled,
|
|
69
|
+
onChange: setIntervalText,
|
|
70
|
+
}), createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, paddingTop: 4 } }, createElement('button', {
|
|
71
|
+
type: 'button',
|
|
72
|
+
onClick: onClose,
|
|
73
|
+
style: dshSettingsButtonStyle,
|
|
74
|
+
}, t('subscriptionUsage.cancel')), createElement('button', {
|
|
75
|
+
type: 'button',
|
|
76
|
+
disabled,
|
|
77
|
+
onClick: save,
|
|
78
|
+
style: dshSettingsPrimaryButtonStyle,
|
|
79
|
+
}, t('subscriptionUsage.save')))));
|
|
80
|
+
}
|
|
81
|
+
/** 整数输入行:文本受控,保存时统一夹取到合法区间。 */
|
|
82
|
+
function UsageQueryNumberField({ label, help, value, min, max, disabled, onChange }) {
|
|
83
|
+
return createElement('div', { style: usageQueryNumberRowStyle }, createElement('span', { style: { minWidth: 0 } }, createElement('strong', { style: { display: 'block', fontSize: 13, lineHeight: 1.4 } }, label), help === undefined ? null : createElement('span', { style: { display: 'block', marginTop: 3, ...dshSettingsHelpStyle } }, help)), createElement('input', {
|
|
84
|
+
type: 'number',
|
|
85
|
+
min,
|
|
86
|
+
max,
|
|
87
|
+
step: 1,
|
|
88
|
+
'aria-label': label,
|
|
89
|
+
value,
|
|
90
|
+
disabled,
|
|
91
|
+
onChange: (event) => onChange(event.currentTarget.value),
|
|
92
|
+
style: usageQueryNumberInputStyle,
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
const usageSettingsOverlayStyle = {
|
|
96
|
+
position: 'fixed',
|
|
97
|
+
inset: 0,
|
|
98
|
+
zIndex: 1400,
|
|
99
|
+
display: 'flex',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
justifyContent: 'center',
|
|
102
|
+
padding: 20,
|
|
103
|
+
boxSizing: 'border-box',
|
|
104
|
+
background: 'rgba(0, 0, 0, 0.35)',
|
|
105
|
+
};
|
|
106
|
+
const usageSettingsDialogStyle = {
|
|
107
|
+
...dshPopupSurfaceStyle,
|
|
108
|
+
width: 'min(100%, 420px)',
|
|
109
|
+
boxSizing: 'border-box',
|
|
110
|
+
padding: 16,
|
|
111
|
+
borderRadius: 12,
|
|
112
|
+
display: 'grid',
|
|
113
|
+
gap: 12,
|
|
114
|
+
};
|
|
115
|
+
const usageQueryNumberRowStyle = { ...dshSettingsListRowStyle, justifyContent: 'space-between' };
|
|
116
|
+
const usageQueryNumberInputStyle = { ...dshFieldStyle, flex: '0 0 auto', width: 110, minHeight: 32, boxSizing: 'border-box', padding: '5px 8px', borderRadius: 6, fontSize: 13 };
|
|
117
|
+
//# sourceMappingURL=subscription-usage-panel.js.map
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { createElement } from 'react';
|
|
1
|
+
import { createElement, useState } from 'react';
|
|
2
2
|
import { CODINGNS_WORKSPACE_SESSION_ENHANCEMENT_FIELD, DEFAULT_WORKSPACE_SESSION_ENHANCEMENT_SETTINGS, } from '../../shared/contracts/config.js';
|
|
3
|
-
import {
|
|
3
|
+
import { SubscriptionUsageSettingsDialog } from './subscription-usage-panel.js';
|
|
4
|
+
import { dshFormRootStyle, dshSettingsButtonStyle, dshSettingsHelpStyle, dshSettingsListRowStyle, dshThemeColor, } from '../theme.js';
|
|
4
5
|
import { useCodingNsTranslator } from '../locale.js';
|
|
5
6
|
/** 工作区会话增强的单列设置面板。 */
|
|
6
7
|
export function WorkspaceSessionEnhancementPanel({ services, enabled, snapshot, notify }) {
|
|
7
8
|
const t = useCodingNsTranslator(services.locale);
|
|
9
|
+
const [usageSettingsOpen, setUsageSettingsOpen] = useState(false);
|
|
8
10
|
const value = snapshot.value?.workspaceSessionEnhancement
|
|
9
11
|
?? DEFAULT_WORKSPACE_SESSION_ENHANCEMENT_SETTINGS;
|
|
10
12
|
const disabled = !enabled || snapshot.status === 'loading' || !snapshot.writable;
|
|
@@ -53,8 +55,10 @@ export function WorkspaceSessionEnhancementPanel({ services, enabled, snapshot,
|
|
|
53
55
|
disabled,
|
|
54
56
|
onChange: (event) => updateSetting('showWorkspaceHiding', event.currentTarget.checked),
|
|
55
57
|
style: { flex: '0 0 auto', accentColor: dshThemeColor.accent },
|
|
56
|
-
})), createElement('
|
|
58
|
+
})), createElement('div', {
|
|
57
59
|
style: dshSettingsListRowStyle,
|
|
60
|
+
}, createElement('label', {
|
|
61
|
+
style: { display: 'flex', alignItems: 'center', gap: 12, flex: '1 1 auto', minWidth: 0, cursor: 'pointer' },
|
|
58
62
|
}, createElement('span', { style: { minWidth: 0 } }, createElement('strong', { style: { display: 'block', fontSize: 13, lineHeight: 1.4 } }, t('workspace.showSubscriptionUsage')), createElement('span', { style: { display: 'block', marginTop: 3, ...dshSettingsHelpStyle } }, t('workspace.subscriptionUsageDescription'))), createElement('input', {
|
|
59
63
|
type: 'checkbox',
|
|
60
64
|
role: 'switch',
|
|
@@ -63,7 +67,15 @@ export function WorkspaceSessionEnhancementPanel({ services, enabled, snapshot,
|
|
|
63
67
|
disabled,
|
|
64
68
|
onChange: (event) => updateSetting('showSubscriptionUsage', event.currentTarget.checked),
|
|
65
69
|
style: { flex: '0 0 auto', accentColor: dshThemeColor.accent },
|
|
66
|
-
})), createElement('
|
|
70
|
+
})), createElement('button', {
|
|
71
|
+
type: 'button',
|
|
72
|
+
'aria-haspopup': 'dialog',
|
|
73
|
+
'aria-expanded': usageSettingsOpen,
|
|
74
|
+
'aria-label': t('workspace.subscriptionUsageSettings'),
|
|
75
|
+
disabled,
|
|
76
|
+
onClick: () => setUsageSettingsOpen(true),
|
|
77
|
+
style: dshSettingsButtonStyle,
|
|
78
|
+
}, t('workspace.subscriptionUsageSettings'))), createElement('label', {
|
|
67
79
|
style: dshSettingsListRowStyle,
|
|
68
80
|
}, createElement('span', { style: { minWidth: 0 } }, createElement('strong', { style: { display: 'block', fontSize: 13, lineHeight: 1.4 } }, t('workspace.showArchivedSessions')), createElement('span', { style: { display: 'block', marginTop: 3, ...dshSettingsHelpStyle } }, t('workspace.archivedSessionsDescription'))), createElement('input', {
|
|
69
81
|
type: 'checkbox',
|
|
@@ -93,6 +105,11 @@ export function WorkspaceSessionEnhancementPanel({ services, enabled, snapshot,
|
|
|
93
105
|
disabled,
|
|
94
106
|
onChange: (event) => updateSetting('rememberConversationRightbarRatio', event.currentTarget.checked),
|
|
95
107
|
style: { flex: '0 0 auto', accentColor: dshThemeColor.accent },
|
|
96
|
-
}))
|
|
108
|
+
})), usageSettingsOpen && createElement(SubscriptionUsageSettingsDialog, {
|
|
109
|
+
services,
|
|
110
|
+
snapshot,
|
|
111
|
+
notify,
|
|
112
|
+
onClose: () => setUsageSettingsOpen(false),
|
|
113
|
+
}));
|
|
97
114
|
}
|
|
98
115
|
//# sourceMappingURL=workspace-session-enhancement-panel.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_WORKSPACE_SESSION_ENHANCEMENT_SETTINGS } from '../../shared/contracts/config.js';
|
|
1
|
+
import { DEFAULT_WORKSPACE_SESSION_ENHANCEMENT_SETTINGS, normalizeSubscriptionUsageSettings } from '../../shared/contracts/config.js';
|
|
2
2
|
import { clearSessionAdapters, fetchSessionAdapters, replaceSessionAdapters, } from '../session-adapter-cache.js';
|
|
3
3
|
import { startWorkspaceSessionLogoDom } from '../workspace-session-logo-dom.js';
|
|
4
4
|
import { startWorkspaceSessionArchiveDom } from '../workspace-session-archive-dom.js';
|
|
@@ -35,6 +35,7 @@ export const workspaceSessionEnhancementFeature = {
|
|
|
35
35
|
let adapterRefreshTimer;
|
|
36
36
|
let disposeSubscription;
|
|
37
37
|
let disposeQuickPhrases;
|
|
38
|
+
let lastSubscriptionUsageSignature = null;
|
|
38
39
|
const disableLogo = () => {
|
|
39
40
|
generation += 1;
|
|
40
41
|
logoDom?.dispose();
|
|
@@ -81,7 +82,8 @@ export const workspaceSessionEnhancementFeature = {
|
|
|
81
82
|
const enableSubscription = () => {
|
|
82
83
|
if (disposeSubscription !== undefined || context.services.slots === undefined)
|
|
83
84
|
return;
|
|
84
|
-
|
|
85
|
+
// 查询间隔对所有适配器统一生效:slot 内部的自动刷新定时器读取同一份设置。
|
|
86
|
+
disposeSubscription = registerSubscriptionSlot(context.services.slots, context.services.rpc, () => (normalizeSubscriptionUsageSettings(context.services.settings.getSnapshot().value?.subscriptionUsage).refreshIntervalMins));
|
|
85
87
|
};
|
|
86
88
|
const disableSubscription = () => {
|
|
87
89
|
disposeSubscription?.();
|
|
@@ -168,6 +170,19 @@ export const workspaceSessionEnhancementFeature = {
|
|
|
168
170
|
enableRightbarMemory();
|
|
169
171
|
else
|
|
170
172
|
disableRightbarMemory();
|
|
173
|
+
// 用量查询间隔变更后立即重挂 slot,让新间隔马上生效,而不是等下一次开关切换。
|
|
174
|
+
const subscriptionUsageSignature = JSON.stringify(context.services.settings.getSnapshot().value?.subscriptionUsage ?? null);
|
|
175
|
+
if (lastSubscriptionUsageSignature === null) {
|
|
176
|
+
lastSubscriptionUsageSignature = subscriptionUsageSignature;
|
|
177
|
+
}
|
|
178
|
+
else if (subscriptionUsageSignature !== lastSubscriptionUsageSignature) {
|
|
179
|
+
lastSubscriptionUsageSignature = subscriptionUsageSignature;
|
|
180
|
+
if (disposeSubscription !== undefined) {
|
|
181
|
+
disableSubscription();
|
|
182
|
+
if (showSubscriptionUsage)
|
|
183
|
+
enableSubscription();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
171
186
|
};
|
|
172
187
|
sync();
|
|
173
188
|
const unsubscribe = context.services.settings.subscribe(sync);
|