@vxnus/siduri 0.0.8 → 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.
- package/README.md +11 -1
- package/dist/builtin-manifests.js +12 -12
- package/dist/clean-machine-e2e.test.js +12 -12
- package/dist/doctor-db.test.js +9 -3
- package/dist/generator.d.ts +2 -0
- package/dist/generator.js +289 -33
- package/dist/generator.test.js +20 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +31 -3
- package/dist/release-check.js +12 -12
- package/dist/web-dist/404/index.html +1 -0
- package/dist/web-dist/404.html +1 -0
- package/dist/web-dist/__next.__PAGE__.txt +34 -0
- package/dist/web-dist/__next._full.txt +27 -0
- package/dist/web-dist/__next._tree.txt +3 -0
- package/dist/web-dist/_next/static/_zOqKpMPc6RDqk04fUScI/_buildManifest.js +11 -0
- package/dist/web-dist/_next/static/_zOqKpMPc6RDqk04fUScI/_clientMiddlewareManifest.js +1 -0
- package/dist/web-dist/_next/static/_zOqKpMPc6RDqk04fUScI/_ssgManifest.js +1 -0
- package/dist/web-dist/_next/static/chunks/0cz1d0mv5g_q7.js +1 -0
- package/dist/web-dist/_next/static/chunks/0uwih7sydv413.js +1 -0
- package/dist/web-dist/_next/static/chunks/1_yae15ffa5f6.js +1 -0
- package/dist/web-dist/_next/static/chunks/1hz3jco387c-k.js +1 -0
- package/dist/web-dist/_next/static/chunks/2bu5_paf4sm3x.css +1 -0
- package/dist/web-dist/_next/static/chunks/2kcqevsmv6kh2.js +31 -0
- package/dist/web-dist/_next/static/chunks/2s3d1bfr0l4b0.js +1 -0
- package/dist/web-dist/_next/static/chunks/32z87id9k6i2z.js +22 -0
- package/dist/web-dist/_next/static/chunks/39nefv-hczfy7.js +30 -0
- package/dist/web-dist/_next/static/chunks/3bx2gs3idjmo1.js +1 -0
- package/dist/web-dist/_next/static/chunks/turbopack-3eipe4c-imn5l.js +1 -0
- package/dist/web-dist/_next/static/media/favicon.2vob68tjqpejf.ico +0 -0
- package/dist/web-dist/_not-found/__next._full.txt +17 -0
- package/dist/web-dist/_not-found/__next._not-found.__PAGE__.txt +23 -0
- package/dist/web-dist/_not-found/__next._tree.txt +3 -0
- package/dist/web-dist/_not-found/index.html +1 -0
- package/dist/web-dist/_not-found/index.txt +17 -0
- package/dist/web-dist/chat/__next._full.txt +18 -0
- package/dist/web-dist/chat/__next._tree.txt +3 -0
- package/dist/web-dist/chat/__next.chat.__PAGE__.txt +24 -0
- package/dist/web-dist/chat/index.html +1 -0
- package/dist/web-dist/chat/index.txt +18 -0
- package/dist/web-dist/favicon.ico +0 -0
- package/dist/web-dist/index.html +1 -0
- package/dist/web-dist/index.txt +27 -0
- package/dist/web-dist/live2d/akaituno/akaituno.8192/texture_00.png +0 -0
- package/dist/web-dist/live2d/akaituno/akaituno.cdi3.json +296 -0
- package/dist/web-dist/live2d/akaituno/akaituno.moc3 +0 -0
- package/dist/web-dist/live2d/akaituno/akaituno.model3.json +42 -0
- package/dist/web-dist/live2d/akaituno/akaituno.physics3.json +839 -0
- package/dist/web-dist/live2d/akaituno/eye_color.exp3.json +12 -0
- package/dist/web-dist/live2d/akaituno/eyes_zetubou.exp3.json +12 -0
- package/dist/web-dist/live2d/akaituno/face_aozame.exp3.json +12 -0
- package/dist/web-dist/live2d/akaituno/hair_open.exp3.json +12 -0
- package/dist/web-dist/live2d/live2dcubismcore.min.js +9 -0
- package/dist/web-dist/operator/__next._full.txt +18 -0
- package/dist/web-dist/operator/__next._tree.txt +3 -0
- package/dist/web-dist/operator/__next.operator.__PAGE__.txt +24 -0
- package/dist/web-dist/operator/index.html +1 -0
- package/dist/web-dist/operator/index.txt +18 -0
- package/dist/web-template.d.ts +2 -0
- package/dist/web-template.js +547 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -8,9 +8,18 @@ Requires Node.js 20 or newer.
|
|
|
8
8
|
npx @vxnus/siduri create my-companion
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
## Prerequisites
|
|
12
|
+
|
|
13
|
+
- **Node.js**: `v20.0.0` or newer
|
|
14
|
+
- **LLM API Key**: e.g. `OPENROUTER_API_KEY` for Brain organ
|
|
15
|
+
- **Optional Local Services**:
|
|
16
|
+
- **Docker**: For running local PostgreSQL database (`docker compose up -d` / `npm run services:up`) and/or VOICEVOX engine.
|
|
17
|
+
- *Non-Docker Alternatives*: Cloud databases (Supabase, Neon) or standalone [VOICEVOX Desktop App](https://voicevox.hiroshiba.jp/).
|
|
18
|
+
|
|
11
19
|
## Features
|
|
12
20
|
|
|
13
21
|
- **Manifest-Driven Organ Discovery**: Dynamically discovers installed `@siduri-x/*` organs and generates custom, standalone ESM instance code.
|
|
22
|
+
- **Interactive Configuration Wizard**: Model catalog discovery for OpenRouter, manifest inspection for E Knowledge Hub, and guided organ parameters.
|
|
14
23
|
- **Zero Monolithic Bundling**: Scaffolds standard Node.js ESM projects with explicit dependency trees.
|
|
15
24
|
- **Diagnostics (`siduri doctor`)**: Runs environment variable validation, external service checks, database health probes, and organ-specific assertions.
|
|
16
25
|
- **Database Migrations (`siduri db push`)**: Inspects database-owning organs (such as `@siduri-x/memory`) and executes SQL migrations with SHA-256 integrity checksums.
|
|
@@ -23,7 +32,7 @@ npx @vxnus/siduri create my-companion
|
|
|
23
32
|
npx @vxnus/siduri create [directory]
|
|
24
33
|
```
|
|
25
34
|
|
|
26
|
-
The interactive wizard allows you to name your companion
|
|
35
|
+
The interactive wizard allows you to name your companion, configure organ providers and models, and inspect Knowledge manifests. It generates:
|
|
27
36
|
|
|
28
37
|
```text
|
|
29
38
|
my-companion/
|
|
@@ -32,6 +41,7 @@ my-companion/
|
|
|
32
41
|
├── siduri.schema.json # Composed JSON Schema from organ manifests
|
|
33
42
|
├── .env.example # Only environment variables required by selected organs
|
|
34
43
|
├── README.md # Instance-specific guide
|
|
44
|
+
├── docker-compose.yml # Scaffolds PostgreSQL / VOICEVOX when selected
|
|
35
45
|
└── src/
|
|
36
46
|
└── index.js # Direct runtime bootstrapping with explicit organ factories
|
|
37
47
|
```
|
|
@@ -5,7 +5,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
5
5
|
{
|
|
6
6
|
name: '@siduri-x/behavior',
|
|
7
7
|
organType: 'behavior',
|
|
8
|
-
version: '1.0.
|
|
8
|
+
version: '1.0.1',
|
|
9
9
|
displayName: 'Behavior (Active Self Directives)',
|
|
10
10
|
description: 'Atomic directive state machine and personality projection compiler',
|
|
11
11
|
entrypoint: './dist/index.js',
|
|
@@ -32,7 +32,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
32
32
|
{
|
|
33
33
|
name: '@siduri-x/body',
|
|
34
34
|
organType: 'body',
|
|
35
|
-
version: '1.0.
|
|
35
|
+
version: '1.0.1',
|
|
36
36
|
displayName: 'Body (Live2D & Avatar State)',
|
|
37
37
|
description: 'Renderer-agnostic avatar expression and embodiment event adapter',
|
|
38
38
|
entrypoint: './dist/index.js',
|
|
@@ -60,7 +60,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
60
60
|
{
|
|
61
61
|
name: '@siduri-x/brain',
|
|
62
62
|
organType: 'brain',
|
|
63
|
-
version: '1.0.
|
|
63
|
+
version: '1.0.1',
|
|
64
64
|
displayName: 'Brain (Cognition & Planning)',
|
|
65
65
|
description: 'Provider-neutral LLM reasoning, response planning, and proposal generation',
|
|
66
66
|
entrypoint: './dist/index.js',
|
|
@@ -116,7 +116,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
116
116
|
{
|
|
117
117
|
name: '@siduri-x/ear',
|
|
118
118
|
organType: 'ear',
|
|
119
|
-
version: '1.0.
|
|
119
|
+
version: '1.0.1',
|
|
120
120
|
displayName: 'Ear (Perception Ingress)',
|
|
121
121
|
description: 'Multi-modal sensory input ingestion, transcription bounds, and mime validation',
|
|
122
122
|
entrypoint: './dist/index.js',
|
|
@@ -147,7 +147,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
147
147
|
{
|
|
148
148
|
name: '@siduri-x/hands',
|
|
149
149
|
organType: 'hands',
|
|
150
|
-
version: '1.0.
|
|
150
|
+
version: '1.0.1',
|
|
151
151
|
displayName: 'Hands (MCP Tool Execution)',
|
|
152
152
|
description: 'Model Context Protocol tool management and cryptographically authorized action execution',
|
|
153
153
|
entrypoint: './dist/index.js',
|
|
@@ -201,7 +201,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
201
201
|
{
|
|
202
202
|
name: '@siduri-x/knowledge',
|
|
203
203
|
organType: 'knowledge',
|
|
204
|
-
version: '1.0.
|
|
204
|
+
version: '1.0.1',
|
|
205
205
|
displayName: 'Knowledge (E-Compatible Cited Facts)',
|
|
206
206
|
description: 'Factual context retrieval from local or hosted E Knowledge packs',
|
|
207
207
|
entrypoint: './dist/index.js',
|
|
@@ -266,7 +266,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
266
266
|
{
|
|
267
267
|
name: '@siduri-x/memory',
|
|
268
268
|
organType: 'memory',
|
|
269
|
-
version: '1.0.
|
|
269
|
+
version: '1.0.1',
|
|
270
270
|
displayName: 'Memory (PostgreSQL FTS Claims)',
|
|
271
271
|
description: 'Relational semantic claims with Full-Text Search and companion isolation',
|
|
272
272
|
entrypoint: './dist/index.js',
|
|
@@ -318,9 +318,9 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
318
318
|
{
|
|
319
319
|
name: '@siduri-x/observation',
|
|
320
320
|
organType: 'observation',
|
|
321
|
-
version: '1.0.
|
|
322
|
-
displayName: 'Observation (
|
|
323
|
-
description: '
|
|
321
|
+
version: '1.0.1',
|
|
322
|
+
displayName: 'Observation (Screen Perception & Frame Ingest)',
|
|
323
|
+
description: 'Screen capture frame ingest, SHA-256 deduplication, and visual grounding for Eyes/Vision',
|
|
324
324
|
entrypoint: './dist/index.js',
|
|
325
325
|
factory: 'FixtureObservationOrgan',
|
|
326
326
|
configKey: 'observation',
|
|
@@ -336,7 +336,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
336
336
|
{
|
|
337
337
|
name: '@siduri-x/vision',
|
|
338
338
|
organType: 'vision',
|
|
339
|
-
version: '1.0.
|
|
339
|
+
version: '1.0.1',
|
|
340
340
|
displayName: 'Vision (Visual Perception & OCR)',
|
|
341
341
|
description: 'Image inspection, cropping, and multi-pass OCR perception adapter',
|
|
342
342
|
entrypoint: './dist/index.js',
|
|
@@ -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.1',
|
|
387
387
|
displayName: 'Voice (VOICEVOX Speech Synthesis)',
|
|
388
388
|
description: 'Queued speech synthesis and audio rendering lifecycle adapter',
|
|
389
389
|
entrypoint: './dist/index.js',
|
|
@@ -13,18 +13,18 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
13
13
|
const tempPackDir = node_path_1.default.resolve(__dirname, '../temp-packs-e2e');
|
|
14
14
|
const cleanMachineRoot = node_path_1.default.resolve(__dirname, '../temp-clean-machine-e2e');
|
|
15
15
|
const ALL_CANONICAL_PACKAGES = [
|
|
16
|
-
{ filter: '@siduri-x/core', tarName: 'siduri-x-core-1.0.
|
|
17
|
-
{ filter: '@siduri-x/brain', tarName: 'siduri-x-brain-1.0.
|
|
18
|
-
{ filter: '@siduri-x/memory', tarName: 'siduri-x-memory-1.0.
|
|
19
|
-
{ filter: '@siduri-x/knowledge', tarName: 'siduri-x-knowledge-1.0.
|
|
20
|
-
{ filter: '@siduri-x/behavior', tarName: 'siduri-x-behavior-1.0.
|
|
21
|
-
{ filter: '@siduri-x/ear', tarName: 'siduri-x-ear-1.0.
|
|
22
|
-
{ filter: '@siduri-x/vision', tarName: 'siduri-x-vision-1.0.
|
|
23
|
-
{ filter: '@siduri-x/hands', tarName: 'siduri-x-hands-1.0.
|
|
24
|
-
{ filter: '@siduri-x/body', tarName: 'siduri-x-body-1.0.
|
|
25
|
-
{ filter: '@siduri-x/voice', tarName: 'siduri-x-voice-1.0.
|
|
26
|
-
{ filter: '@siduri-x/observation', tarName: 'siduri-x-observation-1.0.
|
|
27
|
-
{ filter: '@vxnus/siduri', tarName: 'vxnus-siduri-0.0.
|
|
16
|
+
{ filter: '@siduri-x/core', tarName: 'siduri-x-core-1.0.1.tgz', isOrgan: false },
|
|
17
|
+
{ filter: '@siduri-x/brain', tarName: 'siduri-x-brain-1.0.1.tgz', isOrgan: true },
|
|
18
|
+
{ filter: '@siduri-x/memory', tarName: 'siduri-x-memory-1.0.1.tgz', isOrgan: true },
|
|
19
|
+
{ filter: '@siduri-x/knowledge', tarName: 'siduri-x-knowledge-1.0.1.tgz', isOrgan: true },
|
|
20
|
+
{ filter: '@siduri-x/behavior', tarName: 'siduri-x-behavior-1.0.1.tgz', isOrgan: true },
|
|
21
|
+
{ filter: '@siduri-x/ear', tarName: 'siduri-x-ear-1.0.1.tgz', isOrgan: true },
|
|
22
|
+
{ filter: '@siduri-x/vision', tarName: 'siduri-x-vision-1.0.1.tgz', isOrgan: true },
|
|
23
|
+
{ filter: '@siduri-x/hands', tarName: 'siduri-x-hands-1.0.1.tgz', isOrgan: true },
|
|
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.1.tgz', isOrgan: true },
|
|
26
|
+
{ filter: '@siduri-x/observation', tarName: 'siduri-x-observation-1.0.1.tgz', isOrgan: true },
|
|
27
|
+
{ filter: '@vxnus/siduri', tarName: 'vxnus-siduri-0.0.9.tgz', isOrgan: false },
|
|
28
28
|
];
|
|
29
29
|
beforeAll(() => {
|
|
30
30
|
// 1. Prepare clean directories
|
package/dist/doctor-db.test.js
CHANGED
|
@@ -44,7 +44,9 @@ describe('Phase 4: Diagnostics and Database Provisioning Tests', () => {
|
|
|
44
44
|
});
|
|
45
45
|
for (const [name, content] of Object.entries(files)) {
|
|
46
46
|
if (typeof content === 'string') {
|
|
47
|
-
|
|
47
|
+
const fullPath = node_path_1.default.join(instanceDir, name);
|
|
48
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(fullPath), { recursive: true });
|
|
49
|
+
node_fs_1.default.writeFileSync(fullPath, content);
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
52
|
// 1. Doctor without OPENROUTER_API_KEY -> FAIL (Brain Health Probe fails)
|
|
@@ -73,7 +75,9 @@ describe('Phase 4: Diagnostics and Database Provisioning Tests', () => {
|
|
|
73
75
|
});
|
|
74
76
|
for (const [name, content] of Object.entries(files)) {
|
|
75
77
|
if (typeof content === 'string') {
|
|
76
|
-
|
|
78
|
+
const fullPath = node_path_1.default.join(instanceDir, name);
|
|
79
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(fullPath), { recursive: true });
|
|
80
|
+
node_fs_1.default.writeFileSync(fullPath, content);
|
|
77
81
|
}
|
|
78
82
|
}
|
|
79
83
|
// Doctor checks both keys
|
|
@@ -102,7 +106,9 @@ describe('Phase 4: Diagnostics and Database Provisioning Tests', () => {
|
|
|
102
106
|
});
|
|
103
107
|
for (const [name, content] of Object.entries(files)) {
|
|
104
108
|
if (typeof content === 'string') {
|
|
105
|
-
|
|
109
|
+
const fullPath = node_path_1.default.join(instanceDir, name);
|
|
110
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(fullPath), { recursive: true });
|
|
111
|
+
node_fs_1.default.writeFileSync(fullPath, content);
|
|
106
112
|
}
|
|
107
113
|
}
|
|
108
114
|
// 1. Doctor fails without DATABASE_URL
|
package/dist/generator.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ export interface GeneratedInstanceFiles {
|
|
|
6
6
|
'.env.example': string;
|
|
7
7
|
'README.md': string;
|
|
8
8
|
'src/index.js': string;
|
|
9
|
+
'public/index.html': string;
|
|
10
|
+
'docker-compose.yml'?: string;
|
|
9
11
|
createAssetsBodyDir?: boolean;
|
|
10
12
|
}
|
|
11
13
|
export interface InstanceGeneratorOptions {
|
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.
|
|
69
|
+
const coreVersion = options.coreVersion || '^1.0.1';
|
|
69
70
|
const manifests = options.selectedManifests;
|
|
70
|
-
|
|
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.
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
201
|
+
// 6. src/index.js
|
|
143
202
|
const importLines = [
|
|
144
|
-
`import {
|
|
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(
|
|
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
|
-
`
|
|
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
|
-
//
|
|
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
|
-
'##
|
|
185
|
-
'',
|
|
186
|
-
'1. Install dependencies:',
|
|
187
|
-
'```bash',
|
|
188
|
-
'npm install',
|
|
189
|
-
'```',
|
|
429
|
+
'## Prerequisites',
|
|
190
430
|
'',
|
|
191
|
-
'
|
|
192
|
-
'
|
|
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
|
-
|
|
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('',
|
|
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
|
-
|
|
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(''
|
|
454
|
+
readmeLines.push('');
|
|
205
455
|
const readmeMd = readmeLines.join('\n');
|
|
206
|
-
|
|
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
|
}
|
package/dist/generator.test.js
CHANGED
|
@@ -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
|
});
|