@vxnus/siduri 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtin-manifests.js +26 -1
- package/dist/clean-machine-e2e.test.js +11 -11
- package/dist/configurators/voice.js +33 -11
- package/dist/generator.d.ts +1 -0
- package/dist/generator.js +49 -2
- package/dist/generator.test.js +5 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -2
- package/dist/release-check.js +2 -2
- package/package.json +1 -1
|
@@ -383,7 +383,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
383
383
|
{
|
|
384
384
|
name: '@siduri-x/voice',
|
|
385
385
|
organType: 'voice',
|
|
386
|
-
version: '1.0.
|
|
386
|
+
version: '1.0.2',
|
|
387
387
|
displayName: 'Voice (VOICEVOX Speech Synthesis)',
|
|
388
388
|
description: 'Queued speech synthesis and audio rendering lifecycle adapter',
|
|
389
389
|
entrypoint: './dist/index.js',
|
|
@@ -408,6 +408,19 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
408
408
|
maxQueueDepth: {
|
|
409
409
|
type: 'number',
|
|
410
410
|
default: 50
|
|
411
|
+
},
|
|
412
|
+
rvc: {
|
|
413
|
+
type: 'object',
|
|
414
|
+
properties: {
|
|
415
|
+
enabled: { type: 'boolean', default: false },
|
|
416
|
+
serviceUrl: { type: 'string', default: 'http://localhost:50055' },
|
|
417
|
+
modelName: { type: 'string' },
|
|
418
|
+
modelPath: { type: 'string' },
|
|
419
|
+
indexPath: { type: 'string' },
|
|
420
|
+
pitchShift: { type: 'number', default: 0 },
|
|
421
|
+
f0Method: { type: 'string', enum: ['rmvpe', 'pm', 'harvest', 'crepe'], default: 'rmvpe' },
|
|
422
|
+
indexRate: { type: 'number', default: 0.75 }
|
|
423
|
+
}
|
|
411
424
|
}
|
|
412
425
|
}
|
|
413
426
|
},
|
|
@@ -418,6 +431,13 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
418
431
|
secret: false,
|
|
419
432
|
default: 'http://localhost:50021',
|
|
420
433
|
description: 'Base URL of the running VOICEVOX engine'
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: 'RVC_SERVICE_URL',
|
|
437
|
+
required: false,
|
|
438
|
+
secret: false,
|
|
439
|
+
default: 'http://localhost:50055',
|
|
440
|
+
description: 'Base URL of headless RVC voice conversion microservice'
|
|
421
441
|
}
|
|
422
442
|
],
|
|
423
443
|
services: [
|
|
@@ -425,6 +445,11 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
425
445
|
name: 'VOICEVOX Engine',
|
|
426
446
|
kind: 'http_service',
|
|
427
447
|
optional: false
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
name: 'RVC Headless Service',
|
|
451
|
+
kind: 'http_service',
|
|
452
|
+
optional: true
|
|
428
453
|
}
|
|
429
454
|
],
|
|
430
455
|
database: null,
|
|
@@ -22,9 +22,9 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
22
22
|
{ filter: '@siduri-x/vision', tarName: 'siduri-x-vision-1.0.1.tgz', isOrgan: true },
|
|
23
23
|
{ filter: '@siduri-x/hands', tarName: 'siduri-x-hands-1.0.1.tgz', isOrgan: true },
|
|
24
24
|
{ filter: '@siduri-x/body', tarName: 'siduri-x-body-1.0.1.tgz', isOrgan: true },
|
|
25
|
-
{ filter: '@siduri-x/voice', tarName: 'siduri-x-voice-1.0.
|
|
25
|
+
{ filter: '@siduri-x/voice', tarName: 'siduri-x-voice-1.0.2.tgz', isOrgan: true },
|
|
26
26
|
{ filter: '@siduri-x/observation', tarName: 'siduri-x-observation-1.0.1.tgz', isOrgan: true },
|
|
27
|
-
{ filter: '@vxnus/siduri', tarName: 'vxnus-siduri-0.1.
|
|
27
|
+
{ filter: '@vxnus/siduri', tarName: 'vxnus-siduri-0.1.1.tgz', isOrgan: false },
|
|
28
28
|
];
|
|
29
29
|
beforeAll(() => {
|
|
30
30
|
// 1. Prepare clean directories
|
|
@@ -86,7 +86,7 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
86
86
|
}
|
|
87
87
|
});
|
|
88
88
|
test('memory organ tarball packages migrations/001_initial_schema.sql', () => {
|
|
89
|
-
const memoryTarPath = node_path_1.default.join(tempPackDir, 'siduri-x-memory-1.0.
|
|
89
|
+
const memoryTarPath = node_path_1.default.join(tempPackDir, 'siduri-x-memory-1.0.1.tgz');
|
|
90
90
|
const listing = (0, node_child_process_1.execSync)(`tar -tzf ${memoryTarPath}`, { encoding: 'utf8' });
|
|
91
91
|
expect(listing).toContain('package/migrations/001_initial_schema.sql');
|
|
92
92
|
});
|
|
@@ -106,8 +106,8 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
106
106
|
// Write instance files referencing packed tarballs directly for true clean machine install
|
|
107
107
|
const pkgObj = JSON.parse(files['package.json']);
|
|
108
108
|
pkgObj.dependencies = {
|
|
109
|
-
'@siduri-x/core': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-core-1.0.
|
|
110
|
-
'@siduri-x/brain': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-brain-1.0.
|
|
109
|
+
'@siduri-x/core': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-core-1.0.1.tgz')}`,
|
|
110
|
+
'@siduri-x/brain': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-brain-1.0.1.tgz')}`,
|
|
111
111
|
};
|
|
112
112
|
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'package.json'), JSON.stringify(pkgObj, null, 2) + '\n');
|
|
113
113
|
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.config.json'), files['siduri.config.json']);
|
|
@@ -150,9 +150,9 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
150
150
|
});
|
|
151
151
|
const pkgObj = JSON.parse(files['package.json']);
|
|
152
152
|
pkgObj.dependencies = {
|
|
153
|
-
'@siduri-x/core': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-core-1.0.
|
|
154
|
-
'@siduri-x/brain': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-brain-1.0.
|
|
155
|
-
'@siduri-x/hands': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-hands-1.0.
|
|
153
|
+
'@siduri-x/core': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-core-1.0.1.tgz')}`,
|
|
154
|
+
'@siduri-x/brain': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-brain-1.0.1.tgz')}`,
|
|
155
|
+
'@siduri-x/hands': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-hands-1.0.1.tgz')}`,
|
|
156
156
|
};
|
|
157
157
|
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'package.json'), JSON.stringify(pkgObj, null, 2) + '\n');
|
|
158
158
|
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.config.json'), files['siduri.config.json']);
|
|
@@ -185,9 +185,9 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
185
185
|
});
|
|
186
186
|
const pkgObj = JSON.parse(files['package.json']);
|
|
187
187
|
pkgObj.dependencies = {
|
|
188
|
-
'@siduri-x/core': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-core-1.0.
|
|
189
|
-
'@siduri-x/brain': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-brain-1.0.
|
|
190
|
-
'@siduri-x/memory': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-memory-1.0.
|
|
188
|
+
'@siduri-x/core': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-core-1.0.1.tgz')}`,
|
|
189
|
+
'@siduri-x/brain': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-brain-1.0.1.tgz')}`,
|
|
190
|
+
'@siduri-x/memory': `file:${node_path_1.default.join(tempPackDir, 'siduri-x-memory-1.0.1.tgz')}`,
|
|
191
191
|
};
|
|
192
192
|
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'package.json'), JSON.stringify(pkgObj, null, 2) + '\n');
|
|
193
193
|
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.config.json'), files['siduri.config.json']);
|
|
@@ -35,19 +35,41 @@ async function configureVoice(_context) {
|
|
|
35
35
|
default: '1',
|
|
36
36
|
validate: (v) => !isNaN(parseInt(v, 10)) || 'Speaker ID must be an integer.',
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
type: 'confirm',
|
|
40
|
+
name: 'enableRvc',
|
|
41
|
+
message: 'Enable Headless RVC (Voice Conversion via ./assets/voice/[name])?',
|
|
42
|
+
default: false,
|
|
43
|
+
},
|
|
38
44
|
]);
|
|
39
45
|
const speakerId = parseInt(answers.speakerId, 10) || 1;
|
|
46
|
+
const companionSlug = _context.companionName.toLowerCase().replace(/[^a-z0-9_-]/g, '') || 'default';
|
|
47
|
+
const config = {
|
|
48
|
+
provider: 'voicevox',
|
|
49
|
+
baseUrl: answers.baseUrl.trim(),
|
|
50
|
+
speakerId,
|
|
51
|
+
maxQueueDepth: 50,
|
|
52
|
+
};
|
|
53
|
+
const summary = {
|
|
54
|
+
Provider: 'VOICEVOX',
|
|
55
|
+
'Speaker ID': speakerId,
|
|
56
|
+
'Base URL': answers.baseUrl.trim(),
|
|
57
|
+
};
|
|
58
|
+
if (answers.enableRvc) {
|
|
59
|
+
config.rvc = {
|
|
60
|
+
enabled: true,
|
|
61
|
+
serviceUrl: 'http://localhost:50055',
|
|
62
|
+
modelName: companionSlug,
|
|
63
|
+
modelPath: `./assets/voice/${companionSlug}/${companionSlug}.pth`,
|
|
64
|
+
indexPath: `./assets/voice/${companionSlug}/${companionSlug}.index`,
|
|
65
|
+
pitchShift: 0,
|
|
66
|
+
f0Method: 'rmvpe',
|
|
67
|
+
indexRate: 0.75,
|
|
68
|
+
};
|
|
69
|
+
summary['RVC Post-Processor'] = `Enabled (assets/voice/${companionSlug}/)`;
|
|
70
|
+
}
|
|
40
71
|
return {
|
|
41
|
-
config
|
|
42
|
-
|
|
43
|
-
baseUrl: answers.baseUrl.trim(),
|
|
44
|
-
speakerId,
|
|
45
|
-
maxQueueDepth: 50,
|
|
46
|
-
},
|
|
47
|
-
summary: {
|
|
48
|
-
Provider: 'VOICEVOX',
|
|
49
|
-
'Speaker ID': speakerId,
|
|
50
|
-
'Base URL': answers.baseUrl.trim(),
|
|
51
|
-
},
|
|
72
|
+
config,
|
|
73
|
+
summary,
|
|
52
74
|
};
|
|
53
75
|
}
|
package/dist/generator.d.ts
CHANGED
package/dist/generator.js
CHANGED
|
@@ -107,6 +107,18 @@ function generateInstanceFiles(options) {
|
|
|
107
107
|
' - "50021:50021"',
|
|
108
108
|
].join('\n'));
|
|
109
109
|
}
|
|
110
|
+
if (hasVoice && voiceConfig?.rvc?.enabled) {
|
|
111
|
+
dockerServices.push([
|
|
112
|
+
' rvc:',
|
|
113
|
+
' image: ghcr.io/vxnus-studio/rvc-headless:latest',
|
|
114
|
+
' ports:',
|
|
115
|
+
' - "50055:50055"',
|
|
116
|
+
' volumes:',
|
|
117
|
+
' - ./assets/voice:/app/models',
|
|
118
|
+
' environment:',
|
|
119
|
+
' - RVC_MODELS_DIR=/app/models',
|
|
120
|
+
].join('\n'));
|
|
121
|
+
}
|
|
110
122
|
if (dockerServices.length > 0) {
|
|
111
123
|
const composeLines = [
|
|
112
124
|
'version: "3.8"',
|
|
@@ -201,7 +213,7 @@ function generateInstanceFiles(options) {
|
|
|
201
213
|
// 6. src/index.js
|
|
202
214
|
const importLines = [
|
|
203
215
|
`import { createServer } from 'node:http';`,
|
|
204
|
-
`import { readFile, stat } from 'node:fs/promises';`,
|
|
216
|
+
`import { readFile, stat, readdir } from 'node:fs/promises';`,
|
|
205
217
|
`import path from 'node:path';`,
|
|
206
218
|
`import { fileURLToPath } from 'node:url';`,
|
|
207
219
|
`import { SiduriRuntime } from '@siduri-x/core';`,
|
|
@@ -367,6 +379,33 @@ function generateInstanceFiles(options) {
|
|
|
367
379
|
` return;`,
|
|
368
380
|
` }`,
|
|
369
381
|
'',
|
|
382
|
+
` // API: Model Catalog Discovery (Body & Voice)`,
|
|
383
|
+
` if (pathname === '/api/models/body' && req.method === 'GET') {`,
|
|
384
|
+
` res.writeHead(200, { 'Content-Type': 'application/json' });`,
|
|
385
|
+
` try {`,
|
|
386
|
+
` const bodyDir = path.join(rootDir, 'assets', 'body', 'model');`,
|
|
387
|
+
` const entries = await readdir(bodyDir, { withFileTypes: true }).catch(() => []);`,
|
|
388
|
+
` const models = entries.filter((e) => e.isDirectory()).map((e) => e.name);`,
|
|
389
|
+
` res.end(JSON.stringify({ models: models.length ? models : ['default'] }));`,
|
|
390
|
+
` } catch {`,
|
|
391
|
+
` res.end(JSON.stringify({ models: ['default'] }));`,
|
|
392
|
+
` }`,
|
|
393
|
+
` return;`,
|
|
394
|
+
` }`,
|
|
395
|
+
'',
|
|
396
|
+
` if (pathname === '/api/models/voice' && req.method === 'GET') {`,
|
|
397
|
+
` res.writeHead(200, { 'Content-Type': 'application/json' });`,
|
|
398
|
+
` try {`,
|
|
399
|
+
` const voiceDir = path.join(rootDir, 'assets', 'voice');`,
|
|
400
|
+
` const entries = await readdir(voiceDir, { withFileTypes: true }).catch(() => []);`,
|
|
401
|
+
` const models = entries.filter((e) => e.isDirectory()).map((e) => e.name);`,
|
|
402
|
+
` res.end(JSON.stringify({ models: models.length ? models : ['default'] }));`,
|
|
403
|
+
` } catch {`,
|
|
404
|
+
` res.end(JSON.stringify({ models: ['default'] }));`,
|
|
405
|
+
` }`,
|
|
406
|
+
` return;`,
|
|
407
|
+
` }`,
|
|
408
|
+
'',
|
|
370
409
|
` // Static files & Next.js apps/web export routing`,
|
|
371
410
|
` const normalizedPath = pathname.replace(/^[/]+|[/]+$/g, '');`,
|
|
372
411
|
` const candidatePaths = [`,
|
|
@@ -448,8 +487,15 @@ function generateInstanceFiles(options) {
|
|
|
448
487
|
readmeLines.push('', `### ${dockerComposeYaml ? '4' : '3'}. Database Migrations`, 'Ensure `DATABASE_URL` in `.env` is reachable, then push the memory organ PostgreSQL schema:', '```bash', 'npx @vxnus/siduri db push', '```');
|
|
449
488
|
}
|
|
450
489
|
readmeLines.push('', `### ${hasMemory ? (dockerComposeYaml ? '5' : '4') : (dockerComposeYaml ? '4' : '3')}. Diagnostics & Health Probe`, 'Verify all environment variables, services, and organ connections:', '```bash', 'npm run doctor', '```', '', `### ${hasMemory ? (dockerComposeYaml ? '6' : '5') : (dockerComposeYaml ? '5' : '4')}. Start Companion & Web Console`, 'Launch your companion runtime and Web UI / Memory Control Panel:', '```bash', 'npm start', '```', 'Then open `http://localhost:3000` in your browser.');
|
|
490
|
+
const companionSlug = instanceName.toLowerCase().replace(/[^a-z0-9_-]/g, '') || 'default';
|
|
491
|
+
const createAssetsDirs = [];
|
|
451
492
|
if (hasBody) {
|
|
452
|
-
|
|
493
|
+
createAssetsDirs.push(`assets/body/model/${companionSlug}`);
|
|
494
|
+
readmeLines.push('', '### Body & Avatar Models', `Place your Live2D Cubism model assets into \`./assets/body/model/${companionSlug}/\`:`, '- `model.model3.json`', '- `model.moc3`', '- textures directory');
|
|
495
|
+
}
|
|
496
|
+
if (hasVoice) {
|
|
497
|
+
createAssetsDirs.push(`assets/voice/${companionSlug}`);
|
|
498
|
+
readmeLines.push('', '### Voice & RVC Models', `Place your character RVC voice models into \`./assets/voice/${companionSlug}/\`:`, `- \`${companionSlug}.pth\` (Target voice weights)`, `- \`${companionSlug}.index\` (Feature index file)`);
|
|
453
499
|
}
|
|
454
500
|
readmeLines.push('');
|
|
455
501
|
const readmeMd = readmeLines.join('\n');
|
|
@@ -463,6 +509,7 @@ function generateInstanceFiles(options) {
|
|
|
463
509
|
'src/index.js': srcIndexJs,
|
|
464
510
|
'public/index.html': webHtml,
|
|
465
511
|
createAssetsBodyDir: hasBody,
|
|
512
|
+
createAssetsDirs,
|
|
466
513
|
};
|
|
467
514
|
if (dockerComposeYaml) {
|
|
468
515
|
result['docker-compose.yml'] = dockerComposeYaml;
|
package/dist/generator.test.js
CHANGED
|
@@ -150,10 +150,13 @@ describe('Instance Generator Composition Invariants (Phase 3)', () => {
|
|
|
150
150
|
expect(pkg.dependencies['@siduri-x/hands']).toBeDefined();
|
|
151
151
|
expect(pkg.dependencies['@siduri-x/body']).toBeDefined();
|
|
152
152
|
expect(pkg.dependencies['@siduri-x/voice']).toBeDefined();
|
|
153
|
-
// Body asset
|
|
153
|
+
// Body & voice asset directories requested
|
|
154
154
|
expect(files.createAssetsBodyDir).toBe(true);
|
|
155
|
+
expect(files.createAssetsDirs).toContain('assets/body/model/companion-full');
|
|
156
|
+
expect(files.createAssetsDirs).toContain('assets/voice/companion-full');
|
|
155
157
|
// README mentions Live2D model assets & prerequisites
|
|
156
|
-
expect(files['README.md']).toContain('assets/body/model');
|
|
158
|
+
expect(files['README.md']).toContain('assets/body/model/companion-full');
|
|
159
|
+
expect(files['README.md']).toContain('assets/voice/companion-full');
|
|
157
160
|
expect(files['README.md']).toContain('Prerequisites');
|
|
158
161
|
// Docker compose generated for memory & voice
|
|
159
162
|
expect(files['docker-compose.yml']).toBeDefined();
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ const doctor_1 = require("./doctor");
|
|
|
26
26
|
const db_1 = require("./db");
|
|
27
27
|
const configurators_1 = require("./configurators");
|
|
28
28
|
const execFile = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
29
|
-
exports.CLI_VERSION = '0.1.
|
|
29
|
+
exports.CLI_VERSION = '0.1.1';
|
|
30
30
|
exports.colors = {
|
|
31
31
|
cyan: '\u001b[36m',
|
|
32
32
|
dim: '\u001b[2m',
|
|
@@ -244,7 +244,12 @@ async function runCreateWizard(targetDir) {
|
|
|
244
244
|
if (files['docker-compose.yml']) {
|
|
245
245
|
await (0, promises_1.writeFile)(node_path_1.default.join(projectDir, 'docker-compose.yml'), files['docker-compose.yml'], 'utf8');
|
|
246
246
|
}
|
|
247
|
-
if (files.
|
|
247
|
+
if (files.createAssetsDirs && files.createAssetsDirs.length > 0) {
|
|
248
|
+
for (const dir of files.createAssetsDirs) {
|
|
249
|
+
await (0, promises_1.mkdir)(node_path_1.default.join(projectDir, dir), { recursive: true });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
else if (files.createAssetsBodyDir) {
|
|
248
253
|
await (0, promises_1.mkdir)(node_path_1.default.join(projectDir, 'assets/body/model'), { recursive: true });
|
|
249
254
|
}
|
|
250
255
|
printSuccess(`Generated standalone files at ${projectDir}`);
|
package/dist/release-check.js
CHANGED
|
@@ -26,9 +26,9 @@ function runReleaseCheck(repoRoot = node_path_1.default.resolve(__dirname, '../.
|
|
|
26
26
|
{ name: '@siduri-x/vision', dir: 'packages/organs/vision', isOrgan: true, tarName: 'siduri-x-vision-1.0.1.tgz' },
|
|
27
27
|
{ name: '@siduri-x/hands', dir: 'packages/organs/hands', isOrgan: true, tarName: 'siduri-x-hands-1.0.1.tgz' },
|
|
28
28
|
{ name: '@siduri-x/body', dir: 'packages/organs/body', isOrgan: true, tarName: 'siduri-x-body-1.0.1.tgz' },
|
|
29
|
-
{ name: '@siduri-x/voice', dir: 'packages/organs/voice', isOrgan: true, tarName: 'siduri-x-voice-1.0.
|
|
29
|
+
{ name: '@siduri-x/voice', dir: 'packages/organs/voice', isOrgan: true, tarName: 'siduri-x-voice-1.0.2.tgz' },
|
|
30
30
|
{ name: '@siduri-x/observation', dir: 'packages/organs/observation', isOrgan: true, tarName: 'siduri-x-observation-1.0.1.tgz' },
|
|
31
|
-
{ name: '@vxnus/siduri', dir: 'cli', isOrgan: false, tarName: 'vxnus-siduri-0.1.
|
|
31
|
+
{ name: '@vxnus/siduri', dir: 'cli', isOrgan: false, tarName: 'vxnus-siduri-0.1.1.tgz' },
|
|
32
32
|
];
|
|
33
33
|
let packagesChecked = 0;
|
|
34
34
|
let tarballsInspected = 0;
|