@goodandready/dsh-russian-lang 0.2.16 → 0.2.19
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 +3 -2
- package/README.ru.md +2 -1
- package/docs/media/banner.jpg +0 -0
- package/lib/client.js +282 -93
- package/lib/index.js +20 -0
- package/lib/plugin-updater.js +333 -0
- package/package.json +3 -3
- package/ru/01-core-small.json +27 -3
- package/ru/02-conversation.json +16 -4
- package/ru/03-models-workspace.json +4 -1
- package/ru/04-preset-cordis.json +8 -2
- package/ru/07-sidebar-previewers.json +2 -1
- package/ru/08-sidebar-terminal-archive.json +48 -0
- package/ru-plugins/03-dshmarket.json +5 -2
- package/ru-plugins/15-commandcode.json +55 -2
- package/ru-plugins/17-dsh-context.json +7 -2
- package/ru-plugins/23-moa.json +172 -1
- package/ru-plugins/26-config-manager.json +17 -2
- package/ru-plugins/33-messenger-gateway.json +56 -1
- package/ru-plugins/35-gitea.json +89 -1
- package/ru-plugins/41-lanmode.json +160 -2
- package/ru-plugins/42-plugin-console.json +29 -1
- package/ru-plugins/49-usage-guard.json +33 -1
- package/ru-plugins/50-automode.json +20 -2
- package/ru-plugins/56-cron.json +13 -2
- package/ru-plugins/61-issue-reporter.json +143 -1
- package/ru-plugins/62-model-search.json +15 -1
- package/ru-plugins/63-agent-orchestrator.json +11 -0
- package/ru-plugins/64-key-limits.json +27 -0
- package/ru-plugins/65-subscriptions.json +185 -0
- package/ru-plugins/66-tts.json +211 -0
- package/ru-plugins/67-voice.json +158 -0
- package/ru-plugins/68-image-gen.json +143 -0
- package/ru-plugins/69-plugin-notify.json +29 -0
package/lib/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerPluginUpdater } from './plugin-updater.js'
|
|
1
2
|
// dsh-russian-lang — серверная половина.
|
|
2
3
|
//
|
|
3
4
|
// Словари и переключатель живут в браузере (lib/client.js). Хосту переводить
|
|
@@ -49,4 +50,23 @@ export function apply(ctx) {
|
|
|
49
50
|
ctx.on('settings/updated', (ns) => { if (ns === NS) sync() })
|
|
50
51
|
sync()
|
|
51
52
|
})
|
|
53
|
+
|
|
54
|
+
// One-click plugin updater per DSH standard
|
|
55
|
+
if (typeof ctx.inject === 'function') {
|
|
56
|
+
ctx.inject(['webServer'], (wctx) => {
|
|
57
|
+
try {
|
|
58
|
+
const mount = () => registerPluginUpdater(wctx, {
|
|
59
|
+
endpoint: '/api/dsh-russian-lang/update',
|
|
60
|
+
packageName: '@goodandready/dsh-russian-lang',
|
|
61
|
+
manifestUrl: new URL('../package.json', import.meta.url),
|
|
62
|
+
})
|
|
63
|
+
if (typeof wctx.effect === 'function') wctx.effect(mount, 'russian-lang: plugin updater')
|
|
64
|
+
else mount()
|
|
65
|
+
} catch (err) {
|
|
66
|
+
if (typeof wctx.logger?.warn === 'function') {
|
|
67
|
+
wctx.logger.warn('Failed to mount plugin updater route:', err)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
}
|
|
52
72
|
}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process'
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
3
|
+
import { readFile } from 'node:fs/promises'
|
|
4
|
+
import { homedir } from 'node:os'
|
|
5
|
+
import { basename, dirname, isAbsolute, resolve } from 'node:path'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Host-side one-click updater for @goodandready/dsh-russian-lang.
|
|
10
|
+
* Implements the unified DSH plugin updater standard.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const UPDATE_HEADER = 'x-dsh-plugin-update'
|
|
14
|
+
const UPDATE_TIMEOUT_MS = 10 * 60_000
|
|
15
|
+
const VERSION_CACHE_MS = 5 * 60_000
|
|
16
|
+
let latestCache = undefined
|
|
17
|
+
|
|
18
|
+
function header(request, name) {
|
|
19
|
+
const value = request.headers?.[name]
|
|
20
|
+
return Array.isArray(value) ? value[0] : value
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isLoopback(value) {
|
|
24
|
+
const address = value?.toLowerCase().replace(/^\[|\]$/g, '')
|
|
25
|
+
return (
|
|
26
|
+
address === 'localhost' ||
|
|
27
|
+
address === 'localhost.' ||
|
|
28
|
+
address === '::1' ||
|
|
29
|
+
address?.startsWith('127.') === true ||
|
|
30
|
+
address?.startsWith('::ffff:127.') === true
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isTrustedUpdateRequest(request) {
|
|
35
|
+
if (header(request, UPDATE_HEADER) !== '1') return false
|
|
36
|
+
if (!isLoopback(request.socket?.remoteAddress)) return false
|
|
37
|
+
const site = header(request, 'sec-fetch-site')
|
|
38
|
+
if (site !== undefined && site !== 'same-origin') return false
|
|
39
|
+
const origin = header(request, 'origin')
|
|
40
|
+
const host = header(request, 'host')
|
|
41
|
+
if (origin === undefined || host === undefined) return false
|
|
42
|
+
try {
|
|
43
|
+
const url = new URL(origin)
|
|
44
|
+
return (
|
|
45
|
+
(url.protocol === 'http:' || url.protocol === 'https:') &&
|
|
46
|
+
isLoopback(url.hostname) &&
|
|
47
|
+
url.host === host
|
|
48
|
+
)
|
|
49
|
+
} catch {
|
|
50
|
+
return false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function validProfileName(value) {
|
|
55
|
+
return (
|
|
56
|
+
typeof value === 'string' &&
|
|
57
|
+
value !== '' &&
|
|
58
|
+
value !== '.' &&
|
|
59
|
+
value !== '..' &&
|
|
60
|
+
!value.includes('/') &&
|
|
61
|
+
!value.includes('\\') &&
|
|
62
|
+
!/[\0-\x1f\x7f]/.test(value)
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function profileNameFromArgv(argv) {
|
|
67
|
+
for (let index = 2; index < argv.length; index += 1) {
|
|
68
|
+
if (argv[index] === '--profile') return argv[index + 1]
|
|
69
|
+
if (argv[index]?.startsWith('--profile=')) return argv[index].slice('--profile='.length)
|
|
70
|
+
}
|
|
71
|
+
return argv[2] === 'web' ? 'web' : undefined
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function findDshCliEntry() {
|
|
75
|
+
const value = process.argv[1]
|
|
76
|
+
if (value === undefined || value === '') return undefined
|
|
77
|
+
const entry = value.startsWith('file:') ? fileURLToPath(value) : resolve(process.cwd(), value)
|
|
78
|
+
if (!existsSync(entry)) return undefined
|
|
79
|
+
for (let directory = dirname(entry); ; directory = dirname(directory)) {
|
|
80
|
+
const manifestPath = resolve(directory, 'package.json')
|
|
81
|
+
if (existsSync(manifestPath)) {
|
|
82
|
+
try {
|
|
83
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'))
|
|
84
|
+
const bin =
|
|
85
|
+
typeof manifest.bin === 'string'
|
|
86
|
+
? manifest.bin
|
|
87
|
+
: typeof manifest.bin === 'object' && manifest.bin !== null
|
|
88
|
+
? manifest.bin.dsh
|
|
89
|
+
: undefined
|
|
90
|
+
if (
|
|
91
|
+
manifest.name === '@deepseek-ai/dsh' &&
|
|
92
|
+
typeof bin === 'string' &&
|
|
93
|
+
!isAbsolute(bin) &&
|
|
94
|
+
resolve(directory, bin) === resolve(entry)
|
|
95
|
+
) {
|
|
96
|
+
return entry
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
// Continue searching parent directories
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const parent = dirname(directory)
|
|
103
|
+
if (parent === directory) return undefined
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function runtime() {
|
|
108
|
+
const profileDir = resolve(
|
|
109
|
+
process.env.DSH_PROFILE_DIR ?? resolve(homedir(), '.dsh', 'profiles', 'web')
|
|
110
|
+
)
|
|
111
|
+
const selected = profileNameFromArgv(process.argv)
|
|
112
|
+
const profileName = validProfileName(selected)
|
|
113
|
+
? selected
|
|
114
|
+
: validProfileName(basename(profileDir))
|
|
115
|
+
? basename(profileDir)
|
|
116
|
+
: 'web'
|
|
117
|
+
const cliEntry = findDshCliEntry()
|
|
118
|
+
return cliEntry === undefined ? { profileName, profileDir } : { profileName, profileDir, cliEntry }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function parseSemver(value) {
|
|
122
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value)
|
|
123
|
+
if (match === null) return undefined
|
|
124
|
+
return {
|
|
125
|
+
core: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
126
|
+
prerelease: match[4]?.split('.') ?? [],
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function comparePrerelease(left, right) {
|
|
131
|
+
if (left.length === 0 || right.length === 0) {
|
|
132
|
+
return left.length === right.length ? 0 : left.length === 0 ? 1 : -1
|
|
133
|
+
}
|
|
134
|
+
const length = Math.max(left.length, right.length)
|
|
135
|
+
for (let index = 0; index < length; index += 1) {
|
|
136
|
+
const a = left[index]
|
|
137
|
+
const b = right[index]
|
|
138
|
+
if (a === undefined || b === undefined) return a === b ? 0 : a === undefined ? -1 : 1
|
|
139
|
+
if (a === b) continue
|
|
140
|
+
const aNumeric = /^\d+$/.test(a)
|
|
141
|
+
const bNumeric = /^\d+$/.test(b)
|
|
142
|
+
if (aNumeric && bNumeric) {
|
|
143
|
+
const aNumber = BigInt(a)
|
|
144
|
+
const bNumber = BigInt(b)
|
|
145
|
+
if (aNumber !== bNumber) return aNumber > bNumber ? 1 : -1
|
|
146
|
+
continue
|
|
147
|
+
}
|
|
148
|
+
if (aNumeric !== bNumeric) return aNumeric ? -1 : 1
|
|
149
|
+
return a > b ? 1 : -1
|
|
150
|
+
}
|
|
151
|
+
return 0
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function isNewerVersion(currentValue, candidateValue) {
|
|
155
|
+
const current = parseSemver(currentValue)
|
|
156
|
+
const candidate = parseSemver(candidateValue)
|
|
157
|
+
if (current === undefined || candidate === undefined) return false
|
|
158
|
+
for (let index = 0; index < 3; index += 1) {
|
|
159
|
+
if (candidate.core[index] !== current.core[index]) {
|
|
160
|
+
return candidate.core[index] > current.core[index]
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return comparePrerelease(candidate.prerelease, current.prerelease) > 0
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function latestVersion(packageName, registry) {
|
|
167
|
+
if (
|
|
168
|
+
latestCache?.packageName === packageName &&
|
|
169
|
+
latestCache.registry === registry &&
|
|
170
|
+
Date.now() < latestCache.expiresAt
|
|
171
|
+
) {
|
|
172
|
+
return latestCache.version
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const response = await fetch(
|
|
176
|
+
`${registry.replace(/\/$/, '')}/${encodeURIComponent(packageName)}/latest`,
|
|
177
|
+
{ signal: AbortSignal.timeout(8_000) }
|
|
178
|
+
)
|
|
179
|
+
if (!response.ok) return undefined
|
|
180
|
+
const value = await response.json()
|
|
181
|
+
if (typeof value.version !== 'string' || value.version === '') return undefined
|
|
182
|
+
latestCache = {
|
|
183
|
+
packageName,
|
|
184
|
+
registry,
|
|
185
|
+
version: value.version,
|
|
186
|
+
expiresAt: Date.now() + VERSION_CACHE_MS,
|
|
187
|
+
}
|
|
188
|
+
return value.version
|
|
189
|
+
} catch {
|
|
190
|
+
return undefined
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function currentVersion(manifestUrl) {
|
|
195
|
+
const value = JSON.parse(await readFile(manifestUrl, 'utf8'))
|
|
196
|
+
if (typeof value.version !== 'string' || value.version === '') {
|
|
197
|
+
throw new Error('Cannot read current plugin version.')
|
|
198
|
+
}
|
|
199
|
+
return value.version
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function status(options, target) {
|
|
203
|
+
const current = await currentVersion(options.manifestUrl)
|
|
204
|
+
const latest = await latestVersion(options.packageName, options.registry ?? 'https://registry.npmjs.org')
|
|
205
|
+
return {
|
|
206
|
+
packageName: options.packageName,
|
|
207
|
+
currentVersion: current,
|
|
208
|
+
...(latest === undefined ? {} : { latestVersion: latest }),
|
|
209
|
+
latestCheckFailed: latest === undefined,
|
|
210
|
+
updateAvailable: latest !== undefined && isNewerVersion(current, latest),
|
|
211
|
+
profileName: target.profileName,
|
|
212
|
+
canAutoUpdate: target.cliEntry !== undefined,
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function installExact(target, packageSpec, options) {
|
|
217
|
+
if (target.cliEntry === undefined) {
|
|
218
|
+
throw new Error('Automatic update is unavailable in this runtime.')
|
|
219
|
+
}
|
|
220
|
+
await new Promise((resolvePromise, reject) => {
|
|
221
|
+
const child = spawn(
|
|
222
|
+
process.execPath,
|
|
223
|
+
[
|
|
224
|
+
target.cliEntry,
|
|
225
|
+
'plugin',
|
|
226
|
+
'--profile',
|
|
227
|
+
target.profileName,
|
|
228
|
+
'add',
|
|
229
|
+
'--config.minimumReleaseAge=0',
|
|
230
|
+
packageSpec,
|
|
231
|
+
`--registry=${options.registry ?? 'https://registry.npmjs.org/'}`,
|
|
232
|
+
],
|
|
233
|
+
{
|
|
234
|
+
cwd: target.profileDir,
|
|
235
|
+
windowsHide: true,
|
|
236
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
237
|
+
env: { ...process.env, NO_COLOR: '1' },
|
|
238
|
+
}
|
|
239
|
+
)
|
|
240
|
+
let detail = ''
|
|
241
|
+
child.stdout?.on('data', (chunk) => {
|
|
242
|
+
detail = (detail + String(chunk)).slice(-4_000)
|
|
243
|
+
})
|
|
244
|
+
child.stderr?.on('data', (chunk) => {
|
|
245
|
+
detail = (detail + String(chunk)).slice(-4_000)
|
|
246
|
+
})
|
|
247
|
+
const timer = setTimeout(() => {
|
|
248
|
+
child.kill()
|
|
249
|
+
reject(new Error('Update timed out; use the normal DSH update flow.'))
|
|
250
|
+
}, UPDATE_TIMEOUT_MS)
|
|
251
|
+
child.once('error', (error) => {
|
|
252
|
+
clearTimeout(timer)
|
|
253
|
+
reject(error)
|
|
254
|
+
})
|
|
255
|
+
child.once('exit', (code) => {
|
|
256
|
+
clearTimeout(timer)
|
|
257
|
+
if (code === 0) resolvePromise()
|
|
258
|
+
else reject(new Error(detail.trim() || `Update exited with code ${String(code)}.`))
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function json(response, statusCode, value) {
|
|
264
|
+
response.writeHead(statusCode, {
|
|
265
|
+
'content-type': 'application/json; charset=utf-8',
|
|
266
|
+
'cache-control': 'no-store',
|
|
267
|
+
})
|
|
268
|
+
response.end(JSON.stringify(value))
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function registerPluginUpdater(ctx, options) {
|
|
272
|
+
const host = ctx
|
|
273
|
+
if (!host.webServer || typeof host.webServer.register !== 'function') {
|
|
274
|
+
return () => {}
|
|
275
|
+
}
|
|
276
|
+
let installing = false
|
|
277
|
+
return host.webServer.register({
|
|
278
|
+
kind: 'exact',
|
|
279
|
+
path: options.endpoint,
|
|
280
|
+
handler: async (request, response) => {
|
|
281
|
+
try {
|
|
282
|
+
const target = runtime()
|
|
283
|
+
if (request.method === 'GET' || request.method === 'HEAD') {
|
|
284
|
+
const payload = await status(options, target)
|
|
285
|
+
response.writeHead(200, {
|
|
286
|
+
'content-type': 'application/json; charset=utf-8',
|
|
287
|
+
'cache-control': 'no-store',
|
|
288
|
+
})
|
|
289
|
+
response.end(request.method === 'HEAD' ? undefined : JSON.stringify(payload))
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
|
+
if (request.method !== 'POST') {
|
|
293
|
+
response.writeHead(405, { allow: 'GET, HEAD, POST' })
|
|
294
|
+
response.end()
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
if (!isTrustedUpdateRequest(request)) {
|
|
298
|
+
json(response, 403, { error: 'Rejected non-local or cross-origin update request.' })
|
|
299
|
+
return
|
|
300
|
+
}
|
|
301
|
+
if (installing) {
|
|
302
|
+
json(response, 409, { error: 'This plugin is already updating.' })
|
|
303
|
+
return
|
|
304
|
+
}
|
|
305
|
+
installing = true
|
|
306
|
+
try {
|
|
307
|
+
const before = await status(options, target)
|
|
308
|
+
if (before.latestVersion === undefined) {
|
|
309
|
+
json(response, 503, { error: 'The latest version is temporarily unavailable.' })
|
|
310
|
+
return
|
|
311
|
+
}
|
|
312
|
+
if (!before.updateAvailable) {
|
|
313
|
+
json(response, 200, before)
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
await installExact(target, `${options.packageName}@${before.latestVersion}`, options)
|
|
317
|
+
json(response, 200, {
|
|
318
|
+
...before,
|
|
319
|
+
updatedVersion: before.latestVersion,
|
|
320
|
+
restartRequired: true,
|
|
321
|
+
})
|
|
322
|
+
} finally {
|
|
323
|
+
installing = false
|
|
324
|
+
}
|
|
325
|
+
} catch (error) {
|
|
326
|
+
if (ctx.logger && typeof ctx.logger.warn === 'function') {
|
|
327
|
+
ctx.logger.warn(`plugin updater failed: ${String(error)}`)
|
|
328
|
+
}
|
|
329
|
+
json(response, 503, { error: 'Plugin update failed; see server logs.' })
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
})
|
|
333
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-russian-lang",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Russian localization for DeepSeek Harness (DSH v0.1.
|
|
3
|
+
"version": "0.2.19",
|
|
4
|
+
"description": "Russian localization for DeepSeek Harness (DSH v0.1.6-alpha.1+) web UI: adds Русский as native language option. 100.0% coverage across 11,295 keys (127 namespaces). Features full DSH v0.1.6-alpha.1 core support (terminal, archive, command palette, Auto review, trajectory, PTC/Creator presets) and complete GoodAndReady plugin catalog (subscriptions, tts, image-gen, voice, key-limits, notify, commandcode, plugin-console).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|
|
@@ -68,4 +68,4 @@
|
|
|
68
68
|
"bugs": {
|
|
69
69
|
"url": "https://github.com/GooDAnDReaDY/dsh-russian-lang/issues"
|
|
70
70
|
}
|
|
71
|
-
}
|
|
71
|
+
}
|
package/ru/01-core-small.json
CHANGED
|
@@ -150,7 +150,21 @@
|
|
|
150
150
|
"description.goal": "Установить или просмотреть цель для длительной задачи",
|
|
151
151
|
"description.permission": "Переключить пресет разрешений (режим песочницы и политики подтверждений)",
|
|
152
152
|
"description.plan": "Войти или выйти из режима планирования",
|
|
153
|
-
"notice.attachmentsUnsupported": "/{command} не принимает вложения; сначала удалите их"
|
|
153
|
+
"notice.attachmentsUnsupported": "/{command} не принимает вложения; сначала удалите их",
|
|
154
|
+
"section.add": "Добавить",
|
|
155
|
+
"section.commands": "Команды",
|
|
156
|
+
"label.goal": "Цель",
|
|
157
|
+
"label.plan": "План",
|
|
158
|
+
"label.feedback": "Обратная связь",
|
|
159
|
+
"label.compact": "Сжатие",
|
|
160
|
+
"label.permission": "Режим доступа",
|
|
161
|
+
"label.export": "Экспорт",
|
|
162
|
+
"token.goal": "цель",
|
|
163
|
+
"token.plan": "план",
|
|
164
|
+
"token.feedback": "отзыв",
|
|
165
|
+
"token.compact": "сжатие",
|
|
166
|
+
"token.permission": "доступ",
|
|
167
|
+
"token.export": "экспорт"
|
|
154
168
|
},
|
|
155
169
|
"session-log-download": {
|
|
156
170
|
"dialog.close": "Закрыть",
|
|
@@ -177,12 +191,22 @@
|
|
|
177
191
|
"model": {
|
|
178
192
|
"trigger.loading": "Загрузка моделей…",
|
|
179
193
|
"option.deepseekV4Flash.description": "Быстрая, эффективная и экономичная; подходит для типовых, повседневных или параллельных задач.",
|
|
180
|
-
"option.deepseekV4Pro.description": "Улучшенное агентное написание кода, глубокие знания и сложное мышление; подходит для комплексных задач с высокими требованиями к качеству при более высокой стоимости."
|
|
194
|
+
"option.deepseekV4Pro.description": "Улучшенное агентное написание кода, глубокие знания и сложное мышление; подходит для комплексных задач с высокими требованиями к качеству при более высокой стоимости.",
|
|
195
|
+
"command.label": "Модель"
|
|
181
196
|
},
|
|
182
197
|
"permission.access": {
|
|
183
198
|
"preset.fullAccess": "Полный доступ",
|
|
184
199
|
"preset.readOnly": "Только чтение",
|
|
185
|
-
"preset.workspaceWrite": "Запись в рабочую область"
|
|
200
|
+
"preset.workspaceWrite": "Запись в рабочую область",
|
|
201
|
+
"mode": "Режим доступа, текущий: {name}",
|
|
202
|
+
"close": "Закрыть",
|
|
203
|
+
"auto.label": "Автопроверка",
|
|
204
|
+
"auto.badge": "ЭКСП",
|
|
205
|
+
"auto.description": "Запуск без песочницы с экспериментальной проверкой каждого вызова инструментов и внутренних вызовов PTC той же моделью.",
|
|
206
|
+
"auto.confirm.title": "Включить автопроверку (экспериментально)?",
|
|
207
|
+
"auto.confirm.description": "Автопроверка работает без песочницы. Перед каждым вызовом нативного инструмента и внутренним вызовом PTC та же модель, что и текущий агент, проверяет, разрешить ли его. Эта функция экспериментальная, может ошибочно разрешать или блокировать действия и расходует дополнительные токены.",
|
|
208
|
+
"auto.confirm.acknowledge": "Я понимаю эти риски и хочу продолжить",
|
|
209
|
+
"auto.confirm.enable": "Включить автопроверку"
|
|
186
210
|
},
|
|
187
211
|
"reference": {
|
|
188
212
|
"crumb.root": "Рабочая область",
|
package/ru/02-conversation.json
CHANGED
|
@@ -260,7 +260,11 @@
|
|
|
260
260
|
"input.send.steer": "Направить сообщение",
|
|
261
261
|
"queue.file": "Файл в очереди: {name}",
|
|
262
262
|
"queue.sending": "Отправка…",
|
|
263
|
-
"tool.title.readImage": "Просмотр изображения"
|
|
263
|
+
"tool.title.readImage": "Просмотр изображения",
|
|
264
|
+
"input.file": "Файл",
|
|
265
|
+
"tool.autoReviewRejected": "Отклонено автопроверкой",
|
|
266
|
+
"tool.autoReviewNotExecuted": "Инструмент не был выполнен. Причина: {reason}",
|
|
267
|
+
"tool.autoReviewReasonFallback": "Автопроверка не разрешила это действие"
|
|
264
268
|
},
|
|
265
269
|
"chat": {
|
|
266
270
|
"chat.deepDiving": "Глубокий анализ…",
|
|
@@ -379,7 +383,8 @@
|
|
|
379
383
|
"stats.dialog.title": "Статистика сессии",
|
|
380
384
|
"stats.dialog.toolTime": "Время инструментов",
|
|
381
385
|
"stats.dialog.ttft": "Среднее время до первого токена (TTFT)",
|
|
382
|
-
"stats.dialog.usageTitle": "Использование токенов"
|
|
386
|
+
"stats.dialog.usageTitle": "Использование токенов",
|
|
387
|
+
"duration.hours": "{hours} ч {minutes} мин {seconds} с"
|
|
383
388
|
},
|
|
384
389
|
"trajectory": {
|
|
385
390
|
"block.label": "Блок #{index} {type}",
|
|
@@ -823,7 +828,14 @@
|
|
|
823
828
|
"usage.thisRequest": "This request",
|
|
824
829
|
"usage.tokens": "Токены",
|
|
825
830
|
"view.trajectory": "Trajectory",
|
|
826
|
-
"layout.fileAttachments": "Файлы ×{count}"
|
|
831
|
+
"layout.fileAttachments": "Файлы ×{count}",
|
|
832
|
+
"code.copyOutput": "Скопировать вывод",
|
|
833
|
+
"code.copySource": "Скопировать код",
|
|
834
|
+
"code.originalJson": "Оригинальный JSON",
|
|
835
|
+
"code.output": "Вывод",
|
|
836
|
+
"code.running": "Выполняется…",
|
|
837
|
+
"code.source": "Код",
|
|
838
|
+
"record.wrapLines": "Переносить строки"
|
|
827
839
|
},
|
|
828
840
|
"slash.menu": {
|
|
829
841
|
"crumbs.aria": "Навигация по папкам",
|
|
@@ -831,4 +843,4 @@
|
|
|
831
843
|
"drill.hint": "Открыть папку",
|
|
832
844
|
"drill.key": "Tab"
|
|
833
845
|
}
|
|
834
|
-
}
|
|
846
|
+
}
|
|
@@ -99,7 +99,10 @@
|
|
|
99
99
|
"welcomeBody": "DeepSeek Harness 0.1 находится на стадии тестирования для разработчиков Harness. Многие компоненты требуют дальнейшего улучшения, и мы приветствуем обратную связь от сообщества. Базовые плагины и системные API DeepSeek Harness продолжат активно развиваться в ближайшие месяцы.\n\nМы с нетерпением ждём возможности исследовать границы искусственного интеллекта вместе с разработчиками по всему миру на базе открытой, модульной и повторно используемой инфраструктуры. Приглашаем разработчиков присоединиться к экосистеме плагинов DSH.",
|
|
100
100
|
"welcomeContinue": "Продолжить",
|
|
101
101
|
"welcomeTitle": "Уведомление о внутреннем тестировании",
|
|
102
|
-
"customBaseUrlInvalid": "Введите корректный URL (HTTP или HTTPS)."
|
|
102
|
+
"customBaseUrlInvalid": "Введите корректный URL (HTTP или HTTPS).",
|
|
103
|
+
"deepSeekChatBaseUrl": "https://api.deepseek.com",
|
|
104
|
+
"deepSeekMessagesBaseUrl": "https://api.deepseek.com/anthropic",
|
|
105
|
+
"deepSeekEndpointHint": "Используйте эндпоинт, совместимый с настроенным подключением."
|
|
103
106
|
},
|
|
104
107
|
"workspace": {
|
|
105
108
|
"actions.newSession.aria": "Новая сессия в {name}",
|
package/ru/04-preset-cordis.json
CHANGED
|
@@ -55,7 +55,13 @@
|
|
|
55
55
|
"view": "Посмотреть",
|
|
56
56
|
"presetPtcDescription": "Полноценный агент разработки без инструмента рабочих процессов; остальные инструменты доступны через SDK режима PTC, что позволяет модели объединять многоэтапные операции в одну программу TypeScript.",
|
|
57
57
|
"presetPtcName": "Режим PTC",
|
|
58
|
-
"switchRefused": "Не удалось переключиться на {name}: {reason}"
|
|
58
|
+
"switchRefused": "Не удалось переключиться на {name}: {reason}",
|
|
59
|
+
"selectionOffDefault": "По умолчанию",
|
|
60
|
+
"showPicker": "Разрешить выбор режимов агента",
|
|
61
|
+
"showPickerBeta": "Бета",
|
|
62
|
+
"showPickerDescription": "Если включено, для новых задач можно выбирать режимы: Стандартный, PTC, Creator, Минимальный и пользовательские. Если выключено, все новые задачи используют режим по умолчанию (по умолчанию Стандартный; настраивается). Влияет только на новые задачи.",
|
|
63
|
+
"enablePickerToSetDefault": "Включите выбор режимов агента, чтобы задать режим по умолчанию",
|
|
64
|
+
"enablePickerToCreate": "Включите выбор режимов агента, чтобы запустить режим Creator"
|
|
59
65
|
},
|
|
60
66
|
"cordis": {
|
|
61
67
|
"a11y.defining": "Определение плагина",
|
|
@@ -133,4 +139,4 @@
|
|
|
133
139
|
"preset.readOnly": "Только чтение",
|
|
134
140
|
"preset.workspaceWrite": "Запись в рабочую область"
|
|
135
141
|
}
|
|
136
|
-
}
|
|
142
|
+
}
|
|
@@ -52,6 +52,7 @@
|
|
|
52
52
|
"viewer.text": "Простой текст",
|
|
53
53
|
"wrap.aria": "Перенос строк",
|
|
54
54
|
"wrap.disable": "Отключить перенос строк",
|
|
55
|
-
"wrap.enable": "Включить перенос строк"
|
|
55
|
+
"wrap.enable": "Включить перенос строк",
|
|
56
|
+
"unsupportedFile": "Предпросмотр для этого типа файлов пока недоступен."
|
|
56
57
|
}
|
|
57
58
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"settings.archivedSessions": {
|
|
3
|
+
"nav": "Архив сессий",
|
|
4
|
+
"search": "Поиск по архивным сессиям",
|
|
5
|
+
"loading": "Чтение сессий…",
|
|
6
|
+
"empty": "Нет архивных сессий.",
|
|
7
|
+
"unavailable": "Здесь нет архивных сессий, доступных для восстановления.",
|
|
8
|
+
"emptySearch": "Нет подходящих сессий.",
|
|
9
|
+
"unarchive": "Разархивировать",
|
|
10
|
+
"unarchiveNamed": "Разархивировать «{title}»",
|
|
11
|
+
"ungrouped": "Без группы",
|
|
12
|
+
"time.now": "сейчас",
|
|
13
|
+
"time.minutes": "{n} мин",
|
|
14
|
+
"time.hours": "{n} ч",
|
|
15
|
+
"time.days": "{n} дн",
|
|
16
|
+
"time.months": "{n} мес",
|
|
17
|
+
"time.years": "{n} г"
|
|
18
|
+
},
|
|
19
|
+
"sidebarTerminal": {
|
|
20
|
+
"attachmentEnded": "Подключение к терминалу завершено. Переподключитесь для продолжения.",
|
|
21
|
+
"cleanupFailed": "Не удалось завершить терминал «{title}»: {message}",
|
|
22
|
+
"closed": "Терминал закрыт.",
|
|
23
|
+
"connecting": "Подключение…",
|
|
24
|
+
"control": "Перехватить управление",
|
|
25
|
+
"creating": "Запуск…",
|
|
26
|
+
"description": "Выполнение команд в рабочей области сессии",
|
|
27
|
+
"disconnected": "Отключено.",
|
|
28
|
+
"exited": "Процесс завершился с кодом {code}",
|
|
29
|
+
"failed": "Ошибка терминала: {message}",
|
|
30
|
+
"inputFull": "Буфер ввода переполнен. Переподключитесь и повторите попытку.",
|
|
31
|
+
"invalidOutput": "Не удалось получить экран терминала. Переподключитесь для восстановления.",
|
|
32
|
+
"loading": "Чтение окружения терминала…",
|
|
33
|
+
"missingTerminal": "Этот терминал больше не существует. Откройте новый терминал.",
|
|
34
|
+
"new": "Новый терминал",
|
|
35
|
+
"readonly": "Этот вид доступен только для чтения.",
|
|
36
|
+
"reconnect": "Переподключиться",
|
|
37
|
+
"recoveryFailed": "Не удалось восстановить терминал: {message}",
|
|
38
|
+
"rename": "Название терминала",
|
|
39
|
+
"retry": "Повторить",
|
|
40
|
+
"retryRecovery": "Повторить восстановление терминала",
|
|
41
|
+
"shell": "Выбрать командную оболочку",
|
|
42
|
+
"shellEmpty": "Нет доступных оболочек",
|
|
43
|
+
"shellLoading": "Загрузка оболочек…",
|
|
44
|
+
"terminalLimit": "Достигнут лимит терминалов. Закройте неиспользуемые терминалы и повторите попытку. Завершённые терминалы также учитываются в лимите.",
|
|
45
|
+
"title": "Терминал",
|
|
46
|
+
"unavailable": "Недоступно"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -531,6 +531,9 @@
|
|
|
531
531
|
"hostIncompatibleBody": "Плагин {plugin} требует DSH версии {requirement}, а на этом сервере запущен {host}. Установка, скорее всего, нарушит работу плагина, и единственным выходом будет ручной откат версии.\nНичего не было изменено — плагин остаётся на текущей версии.\nЕсли вы уверены, что номер версии определён неверно, вы можете продолжить обновление.",
|
|
532
532
|
"hostIncompatibleKeep": "Оставить текущую версию",
|
|
533
533
|
"hostIncompatibleTitle": "Для этого релиза требуется более новая версия DSH",
|
|
534
|
-
"hostIncompatibleUnknown": "неизвестная версия"
|
|
534
|
+
"hostIncompatibleUnknown": "неизвестная версия",
|
|
535
|
+
"hostUpdateHint": "Установлено и обновляется десктоп-клиентом; маркет только отображает статус",
|
|
536
|
+
"hostUpdateReady": "Новая версия {0}",
|
|
537
|
+
"setSelfHostManagedHint": "Эта копия установлена десктоп-клиентом; обновляйте её через десктопное приложение."
|
|
535
538
|
}
|
|
536
|
-
}
|
|
539
|
+
}
|
|
@@ -163,7 +163,9 @@
|
|
|
163
163
|
"visibleModelsHideInactive": "Скрыть неактивные",
|
|
164
164
|
"visibleModelsReset": "Сбросить выбор моделей",
|
|
165
165
|
"visibleModelsStaleHint": "{count} выбранных моделей больше нет в каталоге (возможно, выведены из эксплуатации); на остальные модели это не влияет. Удалите их или сохраните.",
|
|
166
|
-
"visibleModelsTitle": "Видимые модели"
|
|
166
|
+
"visibleModelsTitle": "Видимые модели",
|
|
167
|
+
"showSidebarQuota": "Показывать карточку квоты в боковой панели",
|
|
168
|
+
"showSidebarQuotaHint": "Когда включено, карточка тарифа и квоты Command Code располагается внизу боковой панели (над Настройками) и открывает полный дашборд. По умолчанию выключено: ничего не отображается слева и фоновый опрос не выполняется. Применяется при сохранении."
|
|
167
169
|
},
|
|
168
170
|
"commandcode": {
|
|
169
171
|
"title": "📊 Использование Command Code{account}",
|
|
@@ -196,5 +198,56 @@
|
|
|
196
198
|
"noData": "(нет данных — проверьте API-ключ)",
|
|
197
199
|
"errorText": "Не удалось получить статистику Command Code: {message}",
|
|
198
200
|
"imageGate": "В сессии уже есть изображения, а модель {model} не поддерживает изображения; выберите модель с поддержкой зрения или удалите изображения из сессии."
|
|
201
|
+
},
|
|
202
|
+
"panel.commandcode": {
|
|
203
|
+
"active": "Активен",
|
|
204
|
+
"close": "Закрыть",
|
|
205
|
+
"closeHint": "Вернуться к диалогу",
|
|
206
|
+
"coolingDown": "Охлаждение",
|
|
207
|
+
"credits": "Кредиты",
|
|
208
|
+
"errorGeneric": "Не удалось получить данные об использовании аккаунта",
|
|
209
|
+
"errorInvalidKey": "API-ключ недействителен или истёк",
|
|
210
|
+
"errorInvalidKeyHint": "Сервер отклонил все запросы (401). Проверьте ключ для этого аккаунта или сгенерируйте новый в консоли commandcode.ai.",
|
|
211
|
+
"errorNetwork": "Не удалось связаться с сервисом Command Code",
|
|
212
|
+
"errorNetworkHint": "Запрос не достиг сервера. Проверьте сетевое подключение или настройку базового URL API.",
|
|
213
|
+
"errorServiceUnavailable": "Сервис Command Code временно недоступен",
|
|
214
|
+
"errorServiceUnavailableHint": "Сервер возвращает ошибки (5xx). Повторите попытку через минуту.",
|
|
215
|
+
"exceeded": "Превышен",
|
|
216
|
+
"exhausted": "Исчерпан",
|
|
217
|
+
"failed": "ошибок",
|
|
218
|
+
"fiveHour": "5-часовое окно",
|
|
219
|
+
"fiveHourShort": "5 ч",
|
|
220
|
+
"free": "Бесплатно",
|
|
221
|
+
"invalidKey": "Неверный ключ",
|
|
222
|
+
"limits": "Лимиты",
|
|
223
|
+
"loading": "Загрузка…",
|
|
224
|
+
"monthly": "Месячный лимит",
|
|
225
|
+
"monthlyLimit": "Месячный лимит",
|
|
226
|
+
"monthlyUsed": "Использовано за месяц",
|
|
227
|
+
"nav": "Command Code",
|
|
228
|
+
"noKey": "Ключ не настроен",
|
|
229
|
+
"noKeyHint": "Добавьте API-ключ Command Code в Настройках, чтобы отслеживать квоту и кредиты.",
|
|
230
|
+
"partial": "Частично",
|
|
231
|
+
"periodEnds": "Окончание периода",
|
|
232
|
+
"plan": "Тариф",
|
|
233
|
+
"purchased": "Приобретено",
|
|
234
|
+
"refresh": "Обновить",
|
|
235
|
+
"refreshing": "Обновление…",
|
|
236
|
+
"remaining": "Осталось",
|
|
237
|
+
"requests": "Запросы",
|
|
238
|
+
"resets": "Сброс через",
|
|
239
|
+
"spend": "Расходы",
|
|
240
|
+
"subtitle": "Тарифы, кредиты и плавающие окна квот",
|
|
241
|
+
"successRate": "Успешность",
|
|
242
|
+
"tokens": "Токены",
|
|
243
|
+
"tokensIn": "Входящие токены",
|
|
244
|
+
"tokensOut": "Исходящие токены",
|
|
245
|
+
"unavailable": "Недоступно",
|
|
246
|
+
"unconfigured": "Не настроено",
|
|
247
|
+
"updated": "Обновлено",
|
|
248
|
+
"usage": "Использование",
|
|
249
|
+
"weekly": "Недельное окно",
|
|
250
|
+
"weeklyShort": "7 дн",
|
|
251
|
+
"windowUnlimited": "Без ограничений"
|
|
199
252
|
}
|
|
200
|
-
}
|
|
253
|
+
}
|