@vxnus/siduri 0.1.7 → 0.1.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 (98) hide show
  1. package/package.json +1 -1
  2. package/dist/builtin-manifests.d.ts +0 -2
  3. package/dist/builtin-manifests.js +0 -464
  4. package/dist/clean-machine-e2e.test.d.ts +0 -1
  5. package/dist/clean-machine-e2e.test.js +0 -230
  6. package/dist/configurators/behavior.d.ts +0 -2
  7. package/dist/configurators/behavior.js +0 -36
  8. package/dist/configurators/body.d.ts +0 -2
  9. package/dist/configurators/body.js +0 -60
  10. package/dist/configurators/brain.d.ts +0 -6
  11. package/dist/configurators/brain.js +0 -80
  12. package/dist/configurators/ear.d.ts +0 -2
  13. package/dist/configurators/ear.js +0 -28
  14. package/dist/configurators/hands.d.ts +0 -2
  15. package/dist/configurators/hands.js +0 -27
  16. package/dist/configurators/index.d.ts +0 -23
  17. package/dist/configurators/index.js +0 -74
  18. package/dist/configurators/knowledge.d.ts +0 -6
  19. package/dist/configurators/knowledge.js +0 -100
  20. package/dist/configurators/memory.d.ts +0 -2
  21. package/dist/configurators/memory.js +0 -44
  22. package/dist/configurators/observation.d.ts +0 -2
  23. package/dist/configurators/observation.js +0 -12
  24. package/dist/configurators/types.d.ts +0 -12
  25. package/dist/configurators/types.js +0 -2
  26. package/dist/configurators/vision.d.ts +0 -2
  27. package/dist/configurators/vision.js +0 -39
  28. package/dist/configurators/voice.d.ts +0 -2
  29. package/dist/configurators/voice.js +0 -363
  30. package/dist/configurators.test.d.ts +0 -1
  31. package/dist/configurators.test.js +0 -299
  32. package/dist/db.d.ts +0 -25
  33. package/dist/db.js +0 -152
  34. package/dist/discovery.d.ts +0 -13
  35. package/dist/discovery.js +0 -87
  36. package/dist/discovery.test.d.ts +0 -1
  37. package/dist/discovery.test.js +0 -67
  38. package/dist/doctor-db.test.d.ts +0 -1
  39. package/dist/doctor-db.test.js +0 -141
  40. package/dist/doctor.d.ts +0 -19
  41. package/dist/doctor.js +0 -225
  42. package/dist/generator.d.ts +0 -21
  43. package/dist/generator.js +0 -542
  44. package/dist/generator.test.d.ts +0 -1
  45. package/dist/generator.test.js +0 -197
  46. package/dist/index.d.ts +0 -20
  47. package/dist/index.js +0 -401
  48. package/dist/manifest.d.ts +0 -33
  49. package/dist/manifest.js +0 -40
  50. package/dist/providers/knowledge-hub.d.ts +0 -37
  51. package/dist/providers/knowledge-hub.js +0 -141
  52. package/dist/providers/openrouter.d.ts +0 -32
  53. package/dist/providers/openrouter.js +0 -201
  54. package/dist/release-check.d.ts +0 -9
  55. package/dist/release-check.js +0 -131
  56. package/dist/runtime-parity.test.d.ts +0 -1
  57. package/dist/runtime-parity.test.js +0 -140
  58. package/dist/web-dist/404/index.html +0 -1
  59. package/dist/web-dist/404.html +0 -1
  60. package/dist/web-dist/__next.__PAGE__.txt +0 -34
  61. package/dist/web-dist/__next._full.txt +0 -31
  62. package/dist/web-dist/__next._tree.txt +0 -3
  63. package/dist/web-dist/_next/static/chunks/0bncolc2nuzmo.css +0 -1
  64. package/dist/web-dist/_next/static/chunks/0cz1d0mv5g_q7.js +0 -1
  65. package/dist/web-dist/_next/static/chunks/0uwih7sydv413.js +0 -1
  66. package/dist/web-dist/_next/static/chunks/0xe6aaej0xsj8.js +0 -30
  67. package/dist/web-dist/_next/static/chunks/10rmct1ax-opq.js +0 -1
  68. package/dist/web-dist/_next/static/chunks/1_yae15ffa5f6.js +0 -1
  69. package/dist/web-dist/_next/static/chunks/2kcqevsmv6kh2.js +0 -31
  70. package/dist/web-dist/_next/static/chunks/2s3d1bfr0l4b0.js +0 -1
  71. package/dist/web-dist/_next/static/chunks/32z87id9k6i2z.js +0 -22
  72. package/dist/web-dist/_next/static/chunks/3bx2gs3idjmo1.js +0 -1
  73. package/dist/web-dist/_next/static/chunks/turbopack-3eipe4c-imn5l.js +0 -1
  74. package/dist/web-dist/_next/static/media/favicon.2vob68tjqpejf.ico +0 -0
  75. package/dist/web-dist/_next/static/uvsK8ULhL7fHQMR8sr_JL/_buildManifest.js +0 -11
  76. package/dist/web-dist/_next/static/uvsK8ULhL7fHQMR8sr_JL/_clientMiddlewareManifest.js +0 -1
  77. package/dist/web-dist/_next/static/uvsK8ULhL7fHQMR8sr_JL/_ssgManifest.js +0 -1
  78. package/dist/web-dist/_not-found/__next._full.txt +0 -17
  79. package/dist/web-dist/_not-found/__next._not-found.__PAGE__.txt +0 -23
  80. package/dist/web-dist/_not-found/__next._tree.txt +0 -3
  81. package/dist/web-dist/_not-found/index.html +0 -1
  82. package/dist/web-dist/_not-found/index.txt +0 -17
  83. package/dist/web-dist/chat/__next._full.txt +0 -18
  84. package/dist/web-dist/chat/__next._tree.txt +0 -3
  85. package/dist/web-dist/chat/__next.chat.__PAGE__.txt +0 -24
  86. package/dist/web-dist/chat/index.html +0 -1
  87. package/dist/web-dist/chat/index.txt +0 -18
  88. package/dist/web-dist/favicon.ico +0 -0
  89. package/dist/web-dist/index.html +0 -1
  90. package/dist/web-dist/index.txt +0 -31
  91. package/dist/web-dist/live2d/live2dcubismcore.min.js +0 -9
  92. package/dist/web-dist/operator/__next._full.txt +0 -18
  93. package/dist/web-dist/operator/__next._tree.txt +0 -3
  94. package/dist/web-dist/operator/__next.operator.__PAGE__.txt +0 -24
  95. package/dist/web-dist/operator/index.html +0 -1
  96. package/dist/web-dist/operator/index.txt +0 -18
  97. package/dist/web-template.d.ts +0 -2
  98. package/dist/web-template.js +0 -556
@@ -1,299 +0,0 @@
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' })
195
- .mockResolvedValueOnce({ speaker: { id: 2, label: '四国めたん (Shikoku Metan) - ノーマル (Normal) (ID: 2)' } });
196
- const result = await (0, voice_1.configureVoice)({ companionName: 'Sparkle', manifest: voiceManifest });
197
- expect(result.config.provider).toBe('voicevox');
198
- expect(result.config.speakerId).toBe(2);
199
- expect(result.summary?.['Speaker ID']).toBe(2);
200
- });
201
- test('Voice configurator configures RVC with Base TTS Engine', async () => {
202
- inquirer_1.default.prompt
203
- .mockResolvedValueOnce({ provider: 'rvc' })
204
- .mockResolvedValueOnce({
205
- modelPath: './assets/voice/sparkle/sparkle.pth',
206
- indexPath: './assets/voice/sparkle/sparkle.index',
207
- pitchShift: '12',
208
- f0Method: 'rmvpe',
209
- baseTts: 'edge-tts',
210
- serviceUrl: 'http://localhost:50055',
211
- });
212
- const result = await (0, voice_1.configureVoice)({ companionName: 'Sparkle', manifest: voiceManifest });
213
- expect(result.config.provider).toBe('edge-tts');
214
- expect(result.config.rvc.enabled).toBe(true);
215
- expect(result.config.rvc.pitchShift).toBe(12);
216
- expect(result.summary?.['Base TTS']).toBe('edge-tts');
217
- });
218
- test('Body configurator configures Live2D, custom model path, and expression', async () => {
219
- inquirer_1.default.prompt
220
- .mockResolvedValueOnce({ provider: 'live2d' })
221
- .mockResolvedValueOnce({
222
- modelSource: './assets/body/sparkle/model.model3.json',
223
- initialExpression: 'happy',
224
- });
225
- const result = await (0, body_1.configureBody)({ companionName: 'Sparkle', manifest: bodyManifest });
226
- expect(result.config.provider).toBe('live2d');
227
- expect(result.config.modelPath).toBe('./assets/body/sparkle/model.model3.json');
228
- expect(result.config.modelUrl).toBe('/assets/body/sparkle/model.model3.json');
229
- expect(result.config.initialExpression).toBe('happy');
230
- expect(result.summary?.['Model Path']).toBe('./assets/body/sparkle/model.model3.json');
231
- });
232
- test('Hands configurator configures MCP tool execution timeout', async () => {
233
- inquirer_1.default.prompt.mockResolvedValueOnce({ timeoutSeconds: '15' });
234
- const result = await (0, hands_1.configureHands)({ companionName: 'Sparkle', manifest: handsManifest });
235
- expect(result.config.defaultTimeoutMs).toBe(15000);
236
- });
237
- test('Behavior configurator configures active self personality preset', async () => {
238
- inquirer_1.default.prompt.mockResolvedValueOnce({ preset: 'cheerful' });
239
- const result = await (0, behavior_1.configureBehavior)({ companionName: 'Sparkle', manifest: behaviorManifest });
240
- expect(result.config.provider).toBe('active_self');
241
- expect(result.config.preset).toBe('cheerful');
242
- });
243
- test('Vision configurator configures OpenRouter vision model', async () => {
244
- inquirer_1.default.prompt.mockResolvedValueOnce({ model: 'gpt-4-vision' });
245
- const result = await (0, vision_1.configureVision)({ companionName: 'Sparkle', manifest: visionManifest });
246
- expect(result.config.provider).toBe('openrouter');
247
- expect(result.config.model).toBe('gpt-4-vision');
248
- });
249
- });
250
- describe('Review Summary Formatting & Generator Integration', () => {
251
- test('formatReviewSummary displays actual configuration values and metadata', () => {
252
- const summaryOutput = (0, index_1.formatReviewSummary)('Sparkle', [brainManifest, memoryManifest, knowledgeManifest, voiceManifest], {
253
- brain: { Provider: 'OpenRouter', Model: 'openai/gpt-4o-mini' },
254
- memory: { Database: 'PostgreSQL', Deploy: 'Supabase' },
255
- knowledge: { Source: 'E Knowledge Hub', Provider: 'E Teyvat', Package: '@vxnus/e-teyvat', Version: '1.2.0' },
256
- voice: { Provider: 'VOICEVOX', 'Speaker ID': 1 },
257
- });
258
- expect(summaryOutput).toContain('Sparkle');
259
- expect(summaryOutput).toContain('Brain');
260
- expect(summaryOutput).toContain('OpenRouter');
261
- expect(summaryOutput).toContain('openai/gpt-4o-mini');
262
- expect(summaryOutput).toContain('Memory');
263
- expect(summaryOutput).toContain('PostgreSQL');
264
- expect(summaryOutput).toContain('Supabase');
265
- expect(summaryOutput).toContain('Knowledge');
266
- expect(summaryOutput).toContain('E Knowledge Hub');
267
- expect(summaryOutput).toContain('E Teyvat');
268
- expect(summaryOutput).toContain('@vxnus/e-teyvat');
269
- expect(summaryOutput).toContain('Voice');
270
- expect(summaryOutput).toContain('VOICEVOX');
271
- });
272
- test('generateInstanceFiles accurately embeds configured organ values in siduri.config.json', () => {
273
- const organConfigs = {
274
- brain: { provider: 'openrouter', model: 'openai/gpt-4o-mini', apiKeyEnv: 'OPENROUTER_API_KEY' },
275
- memory: { provider: 'postgres', deployment: 'supabase' },
276
- knowledge: { provider: 'e-hub', registryUrl: 'https://e.vxnus.xyz/api/v1/knowledge', packId: '@vxnus/e-teyvat' },
277
- voice: { provider: 'voicevox', speakerId: 2, baseUrl: 'http://localhost:50021' },
278
- };
279
- const files = (0, generator_1.generateInstanceFiles)({
280
- name: 'Sparkle',
281
- selectedManifests: [brainManifest, memoryManifest, knowledgeManifest, voiceManifest],
282
- organConfigs,
283
- });
284
- const config = JSON.parse(files['siduri.config.json']);
285
- expect(config.name).toBe('Sparkle');
286
- expect(config.organs.brain.provider).toBe('openrouter');
287
- expect(config.organs.brain.model).toBe('openai/gpt-4o-mini');
288
- expect(config.organs.memory.deployment).toBe('supabase');
289
- expect(config.organs.knowledge.provider).toBe('e-hub');
290
- expect(config.organs.knowledge.packId).toBe('@vxnus/e-teyvat');
291
- expect(config.organs.voice.speakerId).toBe(2);
292
- // Verify explicit imports in src/index.js
293
- expect(files['src/index.js']).toContain("import { OpenRouterBrain } from '@siduri-x/brain'");
294
- expect(files['src/index.js']).toContain("import { PostgresMemoryOrgan } from '@siduri-x/memory'");
295
- expect(files['src/index.js']).toContain("import { EKnowledgeAdapter } from '@siduri-x/knowledge'");
296
- expect(files['src/index.js']).toContain("import { VoiceAdapter } from '@siduri-x/voice'");
297
- });
298
- });
299
- });
package/dist/db.d.ts DELETED
@@ -1,25 +0,0 @@
1
- export declare function redactDatabaseUrl(url: string): string;
2
- export interface MigrationFile {
3
- name: string;
4
- fullPath: string;
5
- sql: string;
6
- checksum: string;
7
- }
8
- export interface MigrationRecord {
9
- id: number;
10
- name: string;
11
- checksum: string;
12
- applied_at: string;
13
- }
14
- export interface DbPushOptions {
15
- projectDir?: string;
16
- connectionString?: string;
17
- env?: Record<string, string | undefined>;
18
- }
19
- export interface DbPushResult {
20
- status: 'NOOP' | 'APPLIED' | 'UP_TO_DATE';
21
- appliedMigrations: string[];
22
- skippedMigrations: string[];
23
- message: string;
24
- }
25
- export declare function runDbPush(options?: DbPushOptions): Promise<DbPushResult>;
package/dist/db.js DELETED
@@ -1,152 +0,0 @@
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
- exports.redactDatabaseUrl = redactDatabaseUrl;
7
- exports.runDbPush = runDbPush;
8
- const node_crypto_1 = require("node:crypto");
9
- const node_fs_1 = __importDefault(require("node:fs"));
10
- const node_path_1 = __importDefault(require("node:path"));
11
- const pg_1 = require("pg");
12
- const discovery_1 = require("./discovery");
13
- function redactDatabaseUrl(url) {
14
- try {
15
- const parsed = new URL(url);
16
- if (parsed.password) {
17
- parsed.password = '***';
18
- }
19
- return parsed.toString();
20
- }
21
- catch {
22
- return 'postgres://***:***@...';
23
- }
24
- }
25
- async function runDbPush(options = {}) {
26
- const projectDir = options.projectDir ? node_path_1.default.resolve(options.projectDir) : process.cwd();
27
- const configPath = node_path_1.default.join(projectDir, 'siduri.config.json');
28
- if (!node_fs_1.default.existsSync(configPath)) {
29
- throw new Error(`siduri.config.json not found at ${projectDir}. Make sure you are in a Siduri instance directory.`);
30
- }
31
- const config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf8'));
32
- const configuredOrgans = config.organs || {};
33
- // 1. Discover manifests of configured organs
34
- const registry = discovery_1.OrganRegistry.discover([
35
- node_path_1.default.join(projectDir, 'node_modules/@siduri-x'),
36
- node_path_1.default.resolve(__dirname, '../../packages/organs'),
37
- node_path_1.default.resolve(process.cwd(), 'packages/organs'),
38
- ]);
39
- const databaseOrgans = [];
40
- for (const [key] of Object.entries(configuredOrgans)) {
41
- const manifest = registry.get(key) || registry.getAll().find((m) => m.configKey === key || m.organType === key);
42
- if (manifest && manifest.database && manifest.database.migrationsDir) {
43
- // Resolve migrations directory relative to the organ package
44
- const candidatePaths = [
45
- node_path_1.default.resolve(projectDir, 'node_modules', manifest.name, manifest.database.migrationsDir),
46
- node_path_1.default.resolve(__dirname, '../../packages/organs', manifest.organType, manifest.database.migrationsDir),
47
- node_path_1.default.resolve(process.cwd(), 'packages/organs', manifest.organType, manifest.database.migrationsDir),
48
- ];
49
- const resolvedDir = candidatePaths.find((p) => node_fs_1.default.existsSync(p));
50
- if (resolvedDir) {
51
- databaseOrgans.push({
52
- organType: manifest.organType,
53
- manifest,
54
- migrationsDir: resolvedDir,
55
- });
56
- }
57
- }
58
- }
59
- if (databaseOrgans.length === 0) {
60
- return {
61
- status: 'NOOP',
62
- appliedMigrations: [],
63
- skippedMigrations: [],
64
- message: 'No database migrations are required by this instance.',
65
- };
66
- }
67
- // 2. Resolve DATABASE_URL
68
- const connectionString = options.connectionString ||
69
- options.env?.DATABASE_URL ||
70
- process.env.DATABASE_URL;
71
- if (!connectionString) {
72
- throw new Error('DATABASE_URL is required to run database migrations. Set it in .env or environment.');
73
- }
74
- const pool = new pg_1.Pool({
75
- connectionString,
76
- connectionTimeoutMillis: 5000,
77
- });
78
- const client = await pool.connect();
79
- try {
80
- // 3. Ensure migrations table exists
81
- await client.query(`
82
- CREATE TABLE IF NOT EXISTS siduri_migrations (
83
- id SERIAL PRIMARY KEY,
84
- name VARCHAR(255) UNIQUE NOT NULL,
85
- checksum VARCHAR(64) NOT NULL,
86
- applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
87
- );
88
- `);
89
- // 4. Fetch applied migrations
90
- const existingRes = await client.query('SELECT name, checksum FROM siduri_migrations ORDER BY id ASC;');
91
- const appliedMap = new Map();
92
- for (const row of existingRes.rows) {
93
- appliedMap.set(row.name, row.checksum);
94
- }
95
- // 5. Gather all migration files across database organs in deterministic order
96
- const allMigrationFiles = [];
97
- for (const item of databaseOrgans) {
98
- const files = node_fs_1.default.readdirSync(item.migrationsDir).filter((f) => f.endsWith('.sql')).sort();
99
- for (const file of files) {
100
- const fullPath = node_path_1.default.join(item.migrationsDir, file);
101
- const sql = node_fs_1.default.readFileSync(fullPath, 'utf8');
102
- const checksum = (0, node_crypto_1.createHash)('sha256').update(sql).digest('hex');
103
- allMigrationFiles.push({
104
- name: `${item.organType}/${file}`,
105
- fullPath,
106
- sql,
107
- checksum,
108
- });
109
- }
110
- }
111
- allMigrationFiles.sort((a, b) => a.name.localeCompare(b.name));
112
- const appliedList = [];
113
- const skippedList = [];
114
- for (const migration of allMigrationFiles) {
115
- const recordedChecksum = appliedMap.get(migration.name);
116
- if (recordedChecksum !== undefined) {
117
- if (recordedChecksum !== migration.checksum) {
118
- throw new Error(`Migration checksum mismatch for ${migration.name}! Recorded: ${recordedChecksum.slice(0, 8)}, Current: ${migration.checksum.slice(0, 8)}. Refusing to apply modified migration.`);
119
- }
120
- skippedList.push(migration.name);
121
- }
122
- else {
123
- // Apply migration in transaction
124
- await client.query('BEGIN');
125
- try {
126
- await client.query(migration.sql);
127
- await client.query('INSERT INTO siduri_migrations (name, checksum) VALUES ($1, $2)', [migration.name, migration.checksum]);
128
- await client.query('COMMIT');
129
- appliedList.push(migration.name);
130
- }
131
- catch (err) {
132
- await client.query('ROLLBACK');
133
- throw err;
134
- }
135
- }
136
- }
137
- const redacted = redactDatabaseUrl(connectionString);
138
- const message = appliedList.length > 0
139
- ? `Applied ${appliedList.length} migration(s) to ${redacted}`
140
- : `Database schema up to date on ${redacted}`;
141
- return {
142
- status: appliedList.length > 0 ? 'APPLIED' : 'UP_TO_DATE',
143
- appliedMigrations: appliedList,
144
- skippedMigrations: skippedList,
145
- message,
146
- };
147
- }
148
- finally {
149
- client.release();
150
- await pool.end().catch(() => { });
151
- }
152
- }
@@ -1,13 +0,0 @@
1
- import { OrganManifest } from './manifest';
2
- export declare class OrganRegistry {
3
- private manifests;
4
- constructor(manifests?: OrganManifest[]);
5
- register(manifest: OrganManifest): void;
6
- get(organType: string): OrganManifest | undefined;
7
- getAll(): OrganManifest[];
8
- getAvailableOrganTypes(): string[];
9
- /**
10
- * Discover installed or monorepo organ packages.
11
- */
12
- static discover(searchRoots?: string[]): OrganRegistry;
13
- }
package/dist/discovery.js DELETED
@@ -1,87 +0,0 @@
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
- exports.OrganRegistry = void 0;
7
- const node_fs_1 = __importDefault(require("node:fs"));
8
- const node_path_1 = __importDefault(require("node:path"));
9
- const manifest_1 = require("./manifest");
10
- const builtin_manifests_1 = require("./builtin-manifests");
11
- class OrganRegistry {
12
- manifests = new Map();
13
- constructor(manifests) {
14
- if (manifests) {
15
- for (const m of manifests) {
16
- this.manifests.set(m.organType, m);
17
- }
18
- }
19
- }
20
- register(manifest) {
21
- const validated = (0, manifest_1.validateOrganManifest)(manifest);
22
- this.manifests.set(validated.organType, validated);
23
- }
24
- get(organType) {
25
- return this.manifests.get(organType);
26
- }
27
- getAll() {
28
- return Array.from(this.manifests.values());
29
- }
30
- getAvailableOrganTypes() {
31
- return Array.from(this.manifests.keys());
32
- }
33
- /**
34
- * Discover installed or monorepo organ packages.
35
- */
36
- static discover(searchRoots) {
37
- const registry = new OrganRegistry();
38
- const rootsToScan = searchRoots && searchRoots.length > 0
39
- ? searchRoots
40
- : [
41
- // 1. Monorepo organs folder relative to cli
42
- node_path_1.default.resolve(__dirname, '../../packages/organs'),
43
- // 2. Monorepo organs folder relative to cwd
44
- node_path_1.default.resolve(process.cwd(), 'packages/organs'),
45
- // 3. Node modules of current directory or global resolution
46
- node_path_1.default.resolve(process.cwd(), 'node_modules/@siduri-x'),
47
- node_path_1.default.resolve(__dirname, '../node_modules/@siduri-x'),
48
- ];
49
- const visitedDirs = new Set();
50
- for (const root of rootsToScan) {
51
- if (!node_fs_1.default.existsSync(root) || visitedDirs.has(root))
52
- continue;
53
- visitedDirs.add(root);
54
- try {
55
- const entries = node_fs_1.default.readdirSync(root, { withFileTypes: true });
56
- for (const entry of entries) {
57
- if (!entry.isDirectory())
58
- continue;
59
- const candidateDir = node_path_1.default.join(root, entry.name);
60
- const manifestPath = node_path_1.default.join(candidateDir, 'organ-manifest.json');
61
- if (node_fs_1.default.existsSync(manifestPath)) {
62
- try {
63
- const raw = JSON.parse(node_fs_1.default.readFileSync(manifestPath, 'utf8'));
64
- const manifest = (0, manifest_1.validateOrganManifest)(raw, manifestPath);
65
- registry.register(manifest);
66
- }
67
- catch (err) {
68
- console.warn(`[Siduri CLI] Warning: failed to parse manifest at ${manifestPath}: ${err.message}`);
69
- }
70
- }
71
- }
72
- }
73
- catch (err) {
74
- // Continue scanning other roots
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
- }
84
- return registry;
85
- }
86
- }
87
- exports.OrganRegistry = OrganRegistry;
@@ -1 +0,0 @@
1
- export {};
@@ -1,67 +0,0 @@
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 node_path_1 = __importDefault(require("node:path"));
7
- const discovery_1 = require("./discovery");
8
- const generator_1 = require("./generator");
9
- describe('Discovery & Dynamic Composition System Tests (Phase 3)', () => {
10
- const rootOrgansDir = node_path_1.default.resolve(__dirname, '../../packages/organs');
11
- test('Registry successfully discovers all 10 organ packages from workspace', () => {
12
- const registry = discovery_1.OrganRegistry.discover([rootOrgansDir]);
13
- const manifests = registry.getAll();
14
- expect(manifests.length).toBe(10);
15
- const organTypes = registry.getAvailableOrganTypes().sort();
16
- expect(organTypes).toEqual([
17
- 'behavior',
18
- 'body',
19
- 'brain',
20
- 'ear',
21
- 'hands',
22
- 'knowledge',
23
- 'memory',
24
- 'observation',
25
- 'vision',
26
- 'voice',
27
- ]);
28
- });
29
- test('Generates valid instances from discovered manifests', () => {
30
- const registry = discovery_1.OrganRegistry.discover([rootOrgansDir]);
31
- // Test Brain + Hands + Vision
32
- const brain = registry.get('brain');
33
- const hands = registry.get('hands');
34
- const vision = registry.get('vision');
35
- const files = (0, generator_1.generateInstanceFiles)({
36
- name: 'robotics-agent',
37
- selectedManifests: [brain, hands, vision],
38
- });
39
- const pkg = JSON.parse(files['package.json']);
40
- expect(Object.keys(pkg.dependencies).sort()).toEqual([
41
- '@siduri-x/brain',
42
- '@siduri-x/core',
43
- '@siduri-x/hands',
44
- '@siduri-x/vision',
45
- ]);
46
- const schema = JSON.parse(files['siduri.schema.json']);
47
- expect(Object.keys(schema.properties.organs.properties).sort()).toEqual(['brain', 'hands', 'vision']);
48
- const config = JSON.parse(files['siduri.config.json']);
49
- expect(Object.keys(config.organs).sort()).toEqual(['brain', 'hands', 'vision']);
50
- expect(files['.env.example']).toContain('OPENROUTER_API_KEY');
51
- expect(files['.env.example']).toContain('ACTION_POLICY_SECRET');
52
- expect(files['.env.example']).not.toContain('DATABASE_URL');
53
- });
54
- test('Validates manifest contract integrity during discovery', () => {
55
- expect(() => {
56
- const invalidRegistry = new discovery_1.OrganRegistry();
57
- invalidRegistry.register({});
58
- }).toThrow(/Invalid manifest/);
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
- });
67
- });
@@ -1 +0,0 @@
1
- export {};