@vxnus/siduri 0.0.5 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -28
- package/dist/builtin-manifests.d.ts +2 -0
- package/dist/builtin-manifests.js +433 -0
- package/dist/clean-machine-e2e.test.d.ts +1 -0
- package/dist/clean-machine-e2e.test.js +222 -0
- package/dist/db.d.ts +25 -0
- package/dist/db.js +152 -0
- package/dist/discovery.d.ts +13 -0
- package/dist/discovery.js +87 -0
- package/dist/discovery.test.d.ts +1 -0
- package/dist/discovery.test.js +67 -0
- package/dist/doctor-db.test.d.ts +1 -0
- package/dist/doctor-db.test.js +135 -0
- package/dist/doctor.d.ts +19 -0
- package/dist/doctor.js +225 -0
- package/dist/generator.d.ts +18 -0
- package/dist/generator.js +215 -0
- package/dist/generator.test.d.ts +1 -0
- package/dist/generator.test.js +158 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +202 -360
- package/dist/manifest.d.ts +33 -0
- package/dist/manifest.js +40 -0
- package/dist/release-check.d.ts +9 -0
- package/dist/release-check.js +131 -0
- package/package.json +12 -11
- package/dist/runtime.js +0 -3299
package/dist/index.js
CHANGED
|
@@ -4,26 +4,20 @@ 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
|
-
|
|
7
|
+
exports.runCreateWizard = runCreateWizard;
|
|
8
|
+
exports.runCliDoctor = runCliDoctor;
|
|
9
|
+
exports.runCliDb = runCliDb;
|
|
8
10
|
const node_child_process_1 = require("node:child_process");
|
|
9
11
|
const promises_1 = require("node:fs/promises");
|
|
10
|
-
const node_os_1 = require("node:os");
|
|
11
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
12
13
|
const node_util_1 = require("node:util");
|
|
13
14
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
14
|
-
const
|
|
15
|
+
const discovery_1 = require("./discovery");
|
|
16
|
+
const generator_1 = require("./generator");
|
|
17
|
+
const doctor_1 = require("./doctor");
|
|
18
|
+
const db_1 = require("./db");
|
|
15
19
|
const execFile = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
16
|
-
const
|
|
17
|
-
const CLI_VERSION = '0.0.5';
|
|
18
|
-
const RUNTIME_DEPENDENCIES = {
|
|
19
|
-
'@vxnus/e': '^0.1.4',
|
|
20
|
-
'@vxnus/e-knowledge': '^0.1.4',
|
|
21
|
-
cors: '^2.8.5',
|
|
22
|
-
express: '^4.18.2',
|
|
23
|
-
pg: '^8.23.0',
|
|
24
|
-
ws: '^8.21.3',
|
|
25
|
-
zod: '^4.4.3',
|
|
26
|
-
};
|
|
20
|
+
const CLI_VERSION = '0.0.7';
|
|
27
21
|
const colors = {
|
|
28
22
|
cyan: '\u001b[36m',
|
|
29
23
|
dim: '\u001b[2m',
|
|
@@ -32,8 +26,8 @@ const colors = {
|
|
|
32
26
|
reset: '\u001b[0m',
|
|
33
27
|
};
|
|
34
28
|
function printHeader() {
|
|
35
|
-
console.log(`\n${colors.cyan}◈ SIDURI${colors.reset} ${colors.dim}companion setup${colors.reset}`);
|
|
36
|
-
console.log(`${colors.yellow}
|
|
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`);
|
|
37
31
|
}
|
|
38
32
|
function printSection(title) {
|
|
39
33
|
console.log(`\n${colors.cyan}── ${title} ${'─'.repeat(Math.max(2, 42 - title.length))}${colors.reset}`);
|
|
@@ -41,35 +35,13 @@ function printSection(title) {
|
|
|
41
35
|
function printSuccess(message) {
|
|
42
36
|
console.log(`${colors.green}✓${colors.reset} ${message}`);
|
|
43
37
|
}
|
|
44
|
-
function
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
async function startRuntime() {
|
|
58
|
-
const filePath = runtimePath();
|
|
59
|
-
if (!pathExists(filePath)) {
|
|
60
|
-
throw new Error('Siduri runtime bundle is missing. Reinstall the CLI or rebuild the package.');
|
|
61
|
-
}
|
|
62
|
-
await new Promise((resolve, reject) => {
|
|
63
|
-
const child = (0, node_child_process_1.spawn)(process.execPath, [filePath], { stdio: 'inherit', env: process.env });
|
|
64
|
-
child.once('error', reject);
|
|
65
|
-
child.once('exit', (code, signal) => {
|
|
66
|
-
if (signal)
|
|
67
|
-
process.exitCode = 1;
|
|
68
|
-
else if (code !== null)
|
|
69
|
-
process.exitCode = code;
|
|
70
|
-
resolve();
|
|
71
|
-
});
|
|
72
|
-
});
|
|
38
|
+
function projectDirectoryName(value) {
|
|
39
|
+
const slug = value
|
|
40
|
+
.trim()
|
|
41
|
+
.toLowerCase()
|
|
42
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
43
|
+
.replace(/^-+|-+$/g, '');
|
|
44
|
+
return slug || 'siduri';
|
|
73
45
|
}
|
|
74
46
|
async function withTask(label, task) {
|
|
75
47
|
process.stdout.write(`${colors.dim}${label}${colors.reset}`);
|
|
@@ -93,351 +65,221 @@ async function withTask(label, task) {
|
|
|
93
65
|
function nonEmpty(value) {
|
|
94
66
|
return value.trim().length > 0 || 'Please enter a value.';
|
|
95
67
|
}
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
68
|
+
async function runCreateWizard(targetDir) {
|
|
69
|
+
printHeader();
|
|
70
|
+
// 1. Discover manifests from installed or monorepo packages
|
|
71
|
+
const registry = discovery_1.OrganRegistry.discover();
|
|
72
|
+
const availableManifests = registry.getAll();
|
|
73
|
+
if (availableManifests.length === 0) {
|
|
74
|
+
throw new Error('No @siduri-x/* organ packages found. Please ensure organs are installed or in workspace.');
|
|
100
75
|
}
|
|
101
|
-
|
|
102
|
-
|
|
76
|
+
printSection('Companion Details');
|
|
77
|
+
const basicAnswers = await inquirer_1.default.prompt([
|
|
78
|
+
{
|
|
79
|
+
type: 'input',
|
|
80
|
+
name: 'name',
|
|
81
|
+
message: 'Companion name:',
|
|
82
|
+
default: 'Siduri',
|
|
83
|
+
validate: nonEmpty,
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
const companionName = basicAnswers.name;
|
|
87
|
+
const projectDir = targetDir ? node_path_1.default.resolve(process.cwd(), targetDir) : node_path_1.default.resolve(process.cwd(), projectDirectoryName(companionName));
|
|
88
|
+
printSection('Organ Selection');
|
|
89
|
+
console.log(`${colors.dim}Select any combination of organs to compose into your standalone instance.${colors.reset}\n`);
|
|
90
|
+
// Brain is required by architecture contract for cognition
|
|
91
|
+
const brainManifest = registry.get('brain');
|
|
92
|
+
const nonBrainManifests = availableManifests.filter((m) => m.organType !== 'brain');
|
|
93
|
+
const organChoices = nonBrainManifests.map((m) => ({
|
|
94
|
+
name: `${m.displayName} (${m.name})`,
|
|
95
|
+
value: m.organType,
|
|
96
|
+
checked: m.organType === 'memory', // default memory checked
|
|
97
|
+
}));
|
|
98
|
+
const { selectedOrganTypes } = await inquirer_1.default.prompt({
|
|
99
|
+
type: 'checkbox',
|
|
100
|
+
name: 'selectedOrganTypes',
|
|
101
|
+
message: 'Select organs to install:',
|
|
102
|
+
choices: organChoices,
|
|
103
|
+
});
|
|
104
|
+
const selectedManifests = [];
|
|
105
|
+
if (brainManifest) {
|
|
106
|
+
selectedManifests.push(brainManifest);
|
|
103
107
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
function projectDirectoryName(value) {
|
|
109
|
-
const slug = value
|
|
110
|
-
.trim()
|
|
111
|
-
.toLowerCase()
|
|
112
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
113
|
-
.replace(/^-+|-+$/g, '');
|
|
114
|
-
return slug || 'siduri';
|
|
115
|
-
}
|
|
116
|
-
async function getJson(url) {
|
|
117
|
-
const controller = new AbortController();
|
|
118
|
-
const timeout = setTimeout(() => controller.abort(), 15000);
|
|
119
|
-
let response;
|
|
120
|
-
try {
|
|
121
|
-
response = await fetch(url, { headers: { accept: 'application/json' }, signal: controller.signal });
|
|
108
|
+
for (const organType of selectedOrganTypes) {
|
|
109
|
+
const m = registry.get(organType);
|
|
110
|
+
if (m)
|
|
111
|
+
selectedManifests.push(m);
|
|
122
112
|
}
|
|
123
|
-
|
|
124
|
-
|
|
113
|
+
printSection('Review Composition');
|
|
114
|
+
console.log(` ${colors.dim}Companion Name:${colors.reset} ${companionName}`);
|
|
115
|
+
console.log(` ${colors.dim}Target Path:${colors.reset} ${projectDir}`);
|
|
116
|
+
console.log(` ${colors.dim}Core Protocol:${colors.reset} @siduri-x/core`);
|
|
117
|
+
console.log(` ${colors.dim}Selected Organs:${colors.reset}`);
|
|
118
|
+
for (const m of selectedManifests) {
|
|
119
|
+
console.log(` - ${m.displayName} (${colors.dim}${m.name}${colors.reset})`);
|
|
125
120
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
121
|
+
const { confirm } = await inquirer_1.default.prompt({
|
|
122
|
+
type: 'confirm',
|
|
123
|
+
name: 'confirm',
|
|
124
|
+
message: 'Generate standalone Siduri instance with these organs?',
|
|
125
|
+
default: true,
|
|
126
|
+
});
|
|
127
|
+
if (!confirm) {
|
|
128
|
+
console.log('Instance creation cancelled.');
|
|
129
|
+
return;
|
|
134
130
|
}
|
|
135
|
-
|
|
136
|
-
|
|
131
|
+
// 2. Generate Files
|
|
132
|
+
const files = (0, generator_1.generateInstanceFiles)({
|
|
133
|
+
name: companionName,
|
|
134
|
+
selectedManifests,
|
|
135
|
+
});
|
|
136
|
+
await (0, promises_1.mkdir)(projectDir, { recursive: true });
|
|
137
|
+
await (0, promises_1.mkdir)(node_path_1.default.join(projectDir, 'src'), { recursive: true });
|
|
138
|
+
await (0, promises_1.writeFile)(node_path_1.default.join(projectDir, 'package.json'), files['package.json'], 'utf8');
|
|
139
|
+
await (0, promises_1.writeFile)(node_path_1.default.join(projectDir, 'siduri.config.json'), files['siduri.config.json'], 'utf8');
|
|
140
|
+
await (0, promises_1.writeFile)(node_path_1.default.join(projectDir, 'siduri.schema.json'), files['siduri.schema.json'], 'utf8');
|
|
141
|
+
await (0, promises_1.writeFile)(node_path_1.default.join(projectDir, '.env.example'), files['.env.example'], 'utf8');
|
|
142
|
+
await (0, promises_1.writeFile)(node_path_1.default.join(projectDir, 'README.md'), files['README.md'], 'utf8');
|
|
143
|
+
await (0, promises_1.writeFile)(node_path_1.default.join(projectDir, 'src/index.js'), files['src/index.js'], 'utf8');
|
|
144
|
+
if (files.createAssetsBodyDir) {
|
|
145
|
+
await (0, promises_1.mkdir)(node_path_1.default.join(projectDir, 'assets/body/model'), { recursive: true });
|
|
137
146
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
await (
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
catch {
|
|
146
|
-
// Continue searching immediate children.
|
|
147
|
-
}
|
|
147
|
+
printSuccess(`Generated standalone files at ${projectDir}`);
|
|
148
|
+
// 3. Install packages if not in dry-run
|
|
149
|
+
try {
|
|
150
|
+
await withTask('Installing dependencies (npm install)', async () => {
|
|
151
|
+
await execFile('npm', ['install', '--no-audit', '--no-fund'], { cwd: projectDir });
|
|
152
|
+
});
|
|
153
|
+
printSuccess('Dependencies installed successfully.');
|
|
148
154
|
}
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
async function installArchive(pack) {
|
|
152
|
-
const response = await fetch(pack.distribution.url);
|
|
153
|
-
if (!response.ok)
|
|
154
|
-
throw new Error(`Pack archive returned HTTP ${response.status}`);
|
|
155
|
-
const archive = Buffer.from(await response.arrayBuffer());
|
|
156
|
-
if (pack.distribution.checksum) {
|
|
157
|
-
const checksum = (0, node_crypto_1.createHash)('sha256').update(archive).digest('hex');
|
|
158
|
-
if (checksum !== pack.distribution.checksum) {
|
|
159
|
-
throw new Error(`Pack archive checksum mismatch for ${pack.id}@${pack.version}`);
|
|
160
|
-
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
console.warn(`${colors.yellow}!${colors.reset} Notice: npm install had warnings or requires network: ${err.message}`);
|
|
161
157
|
}
|
|
162
|
-
|
|
158
|
+
printSection('Instance Ready');
|
|
159
|
+
console.log(`\nYour Siduri companion is ready! Next steps:\n`);
|
|
160
|
+
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`);
|
|
163
|
+
}
|
|
164
|
+
async function runCliDoctor(targetDir) {
|
|
165
|
+
printHeader();
|
|
166
|
+
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`);
|
|
163
169
|
try {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
170
|
+
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`);
|
|
173
|
+
const categories = ['Environment', 'Services', 'Database', 'Health Probe'];
|
|
174
|
+
for (const cat of categories) {
|
|
175
|
+
const items = report.results.filter((r) => r.category === cat);
|
|
176
|
+
if (items.length > 0) {
|
|
177
|
+
console.log(`${colors.cyan}${cat}${colors.reset}`);
|
|
178
|
+
for (const item of items) {
|
|
179
|
+
if (item.status === 'PASS') {
|
|
180
|
+
console.log(` ${colors.green}✓${colors.reset} ${item.name} ${colors.dim}(${item.message || 'OK'})${colors.reset}`);
|
|
181
|
+
}
|
|
182
|
+
else if (item.status === 'OPTIONAL_MISSING') {
|
|
183
|
+
console.log(` ${colors.dim}○${colors.reset} ${item.name} ${colors.dim}(Optional, not set)${colors.reset}`);
|
|
184
|
+
}
|
|
185
|
+
else if (item.status === 'SKIPPED') {
|
|
186
|
+
console.log(` ${colors.dim}— ${item.name} (${item.message})${colors.reset}`);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
console.log(` ${colors.yellow}✗${colors.reset} ${item.name}`);
|
|
190
|
+
if (item.organName) {
|
|
191
|
+
console.log(` ${colors.dim}Required by:${colors.reset} ${item.organName}`);
|
|
192
|
+
}
|
|
193
|
+
if (item.message) {
|
|
194
|
+
console.log(` ${colors.yellow}${item.message}${colors.reset}`);
|
|
195
|
+
}
|
|
196
|
+
if (item.remediation) {
|
|
197
|
+
console.log(` ${colors.dim}Remediation:${colors.reset} ${item.remediation}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
console.log();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (report.passed) {
|
|
205
|
+
console.log(`${colors.green}Result: PASS${colors.reset}\n`);
|
|
206
|
+
process.exitCode = 0;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
console.log(`${colors.yellow}Result: FAIL${colors.reset}\n`);
|
|
210
|
+
process.exitCode = 1;
|
|
171
211
|
}
|
|
172
|
-
await execFile('tar', ['-xzf', archivePath, '-C', extractedPath, '--no-same-owner', '--no-same-permissions', '--no-absolute-names']);
|
|
173
|
-
const sourcePath = await findManifest(extractedPath);
|
|
174
|
-
const destination = node_path_1.default.join((0, node_os_1.homedir)(), '.siduri', 'knowledge', safePart(pack.publisher), safePart(pack.name), safePart(pack.version));
|
|
175
|
-
await (0, promises_1.mkdir)(node_path_1.default.dirname(destination), { recursive: true });
|
|
176
|
-
await (0, promises_1.rm)(destination, { recursive: true, force: true });
|
|
177
|
-
await (0, promises_1.cp)(sourcePath, destination, { recursive: true });
|
|
178
|
-
return destination;
|
|
179
212
|
}
|
|
180
|
-
|
|
181
|
-
|
|
213
|
+
catch (err) {
|
|
214
|
+
console.error(`\n${colors.yellow}Doctor Error:${colors.reset} ${err.message}\n`);
|
|
215
|
+
process.exitCode = 2;
|
|
182
216
|
}
|
|
183
217
|
}
|
|
184
|
-
async function
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const normalized = query.trim().replace(/^@/, '');
|
|
191
|
-
const [publisher, name] = normalized.split('/');
|
|
192
|
-
const result = publisher && name && !query.includes(' ')
|
|
193
|
-
? await withTask('Searching E Knowledge Hub', () => getJson(`${registryUrl}/${encodeURIComponent(publisher)}/${encodeURIComponent(name)}`))
|
|
194
|
-
: await withTask('Searching E Knowledge Hub', () => getJson(`${registryUrl}?q=${encodeURIComponent(query)}&limit=20`));
|
|
195
|
-
const packs = 'packs' in result ? result.packs : [result];
|
|
196
|
-
if (packs.length === 0)
|
|
197
|
-
throw new Error(`No knowledge packs found for '${query}'`);
|
|
198
|
-
if (packs.length === 1)
|
|
199
|
-
return packs[0];
|
|
200
|
-
const { selected } = await inquirer_1.default.prompt({
|
|
201
|
-
type: 'list',
|
|
202
|
-
name: 'selected',
|
|
203
|
-
message: 'Select a knowledge pack:',
|
|
204
|
-
choices: packs.map((pack) => ({ name: `${pack.id} v${pack.version}`, value: pack.id })),
|
|
205
|
-
});
|
|
206
|
-
return packs.find((pack) => pack.id === selected);
|
|
207
|
-
}
|
|
208
|
-
async function configureKnowledge() {
|
|
209
|
-
const { mode } = await inquirer_1.default.prompt({
|
|
210
|
-
type: 'list',
|
|
211
|
-
name: 'mode',
|
|
212
|
-
message: 'Knowledge source?',
|
|
213
|
-
choices: [
|
|
214
|
-
{ name: 'E Knowledge Hub', value: 'hub' },
|
|
215
|
-
{ name: 'Installed local pack', value: 'local' },
|
|
216
|
-
{ name: 'Hosted provider URL', value: 'remote' },
|
|
217
|
-
{ name: 'Do not use knowledge', value: 'none' },
|
|
218
|
-
],
|
|
219
|
-
});
|
|
220
|
-
if (mode === 'none')
|
|
221
|
-
return { provider: 'none' };
|
|
222
|
-
if (mode === 'local') {
|
|
223
|
-
const { packPath } = await inquirer_1.default.prompt({
|
|
224
|
-
type: 'input',
|
|
225
|
-
name: 'packPath',
|
|
226
|
-
message: 'Path to the installed E knowledge pack:',
|
|
227
|
-
default: './knowledge-pack',
|
|
228
|
-
});
|
|
229
|
-
const resolved = node_path_1.default.resolve(packPath);
|
|
230
|
-
const loaded = await withTask('Validating local knowledge pack', () => (0, e_knowledge_1.loadPack)(resolved));
|
|
231
|
-
printSuccess(`Knowledge ready · ${loaded.manifest.id} · revision ${loaded.revision.id}`);
|
|
232
|
-
return { provider: 'e-knowledge', packPath: resolved };
|
|
218
|
+
async function runCliDb(subcommand, targetDir) {
|
|
219
|
+
printHeader();
|
|
220
|
+
if (subcommand !== 'push') {
|
|
221
|
+
console.log('Usage: siduri db push');
|
|
222
|
+
process.exitCode = 2;
|
|
223
|
+
return;
|
|
233
224
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
225
|
+
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`);
|
|
228
|
+
try {
|
|
229
|
+
const res = await (0, db_1.runDbPush)({ projectDir: dir });
|
|
230
|
+
if (res.status === 'NOOP') {
|
|
231
|
+
console.log(`${colors.dim}— ${res.message}${colors.reset}\n`);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
printSuccess(res.message);
|
|
235
|
+
if (res.appliedMigrations.length > 0) {
|
|
236
|
+
console.log(`${colors.dim}Applied:${colors.reset} ${res.appliedMigrations.join(', ')}`);
|
|
244
237
|
}
|
|
245
|
-
|
|
246
|
-
});
|
|
247
|
-
printSuccess(`Provider ready · ${manifest.id}`);
|
|
248
|
-
return { provider: 'e-remote', baseUrl: baseUrl.replace(/\/+$/, ''), timeoutMs: 5000 };
|
|
249
|
-
}
|
|
250
|
-
const registryUrl = (process.env.E_REGISTRY_URL || process.env.SIDURI_KNOWLEDGE_REGISTRY_URL || DEFAULT_REGISTRY_URL).replace(/\/+$/, '');
|
|
251
|
-
const pack = await chooseHubPack(registryUrl);
|
|
252
|
-
if (pack.distribution.kind === 'archive') {
|
|
253
|
-
const { install } = await inquirer_1.default.prompt({
|
|
254
|
-
type: 'confirm',
|
|
255
|
-
name: 'install',
|
|
256
|
-
message: `Install ${pack.id} v${pack.version} locally?`,
|
|
257
|
-
default: true,
|
|
258
|
-
});
|
|
259
|
-
if (install) {
|
|
260
|
-
const packPath = await withTask(`Installing ${pack.id}@${pack.version}`, () => installArchive(pack));
|
|
261
|
-
const loaded = await withTask('Validating installed knowledge pack', () => (0, e_knowledge_1.loadPack)(packPath));
|
|
262
|
-
printSuccess(`Knowledge ready · ${loaded.manifest.id} · revision ${loaded.revision.id}`);
|
|
263
|
-
return { provider: 'e-knowledge', packPath };
|
|
238
|
+
console.log();
|
|
264
239
|
}
|
|
240
|
+
process.exitCode = 0;
|
|
265
241
|
}
|
|
266
|
-
|
|
267
|
-
|
|
242
|
+
catch (err) {
|
|
243
|
+
console.error(`\n${colors.yellow}Database Migration Error:${colors.reset} ${err.message}\n`);
|
|
244
|
+
process.exitCode = 3;
|
|
268
245
|
}
|
|
269
|
-
const provider = (0, e_knowledge_1.createRemoteProvider)({ baseUrl: pack.distribution.url, timeoutMs: 5000, manifest: pack });
|
|
270
|
-
await withTask('Checking provider manifest', async () => {
|
|
271
|
-
if (provider.manifest) {
|
|
272
|
-
return provider.manifest();
|
|
273
|
-
}
|
|
274
|
-
const cleanUrl = pack.distribution.url.replace(/\/+$/, '');
|
|
275
|
-
const res = await fetch(`${cleanUrl}/manifest`, { headers: { accept: 'application/json' } });
|
|
276
|
-
if (res.ok)
|
|
277
|
-
return res.json();
|
|
278
|
-
return pack;
|
|
279
|
-
});
|
|
280
|
-
printSuccess(`Provider ready · ${pack.id}`);
|
|
281
|
-
return { provider: 'e-hub', registryUrl, packId: pack.id, timeoutMs: 5000 };
|
|
282
246
|
}
|
|
283
247
|
async function main() {
|
|
284
|
-
const
|
|
248
|
+
const args = process.argv.slice(2);
|
|
249
|
+
const command = args[0];
|
|
285
250
|
if (command === '--version' || command === '-v') {
|
|
286
251
|
console.log(CLI_VERSION);
|
|
287
252
|
return;
|
|
288
253
|
}
|
|
289
|
-
if (command === '
|
|
290
|
-
|
|
254
|
+
if (command === 'create') {
|
|
255
|
+
const targetDir = args[1];
|
|
256
|
+
await runCreateWizard(targetDir);
|
|
291
257
|
return;
|
|
292
258
|
}
|
|
293
|
-
if (command
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
259
|
+
if (command === 'doctor') {
|
|
260
|
+
const targetDir = args[1];
|
|
261
|
+
await runCliDoctor(targetDir);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (command === 'db') {
|
|
265
|
+
const subcommand = args[1];
|
|
266
|
+
const targetDir = args[2];
|
|
267
|
+
await runCliDb(subcommand, targetDir);
|
|
298
268
|
return;
|
|
299
269
|
}
|
|
300
270
|
printHeader();
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
{ name: 'SQLite (future)', value: 'sqlite', disabled: 'Coming soon' },
|
|
311
|
-
],
|
|
312
|
-
},
|
|
313
|
-
]);
|
|
314
|
-
const { memoryDeployment } = await inquirer_1.default.prompt({
|
|
315
|
-
type: 'list',
|
|
316
|
-
name: 'memoryDeployment',
|
|
317
|
-
message: 'PostgreSQL deployment?',
|
|
318
|
-
choices: [
|
|
319
|
-
{ name: 'Local PostgreSQL', value: 'local' },
|
|
320
|
-
{ name: 'Neon', value: 'neon' },
|
|
321
|
-
{ name: 'Supabase', value: 'supabase' },
|
|
322
|
-
{ name: 'Other PostgreSQL provider', value: 'other' },
|
|
323
|
-
],
|
|
324
|
-
});
|
|
325
|
-
const projectPath = node_path_1.default.resolve(process.cwd(), projectDirectoryName(answers.name));
|
|
326
|
-
await (0, promises_1.mkdir)(projectPath, { recursive: true });
|
|
327
|
-
printSuccess(`${answers.name} · PostgreSQL / ${memoryDeployment} · ${projectPath}`);
|
|
328
|
-
printSection('Brain · required');
|
|
329
|
-
const { brainProvider } = await inquirer_1.default.prompt({
|
|
330
|
-
type: 'list',
|
|
331
|
-
name: 'brainProvider',
|
|
332
|
-
message: 'Brain provider?',
|
|
333
|
-
choices: [
|
|
334
|
-
{ name: 'OpenRouter (managed model routing)', value: 'openrouter' },
|
|
335
|
-
{ name: 'OpenAI-compatible API (custom endpoint)', value: 'openai-compatible' },
|
|
336
|
-
],
|
|
337
|
-
});
|
|
338
|
-
const brain = brainProvider === 'openrouter'
|
|
339
|
-
? {
|
|
340
|
-
provider: 'openrouter',
|
|
341
|
-
model: (await inquirer_1.default.prompt({ type: 'input', name: 'model', message: 'Model ID:', default: 'openai/gpt-4o-mini', validate: nonEmpty })).model,
|
|
342
|
-
apiKeyEnv: 'OPENROUTER_API_KEY',
|
|
343
|
-
}
|
|
344
|
-
: await (async () => {
|
|
345
|
-
const values = await inquirer_1.default.prompt([
|
|
346
|
-
{ type: 'input', name: 'baseUrl', message: 'OpenAI-compatible API base URL:', default: 'http://127.0.0.1:1234/v1', validate: urlValue },
|
|
347
|
-
{ type: 'input', name: 'model', message: 'Model ID:', default: 'local-model', validate: nonEmpty },
|
|
348
|
-
{ type: 'input', name: 'apiKeyEnv', message: 'API key environment variable:', default: 'OPENAI_COMPATIBLE_API_KEY', validate: nonEmpty },
|
|
349
|
-
]);
|
|
350
|
-
return { provider: 'openai-compatible', ...values };
|
|
351
|
-
})();
|
|
352
|
-
printSuccess(`${brain.provider} · ${brain.model}`);
|
|
353
|
-
printSection('Optional organs');
|
|
354
|
-
const { voice } = await inquirer_1.default.prompt({
|
|
355
|
-
type: 'list',
|
|
356
|
-
name: 'voice',
|
|
357
|
-
message: 'Voice provider?',
|
|
358
|
-
choices: [
|
|
359
|
-
{ name: 'VOICEVOX', value: 'voicevox' },
|
|
360
|
-
{ name: 'Do not use voice', value: 'none' },
|
|
361
|
-
],
|
|
362
|
-
});
|
|
363
|
-
const knowledge = await configureKnowledge();
|
|
364
|
-
const remaining = await inquirer_1.default.prompt([
|
|
365
|
-
{ type: 'list', name: 'behavior', message: 'Behavior preset?', choices: [{ name: 'Calm', value: 'Calm' }, { name: 'Cheerful, Encouraging', value: 'Cheerful, Encouraging' }, { name: 'Do not use custom behavior', value: 'none' }] },
|
|
366
|
-
{ type: 'list', name: 'body', message: 'Body provider?', choices: [{ name: 'Live2D Body (Renderer-agnostic)', value: 'live2d' }, { name: 'Do not use body', value: 'none' }] },
|
|
367
|
-
{ type: 'list', name: 'vision', message: 'Vision provider?', choices: [{ name: 'OpenRouter vision', value: 'openrouter' }, { name: 'Do not use vision', value: 'none' }] },
|
|
368
|
-
]);
|
|
369
|
-
const config = {
|
|
370
|
-
id: 'default',
|
|
371
|
-
name: answers.name,
|
|
372
|
-
brain,
|
|
373
|
-
voice: { provider: voice, speakerId: 1 },
|
|
374
|
-
memory: { provider: answers.memoryEngine, deployment: memoryDeployment },
|
|
375
|
-
knowledge,
|
|
376
|
-
behavior: { provider: remaining.behavior === 'none' ? 'none' : 'active_self', preset: remaining.behavior },
|
|
377
|
-
body: { provider: remaining.body },
|
|
378
|
-
vision: { provider: remaining.vision, model: 'gpt-4-vision' },
|
|
379
|
-
};
|
|
380
|
-
const configPath = node_path_1.default.join(projectPath, 'siduri.config.json');
|
|
381
|
-
try {
|
|
382
|
-
await (0, promises_1.readFile)(configPath, 'utf8');
|
|
383
|
-
const { overwrite } = await inquirer_1.default.prompt({
|
|
384
|
-
type: 'confirm',
|
|
385
|
-
name: 'overwrite',
|
|
386
|
-
message: `${node_path_1.default.basename(configPath)} already exists. Replace it?`,
|
|
387
|
-
default: false,
|
|
388
|
-
});
|
|
389
|
-
if (!overwrite) {
|
|
390
|
-
console.log('Configuration left unchanged.');
|
|
271
|
+
console.log('Usage: npx @vxnus/siduri create [directory]');
|
|
272
|
+
console.log(' npx @vxnus/siduri doctor [directory]');
|
|
273
|
+
console.log(' npx @vxnus/siduri db push [directory]');
|
|
274
|
+
console.log(' npx @vxnus/siduri --version\n');
|
|
275
|
+
}
|
|
276
|
+
if (require.main === module) {
|
|
277
|
+
main().catch((error) => {
|
|
278
|
+
if (error && typeof error === 'object' && 'name' in error && error.name === 'ExitPromptError') {
|
|
279
|
+
console.log('\nOperation cancelled.');
|
|
391
280
|
return;
|
|
392
281
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
// New configuration.
|
|
396
|
-
}
|
|
397
|
-
printSection('Review');
|
|
398
|
-
console.log(` ${colors.dim}companion${colors.reset} ${config.name}`);
|
|
399
|
-
console.log(` ${colors.dim}brain${colors.reset} ${config.brain.provider} · ${config.brain.model}`);
|
|
400
|
-
console.log(` ${colors.dim}memory${colors.reset} ${config.memory.provider}`);
|
|
401
|
-
console.log(` ${colors.dim}voice${colors.reset} ${config.voice.provider}`);
|
|
402
|
-
console.log(` ${colors.dim}knowledge${colors.reset} ${config.knowledge.provider}`);
|
|
403
|
-
console.log(` ${colors.dim}behavior${colors.reset} ${config.behavior.provider}`);
|
|
404
|
-
console.log(` ${colors.dim}body${colors.reset} ${config.body.provider}`);
|
|
405
|
-
console.log(` ${colors.dim}vision${colors.reset} ${config.vision.provider}`);
|
|
406
|
-
const { confirm } = await inquirer_1.default.prompt({
|
|
407
|
-
type: 'confirm',
|
|
408
|
-
name: 'confirm',
|
|
409
|
-
message: 'Write this Siduri configuration?',
|
|
410
|
-
default: true,
|
|
282
|
+
console.error(`\n${colors.yellow}!${colors.reset} ${error instanceof Error ? error.message : error}`);
|
|
283
|
+
process.exitCode = 1;
|
|
411
284
|
});
|
|
412
|
-
if (!confirm) {
|
|
413
|
-
console.log('Configuration cancelled.');
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
await (0, promises_1.writeFile)(configPath, JSON.stringify(config, null, 2) + '\n', { mode: 0o600 });
|
|
417
|
-
printSuccess(`Configuration written · ${configPath}`);
|
|
418
|
-
await (0, promises_1.cp)(node_path_1.default.join(__dirname, 'runtime.js'), node_path_1.default.join(projectPath, 'siduri-runtime.js'));
|
|
419
|
-
await (0, promises_1.writeFile)(node_path_1.default.join(projectPath, 'package.json'), JSON.stringify({
|
|
420
|
-
name: projectDirectoryName(answers.name),
|
|
421
|
-
private: true,
|
|
422
|
-
version: '0.0.0',
|
|
423
|
-
scripts: { start: 'node siduri-runtime.js', dev: 'node siduri-runtime.js' },
|
|
424
|
-
dependencies: RUNTIME_DEPENDENCIES,
|
|
425
|
-
}, null, 2) + '\n', { mode: 0o600 });
|
|
426
|
-
const keyEnv = config.brain.apiKeyEnv || 'OPENROUTER_API_KEY';
|
|
427
|
-
await (0, promises_1.writeFile)(node_path_1.default.join(projectPath, '.env.example'), [
|
|
428
|
-
`${keyEnv}=`,
|
|
429
|
-
'DATABASE_URL=postgresql://postgres:postgres@localhost:5432/siduri',
|
|
430
|
-
'',
|
|
431
|
-
].join('\n'), { mode: 0o600 });
|
|
432
|
-
await withTask('Installing Siduri runtime dependencies', () => execFile('npm', ['install', '--no-audit', '--no-fund'], { cwd: projectPath }).then(() => undefined));
|
|
433
|
-
printSuccess(`Siduri instance ready · ${projectPath}`);
|
|
434
|
-
console.log(`${colors.dim}Next: cd ${projectDirectoryName(answers.name)} && npm run start${colors.reset}\n`);
|
|
435
285
|
}
|
|
436
|
-
main().catch((error) => {
|
|
437
|
-
if (error && typeof error === 'object' && 'name' in error && error.name === 'ExitPromptError') {
|
|
438
|
-
console.log('\nConfiguration cancelled.');
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
console.error(`\n${colors.yellow}!${colors.reset} ${error instanceof Error ? error.message : error}`);
|
|
442
|
-
process.exitCode = 1;
|
|
443
|
-
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface OrganEnvironmentVar {
|
|
2
|
+
name: string;
|
|
3
|
+
required?: boolean;
|
|
4
|
+
secret?: boolean;
|
|
5
|
+
default?: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface OrganServiceRequirement {
|
|
9
|
+
name: string;
|
|
10
|
+
kind: 'database' | 'http_service' | 'process' | string;
|
|
11
|
+
optional?: boolean;
|
|
12
|
+
description?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface OrganDatabaseRequirement {
|
|
15
|
+
engine: 'postgres' | 'sqlite' | string;
|
|
16
|
+
migrationsDir?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface OrganManifest {
|
|
19
|
+
name: string;
|
|
20
|
+
organType: string;
|
|
21
|
+
version: string;
|
|
22
|
+
displayName: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
entrypoint: string;
|
|
25
|
+
factory: string;
|
|
26
|
+
configKey: string;
|
|
27
|
+
configSchema: Record<string, any>;
|
|
28
|
+
environment: OrganEnvironmentVar[];
|
|
29
|
+
services: OrganServiceRequirement[];
|
|
30
|
+
database?: OrganDatabaseRequirement | null;
|
|
31
|
+
healthCheck?: string | null;
|
|
32
|
+
}
|
|
33
|
+
export declare function validateOrganManifest(manifest: unknown, sourcePath?: string): OrganManifest;
|