@vxnus/siduri 0.0.6 → 0.0.8

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 (39) hide show
  1. package/dist/builtin-manifests.d.ts +2 -0
  2. package/dist/builtin-manifests.js +433 -0
  3. package/dist/clean-machine-e2e.test.js +1 -1
  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/discovery.js +8 -0
  31. package/dist/discovery.test.js +7 -0
  32. package/dist/index.d.ts +16 -0
  33. package/dist/index.js +137 -54
  34. package/dist/providers/knowledge-hub.d.ts +37 -0
  35. package/dist/providers/knowledge-hub.js +141 -0
  36. package/dist/providers/openrouter.d.ts +32 -0
  37. package/dist/providers/openrouter.js +201 -0
  38. package/dist/release-check.js +1 -1
  39. package/package.json +1 -1
@@ -0,0 +1,275 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const inquirer_1 = __importDefault(require("inquirer"));
7
+ const discovery_1 = require("./discovery");
8
+ const generator_1 = require("./generator");
9
+ const index_1 = require("./index");
10
+ const openrouter_1 = require("./providers/openrouter");
11
+ const knowledge_hub_1 = require("./providers/knowledge-hub");
12
+ const brain_1 = require("./configurators/brain");
13
+ const knowledge_1 = require("./configurators/knowledge");
14
+ const memory_1 = require("./configurators/memory");
15
+ const voice_1 = require("./configurators/voice");
16
+ const body_1 = require("./configurators/body");
17
+ const hands_1 = require("./configurators/hands");
18
+ const behavior_1 = require("./configurators/behavior");
19
+ const vision_1 = require("./configurators/vision");
20
+ // Mock inquirer.prompt
21
+ jest.mock('inquirer', () => ({
22
+ prompt: jest.fn(),
23
+ Separator: jest.fn((label) => ({ type: 'separator', line: label })),
24
+ }));
25
+ describe('Guided Manifest-Driven Configuration UX Specification Tests', () => {
26
+ const registry = discovery_1.OrganRegistry.discover();
27
+ const brainManifest = registry.get('brain');
28
+ const knowledgeManifest = registry.get('knowledge');
29
+ const memoryManifest = registry.get('memory');
30
+ const voiceManifest = registry.get('voice');
31
+ const bodyManifest = registry.get('body');
32
+ const handsManifest = registry.get('hands');
33
+ const behaviorManifest = registry.get('behavior');
34
+ const visionManifest = registry.get('vision');
35
+ beforeEach(() => {
36
+ jest.clearAllMocks();
37
+ });
38
+ describe('Discrepancy #1: OpenRouter Model Discovery & Brain Configurator', () => {
39
+ test('OpenRouter model provider parses model catalog correctly', async () => {
40
+ const mockModels = [
41
+ { id: 'openai/gpt-4o-mini', name: 'OpenAI: GPT-4o Mini', description: 'Fast model', context_length: 128000 },
42
+ { id: 'anthropic/claude-3.5-sonnet', name: 'Anthropic: Claude 3.5 Sonnet', context_length: 200000 },
43
+ ];
44
+ const globalFetch = global.fetch;
45
+ global.fetch = jest.fn().mockResolvedValue({
46
+ ok: true,
47
+ json: async () => ({ data: mockModels }),
48
+ });
49
+ const provider = new openrouter_1.OpenRouterModelProvider('https://mock-api.com/models');
50
+ const models = await provider.listModels();
51
+ expect(models.length).toBe(2);
52
+ expect(models[0].id).toBe('openai/gpt-4o-mini');
53
+ expect(models[0].name).toBe('OpenAI: GPT-4o Mini');
54
+ expect(models[1].id).toBe('anthropic/claude-3.5-sonnet');
55
+ global.fetch = globalFetch;
56
+ });
57
+ test('Brain configurator selects OpenRouter model and retains canonical model ID', async () => {
58
+ const mockProvider = {
59
+ listModels: jest.fn().mockResolvedValue([
60
+ { id: 'openai/gpt-4o-mini', name: 'OpenAI: GPT-4o Mini' },
61
+ { id: 'anthropic/claude-3.5-sonnet', name: 'Anthropic: Claude 3.5 Sonnet' },
62
+ ]),
63
+ };
64
+ inquirer_1.default.prompt
65
+ .mockResolvedValueOnce({ provider: 'openrouter' }) // Brain provider
66
+ .mockResolvedValueOnce({ selectedValue: 'openai/gpt-4o-mini' }); // Select model
67
+ const result = await (0, brain_1.configureBrain)({ companionName: 'Sparkle', manifest: brainManifest }, { modelProvider: mockProvider });
68
+ expect(result.config.provider).toBe('openrouter');
69
+ expect(result.config.model).toBe('openai/gpt-4o-mini');
70
+ expect(result.summary?.Model).toBe('openai/gpt-4o-mini');
71
+ expect(result.summary?.Provider).toBe('OpenRouter');
72
+ });
73
+ test('Brain configurator handles discovery failure with curated fallback models', async () => {
74
+ const failingProvider = {
75
+ listModels: jest.fn().mockRejectedValue(new Error('Network request failed')),
76
+ };
77
+ inquirer_1.default.prompt
78
+ .mockResolvedValueOnce({ provider: 'openrouter' }) // Brain provider
79
+ .mockResolvedValueOnce({ failureAction: 'curated' }) // Pick curated fallback
80
+ .mockResolvedValueOnce({ selectedValue: 'anthropic/claude-3.5-sonnet' }); // Select model
81
+ const result = await (0, brain_1.configureBrain)({ companionName: 'Sparkle', manifest: brainManifest }, { modelProvider: failingProvider });
82
+ expect(result.config.provider).toBe('openrouter');
83
+ expect(result.config.model).toBe('anthropic/claude-3.5-sonnet');
84
+ });
85
+ test('Brain configurator handles discovery failure with manual fallback entry', async () => {
86
+ const failingProvider = {
87
+ listModels: jest.fn().mockRejectedValue(new Error('Network request failed')),
88
+ };
89
+ inquirer_1.default.prompt
90
+ .mockResolvedValueOnce({ provider: 'openrouter' })
91
+ .mockResolvedValueOnce({ failureAction: 'manual' })
92
+ .mockResolvedValueOnce({ manualId: 'mistralai/mistral-large' });
93
+ const result = await (0, brain_1.configureBrain)({ companionName: 'Sparkle', manifest: brainManifest }, { modelProvider: failingProvider });
94
+ expect(result.config.provider).toBe('openrouter');
95
+ expect(result.config.model).toBe('mistralai/mistral-large');
96
+ });
97
+ test('Brain configurator supports OpenAI-compatible / Custom endpoint', async () => {
98
+ inquirer_1.default.prompt
99
+ .mockResolvedValueOnce({ provider: 'openai-compatible' })
100
+ .mockResolvedValueOnce({
101
+ baseUrl: 'http://localhost:11434/v1',
102
+ model: 'llama3:latest',
103
+ apiKeyEnv: 'OLLAMA_API_KEY',
104
+ });
105
+ const result = await (0, brain_1.configureBrain)({ companionName: 'Sparkle', manifest: brainManifest });
106
+ expect(result.config.provider).toBe('openai-compatible');
107
+ expect(result.config.model).toBe('llama3:latest');
108
+ expect(result.config.baseUrl).toBe('http://localhost:11434/v1');
109
+ expect(result.config.apiKeyEnv).toBe('OLLAMA_API_KEY');
110
+ });
111
+ });
112
+ describe('Discrepancy #2 & #3: Knowledge Choices & Manifest Metadata Confirmation', () => {
113
+ test('Selecting "Do not use knowledge" sets provider: none and skips discovery', async () => {
114
+ inquirer_1.default.prompt.mockResolvedValueOnce({ source: 'none' });
115
+ const result = await (0, knowledge_1.configureKnowledge)({
116
+ companionName: 'Sparkle',
117
+ manifest: knowledgeManifest,
118
+ });
119
+ expect(result.config.provider).toBe('none');
120
+ expect(result.summary?.Source).toBe('Do not use knowledge');
121
+ });
122
+ test('Selecting "E Knowledge Hub" discovers manifest, extracts capabilities, and confirms provider', async () => {
123
+ const mockClient = {
124
+ resolveProvider: jest.fn().mockResolvedValue({
125
+ name: 'e-teyvat',
126
+ displayName: 'E Teyvat',
127
+ package: '@vxnus/e-teyvat',
128
+ version: '1.2.0',
129
+ description: 'Teyvat knowledge provider for Siduri.',
130
+ capabilities: ['Search', 'Retrieval', 'Context injection'],
131
+ source: 'E Knowledge Hub',
132
+ }),
133
+ };
134
+ inquirer_1.default.prompt
135
+ .mockResolvedValueOnce({ source: 'e-hub' }) // Knowledge source
136
+ .mockResolvedValueOnce({ packId: '@vxnus/e-teyvat' }) // Package ID
137
+ .mockResolvedValueOnce({ confirmProvider: 'yes' }); // Confirm provider
138
+ const result = await (0, knowledge_1.configureKnowledge)({ companionName: 'Sparkle', manifest: knowledgeManifest }, { client: mockClient });
139
+ expect(result.config.provider).toBe('e-hub');
140
+ expect(result.config.packId).toBe('@vxnus/e-teyvat');
141
+ expect(result.summary?.Source).toBe('E Knowledge Hub');
142
+ expect(result.summary?.Provider).toBe('E Teyvat');
143
+ expect(result.summary?.Package).toBe('@vxnus/e-teyvat');
144
+ expect(result.summary?.Version).toBe('1.2.0');
145
+ });
146
+ test('Knowledge manifest validation enforces name and version', () => {
147
+ expect(() => (0, knowledge_hub_1.validateKnowledgeManifest)({})).toThrow('Invalid knowledge manifest');
148
+ expect(() => (0, knowledge_hub_1.validateKnowledgeManifest)({ name: 'test' })).toThrow('missing \'version\'');
149
+ const valid = (0, knowledge_hub_1.validateKnowledgeManifest)({
150
+ name: 'e-teyvat',
151
+ displayName: 'E Teyvat',
152
+ version: '1.0.0',
153
+ description: 'Teyvat lore',
154
+ }, '@vxnus/e-teyvat');
155
+ expect(valid.name).toBe('e-teyvat');
156
+ expect(valid.package).toBe('@vxnus/e-teyvat');
157
+ expect(valid.version).toBe('1.0.0');
158
+ });
159
+ test('extractCapabilitiesList formats object and array capabilities correctly', () => {
160
+ const arrayCaps = (0, knowledge_hub_1.extractCapabilitiesList)(['Search', 'Retrieval', 'Custom Tool']);
161
+ expect(arrayCaps).toEqual(['Search', 'Retrieval', 'Custom Tool']);
162
+ const objCaps = (0, knowledge_hub_1.extractCapabilitiesList)({
163
+ search: true,
164
+ retrieval: true,
165
+ contextInjection: true,
166
+ semanticSearch: true,
167
+ });
168
+ expect(objCaps).toContain('Search');
169
+ expect(objCaps).toContain('Retrieval');
170
+ expect(objCaps).toContain('Context injection');
171
+ expect(objCaps).toContain('Semantic search');
172
+ });
173
+ test('Knowledge Hub client fallback returns known pack for @vxnus/e-teyvat', async () => {
174
+ const client = new knowledge_hub_1.KnowledgeHubClient('https://invalid-non-existent-url.local/api');
175
+ const manifest = await client.resolveProvider('@vxnus/e-teyvat');
176
+ expect(manifest.package).toBe('@vxnus/e-teyvat');
177
+ expect(manifest.displayName).toBe('E Teyvat');
178
+ expect(manifest.version).toBe('1.2.0');
179
+ });
180
+ });
181
+ describe('Other Organ Configurators & Generic Router', () => {
182
+ test('Memory configurator configures PostgreSQL with Supabase deployment', async () => {
183
+ inquirer_1.default.prompt
184
+ .mockResolvedValueOnce({ database: 'postgres' })
185
+ .mockResolvedValueOnce({ deployment: 'supabase' });
186
+ const result = await (0, memory_1.configureMemory)({ companionName: 'Sparkle', manifest: memoryManifest });
187
+ expect(result.config.provider).toBe('postgres');
188
+ expect(result.config.deployment).toBe('supabase');
189
+ expect(result.summary?.Deploy).toBe('Supabase');
190
+ });
191
+ test('Voice configurator configures VOICEVOX engine and speaker ID', async () => {
192
+ inquirer_1.default.prompt
193
+ .mockResolvedValueOnce({ provider: 'voicevox' })
194
+ .mockResolvedValueOnce({ baseUrl: 'http://localhost:50021', speakerId: '2' });
195
+ const result = await (0, voice_1.configureVoice)({ companionName: 'Sparkle', manifest: voiceManifest });
196
+ expect(result.config.provider).toBe('voicevox');
197
+ expect(result.config.speakerId).toBe(2);
198
+ expect(result.summary?.['Speaker ID']).toBe(2);
199
+ });
200
+ test('Body configurator configures Live2D and expression', async () => {
201
+ inquirer_1.default.prompt
202
+ .mockResolvedValueOnce({ provider: 'live2d' })
203
+ .mockResolvedValueOnce({ initialExpression: 'happy' });
204
+ const result = await (0, body_1.configureBody)({ companionName: 'Sparkle', manifest: bodyManifest });
205
+ expect(result.config.provider).toBe('live2d');
206
+ expect(result.config.initialExpression).toBe('happy');
207
+ });
208
+ test('Hands configurator configures MCP tool execution timeout', async () => {
209
+ inquirer_1.default.prompt.mockResolvedValueOnce({ timeoutSeconds: '15' });
210
+ const result = await (0, hands_1.configureHands)({ companionName: 'Sparkle', manifest: handsManifest });
211
+ expect(result.config.defaultTimeoutMs).toBe(15000);
212
+ });
213
+ test('Behavior configurator configures active self personality preset', async () => {
214
+ inquirer_1.default.prompt.mockResolvedValueOnce({ preset: 'cheerful' });
215
+ const result = await (0, behavior_1.configureBehavior)({ companionName: 'Sparkle', manifest: behaviorManifest });
216
+ expect(result.config.provider).toBe('active_self');
217
+ expect(result.config.preset).toBe('cheerful');
218
+ });
219
+ test('Vision configurator configures OpenRouter vision model', async () => {
220
+ inquirer_1.default.prompt.mockResolvedValueOnce({ model: 'gpt-4-vision' });
221
+ const result = await (0, vision_1.configureVision)({ companionName: 'Sparkle', manifest: visionManifest });
222
+ expect(result.config.provider).toBe('openrouter');
223
+ expect(result.config.model).toBe('gpt-4-vision');
224
+ });
225
+ });
226
+ describe('Review Summary Formatting & Generator Integration', () => {
227
+ test('formatReviewSummary displays actual configuration values and metadata', () => {
228
+ const summaryOutput = (0, index_1.formatReviewSummary)('Sparkle', [brainManifest, memoryManifest, knowledgeManifest, voiceManifest], {
229
+ brain: { Provider: 'OpenRouter', Model: 'openai/gpt-4o-mini' },
230
+ memory: { Database: 'PostgreSQL', Deploy: 'Supabase' },
231
+ knowledge: { Source: 'E Knowledge Hub', Provider: 'E Teyvat', Package: '@vxnus/e-teyvat', Version: '1.2.0' },
232
+ voice: { Provider: 'VOICEVOX', 'Speaker ID': 1 },
233
+ });
234
+ expect(summaryOutput).toContain('Sparkle');
235
+ expect(summaryOutput).toContain('Brain');
236
+ expect(summaryOutput).toContain('OpenRouter');
237
+ expect(summaryOutput).toContain('openai/gpt-4o-mini');
238
+ expect(summaryOutput).toContain('Memory');
239
+ expect(summaryOutput).toContain('PostgreSQL');
240
+ expect(summaryOutput).toContain('Supabase');
241
+ expect(summaryOutput).toContain('Knowledge');
242
+ expect(summaryOutput).toContain('E Knowledge Hub');
243
+ expect(summaryOutput).toContain('E Teyvat');
244
+ expect(summaryOutput).toContain('@vxnus/e-teyvat');
245
+ expect(summaryOutput).toContain('Voice');
246
+ expect(summaryOutput).toContain('VOICEVOX');
247
+ });
248
+ test('generateInstanceFiles accurately embeds configured organ values in siduri.config.json', () => {
249
+ const organConfigs = {
250
+ brain: { provider: 'openrouter', model: 'openai/gpt-4o-mini', apiKeyEnv: 'OPENROUTER_API_KEY' },
251
+ memory: { provider: 'postgres', deployment: 'supabase' },
252
+ knowledge: { provider: 'e-hub', registryUrl: 'https://e.vxnus.xyz/api/v1/knowledge', packId: '@vxnus/e-teyvat' },
253
+ voice: { provider: 'voicevox', speakerId: 2, baseUrl: 'http://localhost:50021' },
254
+ };
255
+ const files = (0, generator_1.generateInstanceFiles)({
256
+ name: 'Sparkle',
257
+ selectedManifests: [brainManifest, memoryManifest, knowledgeManifest, voiceManifest],
258
+ organConfigs,
259
+ });
260
+ const config = JSON.parse(files['siduri.config.json']);
261
+ expect(config.name).toBe('Sparkle');
262
+ expect(config.organs.brain.provider).toBe('openrouter');
263
+ expect(config.organs.brain.model).toBe('openai/gpt-4o-mini');
264
+ expect(config.organs.memory.deployment).toBe('supabase');
265
+ expect(config.organs.knowledge.provider).toBe('e-hub');
266
+ expect(config.organs.knowledge.packId).toBe('@vxnus/e-teyvat');
267
+ expect(config.organs.voice.speakerId).toBe(2);
268
+ // Verify explicit imports in src/index.js
269
+ expect(files['src/index.js']).toContain("import { OpenRouterBrain } from '@siduri-x/brain'");
270
+ expect(files['src/index.js']).toContain("import { PostgresMemoryOrgan } from '@siduri-x/memory'");
271
+ expect(files['src/index.js']).toContain("import { EKnowledgeAdapter } from '@siduri-x/knowledge'");
272
+ expect(files['src/index.js']).toContain("import { VoicevoxAdapter } from '@siduri-x/voice'");
273
+ });
274
+ });
275
+ });
package/dist/discovery.js CHANGED
@@ -7,6 +7,7 @@ exports.OrganRegistry = void 0;
7
7
  const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
9
  const manifest_1 = require("./manifest");
10
+ const builtin_manifests_1 = require("./builtin-manifests");
10
11
  class OrganRegistry {
11
12
  manifests = new Map();
12
13
  constructor(manifests) {
@@ -73,6 +74,13 @@ class OrganRegistry {
73
74
  // Continue scanning other roots
74
75
  }
75
76
  }
77
+ // If running in a standalone environment without local package folders (e.g. via npx in an empty directory),
78
+ // register canonical built-in @siduri-x/* organ manifests as fallback.
79
+ if (registry.getAll().length === 0) {
80
+ for (const builtinManifest of builtin_manifests_1.BUILTIN_ORGAN_MANIFESTS) {
81
+ registry.register(builtinManifest);
82
+ }
83
+ }
76
84
  return registry;
77
85
  }
78
86
  }
@@ -57,4 +57,11 @@ describe('Discovery & Dynamic Composition System Tests (Phase 3)', () => {
57
57
  invalidRegistry.register({});
58
58
  }).toThrow(/Invalid manifest/);
59
59
  });
60
+ test('Fallback to builtin organ manifests when scanning non-existent search roots', () => {
61
+ const registry = discovery_1.OrganRegistry.discover(['/non-existent-directory/empty']);
62
+ const manifests = registry.getAll();
63
+ expect(manifests.length).toBe(10);
64
+ expect(registry.get('brain')).toBeDefined();
65
+ expect(registry.get('memory')).toBeDefined();
66
+ });
60
67
  });
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.8";
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>;