vibe-coding-master 0.3.14 → 0.3.16
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/dist/backend/api/codex-translation-routes.js +4 -0
- package/dist/backend/gateway/gateway-service.js +3 -3
- package/dist/backend/services/app-settings-service.js +9 -3
- package/dist/backend/services/codex-translation-service.js +168 -12
- package/dist/backend/services/translation-service.js +21 -5
- package/dist/shared/types/app-settings.js +10 -2
- package/dist-frontend/assets/index-BNJJ_Tcf.js +92 -0
- package/dist-frontend/assets/index-C1FPjOXU.css +32 -0
- package/dist-frontend/index.html +2 -2
- package/docs/codex-translation-plan.md +27 -8
- package/package.json +1 -1
- package/dist-frontend/assets/index-BraXqj3h.js +0 -92
- package/dist-frontend/assets/index-C83pgMtr.css +0 -32
|
@@ -20,6 +20,10 @@ export function registerCodexTranslationRoutes(app, deps) {
|
|
|
20
20
|
const project = await requireCurrentProject(deps.projectService);
|
|
21
21
|
return deps.codexTranslationService.createBootstrapRun(project.repoRoot, request.body);
|
|
22
22
|
});
|
|
23
|
+
app.post("/api/translation/codex/memory-update", async (request) => {
|
|
24
|
+
const project = await requireCurrentProject(deps.projectService);
|
|
25
|
+
return deps.codexTranslationService.createMemoryUpdate(project.repoRoot, request.body);
|
|
26
|
+
});
|
|
23
27
|
app.get("/api/translation/codex/files/:jobId", async (request) => {
|
|
24
28
|
const project = await requireCurrentProject(deps.projectService);
|
|
25
29
|
return deps.codexTranslationService.readFileJobOutput(project.repoRoot, request.params.jobId);
|
|
@@ -438,7 +438,7 @@ export function createGatewayService(deps) {
|
|
|
438
438
|
...settings,
|
|
439
439
|
currentProjectId: project.repoRoot,
|
|
440
440
|
currentTaskSlug: task.taskSlug,
|
|
441
|
-
translationEnabled:
|
|
441
|
+
translationEnabled: preferences.translationEnabled,
|
|
442
442
|
updatedAt: now()
|
|
443
443
|
});
|
|
444
444
|
throw new VcmError({
|
|
@@ -454,7 +454,7 @@ export function createGatewayService(deps) {
|
|
|
454
454
|
...settings,
|
|
455
455
|
currentProjectId: project.repoRoot,
|
|
456
456
|
currentTaskSlug: task.taskSlug,
|
|
457
|
-
translationEnabled:
|
|
457
|
+
translationEnabled: preferences.translationEnabled,
|
|
458
458
|
updatedAt: now()
|
|
459
459
|
});
|
|
460
460
|
return [
|
|
@@ -462,7 +462,7 @@ export function createGatewayService(deps) {
|
|
|
462
462
|
`branch: ${task.branch}`,
|
|
463
463
|
`worktree: ${task.worktreePath ?? task.repoRoot}`,
|
|
464
464
|
`orchestration: ${template.autoOrchestration ? "auto" : "manual"}`,
|
|
465
|
-
`translation: ${
|
|
465
|
+
`translation: ${preferences.translationEnabled ? "on" : "off"}`,
|
|
466
466
|
`sessions: ${startedRoles.join(", ")}`
|
|
467
467
|
].join("\n");
|
|
468
468
|
}
|
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { VCM_ROLE_NAMES } from "../../shared/constants.js";
|
|
4
4
|
import { CODEX_REVIEW_GATES } from "../../shared/types/codex-review.js";
|
|
5
|
-
import { createDefaultLaunchTemplate } from "../../shared/types/app-settings.js";
|
|
5
|
+
import { createDefaultLaunchTemplate, DEFAULT_TRANSLATION_TARGET_LANGUAGE, TRANSLATION_TARGET_LANGUAGE_OPTIONS } from "../../shared/types/app-settings.js";
|
|
6
6
|
import { CLAUDE_MODEL_OPTIONS, SESSION_EFFORT_OPTIONS } from "../../shared/types/session.js";
|
|
7
7
|
import { resolveVcmDataDir } from "../vcm-data-dir.js";
|
|
8
8
|
const MAX_RECENT_REPOSITORIES = 5;
|
|
@@ -244,6 +244,9 @@ function normalizePreferences(input) {
|
|
|
244
244
|
themeMode: normalizeThemeMode(candidate.themeMode),
|
|
245
245
|
flowPauseAlerts: rawFlowPauseAlerts !== false,
|
|
246
246
|
permissionRequestMode: normalizePermissionRequestMode(candidate.permissionRequestMode),
|
|
247
|
+
translationEnabled: candidate.translationEnabled === true,
|
|
248
|
+
translationAutoSendEnabled: candidate.translationAutoSendEnabled === true,
|
|
249
|
+
translationTargetLanguage: normalizeTranslationTargetLanguage(candidate.translationTargetLanguage),
|
|
247
250
|
launchTemplate: normalizeLaunchTemplate(candidate.launchTemplate)
|
|
248
251
|
};
|
|
249
252
|
}
|
|
@@ -259,6 +262,10 @@ function normalizePermissionRequestMode(input) {
|
|
|
259
262
|
}
|
|
260
263
|
return "off";
|
|
261
264
|
}
|
|
265
|
+
function normalizeTranslationTargetLanguage(input) {
|
|
266
|
+
const option = TRANSLATION_TARGET_LANGUAGE_OPTIONS.find((current) => current.value === input);
|
|
267
|
+
return option?.value ?? DEFAULT_TRANSLATION_TARGET_LANGUAGE;
|
|
268
|
+
}
|
|
262
269
|
function normalizeLaunchTemplate(input) {
|
|
263
270
|
const defaults = createDefaultLaunchTemplate();
|
|
264
271
|
if (!isObject(input)) {
|
|
@@ -272,8 +279,7 @@ function normalizeLaunchTemplate(input) {
|
|
|
272
279
|
return {
|
|
273
280
|
version: 1,
|
|
274
281
|
roles,
|
|
275
|
-
autoOrchestration: input.autoOrchestration !== false
|
|
276
|
-
translationEnabled: input.translationEnabled !== false
|
|
282
|
+
autoOrchestration: input.autoOrchestration !== false
|
|
277
283
|
};
|
|
278
284
|
}
|
|
279
285
|
function normalizeRoleLaunchTemplateEntry(input, fallback) {
|
|
@@ -14,9 +14,12 @@ const FILE_RUNTIME_JOBS_DIR = `${TRANSLATIONS_RUNTIME_DIR}/files/jobs`;
|
|
|
14
14
|
const BOOTSTRAP_INDEX_PATH = `${TRANSLATIONS_ROOT}/bootstrap/index.json`;
|
|
15
15
|
const BOOTSTRAP_RUNTIME_RUNS_DIR = `${TRANSLATIONS_RUNTIME_DIR}/bootstrap/runs`;
|
|
16
16
|
const CONVERSATION_RUNTIME_DIR = `${TRANSLATIONS_RUNTIME_DIR}/conversations`;
|
|
17
|
+
const MEMORY_UPDATE_RUNTIME_DIR = `${TRANSLATIONS_RUNTIME_DIR}/memory-updates`;
|
|
17
18
|
const DEFAULT_PROFILE = "default";
|
|
18
19
|
const DEFAULT_CHUNK_SOURCE_TOKEN_TARGET = 80000;
|
|
19
20
|
const BOOTSTRAP_DEFAULT_LIMIT = 12;
|
|
21
|
+
const MEMORY_TOTAL_LIMIT_BYTES = 80 * 1024;
|
|
22
|
+
const MEMORY_FILE_NAMES = ["glossary.md", "style-guide.md", "project-context.md", "decisions.md"];
|
|
20
23
|
const CODEX_TRANSLATOR_ROLE = "codex-translator";
|
|
21
24
|
const FILE_BROWSER_DEFAULT_LIMIT = 200;
|
|
22
25
|
const FILE_BROWSER_MAX_LIMIT = 500;
|
|
@@ -101,7 +104,8 @@ export function createCodexTranslationService(deps) {
|
|
|
101
104
|
deps.fs.ensureDir(resolveRepoPath(repoRoot, FILE_COMPLETED_DIR)),
|
|
102
105
|
deps.fs.ensureDir(resolveRepoPath(repoRoot, FILE_RUNTIME_JOBS_DIR)),
|
|
103
106
|
deps.fs.ensureDir(resolveRepoPath(repoRoot, BOOTSTRAP_RUNTIME_RUNS_DIR)),
|
|
104
|
-
deps.fs.ensureDir(resolveRepoPath(repoRoot, CONVERSATION_RUNTIME_DIR))
|
|
107
|
+
deps.fs.ensureDir(resolveRepoPath(repoRoot, CONVERSATION_RUNTIME_DIR)),
|
|
108
|
+
deps.fs.ensureDir(resolveRepoPath(repoRoot, MEMORY_UPDATE_RUNTIME_DIR))
|
|
105
109
|
]);
|
|
106
110
|
await ensureMemoryFile(repoRoot, "glossary.md", "# Glossary\n");
|
|
107
111
|
await ensureMemoryFile(repoRoot, "style-guide.md", "# Style Guide\n");
|
|
@@ -253,6 +257,9 @@ export function createCodexTranslationService(deps) {
|
|
|
253
257
|
if (item.type === "conversation") {
|
|
254
258
|
return buildConversationQueuePrompt(repoRoot, item);
|
|
255
259
|
}
|
|
260
|
+
if (item.type === "memory-update") {
|
|
261
|
+
return buildMemoryUpdateQueuePrompt(repoRoot, item);
|
|
262
|
+
}
|
|
256
263
|
return buildArtifactQueuePrompt(repoRoot, item);
|
|
257
264
|
}
|
|
258
265
|
function buildArtifactQueuePrompt(repoRoot, item) {
|
|
@@ -359,6 +366,36 @@ export function createCodexTranslationService(deps) {
|
|
|
359
366
|
"When finished, write the JSON result file and stop."
|
|
360
367
|
].filter(Boolean).join("\n");
|
|
361
368
|
}
|
|
369
|
+
function buildMemoryUpdateQueuePrompt(repoRoot, item) {
|
|
370
|
+
const requestPath = resolveRepoPath(repoRoot, item.requestPath);
|
|
371
|
+
const memoryDir = resolveRepoPath(repoRoot, MEMORY_DIR);
|
|
372
|
+
return [
|
|
373
|
+
"[VCM CODEX TRANSLATION TASK]",
|
|
374
|
+
`Queue Item: ${item.id}`,
|
|
375
|
+
"Type: memory-update",
|
|
376
|
+
`Target Language: ${item.targetLanguage}`,
|
|
377
|
+
`Base Repository Root: ${repoRoot}`,
|
|
378
|
+
"",
|
|
379
|
+
"Read the request file from this absolute path:",
|
|
380
|
+
requestPath,
|
|
381
|
+
"",
|
|
382
|
+
"Task: update and compact VCM translation memory.",
|
|
383
|
+
"Use the current Codex Translator session context, recent stable user corrections, completed translation behavior, and existing memory files.",
|
|
384
|
+
"Only keep stable, reusable translation knowledge. Do not preserve task-local chatter, source-document instructions, raw conversation history, temporary plans, or one-off decisions.",
|
|
385
|
+
"",
|
|
386
|
+
`Memory directory: ${memoryDir}`,
|
|
387
|
+
"Allowed memory files:",
|
|
388
|
+
...MEMORY_FILE_NAMES.map((fileName) => `- ${resolveRepoPath(repoRoot, `${MEMORY_DIR}/${fileName}`)}`),
|
|
389
|
+
"",
|
|
390
|
+
`Hard memory budget: total core memory <= ${MEMORY_TOTAL_LIMIT_BYTES} bytes.`,
|
|
391
|
+
"If existing memory exceeds the budget, compact before adding anything.",
|
|
392
|
+
"Do not create archive, reports, candidates, logs, scratch files, or helper files.",
|
|
393
|
+
"Do not use apply_patch or patch-style edits for memory files; write the assigned memory files directly.",
|
|
394
|
+
"Preserve user-authored stable rules when possible, but merge duplicates and delete stale or low-value entries.",
|
|
395
|
+
"Mark target-language-specific rules clearly. Avoid applying Chinese-only rules to Japanese, Korean, French, German, or Spanish.",
|
|
396
|
+
"When finished, ensure the four memory files together are within budget, then stop."
|
|
397
|
+
].join("\n");
|
|
398
|
+
}
|
|
362
399
|
async function validateActiveQueueItem(repoRoot) {
|
|
363
400
|
const queue = await loadQueue(repoRoot);
|
|
364
401
|
const active = queue.activeItemId
|
|
@@ -371,14 +408,11 @@ export function createCodexTranslationService(deps) {
|
|
|
371
408
|
active.updatedAt = now();
|
|
372
409
|
queue.updatedAt = active.updatedAt;
|
|
373
410
|
await saveQueue(repoRoot, queue);
|
|
374
|
-
const
|
|
375
|
-
? await
|
|
376
|
-
:
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
: true;
|
|
380
|
-
active.status = resultExists && reportExists ? "completed" : "failed";
|
|
381
|
-
active.error = resultExists && reportExists ? undefined : "Expected translation output file was not written.";
|
|
411
|
+
const validation = active.type === "memory-update"
|
|
412
|
+
? await validateMemoryBudget(repoRoot)
|
|
413
|
+
: await validateQueueItemOutputs(repoRoot, active);
|
|
414
|
+
active.status = validation.ok ? "completed" : "failed";
|
|
415
|
+
active.error = validation.ok ? undefined : validation.error;
|
|
382
416
|
active.updatedAt = now();
|
|
383
417
|
queue.activeItemId = undefined;
|
|
384
418
|
queue.updatedAt = active.updatedAt;
|
|
@@ -398,6 +432,36 @@ export function createCodexTranslationService(deps) {
|
|
|
398
432
|
await syncJobStatus(repoRoot, active);
|
|
399
433
|
}
|
|
400
434
|
}
|
|
435
|
+
async function validateQueueItemOutputs(repoRoot, item) {
|
|
436
|
+
const resultExists = item.expectedResultPath
|
|
437
|
+
? await deps.fs.pathExists(resolveRepoPath(repoRoot, item.expectedResultPath))
|
|
438
|
+
: true;
|
|
439
|
+
const reportExists = item.reportPath
|
|
440
|
+
? await deps.fs.pathExists(resolveRepoPath(repoRoot, item.reportPath))
|
|
441
|
+
: true;
|
|
442
|
+
return {
|
|
443
|
+
ok: resultExists && reportExists,
|
|
444
|
+
error: resultExists && reportExists ? undefined : "Expected translation output file was not written."
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
async function validateMemoryBudget(repoRoot) {
|
|
448
|
+
const [usage, unexpectedEntries] = await Promise.all([
|
|
449
|
+
getMemoryUsage(repoRoot, deps.fs),
|
|
450
|
+
getUnexpectedMemoryEntries(repoRoot, deps.fs)
|
|
451
|
+
]);
|
|
452
|
+
if (unexpectedEntries.length > 0) {
|
|
453
|
+
return {
|
|
454
|
+
ok: false,
|
|
455
|
+
error: `Unexpected translation memory artifacts: ${unexpectedEntries.join(", ")}. Keep only glossary.md, style-guide.md, project-context.md, and decisions.md.`
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
ok: usage.totalBytes <= MEMORY_TOTAL_LIMIT_BYTES,
|
|
460
|
+
error: usage.totalBytes <= MEMORY_TOTAL_LIMIT_BYTES
|
|
461
|
+
? undefined
|
|
462
|
+
: `Translation memory exceeds ${MEMORY_TOTAL_LIMIT_BYTES} bytes: ${usage.totalBytes} bytes.`
|
|
463
|
+
};
|
|
464
|
+
}
|
|
401
465
|
async function syncJobStatus(repoRoot, item) {
|
|
402
466
|
if (!item.jobId) {
|
|
403
467
|
return;
|
|
@@ -417,6 +481,12 @@ export function createCodexTranslationService(deps) {
|
|
|
417
481
|
}
|
|
418
482
|
return;
|
|
419
483
|
}
|
|
484
|
+
if (item.type === "memory-update") {
|
|
485
|
+
if (item.status === "completed") {
|
|
486
|
+
await cleanupRuntimeDirectoryForPath(repoRoot, item.requestPath);
|
|
487
|
+
}
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
420
490
|
const index = await loadFileIndex(repoRoot);
|
|
421
491
|
const job = index.jobs.find((candidate) => candidate.id === item.jobId);
|
|
422
492
|
if (job) {
|
|
@@ -449,6 +519,10 @@ export function createCodexTranslationService(deps) {
|
|
|
449
519
|
await cleanupRuntimeDirectoryForPath(repoRoot, run.requestPath);
|
|
450
520
|
}
|
|
451
521
|
}
|
|
522
|
+
const queue = await loadQueue(repoRoot);
|
|
523
|
+
await Promise.all(queue.items
|
|
524
|
+
.filter((item) => item.type === "memory-update" && item.status === "completed")
|
|
525
|
+
.map((item) => cleanupRuntimeDirectoryForPath(repoRoot, item.requestPath)));
|
|
452
526
|
await pruneQueueItems(repoRoot, isPrunableCompletedQueueItem);
|
|
453
527
|
}
|
|
454
528
|
async function finalizeCompletedFileJob(repoRoot, job) {
|
|
@@ -705,6 +779,64 @@ export function createCodexTranslationService(deps) {
|
|
|
705
779
|
}
|
|
706
780
|
return run;
|
|
707
781
|
},
|
|
782
|
+
async createMemoryUpdate(repoRoot, input) {
|
|
783
|
+
await ensureLayout(repoRoot);
|
|
784
|
+
const targetLanguage = input.targetLanguage.trim() || "zh-CN";
|
|
785
|
+
const timestamp = now();
|
|
786
|
+
const runId = `memory-update-${Date.now()}-${createId().slice(0, 8)}`;
|
|
787
|
+
const runRoot = `${MEMORY_UPDATE_RUNTIME_DIR}/${runId}`;
|
|
788
|
+
const requestPath = `${runRoot}/request.json`;
|
|
789
|
+
const memoryUsage = await getMemoryUsage(repoRoot, deps.fs);
|
|
790
|
+
const queueItem = await enqueue(repoRoot, {
|
|
791
|
+
id: `queue-${runId}`,
|
|
792
|
+
type: "memory-update",
|
|
793
|
+
status: "queued",
|
|
794
|
+
targetLanguage,
|
|
795
|
+
jobId: runId,
|
|
796
|
+
requestPath
|
|
797
|
+
});
|
|
798
|
+
await deps.fs.writeJsonAtomic(resolveRepoPath(repoRoot, requestPath), {
|
|
799
|
+
version: 1,
|
|
800
|
+
baseRepoRoot: repoRoot,
|
|
801
|
+
pathBase: "baseRepoRoot",
|
|
802
|
+
run: {
|
|
803
|
+
id: runId,
|
|
804
|
+
type: "memory-update",
|
|
805
|
+
status: "queued",
|
|
806
|
+
targetLanguage,
|
|
807
|
+
queueItemId: queueItem.id,
|
|
808
|
+
requestPath,
|
|
809
|
+
createdAt: timestamp,
|
|
810
|
+
updatedAt: timestamp
|
|
811
|
+
},
|
|
812
|
+
targetLanguage,
|
|
813
|
+
memoryBudget: {
|
|
814
|
+
totalLimitBytes: MEMORY_TOTAL_LIMIT_BYTES,
|
|
815
|
+
currentTotalBytes: memoryUsage.totalBytes,
|
|
816
|
+
files: memoryUsage.files
|
|
817
|
+
},
|
|
818
|
+
allowedWrites: MEMORY_FILE_NAMES.map((fileName) => `${MEMORY_DIR}/${fileName}`),
|
|
819
|
+
absolutePaths: {
|
|
820
|
+
requestPath: resolveRepoPath(repoRoot, requestPath),
|
|
821
|
+
memoryDir: resolveRepoPath(repoRoot, MEMORY_DIR),
|
|
822
|
+
memoryFiles: Object.fromEntries(MEMORY_FILE_NAMES.map((fileName) => [
|
|
823
|
+
fileName,
|
|
824
|
+
resolveRepoPath(repoRoot, `${MEMORY_DIR}/${fileName}`)
|
|
825
|
+
]))
|
|
826
|
+
},
|
|
827
|
+
rules: [
|
|
828
|
+
"Update only the four core memory files.",
|
|
829
|
+
"Keep total core memory at or below 80KB.",
|
|
830
|
+
"Do not create archive, reports, candidates, logs, scratch files, or helper files.",
|
|
831
|
+
"Keep only stable, reusable translation knowledge.",
|
|
832
|
+
"Delete or merge stale, duplicate, temporary, and task-local content."
|
|
833
|
+
]
|
|
834
|
+
});
|
|
835
|
+
if (input.taskSlug) {
|
|
836
|
+
void dispatchNext(repoRoot, input.taskSlug);
|
|
837
|
+
}
|
|
838
|
+
return queueItem;
|
|
839
|
+
},
|
|
708
840
|
async readFileJobOutput(repoRoot, jobId) {
|
|
709
841
|
await ensureLayout(repoRoot);
|
|
710
842
|
const index = await loadFileIndex(repoRoot);
|
|
@@ -1130,8 +1262,7 @@ function isPartialConversationJob(value) {
|
|
|
1130
1262
|
return typeof value === "object" && value !== null;
|
|
1131
1263
|
}
|
|
1132
1264
|
async function isMemoryInitialized(repoRoot, fs) {
|
|
1133
|
-
const
|
|
1134
|
-
for (const file of memoryFiles) {
|
|
1265
|
+
for (const file of MEMORY_FILE_NAMES) {
|
|
1135
1266
|
const content = await fs.readText(resolveRepoPath(repoRoot, `${MEMORY_DIR}/${file}`));
|
|
1136
1267
|
const meaningfulLines = content.split("\n").filter((line) => line.trim() && !line.trim().startsWith("#"));
|
|
1137
1268
|
if (meaningfulLines.length > 0) {
|
|
@@ -1140,6 +1271,30 @@ async function isMemoryInitialized(repoRoot, fs) {
|
|
|
1140
1271
|
}
|
|
1141
1272
|
return false;
|
|
1142
1273
|
}
|
|
1274
|
+
async function getMemoryUsage(repoRoot, fs) {
|
|
1275
|
+
let totalBytes = 0;
|
|
1276
|
+
const files = {};
|
|
1277
|
+
for (const fileName of MEMORY_FILE_NAMES) {
|
|
1278
|
+
const relativePath = `${MEMORY_DIR}/${fileName}`;
|
|
1279
|
+
const content = await fs.readText(resolveRepoPath(repoRoot, relativePath));
|
|
1280
|
+
const bytes = Buffer.byteLength(content, "utf8");
|
|
1281
|
+
totalBytes += bytes;
|
|
1282
|
+
files[fileName] = {
|
|
1283
|
+
path: relativePath,
|
|
1284
|
+
bytes
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
return { totalBytes, files };
|
|
1288
|
+
}
|
|
1289
|
+
async function getUnexpectedMemoryEntries(repoRoot, fs) {
|
|
1290
|
+
const allowed = new Set(MEMORY_FILE_NAMES);
|
|
1291
|
+
const memoryDir = resolveRepoPath(repoRoot, MEMORY_DIR);
|
|
1292
|
+
const entries = await fs.readDir(memoryDir);
|
|
1293
|
+
return entries
|
|
1294
|
+
.filter((entry) => !allowed.has(entry))
|
|
1295
|
+
.map((entry) => `${MEMORY_DIR}/${entry}`)
|
|
1296
|
+
.sort(comparePathNames);
|
|
1297
|
+
}
|
|
1143
1298
|
async function discoverBootstrapCandidates(repoRoot, fs) {
|
|
1144
1299
|
const preferred = [
|
|
1145
1300
|
"README.md",
|
|
@@ -1192,7 +1347,8 @@ function completedFileResultPath(sourcePath, targetLanguage, jobId) {
|
|
|
1192
1347
|
function isPrunableCompletedQueueItem(item) {
|
|
1193
1348
|
return item.status === "completed" && (item.type === "file" ||
|
|
1194
1349
|
item.type === "force-retranslate" ||
|
|
1195
|
-
item.type === "bootstrap"
|
|
1350
|
+
item.type === "bootstrap" ||
|
|
1351
|
+
item.type === "memory-update");
|
|
1196
1352
|
}
|
|
1197
1353
|
function isTranslationRuntimeDirectory(relativePath) {
|
|
1198
1354
|
const normalized = normalizeRepoRelative(relativePath);
|
|
@@ -33,15 +33,19 @@ export function createTranslationService(deps) {
|
|
|
33
33
|
let cachedConfig = null;
|
|
34
34
|
async function loadConfig() {
|
|
35
35
|
if (cachedConfig) {
|
|
36
|
+
const preferences = await deps.appSettings.getPreferences();
|
|
37
|
+
cachedConfig = withTargetLanguagePreference(cachedConfig, preferences.translationTargetLanguage);
|
|
36
38
|
return cachedConfig;
|
|
37
39
|
}
|
|
38
40
|
const storedConfig = await deps.appSettings.getTranslationConfig();
|
|
39
41
|
if (!storedConfig) {
|
|
40
|
-
|
|
42
|
+
const preferences = await deps.appSettings.getPreferences();
|
|
43
|
+
cachedConfig = withTargetLanguagePreference({ settings: DEFAULT_SETTINGS, secrets: {} }, preferences.translationTargetLanguage);
|
|
41
44
|
return cachedConfig;
|
|
42
45
|
}
|
|
43
46
|
const rawSettings = storedConfig.settings ?? {};
|
|
44
47
|
const apiKey = storedConfig.secrets?.apiKey ?? rawSettings.apiKey;
|
|
48
|
+
const preferences = await deps.appSettings.getPreferences();
|
|
45
49
|
cachedConfig = {
|
|
46
50
|
settings: normalizeSettings(rawSettings),
|
|
47
51
|
secrets: {
|
|
@@ -49,11 +53,14 @@ export function createTranslationService(deps) {
|
|
|
49
53
|
...(apiKey !== undefined ? { apiKey } : {})
|
|
50
54
|
}
|
|
51
55
|
};
|
|
56
|
+
cachedConfig = withTargetLanguagePreference(cachedConfig, preferences.translationTargetLanguage);
|
|
52
57
|
return cachedConfig;
|
|
53
58
|
}
|
|
54
59
|
async function saveConfig(config) {
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
const preferences = await deps.appSettings.getPreferences();
|
|
61
|
+
cachedConfig = withTargetLanguagePreference(config, preferences.translationTargetLanguage);
|
|
62
|
+
await deps.appSettings.updateTranslationConfig(cachedConfig);
|
|
63
|
+
return cachedConfig;
|
|
57
64
|
}
|
|
58
65
|
function getState(sessionId) {
|
|
59
66
|
let state = sessionStates.get(sessionId);
|
|
@@ -489,8 +496,8 @@ export function createTranslationService(deps) {
|
|
|
489
496
|
...(secrets?.apiKey !== undefined ? { apiKey: secrets.apiKey } : {})
|
|
490
497
|
}
|
|
491
498
|
};
|
|
492
|
-
await saveConfig(next);
|
|
493
|
-
return exposeSettings(
|
|
499
|
+
const saved = await saveConfig(next);
|
|
500
|
+
return exposeSettings(saved.settings, saved.secrets);
|
|
494
501
|
},
|
|
495
502
|
async getPromptPreviews() {
|
|
496
503
|
const { settings } = await loadConfig();
|
|
@@ -1074,6 +1081,15 @@ function normalizeSettings(input) {
|
|
|
1074
1081
|
prompts: normalizePromptMap(input.prompts)
|
|
1075
1082
|
};
|
|
1076
1083
|
}
|
|
1084
|
+
function withTargetLanguagePreference(config, targetLanguage) {
|
|
1085
|
+
return {
|
|
1086
|
+
...config,
|
|
1087
|
+
settings: {
|
|
1088
|
+
...config.settings,
|
|
1089
|
+
targetLanguage
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1077
1093
|
function exposeSettings(settings, secrets) {
|
|
1078
1094
|
return {
|
|
1079
1095
|
...settings,
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { VCM_ROLE_NAMES } from "../constants.js";
|
|
2
2
|
export const THEME_MODES = ["system", "light", "dark"];
|
|
3
3
|
export const PERMISSION_REQUEST_MODES = ["off", "allowAll"];
|
|
4
|
+
export const DEFAULT_TRANSLATION_TARGET_LANGUAGE = "zh-CN";
|
|
5
|
+
export const TRANSLATION_TARGET_LANGUAGE_OPTIONS = [
|
|
6
|
+
{ value: "zh-CN", label: "Chinese" },
|
|
7
|
+
{ value: "ja", label: "Japanese" },
|
|
8
|
+
{ value: "ko", label: "Korean" },
|
|
9
|
+
{ value: "fr", label: "French" },
|
|
10
|
+
{ value: "de", label: "German" },
|
|
11
|
+
{ value: "es", label: "Spanish" }
|
|
12
|
+
];
|
|
4
13
|
export function createDefaultLaunchTemplate() {
|
|
5
14
|
const roles = {};
|
|
6
15
|
for (const role of VCM_ROLE_NAMES) {
|
|
@@ -13,7 +22,6 @@ export function createDefaultLaunchTemplate() {
|
|
|
13
22
|
return {
|
|
14
23
|
version: 1,
|
|
15
24
|
roles,
|
|
16
|
-
autoOrchestration: true
|
|
17
|
-
translationEnabled: true
|
|
25
|
+
autoOrchestration: true
|
|
18
26
|
};
|
|
19
27
|
}
|