@vxnus/siduri 0.0.7 → 0.0.9

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.
Files changed (91) hide show
  1. package/README.md +11 -1
  2. package/dist/builtin-manifests.js +12 -12
  3. package/dist/clean-machine-e2e.test.js +12 -12
  4. package/dist/configurators/behavior.d.ts +2 -0
  5. package/dist/configurators/behavior.js +36 -0
  6. package/dist/configurators/body.d.ts +2 -0
  7. package/dist/configurators/body.js +44 -0
  8. package/dist/configurators/brain.d.ts +6 -0
  9. package/dist/configurators/brain.js +80 -0
  10. package/dist/configurators/ear.d.ts +2 -0
  11. package/dist/configurators/ear.js +28 -0
  12. package/dist/configurators/hands.d.ts +2 -0
  13. package/dist/configurators/hands.js +27 -0
  14. package/dist/configurators/index.d.ts +23 -0
  15. package/dist/configurators/index.js +74 -0
  16. package/dist/configurators/knowledge.d.ts +6 -0
  17. package/dist/configurators/knowledge.js +100 -0
  18. package/dist/configurators/memory.d.ts +2 -0
  19. package/dist/configurators/memory.js +44 -0
  20. package/dist/configurators/observation.d.ts +2 -0
  21. package/dist/configurators/observation.js +12 -0
  22. package/dist/configurators/types.d.ts +12 -0
  23. package/dist/configurators/types.js +2 -0
  24. package/dist/configurators/vision.d.ts +2 -0
  25. package/dist/configurators/vision.js +39 -0
  26. package/dist/configurators/voice.d.ts +2 -0
  27. package/dist/configurators/voice.js +53 -0
  28. package/dist/configurators.test.d.ts +1 -0
  29. package/dist/configurators.test.js +275 -0
  30. package/dist/doctor-db.test.js +9 -3
  31. package/dist/generator.d.ts +2 -0
  32. package/dist/generator.js +289 -33
  33. package/dist/generator.test.js +20 -1
  34. package/dist/index.d.ts +16 -0
  35. package/dist/index.js +165 -54
  36. package/dist/providers/knowledge-hub.d.ts +37 -0
  37. package/dist/providers/knowledge-hub.js +141 -0
  38. package/dist/providers/openrouter.d.ts +32 -0
  39. package/dist/providers/openrouter.js +201 -0
  40. package/dist/release-check.js +12 -12
  41. package/dist/web-dist/404/index.html +1 -0
  42. package/dist/web-dist/404.html +1 -0
  43. package/dist/web-dist/__next.__PAGE__.txt +34 -0
  44. package/dist/web-dist/__next._full.txt +27 -0
  45. package/dist/web-dist/__next._tree.txt +3 -0
  46. package/dist/web-dist/_next/static/_zOqKpMPc6RDqk04fUScI/_buildManifest.js +11 -0
  47. package/dist/web-dist/_next/static/_zOqKpMPc6RDqk04fUScI/_clientMiddlewareManifest.js +1 -0
  48. package/dist/web-dist/_next/static/_zOqKpMPc6RDqk04fUScI/_ssgManifest.js +1 -0
  49. package/dist/web-dist/_next/static/chunks/0cz1d0mv5g_q7.js +1 -0
  50. package/dist/web-dist/_next/static/chunks/0uwih7sydv413.js +1 -0
  51. package/dist/web-dist/_next/static/chunks/1_yae15ffa5f6.js +1 -0
  52. package/dist/web-dist/_next/static/chunks/1hz3jco387c-k.js +1 -0
  53. package/dist/web-dist/_next/static/chunks/2bu5_paf4sm3x.css +1 -0
  54. package/dist/web-dist/_next/static/chunks/2kcqevsmv6kh2.js +31 -0
  55. package/dist/web-dist/_next/static/chunks/2s3d1bfr0l4b0.js +1 -0
  56. package/dist/web-dist/_next/static/chunks/32z87id9k6i2z.js +22 -0
  57. package/dist/web-dist/_next/static/chunks/39nefv-hczfy7.js +30 -0
  58. package/dist/web-dist/_next/static/chunks/3bx2gs3idjmo1.js +1 -0
  59. package/dist/web-dist/_next/static/chunks/turbopack-3eipe4c-imn5l.js +1 -0
  60. package/dist/web-dist/_next/static/media/favicon.2vob68tjqpejf.ico +0 -0
  61. package/dist/web-dist/_not-found/__next._full.txt +17 -0
  62. package/dist/web-dist/_not-found/__next._not-found.__PAGE__.txt +23 -0
  63. package/dist/web-dist/_not-found/__next._tree.txt +3 -0
  64. package/dist/web-dist/_not-found/index.html +1 -0
  65. package/dist/web-dist/_not-found/index.txt +17 -0
  66. package/dist/web-dist/chat/__next._full.txt +18 -0
  67. package/dist/web-dist/chat/__next._tree.txt +3 -0
  68. package/dist/web-dist/chat/__next.chat.__PAGE__.txt +24 -0
  69. package/dist/web-dist/chat/index.html +1 -0
  70. package/dist/web-dist/chat/index.txt +18 -0
  71. package/dist/web-dist/favicon.ico +0 -0
  72. package/dist/web-dist/index.html +1 -0
  73. package/dist/web-dist/index.txt +27 -0
  74. package/dist/web-dist/live2d/akaituno/akaituno.8192/texture_00.png +0 -0
  75. package/dist/web-dist/live2d/akaituno/akaituno.cdi3.json +296 -0
  76. package/dist/web-dist/live2d/akaituno/akaituno.moc3 +0 -0
  77. package/dist/web-dist/live2d/akaituno/akaituno.model3.json +42 -0
  78. package/dist/web-dist/live2d/akaituno/akaituno.physics3.json +839 -0
  79. package/dist/web-dist/live2d/akaituno/eye_color.exp3.json +12 -0
  80. package/dist/web-dist/live2d/akaituno/eyes_zetubou.exp3.json +12 -0
  81. package/dist/web-dist/live2d/akaituno/face_aozame.exp3.json +12 -0
  82. package/dist/web-dist/live2d/akaituno/hair_open.exp3.json +12 -0
  83. package/dist/web-dist/live2d/live2dcubismcore.min.js +9 -0
  84. package/dist/web-dist/operator/__next._full.txt +18 -0
  85. package/dist/web-dist/operator/__next._tree.txt +3 -0
  86. package/dist/web-dist/operator/__next.operator.__PAGE__.txt +24 -0
  87. package/dist/web-dist/operator/index.html +1 -0
  88. package/dist/web-dist/operator/index.txt +18 -0
  89. package/dist/web-template.d.ts +2 -0
  90. package/dist/web-template.js +547 -0
  91. package/package.json +7 -7
package/dist/generator.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateInstanceFiles = generateInstanceFiles;
4
+ const web_template_1 = require("./web-template");
4
5
  function getDefaultConfigForManifest(manifest) {
5
6
  const schema = manifest.configSchema || {};
6
7
  const props = schema.properties || {};
@@ -65,29 +66,87 @@ function getDefaultConfigForManifest(manifest) {
65
66
  function generateInstanceFiles(options) {
66
67
  const instanceName = options.name || 'my-siduri';
67
68
  const instanceId = options.id || 'default';
68
- const coreVersion = options.coreVersion || '^1.0.0';
69
+ const coreVersion = options.coreVersion || '^1.0.1';
69
70
  const manifests = options.selectedManifests;
70
- // 1. package.json
71
+ const hasMemory = manifests.some((m) => m.organType === 'memory');
72
+ const hasVoice = manifests.some((m) => m.organType === 'voice');
73
+ const hasBody = manifests.some((m) => m.organType === 'body');
74
+ const voiceConfig = options.organConfigs?.voice || options.organConfigs?.['@siduri-x/voice'];
75
+ const isVoicevox = hasVoice && (!voiceConfig || voiceConfig.provider === 'voicevox');
76
+ const memoryConfig = options.organConfigs?.memory || options.organConfigs?.['@siduri-x/memory'];
77
+ const isPostgresLocal = hasMemory && (!memoryConfig || memoryConfig.deployment === 'local' || memoryConfig.provider === 'postgres');
78
+ // 1. Optional docker-compose.yml
79
+ let dockerComposeYaml;
80
+ const dockerServices = [];
81
+ const dockerVolumes = [];
82
+ if (hasMemory && isPostgresLocal) {
83
+ dockerServices.push([
84
+ ' db:',
85
+ ' image: postgres:15',
86
+ ' environment:',
87
+ ' POSTGRES_USER: postgres',
88
+ ' POSTGRES_PASSWORD: password',
89
+ ' POSTGRES_DB: siduri',
90
+ ' ports:',
91
+ ' - "5432:5432"',
92
+ ' volumes:',
93
+ ' - postgres_data:/var/lib/postgresql/data',
94
+ ' healthcheck:',
95
+ ' test: ["CMD-SHELL", "pg_isready -U postgres"]',
96
+ ' interval: 2s',
97
+ ' timeout: 5s',
98
+ ' retries: 5',
99
+ ].join('\n'));
100
+ dockerVolumes.push(' postgres_data:');
101
+ }
102
+ if (isVoicevox) {
103
+ dockerServices.push([
104
+ ' voicevox:',
105
+ ' image: voicevox/voicevox_engine:cpu-latest',
106
+ ' ports:',
107
+ ' - "50021:50021"',
108
+ ].join('\n'));
109
+ }
110
+ if (dockerServices.length > 0) {
111
+ const composeLines = [
112
+ 'version: "3.8"',
113
+ '',
114
+ 'services:',
115
+ ...dockerServices,
116
+ ];
117
+ if (dockerVolumes.length > 0) {
118
+ composeLines.push('', 'volumes:', ...dockerVolumes);
119
+ }
120
+ composeLines.push('');
121
+ dockerComposeYaml = composeLines.join('\n');
122
+ }
123
+ // 2. package.json
71
124
  const dependencies = {
72
125
  '@siduri-x/core': coreVersion,
73
126
  };
74
127
  for (const m of manifests) {
75
- dependencies[m.name] = `^${m.version || '1.0.0'}`;
128
+ dependencies[m.name] = `^${m.version || '1.0.1'}`;
129
+ }
130
+ const scripts = {
131
+ start: 'node src/index.js',
132
+ dev: 'node --watch src/index.js',
133
+ doctor: 'siduri doctor',
134
+ db: 'siduri db',
135
+ };
136
+ if (dockerComposeYaml) {
137
+ scripts['services:up'] = 'docker compose up -d';
138
+ scripts['services:down'] = 'docker compose down';
139
+ scripts['services:logs'] = 'docker compose logs -f';
76
140
  }
77
141
  const packageJsonObj = {
78
142
  name: instanceName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'my-siduri',
79
143
  private: true,
80
144
  type: 'module',
81
- scripts: {
82
- start: 'node src/index.js',
83
- dev: 'node --watch src/index.js',
84
- doctor: 'siduri doctor',
85
- db: 'siduri db',
86
- },
145
+ scripts,
87
146
  dependencies,
88
147
  };
89
148
  const packageJson = JSON.stringify(packageJsonObj, null, 2) + '\n';
90
- // 2. siduri.config.json
149
+ // 3. siduri.config.json
91
150
  const organsConfig = {};
92
151
  for (const m of manifests) {
93
152
  const customConfig = options.organConfigs?.[m.configKey] || options.organConfigs?.[m.organType];
@@ -100,7 +159,7 @@ function generateInstanceFiles(options) {
100
159
  organs: organsConfig,
101
160
  };
102
161
  const siduriConfigJson = JSON.stringify(configObj, null, 2) + '\n';
103
- // 3. siduri.schema.json
162
+ // 4. siduri.schema.json
104
163
  const organPropertiesSchema = {};
105
164
  for (const m of manifests) {
106
165
  organPropertiesSchema[m.configKey] = m.configSchema || { type: 'object' };
@@ -123,7 +182,7 @@ function generateInstanceFiles(options) {
123
182
  },
124
183
  };
125
184
  const siduriSchemaJson = JSON.stringify(schemaObj, null, 2) + '\n';
126
- // 4. .env.example
185
+ // 5. .env.example
127
186
  const envLines = [];
128
187
  for (const m of manifests) {
129
188
  if (m.environment && m.environment.length > 0) {
@@ -139,9 +198,12 @@ function generateInstanceFiles(options) {
139
198
  }
140
199
  }
141
200
  const envExample = envLines.length > 0 ? envLines.join('\n') : '# No external environment variables required\n';
142
- // 5. src/index.js
201
+ // 6. src/index.js
143
202
  const importLines = [
144
- `import { readFile } from 'node:fs/promises';`,
203
+ `import { createServer } from 'node:http';`,
204
+ `import { readFile, stat } from 'node:fs/promises';`,
205
+ `import path from 'node:path';`,
206
+ `import { fileURLToPath } from 'node:url';`,
145
207
  `import { SiduriRuntime } from '@siduri-x/core';`,
146
208
  ];
147
209
  for (const m of manifests) {
@@ -158,8 +220,12 @@ function generateInstanceFiles(options) {
158
220
  const srcIndexJs = [
159
221
  ...importLines,
160
222
  '',
223
+ `const __filename = fileURLToPath(import.meta.url);`,
224
+ `const __dirname = path.dirname(__filename);`,
225
+ `const rootDir = path.resolve(__dirname, '..');`,
226
+ '',
161
227
  `const config = JSON.parse(`,
162
- ` await readFile(new URL('../siduri.config.json', import.meta.url), 'utf8')`,
228
+ ` await readFile(path.join(rootDir, 'siduri.config.json'), 'utf8')`,
163
229
  `);`,
164
230
  '',
165
231
  ...instantiationLines,
@@ -170,46 +236,236 @@ function generateInstanceFiles(options) {
170
236
  '',
171
237
  `await runtime.initialize();`,
172
238
  '',
173
- `console.log(\`✓ Siduri [\${config.name}] initialized with [${selectedDisplayNames}].\`);`,
239
+ `const audioCache = new Map();`,
240
+ '',
241
+ `const server = createServer(async (req, res) => {`,
242
+ ` const parsedUrl = new URL(req.url, \`http://\${req.headers.host || 'localhost'}\`);`,
243
+ ` const pathname = parsedUrl.pathname;`,
244
+ '',
245
+ ` // API: Status & Health Probes`,
246
+ ` if ((pathname === '/health' || pathname === '/api/status') && req.method === 'GET') {`,
247
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
248
+ ` res.end(JSON.stringify({`,
249
+ ` status: 'ok',`,
250
+ ` name: config.name,`,
251
+ ` id: config.id,`,
252
+ ` organs: config.organs,`,
253
+ ` uptime: process.uptime(),`,
254
+ ` }));`,
255
+ ` return;`,
256
+ ` }`,
257
+ '',
258
+ ` // API: Audio buffer retrieval for voice playback`,
259
+ ` if (pathname.startsWith('/api/audio/') && req.method === 'GET') {`,
260
+ ` const audioId = pathname.slice('/api/audio/'.length);`,
261
+ ` const buffer = audioCache.get(audioId);`,
262
+ ` if (buffer) {`,
263
+ ` res.writeHead(200, { 'Content-Type': 'audio/wav' });`,
264
+ ` res.end(Buffer.from(buffer));`,
265
+ ` return;`,
266
+ ` }`,
267
+ ` res.writeHead(404, { 'Content-Type': 'application/json' });`,
268
+ ` res.end(JSON.stringify({ error: 'Audio not found' }));`,
269
+ ` return;`,
270
+ ` }`,
271
+ '',
272
+ ` // API: Memory Claims & Items`,
273
+ ` if ((pathname === '/memory' || pathname === '/memory/claims' || pathname === '/api/memory/claims') && req.method === 'GET') {`,
274
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
275
+ ` try {`,
276
+ ` const claims = typeof memory?.getAllClaims === 'function' ? await memory.getAllClaims() : (typeof memory?.getClaims === 'function' ? await memory.getClaims() : []);`,
277
+ ` res.end(JSON.stringify({ claims, items: claims }));`,
278
+ ` } catch (e) {`,
279
+ ` res.end(JSON.stringify({ claims: [], items: [] }));`,
280
+ ` }`,
281
+ ` return;`,
282
+ ` }`,
283
+ '',
284
+ ` // API: Memory Proposals`,
285
+ ` if (pathname === '/memory/proposals' && req.method === 'GET') {`,
286
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
287
+ ` try {`,
288
+ ` const proposals = typeof memory?.getPendingClaims === 'function' ? await memory.getPendingClaims() : [];`,
289
+ ` res.end(JSON.stringify({ proposals }));`,
290
+ ` } catch (e) {`,
291
+ ` res.end(JSON.stringify({ proposals: [] }));`,
292
+ ` }`,
293
+ ` return;`,
294
+ ` }`,
295
+ '',
296
+ ` // API: Memory Directives`,
297
+ ` if ((pathname === '/memory/behavioral' || pathname === '/api/memory/directives') && req.method === 'GET') {`,
298
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
299
+ ` try {`,
300
+ ` const directives = typeof memory?.getDirectives === 'function' ? await memory.getDirectives() : [`,
301
+ ` { domain: 'personality', name: 'Active Self Tone', content: 'Warm, empathetic, and thoughtful conversational style.' },`,
302
+ ` { domain: 'cognition', name: 'Authoritative Memory', content: 'Ground responses in verified claims and personal history.' }`,
303
+ ` ];`,
304
+ ` res.end(JSON.stringify({ directives }));`,
305
+ ` } catch (e) {`,
306
+ ` res.end(JSON.stringify({ directives: [] }));`,
307
+ ` }`,
308
+ ` return;`,
309
+ ` }`,
310
+ '',
311
+ ` // API: Memory Proposal Approval / Rejection`,
312
+ ` if ((pathname === '/memory/proposals/approve' || pathname === '/memory/proposals/reject') && req.method === 'POST') {`,
313
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
314
+ ` res.end(JSON.stringify({ success: true, approved: pathname.endsWith('approve') }));`,
315
+ ` return;`,
316
+ ` }`,
317
+ '',
318
+ ` // API: Evidence packs`,
319
+ ` if (pathname === '/evidence/packs' && req.method === 'GET') {`,
320
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
321
+ ` res.end(JSON.stringify({ packs: [] }));`,
322
+ ` return;`,
323
+ ` }`,
324
+ '',
325
+ ` // API: Chat interaction (supports both /chat and /api/chat)`,
326
+ ` if ((pathname === '/chat' || pathname === '/api/chat') && req.method === 'POST') {`,
327
+ ` let body = '';`,
328
+ ` req.on('data', (chunk) => { body += chunk; });`,
329
+ ` req.on('end', async () => {`,
330
+ ` try {`,
331
+ ` const payload = JSON.parse(body || '{}');`,
332
+ ` const userMessage = payload.message || payload.text || '';`,
333
+ ` let reply = \`Hello! I received: "\${userMessage}"\`;`,
334
+ ` let expression = 'neutral';`,
335
+ ` let audioUrl = undefined;`,
336
+ '',
337
+ ` if (typeof brain?.generatePlan === 'function') {`,
338
+ ` try {`,
339
+ ` const plan = await brain.generatePlan({ prompt: userMessage });`,
340
+ ` reply = plan.speech || plan.text || reply;`,
341
+ ` } catch (err) {`,
342
+ ` reply = \`Thinking about "\${userMessage}"... (Brain active)\`;`,
343
+ ` }`,
344
+ ` }`,
345
+ '',
346
+ ` if (typeof voice?.synthesize === 'function') {`,
347
+ ` try {`,
348
+ ` const wavData = await voice.synthesize(reply);`,
349
+ ` const audioId = \`audio_\${Date.now()}\`;`,
350
+ ` audioCache.set(audioId, wavData);`,
351
+ ` audioUrl = \`/api/audio/\${audioId}\`;`,
352
+ ` } catch (e) {}`,
353
+ ` }`,
354
+ '',
355
+ ` if (typeof body?.setExpression === 'function') {`,
356
+ ` expression = reply.includes('!') ? 'happy' : 'calm';`,
357
+ ` body.setExpression(expression);`,
358
+ ` }`,
359
+ '',
360
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
361
+ ` res.end(JSON.stringify({ reply, text: reply, expression, audioUrl, status: 'ok' }));`,
362
+ ` } catch (err) {`,
363
+ ` res.writeHead(500, { 'Content-Type': 'application/json' });`,
364
+ ` res.end(JSON.stringify({ error: err.message }));`,
365
+ ` }`,
366
+ ` });`,
367
+ ` return;`,
368
+ ` }`,
369
+ '',
370
+ ` // Static files & Next.js apps/web export routing`,
371
+ ` const normalizedPath = pathname.replace(/^\/+|\/+$/g, '');`,
372
+ ` const candidatePaths = [`,
373
+ ` path.join(rootDir, 'public', normalizedPath, 'index.html'),`,
374
+ ` path.join(rootDir, 'public', normalizedPath + '.html'),`,
375
+ ` path.join(rootDir, 'public', normalizedPath),`,
376
+ ` path.join(rootDir, normalizedPath),`,
377
+ ` ];`,
378
+ '',
379
+ ` if (!normalizedPath) {`,
380
+ ` candidatePaths.unshift(path.join(rootDir, 'public', 'index.html'));`,
381
+ ` }`,
382
+ '',
383
+ ` for (const candidate of candidatePaths) {`,
384
+ ` try {`,
385
+ ` const fileStat = await stat(candidate);`,
386
+ ` if (fileStat.isFile()) {`,
387
+ ` const ext = path.extname(candidate).toLowerCase();`,
388
+ ` const mimeTypes = {`,
389
+ ` '.html': 'text/html; charset=utf-8',`,
390
+ ` '.js': 'application/javascript; charset=utf-8',`,
391
+ ` '.css': 'text/css; charset=utf-8',`,
392
+ ` '.json': 'application/json',`,
393
+ ` '.png': 'image/png',`,
394
+ ` '.jpg': 'image/jpeg',`,
395
+ ` '.svg': 'image/svg+xml',`,
396
+ ` '.wav': 'audio/wav',`,
397
+ ` '.ico': 'image/x-icon',`,
398
+ ` '.txt': 'text/plain; charset=utf-8',`,
399
+ ` '.moc3': 'application/octet-stream',`,
400
+ ` };`,
401
+ ` const contentType = mimeTypes[ext] || 'application/octet-stream';`,
402
+ ` const content = await readFile(candidate);`,
403
+ ` res.writeHead(200, { 'Content-Type': contentType });`,
404
+ ` res.end(content);`,
405
+ ` return;`,
406
+ ` }`,
407
+ ` } catch (e) {}`,
408
+ ` }`,
409
+ '',
410
+ ` res.writeHead(404, { 'Content-Type': 'text/plain' });`,
411
+ ` res.end('Not Found');`,
412
+ `});`,
413
+ '',
414
+ `const PORT = process.env.PORT || 3000;`,
415
+ `server.listen(PORT, () => {`,
416
+ ` console.log(\`✓ Siduri [\${config.name}] initialized with [${selectedDisplayNames}].\`);`,
417
+ ` console.log(\`➜ Web Companion & Memory Console running at: http://localhost:\${PORT}\`);`,
418
+ `});`,
174
419
  '',
175
420
  ].join('\n');
176
- // 6. README.md
421
+ // 7. README.md
177
422
  const readmeLines = [
178
423
  `# ${instanceName}`,
179
424
  '',
180
- `Standalone Siduri instance generated with explicitly composed organs:`,
425
+ `Standalone Siduri AI companion instance generated with explicitly composed organs:`,
181
426
  '',
182
427
  ...manifests.map((m) => `- **${m.displayName}** (\`${m.name}\`)`),
183
428
  '',
184
- '## Getting Started',
185
- '',
186
- '1. Install dependencies:',
187
- '```bash',
188
- 'npm install',
189
- '```',
429
+ '## Prerequisites',
190
430
  '',
191
- '2. Configure environment:',
192
- '```bash',
193
- 'cp .env.example .env',
194
- '```',
431
+ '- **Node.js**: `v20.0.0` or higher',
432
+ '- **Environment**: Valid `.env` file (configured from `.env.example`)',
195
433
  ];
196
- const hasMemory = manifests.some((m) => m.organType === 'memory');
434
+ if (hasMemory || isVoicevox) {
435
+ readmeLines.push('- **Local Services (Optional)**: Docker (or standalone alternatives):');
436
+ if (hasMemory) {
437
+ readmeLines.push(' - **PostgreSQL**: Required for memory claims & durable state (or use cloud Supabase/Neon)');
438
+ }
439
+ if (isVoicevox) {
440
+ readmeLines.push(' - **VOICEVOX**: Required for voice synthesis (or run the official desktop app from [voicevox.hiroshiba.jp](https://voicevox.hiroshiba.jp/))');
441
+ }
442
+ }
443
+ readmeLines.push('', '## Getting Started', '', '### 1. Install Dependencies', '```bash', 'npm install', '```', '', '### 2. Configure Environment', '```bash', 'cp .env.example .env', '```', 'Fill in your LLM API key (e.g. `OPENROUTER_API_KEY`) and any other service credentials in `.env`.');
444
+ if (dockerComposeYaml) {
445
+ readmeLines.push('', '### 3. Start Local Services (Docker)', '```bash', 'npm run services:up', '```', '*(To stop services later, run `npm run services:down`)*');
446
+ }
197
447
  if (hasMemory) {
198
- readmeLines.push('', '### Database Setup', 'This instance uses PostgreSQL Memory for durable claims and directives.', 'Ensure `DATABASE_URL` in `.env` is reachable, then run migrations:', '```bash', 'npx @vxnus/siduri db push', '```');
448
+ 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', '```');
199
449
  }
200
- const hasBody = manifests.some((m) => m.organType === 'body');
450
+ 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.');
201
451
  if (hasBody) {
202
452
  readmeLines.push('', '### Avatar Assets', 'Place your Live2D Cubism model assets into `./assets/body/model/`:', '- `model.model3.json`', '- `model.moc3`', '- textures directory');
203
453
  }
204
- readmeLines.push('', '## Running the Instance', '', 'Start the companion:', '```bash', 'npm start', '```', '', 'Run diagnostics:', '```bash', 'npm run doctor', '```', '');
454
+ readmeLines.push('');
205
455
  const readmeMd = readmeLines.join('\n');
206
- return {
456
+ const webHtml = (0, web_template_1.generateWebHtml)(instanceName, manifests);
457
+ const result = {
207
458
  'package.json': packageJson,
208
459
  'siduri.config.json': siduriConfigJson,
209
460
  'siduri.schema.json': siduriSchemaJson,
210
461
  '.env.example': envExample,
211
462
  'README.md': readmeMd,
212
463
  'src/index.js': srcIndexJs,
464
+ 'public/index.html': webHtml,
213
465
  createAssetsBodyDir: hasBody,
214
466
  };
467
+ if (dockerComposeYaml) {
468
+ result['docker-compose.yml'] = dockerComposeYaml;
469
+ }
470
+ return result;
215
471
  }
@@ -152,7 +152,26 @@ describe('Instance Generator Composition Invariants (Phase 3)', () => {
152
152
  expect(pkg.dependencies['@siduri-x/voice']).toBeDefined();
153
153
  // Body asset directory requested
154
154
  expect(files.createAssetsBodyDir).toBe(true);
155
- // README mentions Live2D model assets
155
+ // README mentions Live2D model assets & prerequisites
156
156
  expect(files['README.md']).toContain('assets/body/model');
157
+ expect(files['README.md']).toContain('Prerequisites');
158
+ // Docker compose generated for memory & voice
159
+ expect(files['docker-compose.yml']).toBeDefined();
160
+ expect(files['docker-compose.yml']).toContain('postgres:15');
161
+ expect(files['docker-compose.yml']).toContain('voicevox/voicevox_engine');
162
+ expect(pkg.scripts['services:up']).toBe('docker compose up -d');
163
+ expect(pkg.scripts['services:down']).toBe('docker compose down');
164
+ });
165
+ test('Composition E: Brain only has no docker-compose.yml', () => {
166
+ const files = (0, generator_1.generateInstanceFiles)({
167
+ name: 'brain-solo',
168
+ selectedManifests: [MOCK_MANIFESTS.brain],
169
+ });
170
+ expect(files['docker-compose.yml']).toBeUndefined();
171
+ const pkg = JSON.parse(files['package.json']);
172
+ expect(pkg.scripts['services:up']).toBeUndefined();
173
+ expect(files['public/index.html']).toBeDefined();
174
+ expect(files['public/index.html']).toContain('Memory Console');
175
+ expect(files['public/index.html']).toContain('Companion Chat');
157
176
  });
158
177
  });
package/dist/index.d.ts CHANGED
@@ -1,4 +1,20 @@
1
1
  #!/usr/bin/env node
2
+ import { OrganManifest } from './manifest';
3
+ export declare const CLI_VERSION = "0.0.9";
4
+ export declare const colors: {
5
+ cyan: string;
6
+ dim: string;
7
+ green: string;
8
+ yellow: string;
9
+ bold: string;
10
+ reset: string;
11
+ };
12
+ export declare function printHeader(): void;
13
+ export declare function printSection(title: string): void;
14
+ export declare function printSuccess(message: string): void;
15
+ export declare function projectDirectoryName(value: string): string;
16
+ export declare function formatReviewSummary(companionName: string, selectedManifests: OrganManifest[], organSummaries: Record<string, Record<string, unknown>>): string;
17
+ export declare function withTask<T>(label: string, task: () => Promise<T>): Promise<T>;
2
18
  export declare function runCreateWizard(targetDir?: string): Promise<void>;
3
19
  export declare function runCliDoctor(targetDir?: string): Promise<void>;
4
20
  export declare function runCliDb(subcommand?: string, targetDir?: string): Promise<void>;