dsh-code-server-app 0.3.38 → 0.3.40
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.
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
| react | 18.3.1 | MIT |
|
|
12
12
|
| react-dom | 18.3.1 | MIT |
|
|
13
13
|
|
|
14
|
-
生成时间:2026-09-
|
|
14
|
+
生成时间:2026-09-13T13:01:42.741Z
|
|
15
15
|
渲染器:@deepseek-ai/dsh-client-ui-primitives@0.1.5-rc.2(DSH 部署的界面版本:0.1.5-rc.2)
|
|
16
16
|
设计令牌:@deepseek-ai/dsh-client-ui-theme@0.1.5-rc.2(11 段)
|
|
17
17
|
KaTeX:已打包(公式排版与 DSH 界面一致)
|
package/lib/bridge-approval.mjs
CHANGED
|
@@ -120,6 +120,9 @@ export function createApprovalInterceptor(deps) {
|
|
|
120
120
|
const holdMs = deps.holdMs ?? DEFAULT_HOLD_MS;
|
|
121
121
|
const pollMs = deps.pollMs ?? WATCH_POLL_MS;
|
|
122
122
|
const hasPanel = deps.hasPanel ?? (() => true);
|
|
123
|
+
/** 本地生成的请求 id 序号(审批事件本身没有 id;见 handle 里的注释)。 */
|
|
124
|
+
let localSeq = 0;
|
|
125
|
+
|
|
123
126
|
/** agent → disposer(每个被 watch 的 agent 一个监听器) */
|
|
124
127
|
const registered = new Map();
|
|
125
128
|
|
|
@@ -166,8 +169,13 @@ export function createApprovalInterceptor(deps) {
|
|
|
166
169
|
log('没人看会话(hasPanel=false):不拦截,直接交给官方链路');
|
|
167
170
|
return await next();
|
|
168
171
|
}
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
// **审批事件没有 `id` 字段**(0.3.39 的实测:日志里 `收到授权请求:pwsh id=undefined`)。
|
|
173
|
+
// 事件形状是 `{agent, toolName, callId?, reason?, signal?}` —— 我们原来要求 id 是字符串,
|
|
174
|
+
// 否则**静默 next()** 交回官方卡片:这就是"面板里的卡片从来不出现"的最后一块拼图。
|
|
175
|
+
// id 只是我们自己用来把"面板上的那张卡"和"这次请求"对上号,所以本地生成即可;
|
|
176
|
+
// `board.answer(id, outcome)` 结算的是我们自己的 decision promise,DSH 侧不需要知道它。
|
|
177
|
+
const hasId = typeof request?.id === 'string' && request.id !== '';
|
|
178
|
+
const id = hasId ? request.id : 'local-' + String(++localSeq);
|
|
171
179
|
const decision = board.open({
|
|
172
180
|
id,
|
|
173
181
|
toolName: typeof request.toolName === 'string' ? request.toolName : 'tool',
|
package/lib/index.js
CHANGED
|
@@ -756,8 +756,8 @@ export async function apply(ctx, config) {
|
|
|
756
756
|
/** 上次记录过的"有没有人在看"(0.3.30:状态翻转才写诊断日志)。 */
|
|
757
757
|
let bridgeWatchLogged = false;
|
|
758
758
|
/** 首帧是否已写过诊断(0.3.31:"一直没人看"也要留下证据)。 */
|
|
759
|
-
let bridgeWatchWroteFirst = false;
|
|
760
|
-
/** 上次写过的 hasPanel 判据原文(变了才写,避免每 600ms 刷屏)。 */
|
|
759
|
+
let bridgeWatchWroteFirst = false;
|
|
760
|
+
/** 上次写过的 hasPanel 判据原文(变了才写,避免每 600ms 刷屏)。 */
|
|
761
761
|
let bridgeHasPanelLine = '';
|
|
762
762
|
/** 客户端(self-declared)是否能把授权卡片画出来(0.3.30)。 */
|
|
763
763
|
let bridgeApprovalsUi = false;
|
|
@@ -810,16 +810,16 @@ export async function apply(ctx, config) {
|
|
|
810
810
|
// 客户端能画卡片 = 编辑器面板自报可以的 **或** 对话框开着且有会话(0.3.33)。
|
|
811
811
|
// "活着"由 /ask/state 轮询证明)。0.3.31 的日志给出了反例:用户从**对话框**提问 ⇒
|
|
812
812
|
// 编辑器面板是 null ⇒ 旧判据误判成"画不出卡片",授权被让给了 DSH 界面。
|
|
813
|
-
hasPanel: () => {
|
|
814
|
-
const watcher = hasWatcher();
|
|
815
|
-
const dialogCapable = askDialogLive();
|
|
816
|
-
const verdict = watcher && (bridgeApprovalsUi || dialogCapable);
|
|
817
|
-
// 判据的**原始值**一起落日志(0.3.35):只记结论的话,出问题还是只能猜。
|
|
818
|
-
const line = 'hasPanel -> ' + verdict + ' (hasWatcher=' + watcher + ' approvalsUi=' + bridgeApprovalsUi
|
|
819
|
-
+ ' dialogLive=' + dialogCapable + ' polls=' + askDialog.polls + ' open=' + askDialog.open
|
|
820
|
-
+ ' session=' + String(askDialog.sessionId) + ')';
|
|
821
|
-
if (line !== bridgeHasPanelLine) { bridgeHasPanelLine = line; bridgeDiag(line); }
|
|
822
|
-
return verdict;
|
|
813
|
+
hasPanel: () => {
|
|
814
|
+
const watcher = hasWatcher();
|
|
815
|
+
const dialogCapable = askDialogLive();
|
|
816
|
+
const verdict = watcher && (bridgeApprovalsUi || dialogCapable);
|
|
817
|
+
// 判据的**原始值**一起落日志(0.3.35):只记结论的话,出问题还是只能猜。
|
|
818
|
+
const line = 'hasPanel -> ' + verdict + ' (hasWatcher=' + watcher + ' approvalsUi=' + bridgeApprovalsUi
|
|
819
|
+
+ ' dialogLive=' + dialogCapable + ' polls=' + askDialog.polls + ' open=' + askDialog.open
|
|
820
|
+
+ ' session=' + String(askDialog.sessionId) + ')';
|
|
821
|
+
if (line !== bridgeHasPanelLine) { bridgeHasPanelLine = line; bridgeDiag(line); }
|
|
822
|
+
return verdict;
|
|
823
823
|
},
|
|
824
824
|
log: (message) => {
|
|
825
825
|
console.warn(`[code-server] 编辑器桥授权:${message}`);
|
|
@@ -836,7 +836,7 @@ export async function apply(ctx, config) {
|
|
|
836
836
|
* - 状态与动作的 HTTP 面(`/api/code-server/ask/*`,与 DSH 同源、吃它的 cookie/Origin 校验);
|
|
837
837
|
* - 把面板产物(thread.js / thread.css)交给对话框注入(Lib 版本与 DSH 界面同版本,见 build-webview.mjs)。
|
|
838
838
|
*/
|
|
839
|
-
const askDialog = { open: false, mode: 'selection', at: 0, sessionId: null, polls: 0, lastPollAt: 0 };
|
|
839
|
+
const askDialog = { open: false, armed: false, mode: 'selection', at: 0, sessionId: null, polls: 0, lastPollAt: 0 };
|
|
840
840
|
|
|
841
841
|
/**
|
|
842
842
|
* 对话框"活着吗" —— **能力位必须以此为准**(0.3.25)。
|
|
@@ -852,7 +852,12 @@ export async function apply(ctx, config) {
|
|
|
852
852
|
// TTL 取 10 分钟:浏览器后台节流会把 900ms 拖到分钟级,短 TTL 会让"人就在编辑器旁边"
|
|
853
853
|
// 被误判成"没人看"(实测连续踩了两轮)。
|
|
854
854
|
const TTL_MS = 10 * 60 * 1000;
|
|
855
|
-
|
|
855
|
+
// **必须在"武装"状态下**才算有能力显示卡片(0.3.40):
|
|
856
|
+
// 只认"最近轮询过"会让**已经关掉的对话框**在 10 分钟内继续被当成能显示卡片的 UI ——
|
|
857
|
+
// 那时我们仍会接住请求,而卡片无处显示(就是用户说的"没弹出授权")。
|
|
858
|
+
// armed 在 /ask/state 轮询时置位,在 /ask/close 时立刻清除 ⇒ 关掉之后下一次判据复查(≤500ms)
|
|
859
|
+
// 就会把正在等待的请求交回官方卡片。
|
|
860
|
+
if (askDialog.armed === true && askDialog.polls > 0 && Date.now() - askDialog.lastPollAt < TTL_MS) return true;
|
|
856
861
|
// 心跳还没建立(刚重启)时:对话框开着且有会话也算。
|
|
857
862
|
return askDialog.open && askDialog.sessionId !== null;
|
|
858
863
|
}
|
|
@@ -2084,10 +2089,12 @@ export async function apply(ctx, config) {
|
|
|
2084
2089
|
async function handleAskState(request) {
|
|
2085
2090
|
// 这一条就是"客户端半部活着"的证据(能力位 askDialog 以它为准,见 askDialogLive)。
|
|
2086
2091
|
askDialog.polls += 1;
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2092
|
+
// 有轮询 = 对话框活着 ⇒ 武装(关掉时 /ask/close 会立刻解除)。
|
|
2093
|
+
askDialog.armed = true;
|
|
2094
|
+
askDialog.lastPollAt = Date.now();
|
|
2095
|
+
// 心跳证据(0.3.35):首次 + 每 40 次写一行,证明"对话框在持续发活跃通知"。
|
|
2096
|
+
if (askDialog.polls % 40 === 1) {
|
|
2097
|
+
bridgeDiag('dialog heartbeat #' + askDialog.polls + ' (open=' + askDialog.open + ' session=' + String(askDialog.sessionId) + ')');
|
|
2091
2098
|
}
|
|
2092
2099
|
if (askDialog.polls === 1) {
|
|
2093
2100
|
console.log('[code-server] 「问 DSH」对话框已连上客户端半部:/ask/state 开始轮询');
|
|
@@ -2156,6 +2163,9 @@ export async function apply(ctx, config) {
|
|
|
2156
2163
|
/** 对话框关掉:不再看会话 ⇒ 待决授权按"没人看"交回官方链路(拦截器的轮询会立刻放行)。 */
|
|
2157
2164
|
async function handleAskClose() {
|
|
2158
2165
|
askDialog.open = false;
|
|
2166
|
+
// **关闭即解除武装**(0.3.40):否则 10 分钟 TTL 内我们还会继续拦截,而卡片已经没地方显示。
|
|
2167
|
+
askDialog.armed = false;
|
|
2168
|
+
askDialog.lastPollAt = 0;
|
|
2159
2169
|
askDialog.sessionId = null;
|
|
2160
2170
|
refreshDialogWatch();
|
|
2161
2171
|
return jsonResponse({ ok: true });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-code-server-app",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.40",
|
|
4
4
|
"description": "VS Code (from a code-server release) inside DSH: a right-sidebar tab driven by the plugin's own launcher over the in-process VS Code server (lib/launcher.mjs). Since 0.3.0 the bundle also ships an editor bridge (assets/extensions/dshcs-editor-bridge): a read-only channel between the in-tree VS Code extension host and DSH, giving the agent what only the editor knows (unsaved buffers, language-server diagnostics, the active selection) and letting editor gestures drive the session. Since 0.3.22 the ask panel renders that session's new content with DSH's own Markdown renderer (pinned to the deployed DSH UI version); since 0.3.23 it is a full-width dialog with collapsible thinking rows and can answer approval requests in place — the bridge's only non-read-only route (POST /approve, constrained to pending in-process requests and the outcomes allowed-once/rejected; panel-first for 5 minutes, handed back to the official card the moment the panel closes). The tab claims DSH file addresses (dsh-resource://file/**) by file type (setting claimExtensions), so the product's own produced-file chips, delivered-file previews and inline prose mentions open in the workbench. The IDE is a resident surface moved with Element.moveBefore instead of being remounted, so switching sidebar tabs no longer reloads it. Opening the tab switches the right sidebar to fullscreen by default (setting fullscreenOnOpen). Following a workspace switch is lightweight: the workbench re-navigates with the new ?folder= and the IDE process is not restarted (since 0.2.12). Requires a DSH with the right-sidebar services (sidebarRightTabs/sidebarRight, >= 0.1.5-alpha.1); older DSH versions get a single upgrade notice on the settings page and no other UI. Two serving modes: loopback port (default) or same-origin mount on DSH's own webServer (/code-server, protected by ctx.connection.requestRejection). No code-server Node layer, no argon2, no C++ toolchain.",
|
|
5
5
|
"homepage": "https://github.com/jinsiyu/dsh-code-server-app",
|
|
6
6
|
"repository": {
|
package/vendor/VENDOR.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"vscodeVersion": "1.137.0",
|
|
4
4
|
"productPath": "stable-b11dabdaca0d3369986975be285db92c8795cea5",
|
|
5
5
|
"layout": "vscode-only",
|
|
6
|
-
"preparedAt": "2026-09-
|
|
6
|
+
"preparedAt": "2026-09-13T13:01:41.734Z",
|
|
7
7
|
"source": "registry",
|
|
8
8
|
"node": "v24.21.0",
|
|
9
9
|
"platform": "win32",
|