@vxnus/siduri 0.0.7 → 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.
- package/dist/clean-machine-e2e.test.js +1 -1
- package/dist/configurators/behavior.d.ts +2 -0
- package/dist/configurators/behavior.js +36 -0
- package/dist/configurators/body.d.ts +2 -0
- package/dist/configurators/body.js +44 -0
- package/dist/configurators/brain.d.ts +6 -0
- package/dist/configurators/brain.js +80 -0
- package/dist/configurators/ear.d.ts +2 -0
- package/dist/configurators/ear.js +28 -0
- package/dist/configurators/hands.d.ts +2 -0
- package/dist/configurators/hands.js +27 -0
- package/dist/configurators/index.d.ts +23 -0
- package/dist/configurators/index.js +74 -0
- package/dist/configurators/knowledge.d.ts +6 -0
- package/dist/configurators/knowledge.js +100 -0
- package/dist/configurators/memory.d.ts +2 -0
- package/dist/configurators/memory.js +44 -0
- package/dist/configurators/observation.d.ts +2 -0
- package/dist/configurators/observation.js +12 -0
- package/dist/configurators/types.d.ts +12 -0
- package/dist/configurators/types.js +2 -0
- package/dist/configurators/vision.d.ts +2 -0
- package/dist/configurators/vision.js +39 -0
- package/dist/configurators/voice.d.ts +2 -0
- package/dist/configurators/voice.js +53 -0
- package/dist/configurators.test.d.ts +1 -0
- package/dist/configurators.test.js +275 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +137 -54
- package/dist/providers/knowledge-hub.d.ts +37 -0
- package/dist/providers/knowledge-hub.js +141 -0
- package/dist/providers/openrouter.d.ts +32 -0
- package/dist/providers/openrouter.js +201 -0
- package/dist/release-check.js +1 -1
- 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/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>;
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.colors = exports.CLI_VERSION = void 0;
|
|
8
|
+
exports.printHeader = printHeader;
|
|
9
|
+
exports.printSection = printSection;
|
|
10
|
+
exports.printSuccess = printSuccess;
|
|
11
|
+
exports.projectDirectoryName = projectDirectoryName;
|
|
12
|
+
exports.formatReviewSummary = formatReviewSummary;
|
|
13
|
+
exports.withTask = withTask;
|
|
7
14
|
exports.runCreateWizard = runCreateWizard;
|
|
8
15
|
exports.runCliDoctor = runCliDoctor;
|
|
9
16
|
exports.runCliDb = runCliDb;
|
|
@@ -16,24 +23,26 @@ const discovery_1 = require("./discovery");
|
|
|
16
23
|
const generator_1 = require("./generator");
|
|
17
24
|
const doctor_1 = require("./doctor");
|
|
18
25
|
const db_1 = require("./db");
|
|
26
|
+
const configurators_1 = require("./configurators");
|
|
19
27
|
const execFile = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
exports.CLI_VERSION = '0.0.8';
|
|
29
|
+
exports.colors = {
|
|
22
30
|
cyan: '\u001b[36m',
|
|
23
31
|
dim: '\u001b[2m',
|
|
24
32
|
green: '\u001b[32m',
|
|
25
33
|
yellow: '\u001b[33m',
|
|
34
|
+
bold: '\u001b[1m',
|
|
26
35
|
reset: '\u001b[0m',
|
|
27
36
|
};
|
|
28
37
|
function printHeader() {
|
|
29
|
-
console.log(`\n${colors.cyan}◈ SIDURI${colors.reset} ${colors.dim}companion setup (manifest-driven)${colors.reset}`);
|
|
30
|
-
console.log(`${colors.yellow}Version ${CLI_VERSION}${colors.reset} · composable standalone architecture\n`);
|
|
38
|
+
console.log(`\n${exports.colors.cyan}◈ SIDURI${exports.colors.reset} ${exports.colors.dim}companion setup (manifest-driven)${exports.colors.reset}`);
|
|
39
|
+
console.log(`${exports.colors.yellow}Version ${exports.CLI_VERSION}${exports.colors.reset} · composable standalone architecture\n`);
|
|
31
40
|
}
|
|
32
41
|
function printSection(title) {
|
|
33
|
-
console.log(`\n${colors.cyan}── ${title} ${'─'.repeat(Math.max(2, 42 - title.length))}${colors.reset}`);
|
|
42
|
+
console.log(`\n${exports.colors.cyan}── ${title} ${'─'.repeat(Math.max(2, 42 - title.length))}${exports.colors.reset}\n`);
|
|
34
43
|
}
|
|
35
44
|
function printSuccess(message) {
|
|
36
|
-
console.log(`${colors.green}✓${colors.reset} ${message}`);
|
|
45
|
+
console.log(`${exports.colors.green}✓${exports.colors.reset} ${message}`);
|
|
37
46
|
}
|
|
38
47
|
function projectDirectoryName(value) {
|
|
39
48
|
const slug = value
|
|
@@ -43,22 +52,47 @@ function projectDirectoryName(value) {
|
|
|
43
52
|
.replace(/^-+|-+$/g, '');
|
|
44
53
|
return slug || 'siduri';
|
|
45
54
|
}
|
|
55
|
+
function formatReviewSummary(companionName, selectedManifests, organSummaries) {
|
|
56
|
+
const lines = [];
|
|
57
|
+
lines.push(`\n${exports.colors.cyan}── Review ${companionName} ${'─'.repeat(Math.max(2, 42 - (companionName.length + 9)))}${exports.colors.reset}\n`);
|
|
58
|
+
lines.push(` ${exports.colors.bold}Companion${exports.colors.reset}`);
|
|
59
|
+
lines.push(` ${exports.colors.dim}Name:${exports.colors.reset} ${companionName}\n`);
|
|
60
|
+
for (const m of selectedManifests) {
|
|
61
|
+
const isRequired = m.organType === 'brain';
|
|
62
|
+
const tag = isRequired ? ` ${exports.colors.dim}· required${exports.colors.reset}` : '';
|
|
63
|
+
lines.push(` ${exports.colors.bold}${m.displayName.split(' ')[0] || m.organType}${exports.colors.reset}${tag}`);
|
|
64
|
+
const summary = organSummaries[m.configKey] || organSummaries[m.organType] || {};
|
|
65
|
+
const entries = Object.entries(summary);
|
|
66
|
+
if (entries.length === 0) {
|
|
67
|
+
lines.push(` ${exports.colors.dim}Provider:${exports.colors.reset} ${m.displayName}`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
for (const [key, val] of entries) {
|
|
71
|
+
const valStr = String(val);
|
|
72
|
+
lines.push(` ${exports.colors.dim}${key}:${exports.colors.reset}${' '.repeat(Math.max(1, 10 - key.length))}${valStr}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
lines.push('');
|
|
76
|
+
}
|
|
77
|
+
lines.push(`${exports.colors.cyan}${'─'.repeat(46)}${exports.colors.reset}\n`);
|
|
78
|
+
return lines.join('\n');
|
|
79
|
+
}
|
|
46
80
|
async function withTask(label, task) {
|
|
47
|
-
process.stdout.write(`${colors.dim}${label}${colors.reset}`);
|
|
81
|
+
process.stdout.write(`${exports.colors.dim}${label}${exports.colors.reset}`);
|
|
48
82
|
const frames = ['·', '•', '●', '•'];
|
|
49
83
|
let index = 0;
|
|
50
84
|
const timer = setInterval(() => {
|
|
51
|
-
process.stdout.write(`\r${colors.dim}${label} ${frames[index++ % frames.length]}${colors.reset}`);
|
|
85
|
+
process.stdout.write(`\r${exports.colors.dim}${label} ${frames[index++ % frames.length]}${exports.colors.reset}`);
|
|
52
86
|
}, 120);
|
|
53
87
|
try {
|
|
54
88
|
const result = await task();
|
|
55
89
|
clearInterval(timer);
|
|
56
|
-
process.stdout.write(`\r${colors.green}✓${colors.reset} ${label}\n`);
|
|
90
|
+
process.stdout.write(`\r${exports.colors.green}✓${exports.colors.reset} ${label}\n`);
|
|
57
91
|
return result;
|
|
58
92
|
}
|
|
59
93
|
catch (error) {
|
|
60
94
|
clearInterval(timer);
|
|
61
|
-
process.stdout.write(`\r${colors.yellow}!${colors.reset} ${label}\n`);
|
|
95
|
+
process.stdout.write(`\r${exports.colors.yellow}!${exports.colors.reset} ${label}\n`);
|
|
62
96
|
throw error;
|
|
63
97
|
}
|
|
64
98
|
}
|
|
@@ -86,7 +120,7 @@ async function runCreateWizard(targetDir) {
|
|
|
86
120
|
const companionName = basicAnswers.name;
|
|
87
121
|
const projectDir = targetDir ? node_path_1.default.resolve(process.cwd(), targetDir) : node_path_1.default.resolve(process.cwd(), projectDirectoryName(companionName));
|
|
88
122
|
printSection('Organ Selection');
|
|
89
|
-
console.log(`${colors.dim}Select any combination of organs to compose into your standalone instance.${colors.reset}\n`);
|
|
123
|
+
console.log(`${exports.colors.dim}Select any combination of organs to compose into your standalone instance.${exports.colors.reset}\n`);
|
|
90
124
|
// Brain is required by architecture contract for cognition
|
|
91
125
|
const brainManifest = registry.get('brain');
|
|
92
126
|
const nonBrainManifests = availableManifests.filter((m) => m.organType !== 'brain');
|
|
@@ -110,28 +144,77 @@ async function runCreateWizard(targetDir) {
|
|
|
110
144
|
if (m)
|
|
111
145
|
selectedManifests.push(m);
|
|
112
146
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
console.log(` ${colors.dim}Core Protocol:${colors.reset} @siduri-x/core`);
|
|
117
|
-
console.log(` ${colors.dim}Selected Organs:${colors.reset}`);
|
|
147
|
+
// 2. Interactive Organ Configuration Stage
|
|
148
|
+
const organConfigs = {};
|
|
149
|
+
const organSummaries = {};
|
|
118
150
|
for (const m of selectedManifests) {
|
|
119
|
-
|
|
151
|
+
const isRequired = m.organType === 'brain';
|
|
152
|
+
const sectionTitle = isRequired ? `${m.displayName.split(' ')[0] || m.organType} · required` : m.displayName.split(' ')[0] || m.organType;
|
|
153
|
+
printSection(sectionTitle);
|
|
154
|
+
const res = await (0, configurators_1.configureOrgan)(m, {
|
|
155
|
+
companionName,
|
|
156
|
+
existingConfig: organConfigs[m.configKey],
|
|
157
|
+
});
|
|
158
|
+
organConfigs[m.configKey] = res.config;
|
|
159
|
+
organSummaries[m.configKey] = res.summary || {};
|
|
120
160
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
161
|
+
// 3. Final Review and Edit Loop
|
|
162
|
+
while (true) {
|
|
163
|
+
console.log(formatReviewSummary(companionName, selectedManifests, organSummaries));
|
|
164
|
+
const { reviewAction } = await inquirer_1.default.prompt({
|
|
165
|
+
type: 'list',
|
|
166
|
+
name: 'reviewAction',
|
|
167
|
+
message: `Create ${companionName} with this configuration?`,
|
|
168
|
+
choices: [
|
|
169
|
+
{ name: 'Yes, create', value: 'create' },
|
|
170
|
+
{ name: 'Go back and edit', value: 'edit' },
|
|
171
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
172
|
+
],
|
|
173
|
+
});
|
|
174
|
+
if (reviewAction === 'cancel') {
|
|
175
|
+
console.log('Instance creation cancelled.');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (reviewAction === 'create') {
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
if (reviewAction === 'edit') {
|
|
182
|
+
const editChoices = selectedManifests.map((m) => ({
|
|
183
|
+
name: `Edit ${m.displayName}`,
|
|
184
|
+
value: m.configKey,
|
|
185
|
+
}));
|
|
186
|
+
editChoices.push({ name: 'Edit all organs in sequence', value: '__ALL__' });
|
|
187
|
+
editChoices.push({ name: 'Back to review', value: '__BACK__' });
|
|
188
|
+
const { organToEdit } = await inquirer_1.default.prompt({
|
|
189
|
+
type: 'list',
|
|
190
|
+
name: 'organToEdit',
|
|
191
|
+
message: 'Which organ would you like to edit?',
|
|
192
|
+
choices: editChoices,
|
|
193
|
+
});
|
|
194
|
+
if (organToEdit === '__BACK__') {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const organsToReconfigure = organToEdit === '__ALL__'
|
|
198
|
+
? selectedManifests
|
|
199
|
+
: selectedManifests.filter((m) => m.configKey === organToEdit);
|
|
200
|
+
for (const m of organsToReconfigure) {
|
|
201
|
+
const isRequired = m.organType === 'brain';
|
|
202
|
+
const sectionTitle = isRequired ? `${m.displayName.split(' ')[0] || m.organType} · required` : m.displayName.split(' ')[0] || m.organType;
|
|
203
|
+
printSection(sectionTitle);
|
|
204
|
+
const res = await (0, configurators_1.configureOrgan)(m, {
|
|
205
|
+
companionName,
|
|
206
|
+
existingConfig: organConfigs[m.configKey],
|
|
207
|
+
});
|
|
208
|
+
organConfigs[m.configKey] = res.config;
|
|
209
|
+
organSummaries[m.configKey] = res.summary || {};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
130
212
|
}
|
|
131
|
-
//
|
|
213
|
+
// 4. Generate Instance Files
|
|
132
214
|
const files = (0, generator_1.generateInstanceFiles)({
|
|
133
215
|
name: companionName,
|
|
134
216
|
selectedManifests,
|
|
217
|
+
organConfigs,
|
|
135
218
|
});
|
|
136
219
|
await (0, promises_1.mkdir)(projectDir, { recursive: true });
|
|
137
220
|
await (0, promises_1.mkdir)(node_path_1.default.join(projectDir, 'src'), { recursive: true });
|
|
@@ -145,7 +228,7 @@ async function runCreateWizard(targetDir) {
|
|
|
145
228
|
await (0, promises_1.mkdir)(node_path_1.default.join(projectDir, 'assets/body/model'), { recursive: true });
|
|
146
229
|
}
|
|
147
230
|
printSuccess(`Generated standalone files at ${projectDir}`);
|
|
148
|
-
//
|
|
231
|
+
// 5. Install packages if not in dry-run
|
|
149
232
|
try {
|
|
150
233
|
await withTask('Installing dependencies (npm install)', async () => {
|
|
151
234
|
await execFile('npm', ['install', '--no-audit', '--no-fund'], { cwd: projectDir });
|
|
@@ -153,48 +236,48 @@ async function runCreateWizard(targetDir) {
|
|
|
153
236
|
printSuccess('Dependencies installed successfully.');
|
|
154
237
|
}
|
|
155
238
|
catch (err) {
|
|
156
|
-
console.warn(`${colors.yellow}!${colors.reset} Notice: npm install had warnings or requires network: ${err.message}`);
|
|
239
|
+
console.warn(`${exports.colors.yellow}!${exports.colors.reset} Notice: npm install had warnings or requires network: ${err.message}`);
|
|
157
240
|
}
|
|
158
241
|
printSection('Instance Ready');
|
|
159
|
-
console.log(
|
|
242
|
+
console.log(`Your Siduri companion is ready! Next steps:\n`);
|
|
160
243
|
console.log(` cd ${node_path_1.default.relative(process.cwd(), projectDir) || '.'}`);
|
|
161
|
-
console.log(` cp .env.example .env ${colors.dim}# Fill in required API keys/credentials${colors.reset}`);
|
|
162
|
-
console.log(` npm start ${colors.dim}# Start your standalone companion${colors.reset}\n`);
|
|
244
|
+
console.log(` cp .env.example .env ${exports.colors.dim}# Fill in required API keys/credentials${exports.colors.reset}`);
|
|
245
|
+
console.log(` npm start ${exports.colors.dim}# Start your standalone companion${exports.colors.reset}\n`);
|
|
163
246
|
}
|
|
164
247
|
async function runCliDoctor(targetDir) {
|
|
165
248
|
printHeader();
|
|
166
249
|
const dir = targetDir ? node_path_1.default.resolve(process.cwd(), targetDir) : process.cwd();
|
|
167
|
-
console.log(`${colors.cyan}Siduri Doctor${colors.reset}`);
|
|
168
|
-
console.log(`${colors.dim}─────────────${colors.reset}\n`);
|
|
250
|
+
console.log(`${exports.colors.cyan}Siduri Doctor${exports.colors.reset}`);
|
|
251
|
+
console.log(`${exports.colors.dim}─────────────${exports.colors.reset}\n`);
|
|
169
252
|
try {
|
|
170
253
|
const report = await (0, doctor_1.runDoctor)({ projectDir: dir });
|
|
171
|
-
console.log(`${colors.dim}Instance:${colors.reset} ${report.instanceName}`);
|
|
172
|
-
console.log(`${colors.dim}Organs:${colors.reset} ${report.configuredOrgans.join(', ')}\n`);
|
|
254
|
+
console.log(`${exports.colors.dim}Instance:${exports.colors.reset} ${report.instanceName}`);
|
|
255
|
+
console.log(`${exports.colors.dim}Organs:${exports.colors.reset} ${report.configuredOrgans.join(', ')}\n`);
|
|
173
256
|
const categories = ['Environment', 'Services', 'Database', 'Health Probe'];
|
|
174
257
|
for (const cat of categories) {
|
|
175
258
|
const items = report.results.filter((r) => r.category === cat);
|
|
176
259
|
if (items.length > 0) {
|
|
177
|
-
console.log(`${colors.cyan}${cat}${colors.reset}`);
|
|
260
|
+
console.log(`${exports.colors.cyan}${cat}${exports.colors.reset}`);
|
|
178
261
|
for (const item of items) {
|
|
179
262
|
if (item.status === 'PASS') {
|
|
180
|
-
console.log(` ${colors.green}✓${colors.reset} ${item.name} ${colors.dim}(${item.message || 'OK'})${colors.reset}`);
|
|
263
|
+
console.log(` ${exports.colors.green}✓${exports.colors.reset} ${item.name} ${exports.colors.dim}(${item.message || 'OK'})${exports.colors.reset}`);
|
|
181
264
|
}
|
|
182
265
|
else if (item.status === 'OPTIONAL_MISSING') {
|
|
183
|
-
console.log(` ${colors.dim}○${colors.reset} ${item.name} ${colors.dim}(Optional, not set)${colors.reset}`);
|
|
266
|
+
console.log(` ${exports.colors.dim}○${exports.colors.reset} ${item.name} ${exports.colors.dim}(Optional, not set)${exports.colors.reset}`);
|
|
184
267
|
}
|
|
185
268
|
else if (item.status === 'SKIPPED') {
|
|
186
|
-
console.log(` ${colors.dim}— ${item.name} (${item.message})${colors.reset}`);
|
|
269
|
+
console.log(` ${exports.colors.dim}— ${item.name} (${item.message})${exports.colors.reset}`);
|
|
187
270
|
}
|
|
188
271
|
else {
|
|
189
|
-
console.log(` ${colors.yellow}✗${colors.reset} ${item.name}`);
|
|
272
|
+
console.log(` ${exports.colors.yellow}✗${exports.colors.reset} ${item.name}`);
|
|
190
273
|
if (item.organName) {
|
|
191
|
-
console.log(` ${colors.dim}Required by:${colors.reset} ${item.organName}`);
|
|
274
|
+
console.log(` ${exports.colors.dim}Required by:${exports.colors.reset} ${item.organName}`);
|
|
192
275
|
}
|
|
193
276
|
if (item.message) {
|
|
194
|
-
console.log(` ${colors.yellow}${item.message}${colors.reset}`);
|
|
277
|
+
console.log(` ${exports.colors.yellow}${item.message}${exports.colors.reset}`);
|
|
195
278
|
}
|
|
196
279
|
if (item.remediation) {
|
|
197
|
-
console.log(` ${colors.dim}Remediation:${colors.reset} ${item.remediation}`);
|
|
280
|
+
console.log(` ${exports.colors.dim}Remediation:${exports.colors.reset} ${item.remediation}`);
|
|
198
281
|
}
|
|
199
282
|
}
|
|
200
283
|
}
|
|
@@ -202,16 +285,16 @@ async function runCliDoctor(targetDir) {
|
|
|
202
285
|
}
|
|
203
286
|
}
|
|
204
287
|
if (report.passed) {
|
|
205
|
-
console.log(`${colors.green}Result: PASS${colors.reset}\n`);
|
|
288
|
+
console.log(`${exports.colors.green}Result: PASS${exports.colors.reset}\n`);
|
|
206
289
|
process.exitCode = 0;
|
|
207
290
|
}
|
|
208
291
|
else {
|
|
209
|
-
console.log(`${colors.yellow}Result: FAIL${colors.reset}\n`);
|
|
292
|
+
console.log(`${exports.colors.yellow}Result: FAIL${exports.colors.reset}\n`);
|
|
210
293
|
process.exitCode = 1;
|
|
211
294
|
}
|
|
212
295
|
}
|
|
213
296
|
catch (err) {
|
|
214
|
-
console.error(`\n${colors.yellow}Doctor Error:${colors.reset} ${err.message}\n`);
|
|
297
|
+
console.error(`\n${exports.colors.yellow}Doctor Error:${exports.colors.reset} ${err.message}\n`);
|
|
215
298
|
process.exitCode = 2;
|
|
216
299
|
}
|
|
217
300
|
}
|
|
@@ -223,24 +306,24 @@ async function runCliDb(subcommand, targetDir) {
|
|
|
223
306
|
return;
|
|
224
307
|
}
|
|
225
308
|
const dir = targetDir ? node_path_1.default.resolve(process.cwd(), targetDir) : process.cwd();
|
|
226
|
-
console.log(`${colors.cyan}Siduri Database Migrations${colors.reset}`);
|
|
227
|
-
console.log(`${colors.dim}──────────────────────────${colors.reset}\n`);
|
|
309
|
+
console.log(`${exports.colors.cyan}Siduri Database Migrations${exports.colors.reset}`);
|
|
310
|
+
console.log(`${exports.colors.dim}──────────────────────────${exports.colors.reset}\n`);
|
|
228
311
|
try {
|
|
229
312
|
const res = await (0, db_1.runDbPush)({ projectDir: dir });
|
|
230
313
|
if (res.status === 'NOOP') {
|
|
231
|
-
console.log(`${colors.dim}— ${res.message}${colors.reset}\n`);
|
|
314
|
+
console.log(`${exports.colors.dim}— ${res.message}${exports.colors.reset}\n`);
|
|
232
315
|
}
|
|
233
316
|
else {
|
|
234
317
|
printSuccess(res.message);
|
|
235
318
|
if (res.appliedMigrations.length > 0) {
|
|
236
|
-
console.log(`${colors.dim}Applied:${colors.reset} ${res.appliedMigrations.join(', ')}`);
|
|
319
|
+
console.log(`${exports.colors.dim}Applied:${exports.colors.reset} ${res.appliedMigrations.join(', ')}`);
|
|
237
320
|
}
|
|
238
321
|
console.log();
|
|
239
322
|
}
|
|
240
323
|
process.exitCode = 0;
|
|
241
324
|
}
|
|
242
325
|
catch (err) {
|
|
243
|
-
console.error(`\n${colors.yellow}Database Migration Error:${colors.reset} ${err.message}\n`);
|
|
326
|
+
console.error(`\n${exports.colors.yellow}Database Migration Error:${exports.colors.reset} ${err.message}\n`);
|
|
244
327
|
process.exitCode = 3;
|
|
245
328
|
}
|
|
246
329
|
}
|
|
@@ -248,7 +331,7 @@ async function main() {
|
|
|
248
331
|
const args = process.argv.slice(2);
|
|
249
332
|
const command = args[0];
|
|
250
333
|
if (command === '--version' || command === '-v') {
|
|
251
|
-
console.log(CLI_VERSION);
|
|
334
|
+
console.log(exports.CLI_VERSION);
|
|
252
335
|
return;
|
|
253
336
|
}
|
|
254
337
|
if (command === 'create') {
|
|
@@ -279,7 +362,7 @@ if (require.main === module) {
|
|
|
279
362
|
console.log('\nOperation cancelled.');
|
|
280
363
|
return;
|
|
281
364
|
}
|
|
282
|
-
console.error(`\n${colors.yellow}!${colors.reset} ${error instanceof Error ? error.message : error}`);
|
|
365
|
+
console.error(`\n${exports.colors.yellow}!${exports.colors.reset} ${error instanceof Error ? error.message : error}`);
|
|
283
366
|
process.exitCode = 1;
|
|
284
367
|
});
|
|
285
368
|
}
|