@toddzheng024/dscode-desktop 0.7.33 → 0.7.35
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/README.md +8 -7
- package/cordis.patch.yml +5 -4
- package/docs/CHANGELOG.md +18 -1
- package/docs/browser-use.md +38 -21
- package/docs/custom-providers.md +3 -3
- package/docs/demo.md +77 -11
- package/docs/hub-distribution.md +33 -6
- package/docs/memory.md +1 -1
- package/docs/plugin-hub.md +26 -59
- package/docs/releases/0.7.33.md +12 -5
- package/docs/releases/0.7.34.md +24 -0
- package/docs/releases/0.7.35.md +31 -0
- package/docs/triggers.md +1 -1
- package/locale/en.json +1 -1
- package/locale/zh-cn.json +1 -1
- package/package.json +3 -8
- package/plugins/auto-review/index.mjs +2 -2
- package/plugins/browser/config.mjs +2 -2
- package/plugins/browser/desktop-client.mjs +22 -9
- package/plugins/browser/desktop-host.mjs +5 -0
- package/plugins/code-review/baseline.mjs +2 -2
- package/plugins/compaction/threshold.mjs +2 -1
- package/plugins/credentials/index.mjs +11 -0
- package/plugins/custom/desktop-client.mjs +12 -9
- package/plugins/desktop/client.mjs +135 -161
- package/plugins/desktop/desktop-client.mjs +50 -0
- package/plugins/desktop/index.mjs +1 -0
- package/plugins/desktop/settings.css +36 -0
- package/plugins/desktop-workspace/index.mjs +2 -2
- package/plugins/dscode/desktop-client.mjs +3 -3
- package/plugins/dscode/index.mjs +2 -2
- package/plugins/email/desktop-client.mjs +5 -5
- package/plugins/email/desktop-host.mjs +2 -2
- package/plugins/grok/billing.mjs +2 -1
- package/plugins/grok/index.mjs +2 -1
- package/plugins/grok/status.mjs +2 -1
- package/plugins/jev/index.mjs +2 -1
- package/plugins/memory/index.mjs +2 -2
- package/plugins/opencode-go/index.mjs +2 -1
- package/plugins/opencode-go/usage.mjs +2 -1
- package/plugins/openrouter/index.mjs +2 -1
- package/plugins/providers/desktop-client.mjs +22 -15
- package/plugins/session-bridge/index.mjs +2 -3
- package/plugins/session-cards/index.mjs +2 -2
- package/plugins/session-metrics/desktop-client.mjs +3 -3
- package/plugins/session-metrics/index.mjs +2 -1
- package/plugins/session-metrics/view.mjs +4 -3
- package/plugins/triggers/desktop-client.mjs +15 -9
- package/plugins/triggers/desktop-host.mjs +2 -2
- package/plugins/tui-tools/doctor.mjs +2 -1
- package/plugins/tui-tools/index.mjs +2 -1
- package/plugins/hub/catalog.mjs +0 -82
- package/plugins/hub/desktop-client.mjs +0 -114
- package/plugins/hub/desktop-host.mjs +0 -129
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# DSCODE 0.7.35
|
|
2
|
+
|
|
3
|
+
This release separates DSCODE from Hub, confines its Desktop browser integration to DSCODE sessions, and aligns setup and settings with Harness Desktop.
|
|
4
|
+
|
|
5
|
+
- **Desktop browser boundaries:** browser tools, the browser skill and `/browser` are mounted in DSCODE sessions. Standard sessions retain their own tools; DSCODE preview refuses to start a browser from another preset. TUI Browser Use keeps its existing lazy Chrome integration. Shared Desktop credential and subagent compatibility services remain installed.
|
|
6
|
+
- **Hub separation:** the Desktop package no longer contains Hub UI, discovery tools, endpoint, source files or Hub-specific dependencies. DSCODE remains discoverable through the Hub catalog, and the terminal's Hub distribution continues. This release does not provide a separate Hub Desktop plugin. Older 0.7.33/0.7.34 packages retain their published contents.
|
|
7
|
+
- **Setup and interface:** Settings → DSCODE groups Get started, Models, Accounts and Schedules as peer entries, using Harness theme tokens and compact controls. Plugins → DSCODE · Community opens the same page. Activation guidance explains a full application restart, Show coding view, Agent presets and creating a DSCODE session. Existing sessions keep their preset; host-owned list icons remain unchanged.
|
|
8
|
+
- **Existing accounts:** use the normal model picker for models already configured in Desktop. Native credentials take precedence and new Desktop keys are saved to the native store. Legacy terminal keys remain a fallback; explicit removal clears both copies to prevent reactivation and warns before confirmation. External account sign-in flows keep their existing behavior.
|
|
9
|
+
- **Default distribution:** after an exact public installation passes, both npm `preview` and `latest` point to this Desktop version. This replaces the stale default 0.7.33 entry while retaining the preview support boundary. Publication requires the public-install receipt to match the archive.
|
|
10
|
+
|
|
11
|
+
Update in official **Plugins → Add plugin** with `@toddzheng024/dscode-desktop@0.7.35`, then fully quit and reopen the application. Enable **Show coding view** in General and **Agent presets** in Plugins if needed; create a new session and select **DSCODE**. A window close alone does not restart the Host.
|
|
12
|
+
|
|
13
|
+
Verification before release: `npm run check` passed 1,079 unit/component cases,
|
|
14
|
+
both TypeScript checks, coverage, integration/package checks and 63 eval cases.
|
|
15
|
+
Real Chrome checks passed for the TUI and independent Harness 0.2.0-rc.2
|
|
16
|
+
Desktop Host, including screenshot/annotation transport, permissions,
|
|
17
|
+
Standard/DSCODE isolation and repeated session creation before/after restart.
|
|
18
|
+
The signed macOS application passed installation, upgrade from public 0.7.34,
|
|
19
|
+
incompatible-update rejection, removal/reinstallation and data preservation.
|
|
20
|
+
Light/dark setup, models, accounts, schedule empty state and the Plugins entry
|
|
21
|
+
were inspected in the official shared client attached to an isolated Desktop
|
|
22
|
+
Host. The final release workflow requalifies the exact versioned archives.
|
|
23
|
+
|
|
24
|
+
The first local full run included a scheduler cleanup timeout; its isolated
|
|
25
|
+
suite and the full rerun passed without scheduler code changes. The user's
|
|
26
|
+
intermittent inactive-context failure was not reproduced, and hot reload is
|
|
27
|
+
not qualified. Full restart remains required. Live-model task quality,
|
|
28
|
+
external account login, individual mail/usage/delegation panel visual rehearsal,
|
|
29
|
+
macOS Computer Use Accessibility qualification and other platforms/runtimes
|
|
30
|
+
remain unverified by this release. The Desktop target is macOS Apple Silicon,
|
|
31
|
+
Harness runtime 0.2.0-rc.2; no standalone DMG is included.
|
package/docs/triggers.md
CHANGED
|
@@ -7,7 +7,7 @@ A trigger is not a way to reach a session that is already open: that is [session
|
|
|
7
7
|
## Experimental Desktop scheduling
|
|
8
8
|
|
|
9
9
|
The [combined Desktop package](browser-use.md#install-the-experimental-combined-desktop-package)
|
|
10
|
-
adds **Settings → DSCODE
|
|
10
|
+
adds **Settings → DSCODE → Schedules**. Open a DSCODE session in the target project,
|
|
11
11
|
then select that session in the settings page. The form creates or edits an
|
|
12
12
|
interval, calendar, external-event or script task. New form entries default to
|
|
13
13
|
read-only execution and a persistent task-owned session. You can pause a task,
|
package/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "DSCODE",
|
|
4
|
-
"description": "Coding collaboration, session memory
|
|
4
|
+
"description": "Coding collaboration, session memory and browser workflows for Harness Desktop."
|
|
5
5
|
}
|
|
6
6
|
}
|
package/locale/zh-cn.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toddzheng024/dscode-desktop",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.35",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "DSCODE for Harness Desktop: coding collaboration, session memory, browser workflows and task management.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,7 +46,6 @@
|
|
|
46
46
|
"./workspace": "./plugins/desktop-workspace/index.mjs",
|
|
47
47
|
"./credentials": "./plugins/credentials/index.mjs",
|
|
48
48
|
"./custom": "./plugins/custom/index.mjs",
|
|
49
|
-
"./hub": "./plugins/hub/desktop-host.mjs",
|
|
50
49
|
"./openrouter": "./plugins/openrouter/index.mjs",
|
|
51
50
|
"./grok": "./plugins/grok/index.mjs",
|
|
52
51
|
"./opencode-go": "./plugins/opencode-go/index.mjs",
|
|
@@ -92,8 +91,6 @@
|
|
|
92
91
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
93
92
|
"chrome-devtools-mcp": "1.10.1",
|
|
94
93
|
"ws": "8.21.3",
|
|
95
|
-
"@dsh-plugin-hub/schemas": "0.5.0",
|
|
96
|
-
"semver": "7.8.5",
|
|
97
94
|
"imapflow": "2.0.5",
|
|
98
95
|
"mailparser": "3.9.28",
|
|
99
96
|
"nodemailer": "10.0.10",
|
|
@@ -112,7 +109,7 @@
|
|
|
112
109
|
"hub": {
|
|
113
110
|
"schemaVersion": 1,
|
|
114
111
|
"displayName": "DSCODE Desktop",
|
|
115
|
-
"summary": "Coding collaboration, memory, browser workflows and
|
|
112
|
+
"summary": "Coding collaboration, memory, browser workflows and task management for Harness Desktop.",
|
|
116
113
|
"description": "Adds DSCODE to the official Harness Desktop application. Requires the exact DSH runtime 0.2.0-rc.2. Qualified on macOS Apple Silicon. Install the plugin, select the DSCODE agent preset and configure a model account. This package does not include a standalone application or the terminal UI.",
|
|
117
114
|
"homepage": "https://github.com/qiz029/dscode#readme",
|
|
118
115
|
"categories": [
|
|
@@ -123,8 +120,7 @@
|
|
|
123
120
|
"coding",
|
|
124
121
|
"desktop",
|
|
125
122
|
"browser",
|
|
126
|
-
"collaboration"
|
|
127
|
-
"plugin-hub"
|
|
123
|
+
"collaboration"
|
|
128
124
|
],
|
|
129
125
|
"compatibility": {
|
|
130
126
|
"dsh": "0.2.0-rc.2",
|
|
@@ -139,7 +135,6 @@
|
|
|
139
135
|
},
|
|
140
136
|
"entryIds": [
|
|
141
137
|
"dscode-desktop",
|
|
142
|
-
"dscode-hub",
|
|
143
138
|
"dscode-browser"
|
|
144
139
|
],
|
|
145
140
|
"before": [],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
|
|
1
2
|
import z from '@deepseek-ai/schemastery';
|
|
2
3
|
import { BlockAssembler, createSystemMessage, createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
3
4
|
import { REVIEW_POLICY, escalationDiagnosticGrant, needsMcpApproval, redact, fingerprint, contextFor, parseDecision } from './policy.mjs';
|
|
@@ -22,8 +23,7 @@ export const Config = z.object({
|
|
|
22
23
|
|
|
23
24
|
export function apply(ctx, config) {
|
|
24
25
|
if (Boolean(config.provider) !== Boolean(config.model)) throw new Error('Auto review requires both provider and model, or neither');
|
|
25
|
-
|
|
26
|
-
const audit = auditStore(config.auditDirectory ?? join(process.env.DSH_HOME, 'auto-review'));
|
|
26
|
+
const audit = auditStore(config.auditDirectory ?? join(resolveDshHome(), 'auto-review'));
|
|
27
27
|
const calls = new WeakMap();
|
|
28
28
|
const budgets = new WeakMap();
|
|
29
29
|
const mode = agent => ctx.permissionPresets.current(agent.session);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
|
|
1
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
3
|
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
4
|
import path, { join } from 'node:path';
|
|
5
5
|
|
|
6
|
-
export const browserHome = () => join(
|
|
6
|
+
export const browserHome = () => join(resolveDshHome(), 'browser');
|
|
7
7
|
export const DEFAULT_CONFIG = Object.freeze({ mode: 'persistent', headless: false });
|
|
8
8
|
|
|
9
9
|
export function validateConfig(input, paths = path) {
|
|
@@ -4,7 +4,7 @@ globalThis.__ModuleLoader__.load({
|
|
|
4
4
|
factory: require => {
|
|
5
5
|
const { createElement: h, useState, useEffect, useRef } = require('react');
|
|
6
6
|
const id = '@toddzheng024/dscode-browser-desktop';
|
|
7
|
-
const button = { padding: '7px 11px', border: '
|
|
7
|
+
const button = { padding: '7px 11px', border: '.5px solid var(--dsw-alias-border-l3, #8886)', borderRadius: 'var(--dsw-radius-md, 6px)', background: 'transparent', color: 'inherit', cursor: 'pointer', font: 'inherit' };
|
|
8
8
|
function previewButton(props, label) {
|
|
9
9
|
return h('button', { ...props, style: { ...button, ...props.style, ...(props.disabled ? { opacity: 0.5, cursor: 'not-allowed' } : {}) } }, label);
|
|
10
10
|
}
|
|
@@ -197,9 +197,9 @@ globalThis.__ModuleLoader__.load({
|
|
|
197
197
|
setNotice('Browser control resumed. Your draft is kept. Capture a new preview before sending.');
|
|
198
198
|
await list(current, { preserveDraft: true });
|
|
199
199
|
});
|
|
200
|
-
return h('section', { 'aria-label': 'DSCODE browser preview', style: { padding: 12, boxSizing: 'border-box', overflow: 'auto', height: '100%', display: 'flex', flexDirection: 'column', gap: 8, color: 'inherit' } },
|
|
200
|
+
return h('section', { className: 'dscode-ui', 'aria-label': 'DSCODE browser preview', style: { padding: 12, boxSizing: 'border-box', overflow: 'auto', height: '100%', display: 'flex', flexDirection: 'column', gap: 8, color: 'inherit' } },
|
|
201
201
|
h('strong', null, 'Browser preview'),
|
|
202
|
-
handoff && h('div', { 'aria-label': 'Browser handoff', role: 'status', style: { border: '
|
|
202
|
+
handoff && h('div', { 'aria-label': 'Browser handoff', role: 'status', style: { border: '.5px solid var(--dsw-alias-border-l3, #8886)', borderRadius: 8, padding: 10, overflowWrap: 'anywhere' } },
|
|
203
203
|
h('strong', null, 'Waiting for you'),
|
|
204
204
|
h('p', null, handoff.reason),
|
|
205
205
|
handoff.invalidated && h('p', null, 'Chrome restarted. The original tab is unavailable; choose a live tab after resuming.'),
|
|
@@ -213,7 +213,7 @@ globalThis.__ModuleLoader__.load({
|
|
|
213
213
|
previewButton({ style: button, disabled: busy || controlBusy || !pageId || !!handoff, onClick: () => run(refresh) }, 'Refresh preview'),
|
|
214
214
|
frame && previewButton({ style: button, onClick: () => setExpanded(!expanded) }, expanded ? 'Fit image' : 'Expand image')),
|
|
215
215
|
h('select', { 'aria-label': 'Browser tab', value: pageId, disabled: busy || controlBusy, onChange: e => { setPageId(e.target.value); setFrame(null); setPoint(null); setText(''); setLive(false); }, style: { ...button, width: '100%' } }, h('option', { value: '' }, 'Choose a tab'), ...pages.map(page => h('option', { key: page.id, value: String(page.id) }, `${page.id} · ${page.url}`))),
|
|
216
|
-
origin && h('details', { 'aria-label': 'Browser site permissions', style: { border: '
|
|
216
|
+
origin && h('details', { 'aria-label': 'Browser site permissions', style: { border: '.5px solid var(--dsw-alias-border-l3, #8886)', borderRadius: 8, padding: '8px 10px', margin: 0, minWidth: 0 } },
|
|
217
217
|
h('summary', { 'aria-label': `Site permissions · ${access}`, style: { cursor: 'pointer' } }, 'Site permissions · ', h('span', { 'aria-label': 'Site access status' }, access)),
|
|
218
218
|
h('div', { style: { fontSize: 12, overflowWrap: 'anywhere', margin: '8px 0' } }, origin),
|
|
219
219
|
h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 6 } },
|
|
@@ -232,7 +232,7 @@ globalThis.__ModuleLoader__.load({
|
|
|
232
232
|
frame && h('div', null,
|
|
233
233
|
h('div', { style: { fontSize: 12, overflowWrap: 'anywhere', marginBottom: 8 } }, `${frame.url} · ${new Date(frame.capturedAt).toLocaleTimeString()}`),
|
|
234
234
|
h('div', { style: { position: 'relative', lineHeight: 0, display: 'inline-block', maxWidth: '100%', ...(expanded ? { width: '100%' } : {}) } },
|
|
235
|
-
h('img', { src: `data:${frame.image.mimeType};base64,${frame.image.data}`, alt: expired ? 'Expired browser preview; refresh before annotating' : 'Captured browser page; click a point to annotate', role: 'button', tabIndex: 0, 'aria-disabled': busy || controlBusy || expired, 'aria-description': 'Enter or Space selects the center. Arrow keys move one pixel; Shift moves ten. Escape clears the point.', onClick: selectPoint, onKeyDown: selectPointKey, style: { maxWidth: '100%', maxHeight: expanded ? 'none' : '30vh', width: expanded ? '100%' : 'auto', height: 'auto', display: 'block', cursor: expired ? 'default' : 'crosshair', borderRadius:
|
|
235
|
+
h('img', { src: `data:${frame.image.mimeType};base64,${frame.image.data}`, alt: expired ? 'Expired browser preview; refresh before annotating' : 'Captured browser page; click a point to annotate', role: 'button', tabIndex: 0, 'aria-disabled': busy || controlBusy || expired, 'aria-description': 'Enter or Space selects the center. Arrow keys move one pixel; Shift moves ten. Escape clears the point.', onClick: selectPoint, onKeyDown: selectPointKey, style: { maxWidth: '100%', maxHeight: expanded ? 'none' : '30vh', width: expanded ? '100%' : 'auto', height: 'auto', display: 'block', cursor: expired ? 'default' : 'crosshair', borderRadius: 'var(--dsw-radius-md, 6px)' } }),
|
|
236
236
|
point && h('span', { 'aria-label': 'Selected annotation point', style: { position: 'absolute', left: `${point.x * 100}%`, top: `${point.y * 100}%`, width: 20, height: 20, border: '3px solid #f97316', borderRadius: '50%', transform: 'translate(-50%, -50%)', pointerEvents: 'none', boxShadow: '0 0 0 2px white' } }))),
|
|
237
237
|
h('p', { style: { fontSize: 12, margin: 0, opacity: 0.75 } }, 'Preview shows the controlled Chrome tab. Click a point, or focus the image and press Enter or Space to select the center. Arrow keys move one pixel; Shift moves ten. Escape clears the point. Refresh after navigation or after one minute.'),
|
|
238
238
|
frame && point && h('p', { 'aria-label': 'Annotation coordinates', 'aria-live': 'polite', style: { fontSize: 12, margin: 0 } }, `Selected pixel: (${Math.round(point.x * (frame.width - 1))}, ${Math.round(point.y * (frame.height - 1))}) in ${frame.width}×${frame.height}.`),
|
|
@@ -259,18 +259,31 @@ globalThis.__ModuleLoader__.load({
|
|
|
259
259
|
const draftRevision = useRef(0);
|
|
260
260
|
return visible ? h(SessionPreview, { execute, draft: { pageId, setPageId, text, setText, draftRevision } }) : null;
|
|
261
261
|
}
|
|
262
|
-
function
|
|
262
|
+
function PresetPreview({ execute, visible }) {
|
|
263
|
+
const [state, setState] = useState(null);
|
|
264
|
+
useEffect(() => {
|
|
265
|
+
let active = true;
|
|
266
|
+
execute('availability').then(value => { if (active) setState(value); }, error => { if (active) setState({ error: error.message }); });
|
|
267
|
+
return () => { active = false; };
|
|
268
|
+
}, [execute]);
|
|
269
|
+
if (!state) return h('p', { role: 'status' }, 'Checking DSCODE session…');
|
|
270
|
+
if (state.error) return h('p', { role: 'alert' }, state.error);
|
|
271
|
+
if (!state.available) return h('section', { className: 'dscode-ui', style: { padding: 16 } }, h('h3', null, 'DSCODE Browser preview'),
|
|
272
|
+
h('p', null, 'This session uses another preset. Create a new session and select DSCODE to use this preview. Its original browser tools remain available.'));
|
|
273
|
+
return h(SessionPane, { execute, visible });
|
|
274
|
+
}
|
|
275
|
+
function Preview({ sessionId, execute, useTabInfo, presetOnly }) {
|
|
263
276
|
const { tab } = useTabInfo();
|
|
264
277
|
// Session changes discard the draft. Hidden panes discard their request
|
|
265
278
|
// lock and preview, while late responses stay bound to the original session.
|
|
266
|
-
return h(SessionPane, { key: sessionId, execute, visible: tab.visible });
|
|
279
|
+
return h(presetOnly ? PresetPreview : SessionPane, { key: sessionId, execute, visible: tab.visible });
|
|
267
280
|
}
|
|
268
281
|
return {
|
|
269
282
|
inject: ['slots', 'sidebarRightTabs', 'connection'],
|
|
270
|
-
apply(ctx) {
|
|
283
|
+
apply(ctx, options = {}) {
|
|
271
284
|
ctx.effect(() => ctx.sidebarRightTabs.register({ id, kind: 'dscode-browser', multiple: false, keepMounted: true, title: () => 'Browser preview', guide: [{ id: 'open', order: 31, title: () => 'Browser preview', description: () => 'Inspect the controlled Chrome tab and send a visual annotation.' }] }), 'dscode-browser-preview.type');
|
|
272
285
|
ctx.effect(() => ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({ name: 'sidebar.right.pane.tab', key: id,
|
|
273
|
-
inject: sessionId => ({ sessionId, execute: async (action, args = {}) => {
|
|
286
|
+
inject: sessionId => ({ sessionId, presetOnly: options.presetOnly === true, execute: async (action, args = {}) => {
|
|
274
287
|
const response = await ctx.connection.rpc.call('/api', 'dscode-browser', { sessionId, action, ...args });
|
|
275
288
|
if (!response.ok) throw Error(response.error.message);
|
|
276
289
|
return response.value;
|
|
@@ -13,6 +13,11 @@ export function apply(ctx) {
|
|
|
13
13
|
if (!payload || typeof payload !== 'object' || typeof payload.sessionId !== 'string') throw Error('Choose a live session.');
|
|
14
14
|
const agent = ctx.agents.get(payload.sessionId);
|
|
15
15
|
if (!agent) throw Error('The session is no longer active. Open it before using Browser preview.');
|
|
16
|
+
const available = !ctx.get('dscodeDesktop') || agent.session.header.agentPreset === 'dscode';
|
|
17
|
+
if (action === 'availability') return { ok: true, value: { available } };
|
|
18
|
+
if (!available) {
|
|
19
|
+
throw Error('Browser preview belongs to DSCODE. Create a new session with the DSCODE preset; this session keeps its original browser tools.');
|
|
20
|
+
}
|
|
16
21
|
if (action === 'start' || action === 'stop' || action === 'resume') {
|
|
17
22
|
if (action === 'resume') {
|
|
18
23
|
const browser = browserForAgent(agent);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
|
|
1
2
|
import { execFile } from 'node:child_process';
|
|
2
3
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
4
|
import { lstat, mkdir, readdir, realpath, rm, writeFile } from 'node:fs/promises';
|
|
4
|
-
import { homedir } from 'node:os';
|
|
5
5
|
import { isAbsolute, join, relative, sep } from 'node:path';
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
7
|
import { reviewSpec } from './git.mjs';
|
|
@@ -17,7 +17,7 @@ const SENSITIVE_PATTERNS = ['.env', '.env.*', '.npmrc', '.pypirc', 'id_rsa', 'id
|
|
|
17
17
|
const IDENTITY = { GIT_AUTHOR_NAME: 'dscode', GIT_AUTHOR_EMAIL: 'review@dscode.invalid', GIT_COMMITTER_NAME: 'dscode', GIT_COMMITTER_EMAIL: 'review@dscode.invalid' };
|
|
18
18
|
export const snapshotLimits = { files: 20000, bytes: 256 * 1024 * 1024, fileBytes: 4 * 1024 * 1024 };
|
|
19
19
|
|
|
20
|
-
const defaultRoot = () => join(
|
|
20
|
+
const defaultRoot = () => join(resolveDshHome(), 'review-baselines');
|
|
21
21
|
const anchored = path => `/${path.replace(/[\\*?[\]!#]/g, '\\$&').replace(/ $/, '\\ ')}`;
|
|
22
22
|
const refPart = value => String(value).replace(/[^A-Za-z0-9_-]/g, '_');
|
|
23
23
|
const refsFor = task => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
|
|
1
2
|
import { cacheReadRatio } from '../session-metrics/pricing.mjs';
|
|
2
3
|
import { ensureOpenRouterModels } from '../openrouter/models.mjs';
|
|
3
4
|
|
|
@@ -70,7 +71,7 @@ export function fitsInWindow(totalTokens, modelInfo) {
|
|
|
70
71
|
|
|
71
72
|
/** The route's threshold ratio, waiting for the OpenRouter listing when it has not loaded yet. */
|
|
72
73
|
export async function pricedThresholdRatio(provider, model, now = Date.now()) {
|
|
73
|
-
if (provider === 'openrouter') await ensureOpenRouterModels({ home:
|
|
74
|
+
if (provider === 'openrouter') await ensureOpenRouterModels({ home: resolveDshHome(), now });
|
|
74
75
|
return thresholdForCacheRatio(cacheReadRatio(provider, model, now));
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -19,9 +19,11 @@ const LOGINS = new Set(PROVIDERS.filter(provider => provider.login).map(provider
|
|
|
19
19
|
export default class DscodeCredentials extends LocalCredentialProvider {
|
|
20
20
|
shared;
|
|
21
21
|
sharedConfig;
|
|
22
|
+
desktop;
|
|
22
23
|
constructor(ctx, config = {}) {
|
|
23
24
|
// Retain the old profile store, including other providers and OAuth records.
|
|
24
25
|
super(ctx, { ...config, path: undefined });
|
|
26
|
+
this.desktop = config.desktop === true;
|
|
25
27
|
this.sharedConfig = { ...config, path: config.path ?? join(homedir(), '.dscode', 'credentials.yaml') };
|
|
26
28
|
}
|
|
27
29
|
async *[Service.init]() {
|
|
@@ -34,6 +36,10 @@ export default class DscodeCredentials extends LocalCredentialProvider {
|
|
|
34
36
|
yield* super[Service.init]();
|
|
35
37
|
}
|
|
36
38
|
async resolve(ref) {
|
|
39
|
+
if (this.desktop) {
|
|
40
|
+
const native = await super.resolve(ref);
|
|
41
|
+
if (native) return native;
|
|
42
|
+
}
|
|
37
43
|
// dscode: the Grok subscription rail is the official CLI login, read-only. DSCODE never
|
|
38
44
|
// writes that file: the CLI owns refresh-token rotation, and two writers log the other out.
|
|
39
45
|
if (ref === GROK_TOKEN_REF) {
|
|
@@ -47,6 +53,10 @@ export default class DscodeCredentials extends LocalCredentialProvider {
|
|
|
47
53
|
return super.resolve(ref);
|
|
48
54
|
}
|
|
49
55
|
async describe(ref) {
|
|
56
|
+
if (this.desktop) {
|
|
57
|
+
const native = await super.describe(ref);
|
|
58
|
+
if (native.configured) return LOGINS.has(ref) ? { ...native, source: 'account', writable: false } : native;
|
|
59
|
+
}
|
|
50
60
|
if (ref === GROK_TOKEN_REF) {
|
|
51
61
|
const state = grokAuthState();
|
|
52
62
|
return state.kind === 'ready' ? { configured: true, source: 'file', writable: false } : { configured: false, writable: false };
|
|
@@ -63,6 +73,7 @@ export default class DscodeCredentials extends LocalCredentialProvider {
|
|
|
63
73
|
set(ref, value) {
|
|
64
74
|
// The CLI file is not a DSCODE store: saving here would go somewhere nothing reads.
|
|
65
75
|
if (ref === GROK_TOKEN_REF) throw new Error('GROK_CLI_TOKEN comes from ~/.grok/auth.json; run grok login instead');
|
|
76
|
+
if (this.desktop) return super.set(ref, value);
|
|
66
77
|
return (SHARED.has(ref) || isCustomKey(ref)) ? this.shared.set(ref, value) : super.set(ref, value);
|
|
67
78
|
}
|
|
68
79
|
async unset(ref) {
|
|
@@ -3,7 +3,7 @@ globalThis.__ModuleLoader__.load({
|
|
|
3
3
|
id: '@toddzheng024/dscode-desktop',
|
|
4
4
|
factory: require => {
|
|
5
5
|
const { createElement: h, useState, useEffect, useRef } = require('react');
|
|
6
|
-
const control = {
|
|
6
|
+
const control = { boxSizing: 'border-box', minWidth: 0, maxWidth: '100%' };
|
|
7
7
|
const row = { display: 'flex', flexWrap: 'wrap', gap: 10, alignItems: 'center' };
|
|
8
8
|
const resetModel = model => {
|
|
9
9
|
const next = { ...model, thinking: 'default', inputModalities: ['text'] };
|
|
@@ -49,10 +49,10 @@ globalThis.__ModuleLoader__.load({
|
|
|
49
49
|
h('input', { 'aria-label': label, style: { ...control, width: '100%' }, value: value ?? '', onChange: event => onChange(event.target.value), ...options }));
|
|
50
50
|
const choice = (label, value, choices, onChange) => h('label', { style: { display: 'grid', gap: 5, flex: 1 } }, label,
|
|
51
51
|
h('select', { 'aria-label': label, value, onChange: event => onChange(event.target.value), style: control }, ...choices.map(([id, text]) => h('option', { key: id, value: id }, text))));
|
|
52
|
-
return h('section', { 'aria-label': 'DSCODE model settings', style: { padding:
|
|
53
|
-
h('h2', { style: { margin: 0 } }, '
|
|
52
|
+
return h('section', { className: 'dscode-ui', 'aria-label': 'DSCODE model settings', style: { padding: 0, display: 'grid', gap: 16, maxWidth: 850, color: 'inherit' } },
|
|
53
|
+
h('h2', { style: { margin: 0 } }, 'Models'),
|
|
54
54
|
h('p', { style: { margin: 0 } }, 'Connect a private or self-hosted model service. Saved models appear in the model picker.'),
|
|
55
|
-
error && h('div', { role: 'alert', style: { color: '
|
|
55
|
+
error && h('div', { role: 'alert', style: { color: 'var(--dsw-alias-state-error-primary)', whiteSpace: 'pre-wrap' } }, error),
|
|
56
56
|
notice && h('div', { role: 'status' }, notice),
|
|
57
57
|
busy && request.current && h('button', { type: 'button', style: control, disabled: request.current.signal.aborted,
|
|
58
58
|
onClick: () => { request.current?.abort(); setNotice('Cancelling request…'); },
|
|
@@ -85,8 +85,8 @@ globalThis.__ModuleLoader__.load({
|
|
|
85
85
|
change({ backend: found.backend, models });
|
|
86
86
|
setNotice(`Found ${found.models.length} models. Confirm the context size and image support before saving.`);
|
|
87
87
|
}, false, true), button('Add model', async () => change({ models: [...draft.models, { id: '', thinking: 'default', inputModalities: ['text'] }] }))),
|
|
88
|
-
...draft.models.map((model, index) => h('fieldset', { key: index, style: { border: '
|
|
89
|
-
h('
|
|
88
|
+
...draft.models.map((model, index) => h('fieldset', { className: 'dscode-card', key: index, style: { border: '.5px solid var(--dsw-alias-settings-card-stroke)', borderRadius: 'var(--dsw-radius-xl)', padding: 14, display: 'grid', gap: 12 } },
|
|
89
|
+
h('h3', null, `Model ${index + 1}`),
|
|
90
90
|
h('div', { style: row }, input(`Model ${index + 1} ID`, model.id, id => change({ models: draft.models.map((entry, i) => i === index ? { ...resetModel(entry), id } : entry) })),
|
|
91
91
|
input(`Model ${index + 1} context`, model.contextWindow, value => modelChange(index, { contextWindow: value ? Number(value) : undefined, contextSource: 'user' }), { type: 'number', min: 1, step: 1 }),
|
|
92
92
|
input(`Model ${index + 1} output limit`, model.maxTokens, value => modelChange(index, { maxTokens: value ? Number(value) : undefined, outputSource: 'user' }), { type: 'number', min: 1, step: 1 })),
|
|
@@ -115,8 +115,7 @@ globalThis.__ModuleLoader__.load({
|
|
|
115
115
|
}
|
|
116
116
|
return {
|
|
117
117
|
inject: ['slots', 'connection'],
|
|
118
|
-
|
|
119
|
-
ctx.slots.inject('settings.section', () => ctx.slots.register({ name: 'settings.section', id: 'dscode-models', order: 11, label: () => 'DSCODE models',
|
|
118
|
+
settingsPage(ctx) { return { entry: { name: 'settings.section', id: 'dscode-models', order: 11, label: () => 'Models',
|
|
120
119
|
inject: () => ({ execute: async (action, args = {}, signal) => {
|
|
121
120
|
signal?.throwIfAborted();
|
|
122
121
|
const requestId = signal ? crypto.randomUUID() : undefined;
|
|
@@ -143,7 +142,11 @@ globalThis.__ModuleLoader__.load({
|
|
|
143
142
|
if (!response.ok) throw Error(response.error.message);
|
|
144
143
|
return response.value;
|
|
145
144
|
} }),
|
|
146
|
-
}, Settings
|
|
145
|
+
}, Component: Settings }; },
|
|
146
|
+
apply(ctx, options = {}) {
|
|
147
|
+
if (options.settings === false) return;
|
|
148
|
+
const page = this.settingsPage(ctx);
|
|
149
|
+
ctx.slots.inject('settings.section', () => ctx.slots.register(page.entry, page.Component));
|
|
147
150
|
},
|
|
148
151
|
};
|
|
149
152
|
},
|